diff --git a/CHANGELOG.md b/CHANGELOG.md index e72c5a40..f5ff35ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [diagram] Ensure that the suggestion container position of the `AutoCompleteWidget` is rendered correctly [#304](https://github.com/eclipse-glsp/glsp-client/pull/304) - [feature] Extend `ToolPalette`/`CreateOperation` API to support rendering of preview/ghost elements when creating new nodes [#301](https://github.com/eclipse-glsp/glsp-client/pull/301) - [protocol] Fix a bug in `BaseJsonRpcClient` to ensure that it can handle multiple open diagram sessions [#307](https://github.com/eclipse-glsp/glsp-client/pull/307) +- [diagram] Restructure some tools to have a more common infrastructure and support helper lines [#306](https://github.com/eclipse-glsp/glsp-client/pull/306) ## [v2.0.0 - 14/10/2023](https://github.com/eclipse-glsp/glsp-client/releases/tag/v2.0.0) diff --git a/examples/workflow-glsp/src/workflow-diagram-module.ts b/examples/workflow-glsp/src/workflow-diagram-module.ts index b3d1f08f..5c32db65 100644 --- a/examples/workflow-glsp/src/workflow-diagram-module.ts +++ b/examples/workflow-glsp/src/workflow-diagram-module.ts @@ -19,18 +19,18 @@ import { DefaultTypes, DeleteElementContextMenuItemProvider, DiamondNodeView, + GCompartment, + GCompartmentView, GEdge, GGraph, GLSPProjectionView, + GLabel, + GLabelView, GridSnapper, LogLevel, RectangularNodeView, RevealNamedElementActionProvider, RoundedCornerNodeView, - GCompartment, - GCompartmentView, - GLabel, - GLabelView, StructureCompartmentView, TYPES, bindAsService, @@ -38,6 +38,7 @@ import { configureDefaultModelElements, configureModelElement, editLabelFeature, + helperLineModule, initializeDiagramContainer } from '@eclipse-glsp/client'; import 'balloon-css/balloon.min.css'; @@ -81,5 +82,5 @@ export function createWorkflowDiagramContainer(...containerConfiguration: Contai } export function initializeWorkflowDiagramContainer(container: Container, ...containerConfiguration: ContainerConfiguration): Container { - return initializeDiagramContainer(container, workflowDiagramModule, directTaskEditor, ...containerConfiguration); + return initializeDiagramContainer(container, workflowDiagramModule, directTaskEditor, helperLineModule, ...containerConfiguration); } diff --git a/examples/workflow-standalone/scripts/config.json b/examples/workflow-standalone/scripts/config.json index 4c3ec436..eae82b05 100644 --- a/examples/workflow-standalone/scripts/config.json +++ b/examples/workflow-standalone/scripts/config.json @@ -1,4 +1,4 @@ { "fileName": "workflow-server", - "version": "2.0.1" + "version": "next" } diff --git a/packages/client/css/glsp-sprotty.css b/packages/client/css/glsp-sprotty.css index 2fe87a97..8d17f5cb 100644 --- a/packages/client/css/glsp-sprotty.css +++ b/packages/client/css/glsp-sprotty.css @@ -46,6 +46,7 @@ max-width: 400px; min-width: 100px; z-index: 1; + pointer-events: none; } .sprotty-popup > div { diff --git a/packages/client/css/helper-lines.css b/packages/client/css/helper-lines.css new file mode 100644 index 00000000..07a7de0c --- /dev/null +++ b/packages/client/css/helper-lines.css @@ -0,0 +1,33 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +.helper-line { + pointer-events: none; + stroke: red; + stroke-width: 1px; + opacity: 1; +} + +.selection-bounds { + pointer-events: none; + fill: blue; + fill-opacity: 0.05; + stroke-linejoin: miter; + stroke-linecap: round; + stroke: darkblue; + stroke-width: 1px; + stroke-dasharray: 2; +} diff --git a/packages/client/src/base/auto-complete/index.ts b/packages/client/src/base/auto-complete/index.ts new file mode 100644 index 00000000..fb5f7b30 --- /dev/null +++ b/packages/client/src/base/auto-complete/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './auto-complete-actions'; +export * from './auto-complete-widget'; +export * from './autocomplete-suggestion-providers'; +export * from './base-autocomplete-palette'; +export * from './validation-decorator'; diff --git a/packages/client/src/base/feedback/index.ts b/packages/client/src/base/feedback/index.ts new file mode 100644 index 00000000..9a753e06 --- /dev/null +++ b/packages/client/src/base/feedback/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './css-feedback'; +export * from './feedback-action-dispatcher'; +export * from './feedback-command'; +export * from './update-model-command'; diff --git a/packages/client/src/base/focus/index.ts b/packages/client/src/base/focus/index.ts new file mode 100644 index 00000000..4de0e8fd --- /dev/null +++ b/packages/client/src/base/focus/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './focus-state-change-action'; +export * from './focus-tracker'; diff --git a/packages/client/src/base/index.ts b/packages/client/src/base/index.ts new file mode 100644 index 00000000..66976fb8 --- /dev/null +++ b/packages/client/src/base/index.ts @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './action-dispatcher'; +export * from './action-handler-registry'; +export * from './args-feature'; +export * from './auto-complete'; +export * from './command-stack'; +export * from './default.module'; +export * from './drag-aware-mouse-listener'; +export * from './editor-context-service'; +export * from './feedback'; +export * from './focus'; +export * from './model'; +export * from './ranked'; +export * from './selection-clearing-mouse-listener'; +export * from './selection-service'; +export * from './tool-manager'; +export * from './view'; diff --git a/packages/client/src/base/model/index.ts b/packages/client/src/base/model/index.ts new file mode 100644 index 00000000..75f2f155 --- /dev/null +++ b/packages/client/src/base/model/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './diagram-loader'; +export * from './glsp-model-source'; +export * from './model-initialization-constraint'; +export * from './model-registry'; diff --git a/packages/client/src/base/tool-manager/index.ts b/packages/client/src/base/tool-manager/index.ts new file mode 100644 index 00000000..b903332c --- /dev/null +++ b/packages/client/src/base/tool-manager/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './tool-manager'; +export * from './tool'; diff --git a/packages/client/src/base/view/index.ts b/packages/client/src/base/view/index.ts new file mode 100644 index 00000000..fb4efbe2 --- /dev/null +++ b/packages/client/src/base/view/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './key-tool'; +export * from './mouse-tool'; +export * from './view-registry'; diff --git a/packages/client/src/default-modules.ts b/packages/client/src/default-modules.ts index 8d07becf..e260d15f 100644 --- a/packages/client/src/default-modules.ts +++ b/packages/client/src/default-modules.ts @@ -48,11 +48,11 @@ import { layoutModule } from './features/layout/layout-module'; import { navigationModule } from './features/navigation/navigation-module'; import { routingModule } from './features/routing/routing-module'; import { selectModule } from './features/select/select-module'; -import { sourceModelWatcherModule } from './features/source-model-watcher/source-model-wacher-module'; +import { sourceModelWatcherModule } from './features/source-model-watcher/source-model-watcher-module'; import { statusModule } from './features/status/status-module'; import { svgMetadataModule } from './features/svg-metadata/svg-metadata-module'; import { toolPaletteModule } from './features/tool-palette/tool-palette-module'; -import { changeBoundsToolModule } from './features/tools/change-bounds/change-boounds-tool-module'; +import { changeBoundsToolModule } from './features/tools/change-bounds/change-bounds-tool-module'; import { deletionToolModule } from './features/tools/deletion/deletion-tool-module'; import { edgeCreationToolModule } from './features/tools/edge-creation/edege-creation-module'; import { edgeEditToolModule } from './features/tools/edge-edit/edge-edit-module'; diff --git a/packages/client/src/features/accessibility/edge-autocomplete/index.ts b/packages/client/src/features/accessibility/edge-autocomplete/index.ts new file mode 100644 index 00000000..b6f9eee5 --- /dev/null +++ b/packages/client/src/features/accessibility/edge-autocomplete/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './action'; +export * from './edge-autocomplete-context'; +export * from './edge-autocomplete-palette'; +export * from './edge-autocomplete-tool'; diff --git a/packages/client/src/features/accessibility/element-navigation/index.ts b/packages/client/src/features/accessibility/element-navigation/index.ts new file mode 100644 index 00000000..2f30cdae --- /dev/null +++ b/packages/client/src/features/accessibility/element-navigation/index.ts @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './diagram-navigation-tool'; +export * from './element-navigation-module'; +export * from './element-navigator'; +export * from './left-right-top-bottom-navigator'; +export * from './local-element-navigator'; +export * from './position-navigator'; diff --git a/packages/client/src/features/accessibility/focus-tracker/index.ts b/packages/client/src/features/accessibility/focus-tracker/index.ts new file mode 100644 index 00000000..aefa9eaa --- /dev/null +++ b/packages/client/src/features/accessibility/focus-tracker/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './focus-tracker-module'; +export * from './focus-tracker-tool'; diff --git a/packages/client/src/features/accessibility/index.ts b/packages/client/src/features/accessibility/index.ts new file mode 100644 index 00000000..8135cf7c --- /dev/null +++ b/packages/client/src/features/accessibility/index.ts @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './accessibility-module'; +export * from './actions'; +export * from './edge-autocomplete'; +export * from './element-navigation'; +export * from './focus-tracker'; +export * from './global-keylistener-tool'; +export * from './key-shortcut'; +export * from './keyboard-grid'; +export * from './keyboard-pointer'; +export * from './keyboard-tool-palette'; +export * from './move-zoom'; +export * from './resize-key-tool'; +export * from './search'; +export * from './toast'; +export * from './view-key-tools'; diff --git a/packages/client/src/features/accessibility/key-shortcut/index.ts b/packages/client/src/features/accessibility/key-shortcut/index.ts new file mode 100644 index 00000000..8ff23f3e --- /dev/null +++ b/packages/client/src/features/accessibility/key-shortcut/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './accessible-key-shortcut-tool'; +export * from './accessible-key-shortcut'; +export * from './di.config'; diff --git a/packages/client/src/features/accessibility/keyboard-grid/index.ts b/packages/client/src/features/accessibility/keyboard-grid/index.ts new file mode 100644 index 00000000..d3b3f2e7 --- /dev/null +++ b/packages/client/src/features/accessibility/keyboard-grid/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './action'; +export * from './constants'; +export * from './keyboard-grid-search-palette'; +export * from './keyboard-grid'; +export * from './keyboard-node-grid'; diff --git a/packages/client/src/features/accessibility/keyboard-pointer/index.ts b/packages/client/src/features/accessibility/keyboard-pointer/index.ts new file mode 100644 index 00000000..6ec68518 --- /dev/null +++ b/packages/client/src/features/accessibility/keyboard-pointer/index.ts @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './actions'; +export * from './constants'; +export * from './keyboard-pointer-listener'; +export * from './keyboard-pointer-module'; +export * from './keyboard-pointer-position'; +export * from './keyboard-pointer'; diff --git a/packages/client/src/features/accessibility/keyboard-tool-palette/index.ts b/packages/client/src/features/accessibility/keyboard-tool-palette/index.ts new file mode 100644 index 00000000..f49dcc4a --- /dev/null +++ b/packages/client/src/features/accessibility/keyboard-tool-palette/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './keyboard-tool-palette-module'; +export * from './keyboard-tool-palette'; diff --git a/packages/client/src/features/accessibility/move-zoom/index.ts b/packages/client/src/features/accessibility/move-zoom/index.ts new file mode 100644 index 00000000..55c71714 --- /dev/null +++ b/packages/client/src/features/accessibility/move-zoom/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './move-handler'; +export * from './move-zoom-module'; +export * from './zoom-handler'; diff --git a/packages/client/src/features/accessibility/resize-key-tool/index.ts b/packages/client/src/features/accessibility/resize-key-tool/index.ts new file mode 100644 index 00000000..60481ad5 --- /dev/null +++ b/packages/client/src/features/accessibility/resize-key-tool/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './resize-key-handler'; +export * from './resize-key-module'; +export * from './resize-key-tool'; diff --git a/packages/client/src/features/accessibility/search/index.ts b/packages/client/src/features/accessibility/search/index.ts new file mode 100644 index 00000000..7cf637d6 --- /dev/null +++ b/packages/client/src/features/accessibility/search/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './search-palette-module'; +export * from './search-palette'; +export * from './search-tool'; diff --git a/packages/client/src/features/accessibility/toast/index.ts b/packages/client/src/features/accessibility/toast/index.ts new file mode 100644 index 00000000..a15b2551 --- /dev/null +++ b/packages/client/src/features/accessibility/toast/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './toast-handler'; +export * from './toast-module'; +export * from './toast-tool'; diff --git a/packages/client/src/features/accessibility/view-key-tools/index.ts b/packages/client/src/features/accessibility/view-key-tools/index.ts new file mode 100644 index 00000000..93337004 --- /dev/null +++ b/packages/client/src/features/accessibility/view-key-tools/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './deselect-key-tool'; +export * from './movement-key-tool'; +export * from './view-key-tools-module'; +export * from './zoom-key-tool'; diff --git a/packages/client/src/features/accessibility/view-key-tools/movement-key-tool.ts b/packages/client/src/features/accessibility/view-key-tools/movement-key-tool.ts index 770caaab..ddee9974 100644 --- a/packages/client/src/features/accessibility/view-key-tools/movement-key-tool.ts +++ b/packages/client/src/features/accessibility/view-key-tools/movement-key-tool.ts @@ -14,13 +14,13 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { Action, GModelElement, ISnapper, KeyListener, KeyTool, Point, TYPES } from '@eclipse-glsp/sprotty'; +import { Action, GModelElement, ISnapper, KeyListener, KeyTool, TYPES } from '@eclipse-glsp/sprotty'; import { inject, injectable, optional } from 'inversify'; import { matchesKeystroke } from 'sprotty/lib/utils/keyboard'; import { GLSPActionDispatcher } from '../../../base/action-dispatcher'; import { SelectionService } from '../../../base/selection-service'; import { Tool } from '../../../base/tool-manager/tool'; -import { GridSnapper } from '../../change-bounds/snap'; +import { GridSnapper, unsnapModifier, useSnap } from '../../change-bounds/snap'; import { AccessibleKeyShortcutProvider, SetAccessibleKeyShortcutAction } from '../key-shortcut/accessible-key-shortcut'; import { MoveElementAction, MoveViewportAction } from '../move-zoom/move-handler'; @@ -63,18 +63,14 @@ export class MoveKeyListener extends KeyListener implements AccessibleKeyShortcu protected readonly token = MoveKeyListener.name; + protected grid = { x: MoveKeyListener.defaultMoveX, y: MoveKeyListener.defaultMoveY }; + constructor(protected readonly tool: MovementKeyTool) { super(); - } - protected get grid(): Point { if (this.tool.snapper instanceof GridSnapper) { - return this.tool.snapper.grid; + this.grid = this.tool.snapper.grid; } - return { - x: MoveKeyListener.defaultMoveX, - y: MoveKeyListener.defaultMoveY - }; } registerShortcutKey(): void { @@ -86,9 +82,9 @@ export class MoveKeyListener extends KeyListener implements AccessibleKeyShortcu ); } - override keyDown(element: GModelElement, event: KeyboardEvent): Action[] { + override keyDown(_element: GModelElement, event: KeyboardEvent): Action[] { const selectedElementIds = this.tool.selectionService.getSelectedElementIDs(); - const snap = !event.altKey; + const snap = useSnap(event); const offsetX = snap ? this.grid.x : 1; const offsetY = snap ? this.grid.y : 1; @@ -104,31 +100,31 @@ export class MoveKeyListener extends KeyListener implements AccessibleKeyShortcu } } else { if (this.matchesMoveUpKeystroke(event)) { - return [MoveViewportAction.create(0, -this.grid.y)]; + return [MoveViewportAction.create(0, -offsetY)]; } else if (this.matchesMoveDownKeystroke(event)) { - return [MoveViewportAction.create(0, this.grid.y)]; + return [MoveViewportAction.create(0, offsetY)]; } else if (this.matchesMoveRightKeystroke(event)) { - return [MoveViewportAction.create(this.grid.x, 0)]; + return [MoveViewportAction.create(offsetX, 0)]; } else if (this.matchesMoveLeftKeystroke(event)) { - return [MoveViewportAction.create(-this.grid.x, 0)]; + return [MoveViewportAction.create(-offsetX, 0)]; } } return []; } protected matchesMoveUpKeystroke(event: KeyboardEvent): boolean { - return matchesKeystroke(event, 'ArrowUp') || matchesKeystroke(event, 'ArrowUp', 'alt'); + return matchesKeystroke(event, 'ArrowUp') || matchesKeystroke(event, 'ArrowUp', unsnapModifier()); } protected matchesMoveDownKeystroke(event: KeyboardEvent): boolean { - return matchesKeystroke(event, 'ArrowDown') || matchesKeystroke(event, 'ArrowDown', 'alt'); + return matchesKeystroke(event, 'ArrowDown') || matchesKeystroke(event, 'ArrowDown', unsnapModifier()); } protected matchesMoveRightKeystroke(event: KeyboardEvent): boolean { - return matchesKeystroke(event, 'ArrowRight') || matchesKeystroke(event, 'ArrowRight', 'alt'); + return matchesKeystroke(event, 'ArrowRight') || matchesKeystroke(event, 'ArrowRight', unsnapModifier()); } protected matchesMoveLeftKeystroke(event: KeyboardEvent): boolean { - return matchesKeystroke(event, 'ArrowLeft') || matchesKeystroke(event, 'ArrowLeft', 'alt'); + return matchesKeystroke(event, 'ArrowLeft') || matchesKeystroke(event, 'ArrowLeft', unsnapModifier()); } } diff --git a/packages/client/src/features/bounds/bounds-module.ts b/packages/client/src/features/bounds/bounds-module.ts index 239472ef..cf86680a 100644 --- a/packages/client/src/features/bounds/bounds-module.ts +++ b/packages/client/src/features/bounds/bounds-module.ts @@ -26,12 +26,13 @@ import { configureCommand, configureLayout } from '@eclipse-glsp/sprotty'; +import { PositionSnapper } from '../change-bounds/position-snapper'; import { FreeFormLayouter } from './freeform-layout'; import { GLSPHiddenBoundsUpdater } from './glsp-hidden-bounds-updater'; import { HBoxLayouterExt } from './hbox-layout'; import { LayouterExt } from './layouter'; import { LocalComputedBoundsCommand } from './local-bounds'; -import { SetBoundsFeebackCommand } from './set-bounds-feedback-command'; +import { SetBoundsFeedbackCommand } from './set-bounds-feedback-command'; import { VBoxLayouterExt } from './vbox-layout'; export const boundsModule = new FeatureModule((bind, _unbind, isBound, _rebind) => { @@ -42,7 +43,7 @@ export const boundsModule = new FeatureModule((bind, _unbind, isBound, _rebind) bindAsService(context, TYPES.HiddenVNodePostprocessor, GLSPHiddenBoundsUpdater); configureCommand(context, LocalComputedBoundsCommand); - configureCommand(context, SetBoundsFeebackCommand); + configureCommand(context, SetBoundsFeedbackCommand); bind(TYPES.Layouter).to(LayouterExt).inSingletonScope(); bind(TYPES.LayoutRegistry).to(LayoutRegistry).inSingletonScope(); @@ -50,4 +51,6 @@ export const boundsModule = new FeatureModule((bind, _unbind, isBound, _rebind) configureLayout(context, VBoxLayouter.KIND, VBoxLayouterExt); configureLayout(context, HBoxLayouter.KIND, HBoxLayouterExt); configureLayout(context, FreeFormLayouter.KIND, FreeFormLayouter); + + bind(PositionSnapper).toSelf(); }); diff --git a/packages/client/src/features/bounds/index.ts b/packages/client/src/features/bounds/index.ts new file mode 100644 index 00000000..b35779b0 --- /dev/null +++ b/packages/client/src/features/bounds/index.ts @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './bounds-module'; +export * from './freeform-layout'; +export * from './glsp-hidden-bounds-updater'; +export * from './hbox-layout'; +export * from './layouter'; +export * from './local-bounds'; +export * from './set-bounds-feedback-command'; +export * from './vbox-layout'; diff --git a/packages/client/src/features/bounds/local-bounds.ts b/packages/client/src/features/bounds/local-bounds.ts index 82e6c8ac..699e343b 100644 --- a/packages/client/src/features/bounds/local-bounds.ts +++ b/packages/client/src/features/bounds/local-bounds.ts @@ -25,7 +25,8 @@ import { GModelRoot, GModelRootSchema, RequestBoundsAction, - TYPES + TYPES, + ViewerOptions } from '@eclipse-glsp/sprotty'; import { inject, injectable } from 'inversify'; import { ServerAction } from '../../base/model/glsp-model-source'; @@ -63,6 +64,7 @@ export class LocalComputedBoundsCommand extends Command { static readonly KIND: string = ComputedBoundsAction.KIND; @inject(ComputedBoundsApplicator) protected readonly computedBoundsApplicator: ComputedBoundsApplicator; + @inject(TYPES.ViewerOptions) protected readonly viewerOptions: ViewerOptions; constructor(@inject(TYPES.Action) readonly action: ComputedBoundsAction) { super(); @@ -70,10 +72,14 @@ export class LocalComputedBoundsCommand extends Command { override execute(context: CommandExecutionContext): GModelRoot | CommandResult { if (LocalComputedBoundsAction.is(this.action)) { + if (!this.viewerOptions.needsClientLayout) { + return context.root; + } // apply computed bounds from the hidden model and return updated model to render new main model this.computedBoundsApplicator.apply(context.root as unknown as GModelRootSchema, this.action); return context.root; } + // computed bounds action from server -> we do not care and do not trigger any update of the main model return { model: context.root, diff --git a/packages/client/src/features/bounds/set-bounds-feedback-command.ts b/packages/client/src/features/bounds/set-bounds-feedback-command.ts index c8cd3312..5f7f2b02 100644 --- a/packages/client/src/features/bounds/set-bounds-feedback-command.ts +++ b/packages/client/src/features/bounds/set-bounds-feedback-command.ts @@ -45,7 +45,7 @@ export namespace SetBoundsFeedbackAction { } @injectable() -export class SetBoundsFeebackCommand extends SetBoundsCommand implements FeedbackCommand { +export class SetBoundsFeedbackCommand extends SetBoundsCommand implements FeedbackCommand { static override readonly KIND: string = SetBoundsFeedbackAction.KIND; readonly priority: number = 0; diff --git a/packages/client/src/features/change-bounds/index.ts b/packages/client/src/features/change-bounds/index.ts new file mode 100644 index 00000000..85d8b38f --- /dev/null +++ b/packages/client/src/features/change-bounds/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './model'; +export * from './movement-restrictor'; +export * from './point-position-updater'; +export * from './position-snapper'; +export * from './snap'; diff --git a/packages/client/src/features/change-bounds/model.ts b/packages/client/src/features/change-bounds/model.ts index d0242419..506af697 100644 --- a/packages/client/src/features/change-bounds/model.ts +++ b/packages/client/src/features/change-bounds/model.ts @@ -62,12 +62,28 @@ export class SResizeHandle extends GChildElement implements Hoverable { return feature === hoverFeedbackFeature; } + isNwResize(): boolean { + return this.location === ResizeHandleLocation.TopLeft; + } + + isSeResize(): boolean { + return this.location === ResizeHandleLocation.BottomRight; + } + + isNeResize(): boolean { + return this.location === ResizeHandleLocation.TopRight; + } + + isSwResize(): boolean { + return this.location === ResizeHandleLocation.BottomLeft; + } + isNwSeResize(): boolean { - return this.location === ResizeHandleLocation.TopLeft || this.location === ResizeHandleLocation.BottomRight; + return this.isNwResize() || this.isSeResize(); } isNeSwResize(): boolean { - return this.location === ResizeHandleLocation.TopRight || this.location === ResizeHandleLocation.BottomLeft; + return this.isNeResize() || this.isSwResize(); } } diff --git a/packages/client/src/features/change-bounds/point-position-updater.ts b/packages/client/src/features/change-bounds/point-position-updater.ts new file mode 100644 index 00000000..244de68f --- /dev/null +++ b/packages/client/src/features/change-bounds/point-position-updater.ts @@ -0,0 +1,120 @@ +/******************************************************************************** + * Copyright (c) 2020-2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +/* eslint-disable @typescript-eslint/no-shadow */ +import { GModelElement, ISnapper, Point, Writable } from '@eclipse-glsp/sprotty'; +import { calculateDeltaBetweenPoints } from '../../utils/gmodel-util'; +import { isMouseEvent } from '../../utils/html-utils'; +import { IHelperLineManager } from '../helper-lines/helper-line-manager'; +import { Direction, getDirectionOf } from '../helper-lines/model'; +import { PositionSnapper } from './position-snapper'; +import { useSnap } from './snap'; + +/** + * This class can be used to calculate the current position, when an element is + * moved. This includes node movements, node resizing (resize handle movement) + * or edge routing-point movements. + * + * You can initialize a this class with a optional {@link ISnapper}. If a + * snapper is present, the positions will be snapped to the defined grid. + */ +export class PointPositionUpdater { + protected positionSnapper: PositionSnapper; + protected lastDragPosition?: Point; + protected positionDelta: Writable = { x: 0, y: 0 }; + + constructor(snapper?: PositionSnapper); + constructor(snapper?: ISnapper, helperLineManager?: IHelperLineManager); + constructor(first?: PositionSnapper | ISnapper, helperLineManager?: IHelperLineManager) { + this.positionSnapper = first instanceof PositionSnapper ? first : new PositionSnapper(first, helperLineManager); + } + + /** + * Init the position with the {@link Point} of your mouse cursor. + * This method is normally called in the `mouseDown` event. + * @param mousePosition current mouse position e.g `{x: event.pageX, y: event.pageY }` + */ + public updateLastDragPosition(mousePosition: Point): void; + public updateLastDragPosition(mouseEvent: MouseEvent): void; + public updateLastDragPosition(first: Point | MouseEvent): void { + this.lastDragPosition = isMouseEvent(first) ? { x: first.pageX, y: first.pageY } : first; + } + + /** + * Check if the mouse is currently not in a drag mode. + * @returns true if the last drag position is undefined + */ + public isLastDragPositionUndefined(): boolean { + return this.lastDragPosition === undefined; + } + + /** + * Reset the updater for new movements. + * This method is normally called in the `mouseUp` event. + */ + public resetPosition(): void { + this.lastDragPosition = undefined; + this.positionDelta = { x: 0, y: 0 }; + } + + /** + * Calculate the current position of your movement. + * This method is normally called in the `mouseMove` event. + * @param target node which is moved around + * @param mousePosition current mouse position e.g `{x: event.pageX, y: event.pageY }` + * @param useSnap if a snapper is defined you can disable it, e.g when a specific key is pressed `!event.shiftKey` + * @param direction the direction in which the position is updated, will be calculated if not provided + * @returns delta to previous position or undefined if no delta should be applied + */ + public updatePosition(target: GModelElement, mousePosition: Point, useSnap: boolean, direction?: Direction[]): Point | undefined; + public updatePosition(target: GModelElement, mouseEvent: MouseEvent, direction?: Direction[]): Point | undefined; + public updatePosition( + target: GModelElement, + second: Point | MouseEvent, + third?: boolean | Direction[], + fourth?: Direction[] + ): Point | undefined { + if (!this.lastDragPosition) { + return undefined; + } + const mousePosition = isMouseEvent(second) ? { x: second.pageX, y: second.pageY } : second; + const shouldSnap = typeof third === 'boolean' ? third : useSnap(second as MouseEvent); + const direction = typeof third !== 'boolean' ? third : fourth; + + // calculate update to last drag position + const deltaToLastPosition = calculateDeltaBetweenPoints(mousePosition, this.lastDragPosition, target); + this.lastDragPosition = mousePosition; + if (Point.equals(deltaToLastPosition, Point.ORIGIN)) { + return undefined; + } + + // accumulate position delta with latest delta + this.positionDelta.x += deltaToLastPosition.x; + this.positionDelta.y += deltaToLastPosition.y; + + const directions = direction ?? getDirectionOf(this.positionDelta); + + // only send update if the position actually changes + // otherwise accumulate delta until we get to an update + const positionUpdate = this.positionSnapper.snapDelta(this.positionDelta, target, shouldSnap, directions); + if (Point.equals(positionUpdate, Point.ORIGIN)) { + return undefined; + } + // we update our position so we update our delta by the snapped position + this.positionDelta.x -= positionUpdate.x; + this.positionDelta.y -= positionUpdate.y; + return positionUpdate; + } +} diff --git a/packages/client/src/features/change-bounds/position-snapper.ts b/packages/client/src/features/change-bounds/position-snapper.ts new file mode 100644 index 00000000..adaf34ba --- /dev/null +++ b/packages/client/src/features/change-bounds/position-snapper.ts @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { GModelElement, ISnapper, Point, TYPES, Writable } from '@eclipse-glsp/sprotty'; +import { inject, injectable, optional } from 'inversify'; +import { IHelperLineManager } from '../helper-lines/helper-line-manager'; +import { Direction, HelperLine, isHelperLine } from '../helper-lines/model'; + +@injectable() +export class PositionSnapper { + constructor( + @optional() @inject(TYPES.ISnapper) protected snapper?: ISnapper, + @optional() @inject(TYPES.IHelperLineManager) protected helperLineManager?: IHelperLineManager + ) {} + + snapPosition(position: Point, element: GModelElement, isSnap: boolean = true): Point { + return isSnap && this.snapper ? this.snapper.snap(position, element) : { x: position.x, y: position.y }; + } + + snapDelta(positionDelta: Point, element: GModelElement, isSnap: boolean, directions: Direction[]): Point { + const delta: Writable = this.snapPosition(positionDelta, element, isSnap); + const minimumDelta = this.getMinimumDelta(element, isSnap, directions); + if (!minimumDelta) { + return delta; + } + delta.x = Math.abs(delta.x) >= minimumDelta.x ? delta.x : 0; + delta.y = Math.abs(delta.y) >= minimumDelta.y ? delta.y : 0; + return delta; + } + + protected getMinimumDelta(target: GModelElement, isSnap: boolean, directions: Direction[]): Point | undefined { + return this.getHelperLineMinimum(target, isSnap, directions); + } + + protected getHelperLineMinimum(target: GModelElement, isSnap: boolean, directions: Direction[]): Point | undefined { + if (!this.helperLineManager) { + return undefined; + } + const helperLines = target.root.children.filter(child => isHelperLine(child)) as HelperLine[]; + if (helperLines.length === 0) { + return undefined; + } + + const minimum: Writable = { x: 0, y: 0 }; + if (directions.includes(Direction.Left) && helperLines.some(line => line.isLeft || line.isCenter)) { + minimum.x = this.helperLineManager.getHelperLineSnapping(target, isSnap, Direction.Left); + } else if (directions.includes(Direction.Right) && helperLines.some(line => line.isRight || line.isCenter)) { + minimum.x = this.helperLineManager.getHelperLineSnapping(target, isSnap, Direction.Right); + } + if (directions.includes(Direction.Up) && helperLines.some(line => line.isTop || line.isMiddle)) { + minimum.y = this.helperLineManager.getHelperLineSnapping(target, isSnap, Direction.Up); + } else if (directions.includes(Direction.Down) && helperLines.some(line => line.isBottom || line.isMiddle)) { + minimum.y = this.helperLineManager.getHelperLineSnapping(target, isSnap, Direction.Down); + } + return minimum; + } +} diff --git a/packages/client/src/features/change-bounds/snap.spec.ts b/packages/client/src/features/change-bounds/snap.spec.ts index ff30aace..ca6a82e8 100644 --- a/packages/client/src/features/change-bounds/snap.spec.ts +++ b/packages/client/src/features/change-bounds/snap.spec.ts @@ -14,9 +14,10 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { expect } from 'chai'; import { GModelElement } from '@eclipse-glsp/sprotty'; -import { GridSnapper, PointPositionUpdater } from './snap'; +import { expect } from 'chai'; +import { PointPositionUpdater } from './point-position-updater'; +import { GridSnapper } from './snap'; describe('GridSnapper', () => { it('snap', () => { diff --git a/packages/client/src/features/change-bounds/snap.ts b/packages/client/src/features/change-bounds/snap.ts index 7d81c488..36f218ca 100644 --- a/packages/client/src/features/change-bounds/snap.ts +++ b/packages/client/src/features/change-bounds/snap.ts @@ -13,8 +13,9 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +/* eslint-disable @typescript-eslint/no-shadow */ +import { GModelElement, ISnapper, KeyboardModifier, Point } from '@eclipse-glsp/sprotty'; import { injectable } from 'inversify'; -import { ISnapper, Point, GModelElement, Writable, findParentByFeature, isViewport } from '@eclipse-glsp/sprotty'; /** * A {@link ISnapper} implementation that snaps all elements onto a fixed gride size. @@ -30,7 +31,7 @@ import { ISnapper, Point, GModelElement, Writable, findParentByFeature, isViewpo export class GridSnapper implements ISnapper { constructor(public grid: { x: number; y: number } = { x: 10, y: 10 }) {} - snap(position: Point, element: GModelElement): Point { + snap(position: Point, _element: GModelElement): Point { return { x: Math.round(position.x / this.grid.x) * this.grid.x, y: Math.round(position.y / this.grid.y) * this.grid.y @@ -38,85 +39,10 @@ export class GridSnapper implements ISnapper { } } -/** - * This class can be used to calculate the current position, when an element is - * moved. This includes node movements, node resizing (resize handle movement) - * or edge routing-point movements. - * - * You can initialize a this class with a optional {@link ISnapper}. If a - * snapper is present, the positions will be snapped to the defined grid. - */ -export class PointPositionUpdater { - protected lastDragPosition?: Point; - protected positionDelta: Writable = { x: 0, y: 0 }; - - constructor(protected snapper?: ISnapper) {} - - /** - * Init the position with the {@link Point} of your mouse cursor. - * This method is normally called in the `mouseDown` event. - * @param mousePosition current mouse position e.g `{x: event.pageX, y: event.pageY }` - */ - public updateLastDragPosition(mousePosition: Point): void { - this.lastDragPosition = mousePosition; - } - - /** - * Check if the mouse is currently not in a drag mode. - * @returns true if the last drag position is undefined - */ - public isLastDragPositionUndefined(): boolean { - return this.lastDragPosition === undefined; - } - - /** - * Reset the updater for new movements. - * This method is normally called in the `mouseUp` event. - */ - public resetPosition(): void { - this.lastDragPosition = undefined; - this.positionDelta = { x: 0, y: 0 }; - } - - /** - * Calculate the current position of your movement. - * This method is normally called in the `mouseMove` event. - * @param target node which is moved around - * @param mousePosition current mouse position e.g `{x: event.pageX, y: event.pageY }` - * @param isSnapEnabled if a snapper is defined you can disable it, e.g when a specific key is pressed `!event.altKey` - * @returns current position or undefined if updater has no last drag position initialized - */ - public updatePosition(target: GModelElement, mousePosition: Point, isSnapEnabled: boolean): Point | undefined { - if (this.lastDragPosition) { - const newDragPosition = mousePosition; - - const viewport = findParentByFeature(target, isViewport); - const zoom = viewport?.zoom ?? 1; - const dx = (mousePosition.x - this.lastDragPosition.x) / zoom; - const dy = (mousePosition.y - this.lastDragPosition.y) / zoom; - const deltaToLastPosition = { x: dx, y: dy }; - this.lastDragPosition = newDragPosition; - - // update position delta with latest delta - this.positionDelta.x += deltaToLastPosition.x; - this.positionDelta.y += deltaToLastPosition.y; - - // snap our delta and only send update if the position actually changes - // otherwise accumulate delta until we do snap to an update - const positionUpdate = this.snap(this.positionDelta, target, isSnapEnabled); - if (positionUpdate.x === 0 && positionUpdate.y === 0) { - return undefined; - } - - // we update our position so we update our delta by the snapped position - this.positionDelta.x -= positionUpdate.x; - this.positionDelta.y -= positionUpdate.y; - return positionUpdate; - } - return undefined; - } +export function useSnap(event: MouseEvent | KeyboardEvent): boolean { + return !event.shiftKey; +} - protected snap(position: Point, element: GModelElement, isSnap: boolean): Point { - return isSnap && this.snapper ? this.snapper.snap(position, element) : { x: position.x, y: position.y }; - } +export function unsnapModifier(): KeyboardModifier { + return 'shift'; } diff --git a/packages/client/src/features/command-palette/index.ts b/packages/client/src/features/command-palette/index.ts new file mode 100644 index 00000000..a1ce8439 --- /dev/null +++ b/packages/client/src/features/command-palette/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './command-palette-module'; +export * from './command-palette-tool'; +export * from './server-command-palette-provider'; diff --git a/packages/client/src/features/context-menu/index.ts b/packages/client/src/features/context-menu/index.ts new file mode 100644 index 00000000..5cf038b6 --- /dev/null +++ b/packages/client/src/features/context-menu/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './context-menu-module'; +export * from './delete-element-context-menu'; +export * from './glsp-context-menu-mouse-listener'; +export * from './server-context-menu-provider'; diff --git a/packages/client/src/features/copy-paste/index.ts b/packages/client/src/features/copy-paste/index.ts new file mode 100644 index 00000000..e533d256 --- /dev/null +++ b/packages/client/src/features/copy-paste/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './copy-paste-context-menu'; +export * from './copy-paste-handler'; +export * from './copy-paste-modules'; +export * from './copy-paste-standalone'; diff --git a/packages/client/src/features/decoration/index.ts b/packages/client/src/features/decoration/index.ts new file mode 100644 index 00000000..d9948633 --- /dev/null +++ b/packages/client/src/features/decoration/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './decoration-module'; +export * from './decoration-placer'; diff --git a/packages/client/src/features/element-template/index.ts b/packages/client/src/features/element-template/index.ts new file mode 100644 index 00000000..841060fd --- /dev/null +++ b/packages/client/src/features/element-template/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './add-template-element'; +export * from './element-template-module'; +export * from './mouse-tracking-element-position-listener'; +export * from './remove-template-element'; diff --git a/packages/client/src/features/element-template/mouse-tracking-element-position-listener.ts b/packages/client/src/features/element-template/mouse-tracking-element-position-listener.ts index a79a4109..a137aa79 100644 --- a/packages/client/src/features/element-template/mouse-tracking-element-position-listener.ts +++ b/packages/client/src/features/element-template/mouse-tracking-element-position-listener.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { Action, Disposable, GModelElement, ISnapper, MoveAction, Point, isBoundsAware, isMoveable } from '@eclipse-glsp/sprotty'; +import { Action, Disposable, GModelElement, Locateable, MoveAction, Point, isBoundsAware, isMoveable } from '@eclipse-glsp/sprotty'; import { injectable } from 'inversify'; import { DragAwareMouseListener } from '../../base/drag-aware-mouse-listener'; import { CSS_HIDDEN, ModifyCSSFeedbackAction } from '../../base/feedback/css-feedback'; @@ -26,9 +26,12 @@ import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from '../change-bounds/movement-restrictor'; +import { PositionSnapper } from '../change-bounds/position-snapper'; +import { PointPositionUpdater } from '../change-bounds/point-position-updater'; +import { useSnap } from '../change-bounds/snap'; export interface PositioningTool extends Tool { - readonly snapper?: ISnapper; + readonly positionSnapper: PositionSnapper; readonly movementRestrictor?: IMovementRestrictor; registerFeedback(feedbackActions: Action[], feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): Disposable; @@ -37,57 +40,61 @@ export interface PositioningTool extends Tool { @injectable() export class MouseTrackingElementPositionListener extends DragAwareMouseListener { - element?: GModelElement; currentPosition?: Point; + protected positionUpdater: PointPositionUpdater; + constructor( protected elementId: string, protected tool: PositioningTool, protected cursorPosition: 'top-left' | 'middle' = 'top-left' ) { super(); + this.positionUpdater = new PointPositionUpdater(this.tool.positionSnapper); } override mouseMove(target: GModelElement, event: MouseEvent): Action[] { super.mouseMove(target, event); const element = target.root.index.getById(this.elementId); - this.element = element; - if (!element) { + if (!element || !isMoveable(element)) { return []; } - - let newPosition = getAbsolutePosition(target, event); - if (this.cursorPosition === 'middle' && isBoundsAware(element)) { - newPosition = Point.subtract(newPosition, { x: element.bounds.width / 2, y: element.bounds.height / 2 }); + let targetPosition = this.getTargetPosition(target, event, element); + if (this.positionUpdater.isLastDragPositionUndefined()) { + this.positionUpdater.updateLastDragPosition(targetPosition); } - newPosition = this.snap(newPosition, element); - - const finished = false; - if (isMoveable(element)) { - newPosition = this.validateMove(this.currentPosition ?? newPosition, newPosition, element, finished); + const delta = this.positionUpdater.updatePosition(element, targetPosition, useSnap(event)); + if (!delta) { + return []; } - this.currentPosition = newPosition; + targetPosition = this.validateMove(this.currentPosition ?? targetPosition, targetPosition, element, false); const moveGhostElement = MoveAction.create( [ { elementId: element.id, - toPosition: newPosition + fromPosition: this.currentPosition, + toPosition: targetPosition } ], - { animate: false, finished } + { animate: false, finished: false } ); + this.currentPosition = targetPosition; this.tool.registerFeedback([moveGhostElement], this); return element.cssClasses?.includes(CSS_HIDDEN) ? [ModifyCSSFeedbackAction.create({ elements: [element.id], remove: [CSS_HIDDEN] })] : []; } - protected snap(position: Point, element: GModelElement, isSnap = true): Point { - if (isSnap && this.tool.snapper) { - return this.tool.snapper.snap(position, element); - } else { - return position; + protected getTargetPosition(target: GModelElement, event: MouseEvent, element: GModelElement & Locateable): Point { + let targetPosition = getAbsolutePosition(target, event); + if (this.cursorPosition === 'middle' && isBoundsAware(element)) { + targetPosition = Point.subtract(targetPosition, { x: element.bounds.width / 2, y: element.bounds.height / 2 }); } + return targetPosition; + } + + protected snap(position: Point, element: GModelElement, isSnap = true): Point { + return this.tool.positionSnapper.snapPosition(position, element, isSnap); } protected validateMove(startPosition: Point, toPosition: Point, element: GModelElement, isFinished: boolean): Point { diff --git a/packages/client/src/features/export/index.ts b/packages/client/src/features/export/index.ts new file mode 100644 index 00000000..378fddb0 --- /dev/null +++ b/packages/client/src/features/export/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './export-modules'; +export * from './export-svg-action-handler'; +export * from './glsp-svg-exporter'; diff --git a/packages/client/src/features/helper-lines/helper-line-feedback.ts b/packages/client/src/features/helper-lines/helper-line-feedback.ts new file mode 100644 index 00000000..56e6157f --- /dev/null +++ b/packages/client/src/features/helper-lines/helper-line-feedback.ts @@ -0,0 +1,269 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { + Action, + Bounds, + CommandExecutionContext, + CommandReturn, + GModelElement, + GModelRoot, + TYPES, + Viewport, + findParentByFeature, + isBoundsAware, + isViewport +} from '@eclipse-glsp/sprotty'; +import { inject, injectable } from 'inversify'; +import { partition } from 'lodash'; +import '../../../css/helper-lines.css'; +import { FeedbackCommand } from '../../base/feedback/feedback-command'; +import { + bottom, + bottomCenter, + bottomLeft, + bottomRight, + center, + isAbove, + isBefore, + left, + middle, + middleLeft, + middleRight, + right, + sortBy, + top, + topCenter, + topLeft, + topRight +} from '../../utils/geometry-util'; +import { BoundsAwareModelElement, findTopLevelElementByFeature, forEachElement, getMatchingElements } from '../../utils/gmodel-util'; +import { getViewportBounds } from '../../utils/viewpoint-util'; +import { HelperLine, HelperLineType, SelectionBounds, isHelperLine, isSelectionBounds } from './model'; + +export type ViewportLineType = typeof HelperLineType.Center | typeof HelperLineType.Middle | string; + +export const ALL_ELEMENT_LINE_TYPES = Object.values(HelperLineType); +export const ALL_VIEWPORT_LINE_TYPES = [HelperLineType.Center, HelperLineType.Middle]; + +export interface DrawHelperLinesFeedbackAction extends Action { + kind: typeof DrawHelperLinesFeedbackAction.KIND; + elementIds: string[]; + elementLines?: HelperLineType[]; + viewportLines?: ViewportLineType[]; + alignmentEpsilon?: number; +} + +export namespace DrawHelperLinesFeedbackAction { + export const KIND = 'drawHelperLines'; + + export function create(options: Omit): DrawHelperLinesFeedbackAction { + return { + kind: KIND, + ...options + }; + } +} + +@injectable() +export class DrawHelperLinesFeedbackCommand extends FeedbackCommand { + static readonly KIND = DrawHelperLinesFeedbackAction.KIND; + + constructor(@inject(TYPES.Action) public action: DrawHelperLinesFeedbackAction) { + super(); + } + + execute(context: CommandExecutionContext): CommandReturn { + removeHelperLines(context.root); + removeSelectionBounds(context.root); + const boundsAwareElements = getMatchingElements(context.root.index, this.isCompareElement); + const [referenceElements, elements] = partition(boundsAwareElements, element => this.action.elementIds.includes(element.id)); + if (referenceElements.length === 0) { + return context.root; + } + const referenceBounds = this.calcReferenceBounds(referenceElements); + const helperLines = this.calcHelperLines(elements, referenceBounds, context); + if (referenceElements.length > 1) { + context.root.add(new SelectionBounds(referenceBounds)); + } + helperLines.forEach(helperLine => context.root.add(helperLine)); + return context.root; + } + + protected isCompareElement(element: GModelElement): element is BoundsAwareModelElement { + return isBoundsAware(element) && findTopLevelElementByFeature(element, isBoundsAware, isViewport) === element; + } + + protected calcReferenceBounds(referenceElements: BoundsAwareModelElement[]): Bounds { + return referenceElements.map(element => element.bounds).reduce((combined, next) => Bounds.combine(combined, next), Bounds.EMPTY); + } + + protected calcHelperLines(elements: BoundsAwareModelElement[], bounds: Bounds, context: CommandExecutionContext): HelperLine[] { + const helperLines: HelperLine[] = []; + const viewport = findParentByFeature(context.root, isViewport); + if (viewport) { + helperLines.push(...this.calcHelperLinesForViewport(viewport, bounds)); + } + elements.flatMap(element => this.calcHelperLinesForElement(element, bounds)).forEach(line => helperLines.push(line)); + return helperLines; + } + + protected calcHelperLinesForViewport( + root: Viewport & GModelRoot, + bounds: Bounds, + lineTypes: HelperLineType[] = this.action.viewportLines ?? ALL_VIEWPORT_LINE_TYPES + ): HelperLine[] { + const helperLines: HelperLine[] = []; + const viewportBounds = getViewportBounds(root, root.canvasBounds); + if (lineTypes.includes(HelperLineType.Center) && this.isAligned(center, viewportBounds, bounds, 2)) { + helperLines.push(new HelperLine(topCenter(viewportBounds), bottomCenter(viewportBounds), HelperLineType.Center)); + } + if (lineTypes.includes(HelperLineType.Middle) && this.isAligned(middle, viewportBounds, bounds, 2)) { + helperLines.push(new HelperLine(middleLeft(viewportBounds), middleRight(viewportBounds), HelperLineType.Middle)); + } + return helperLines; + } + + protected calcHelperLinesForElement( + element: BoundsAwareModelElement, + bounds: Bounds, + lineTypes: HelperLineType[] = this.action.elementLines ?? ALL_ELEMENT_LINE_TYPES + ): HelperLine[] { + return this.calcHelperLinesForBounds(element.bounds, bounds, lineTypes); + } + + protected calcHelperLinesForBounds( + elementBounds: Bounds, + bounds: Bounds, + lineTypes: HelperLineType[] = this.action.elementLines ?? ALL_ELEMENT_LINE_TYPES + ): HelperLine[] { + const helperLines: HelperLine[] = []; + + if (lineTypes.includes(HelperLineType.Left) && this.isAligned(left, elementBounds, bounds)) { + const [above, below] = sortBy(top, elementBounds, bounds); // higher top-value ==> lower + helperLines.push(new HelperLine(bottomLeft(below), topLeft(above), HelperLineType.Left)); + } + + if (lineTypes.includes(HelperLineType.Center) && this.isAligned(center, elementBounds, bounds)) { + const [above, below] = sortBy(top, elementBounds, bounds); // higher top-value ==> lower + helperLines.push(new HelperLine(topCenter(above), bottomCenter(below), HelperLineType.Center)); + } + + if (lineTypes.includes(HelperLineType.Right) && this.isAligned(right, elementBounds, bounds)) { + const [above, below] = sortBy(top, elementBounds, bounds); // higher top-value ==> lower + helperLines.push(new HelperLine(bottomRight(below), topRight(above), HelperLineType.Right)); + } + + if (lineTypes.includes(HelperLineType.Bottom) && this.isAligned(bottom, elementBounds, bounds)) { + const [before, after] = sortBy(left, elementBounds, bounds); // higher left-value ==> more to the right + helperLines.push(new HelperLine(bottomLeft(before), bottomRight(after), HelperLineType.Bottom)); + } + + if (lineTypes.includes(HelperLineType.Middle) && this.isAligned(middle, elementBounds, bounds)) { + const [before, after] = sortBy(left, elementBounds, bounds); // higher left-value ==> more to the right + helperLines.push(new HelperLine(middleLeft(before), middleRight(after), HelperLineType.Middle)); + } + + if (lineTypes.includes(HelperLineType.Top) && this.isAligned(top, elementBounds, bounds)) { + const [before, after] = sortBy(left, elementBounds, bounds); // higher left-value ==> more to the right + helperLines.push(new HelperLine(topLeft(before), topRight(after), HelperLineType.Top)); + } + + if (lineTypes.includes(HelperLineType.LeftRight) && this.isMatch(left(elementBounds), right(bounds), 2)) { + if (isAbove(bounds, elementBounds)) { + helperLines.push(new HelperLine(bottomLeft(elementBounds), topRight(bounds), HelperLineType.RightLeft)); + } else { + helperLines.push(new HelperLine(topLeft(elementBounds), bottomRight(bounds), HelperLineType.RightLeft)); + } + } + + if (lineTypes.includes(HelperLineType.LeftRight) && this.isMatch(right(elementBounds), left(bounds), 2)) { + if (isAbove(bounds, elementBounds)) { + helperLines.push(new HelperLine(bottomRight(elementBounds), topLeft(bounds), HelperLineType.LeftRight)); + } else { + helperLines.push(new HelperLine(topRight(elementBounds), bottomLeft(bounds), HelperLineType.LeftRight)); + } + } + + if (lineTypes.includes(HelperLineType.TopBottom) && this.isMatch(top(elementBounds), bottom(bounds), 2)) { + if (isBefore(bounds, elementBounds)) { + helperLines.push(new HelperLine(topRight(elementBounds), bottomLeft(bounds), HelperLineType.BottomTop)); + } else { + helperLines.push(new HelperLine(topLeft(elementBounds), bottomRight(bounds), HelperLineType.BottomTop)); + } + } + + if (lineTypes.includes(HelperLineType.TopBottom) && this.isMatch(bottom(elementBounds), top(bounds), 2)) { + if (isBefore(bounds, elementBounds)) { + helperLines.push(new HelperLine(bottomRight(elementBounds), topLeft(bounds), HelperLineType.TopBottom)); + } else { + helperLines.push(new HelperLine(bottomLeft(elementBounds), topRight(bounds), HelperLineType.TopBottom)); + } + } + + return helperLines; + } + + protected isAligned( + coordinate: (elem: Bounds) => number, + leftBounds: Bounds, + rightBounds: Bounds, + epsilon = this.action.alignmentEpsilon ?? 1 + ): boolean { + return this.isMatch(coordinate(leftBounds), coordinate(rightBounds), epsilon); + } + + protected isMatch(leftCoordinate: number, rightCoordinate: number, epsilon = this.action.alignmentEpsilon ?? 1): boolean { + return Math.abs(leftCoordinate - rightCoordinate) < epsilon; + } +} + +export interface RemoveHelperLinesFeedbackAction extends Action { + kind: typeof RemoveHelperLinesFeedbackAction.KIND; +} + +export namespace RemoveHelperLinesFeedbackAction { + export const KIND = 'removeHelperLines'; + + export function create(options: Omit = {}): RemoveHelperLinesFeedbackAction { + return { + kind: KIND, + ...options + }; + } +} + +@injectable() +export class RemoveHelperLinesFeedbackCommand extends FeedbackCommand { + static readonly KIND = RemoveHelperLinesFeedbackAction.KIND; + + constructor(@inject(TYPES.Action) public action: RemoveHelperLinesFeedbackAction) { + super(); + } + override execute(context: CommandExecutionContext): CommandReturn { + removeHelperLines(context.root); + removeSelectionBounds(context.root); + return context.root; + } +} + +export function removeHelperLines(root: GModelRoot): void { + forEachElement(root.index, isHelperLine, line => root.remove(line)); +} + +export function removeSelectionBounds(root: GModelRoot): void { + forEachElement(root.index, isSelectionBounds, line => root.remove(line)); +} diff --git a/packages/client/src/features/helper-lines/helper-line-manager-default.ts b/packages/client/src/features/helper-lines/helper-line-manager-default.ts new file mode 100644 index 00000000..97d371e6 --- /dev/null +++ b/packages/client/src/features/helper-lines/helper-line-manager-default.ts @@ -0,0 +1,114 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { + Action, + DisposableCollection, + GModelElement, + GModelRoot, + IActionHandler, + ISnapper, + MoveAction, + SetBoundsAction, + TYPES +} from '@eclipse-glsp/sprotty'; +import { inject, injectable, optional, postConstruct } from 'inversify'; +import { IFeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher'; +import { ISelectionListener, SelectionService } from '../../base/selection-service'; +import { SetBoundsFeedbackAction } from '../bounds/set-bounds-feedback-command'; +import { GridSnapper } from '../change-bounds/snap'; +import { MoveFinishedEventAction, MoveInitializedEventAction } from '../tools/change-bounds/change-bounds-tool-feedback'; +import { DrawHelperLinesFeedbackAction, RemoveHelperLinesFeedbackAction, ViewportLineType } from './helper-line-feedback'; +import { IHelperLineManager } from './helper-line-manager'; +import { Direction, HelperLineType } from './model'; + +@injectable() +export class HelperLineManager implements IActionHandler, ISelectionListener, IHelperLineManager { + @inject(TYPES.IFeedbackActionDispatcher) protected feedbackDispatcher: IFeedbackActionDispatcher; + @inject(SelectionService) protected selectionService: SelectionService; + @optional() @inject(TYPES.ISnapper) protected snapper?: ISnapper; + + protected snapSize = { x: 20, y: 20 }; + protected feedback: DisposableCollection = new DisposableCollection(); + + protected elementLines?: HelperLineType[]; + protected viewportLines?: ViewportLineType[]; + protected alignmentEpsilon?: number; + + @postConstruct() + protected init(): void { + this.selectionService.onSelectionChanged(change => + this.selectionChanged(change.root, change.selectedElements, change.deselectedElements) + ); + if (this.snapper instanceof GridSnapper) { + this.snapSize = { x: this.snapper.grid.x * 2, y: this.snapper.grid.y * 2 }; + } + } + + handle(action: Action): void { + if (MoveInitializedEventAction.is(action)) { + this.handleMoveInitializedAction(action); + } else if (MoveAction.is(action)) { + this.handleMoveAction(action); + } else if (MoveFinishedEventAction.is(action)) { + this.handleMoveFinishedAction(action); + } else if (SetBoundsAction.is(action) || SetBoundsFeedbackAction.is(action)) { + this.handleSetBoundsAction(action); + } + } + + protected handleMoveInitializedAction(_action: MoveInitializedEventAction): void { + this.feedback.dispose(); + const feedback = this.createHelperLineFeedback(this.selectionService.getSelectedElementIDs()); + this.feedback.push(this.feedbackDispatcher.registerFeedback(this, [feedback], [RemoveHelperLinesFeedbackAction.create()])); + } + + protected handleMoveFinishedAction(_action: MoveFinishedEventAction): void { + this.feedback.dispose(); + } + + protected handleMoveAction(action: MoveAction): void { + if (!action.finished) { + const elementIds = action.moves.map(move => move.elementId); + const feedback = this.createHelperLineFeedback(elementIds); + this.feedback.push(this.feedbackDispatcher.registerFeedback(this, [feedback], [RemoveHelperLinesFeedbackAction.create()])); + } else { + this.feedback.dispose(); + } + } + + protected createHelperLineFeedback(elementIds: string[]): DrawHelperLinesFeedbackAction { + return DrawHelperLinesFeedbackAction.create({ + elementIds, + elementLines: this.elementLines, + viewportLines: this.viewportLines, + alignmentEpsilon: this.alignmentEpsilon + }); + } + + protected handleSetBoundsAction(action: SetBoundsAction | SetBoundsFeedbackAction): void { + const elementIds = action.bounds.map(bound => bound.elementId); + const feedback = this.createHelperLineFeedback(elementIds); + this.feedback.push(this.feedbackDispatcher.registerFeedback(this, [feedback], [RemoveHelperLinesFeedbackAction.create()])); + } + + selectionChanged(root: Readonly, selectedElements: string[], deselectedElements?: string[] | undefined): void { + this.feedback.dispose(); + } + + getHelperLineSnapping(target: GModelElement, isSnap: boolean, direction: Direction): number { + return direction === Direction.Left || direction === Direction.Right ? this.snapSize.x : this.snapSize.y; + } +} diff --git a/packages/client/src/features/helper-lines/helper-line-manager.ts b/packages/client/src/features/helper-lines/helper-line-manager.ts new file mode 100644 index 00000000..be260925 --- /dev/null +++ b/packages/client/src/features/helper-lines/helper-line-manager.ts @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { GModelElement } from '@eclipse-glsp/sprotty'; +import { Direction } from './model'; + +export interface IHelperLineManager { + getHelperLineSnapping(target: GModelElement, isSnap: boolean, direction: Direction): number; +} diff --git a/packages/client/src/features/helper-lines/helper-line-module.ts b/packages/client/src/features/helper-lines/helper-line-module.ts new file mode 100644 index 00000000..03436c9a --- /dev/null +++ b/packages/client/src/features/helper-lines/helper-line-module.ts @@ -0,0 +1,47 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { + FeatureModule, + MoveAction, + SetBoundsAction, + TYPES, + bindAsService, + configureActionHandler, + configureCommand, + configureModelElement +} from '@eclipse-glsp/sprotty'; +import { SetBoundsFeedbackAction } from '../bounds/set-bounds-feedback-command'; +import { MoveFinishedEventAction, MoveInitializedEventAction } from '../tools/change-bounds/change-bounds-tool-feedback'; +import { DrawHelperLinesFeedbackCommand, RemoveHelperLinesFeedbackCommand } from './helper-line-feedback'; +import { HelperLineManager } from './helper-line-manager-default'; +import { HELPER_LINE, HelperLine, SELECTION_BOUNDS, SelectionBounds } from './model'; +import { HelperLineView, SelectionBoundsView } from './view'; + +export const helperLineModule = new FeatureModule((bind, unbind, isBound, rebind) => { + const context = { bind, unbind, isBound, rebind }; + configureModelElement(context, HELPER_LINE, HelperLine, HelperLineView); + configureModelElement(context, SELECTION_BOUNDS, SelectionBounds, SelectionBoundsView); + configureCommand(context, DrawHelperLinesFeedbackCommand); + configureCommand(context, RemoveHelperLinesFeedbackCommand); + + bindAsService(bind, TYPES.IHelperLineManager, HelperLineManager); + configureActionHandler(context, SetBoundsAction.KIND, TYPES.IHelperLineManager); + configureActionHandler(context, SetBoundsFeedbackAction.KIND, TYPES.IHelperLineManager); + configureActionHandler(context, MoveAction.KIND, TYPES.IHelperLineManager); + configureActionHandler(context, MoveInitializedEventAction.KIND, TYPES.IHelperLineManager); + configureActionHandler(context, MoveFinishedEventAction.KIND, TYPES.IHelperLineManager); +}); diff --git a/packages/client/src/features/helper-lines/index.ts b/packages/client/src/features/helper-lines/index.ts new file mode 100644 index 00000000..7e836a1c --- /dev/null +++ b/packages/client/src/features/helper-lines/index.ts @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './helper-line-feedback'; +export * from './helper-line-manager-default'; +export * from './helper-line-manager'; +export * from './helper-line-module'; +export * from './model'; +export * from './view'; diff --git a/packages/client/src/features/helper-lines/model.ts b/packages/client/src/features/helper-lines/model.ts new file mode 100644 index 00000000..21c13d66 --- /dev/null +++ b/packages/client/src/features/helper-lines/model.ts @@ -0,0 +1,140 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { Args, Bounds, GChildElement, GModelElement, GShapeElement, Point } from '@eclipse-glsp/sprotty'; +import { v4 as uuid } from 'uuid'; +import { ArgsAware } from '../../base/args-feature'; +import { ResizeHandleLocation } from '../change-bounds/model'; + +export const HelperLineType = { + Left: 'left', + Right: 'right', + Center: 'center', + Top: 'top', + Bottom: 'bottom', + Middle: 'middle', + LeftRight: 'left-right', + RightLeft: 'right-left', + BottomTop: 'bottom-top', + TopBottom: 'top-bottom' +} as const; + +// allow any string to be set as helper line type to support customization +export type HelperLineType = (typeof HelperLineType)[keyof typeof HelperLineType] | string; + +export const HELPER_LINE = 'helper-line'; + +export class HelperLine extends GChildElement implements ArgsAware { + args?: Args; + + constructor( + readonly startPoint = Point.ORIGIN, + readonly endPoint = Point.ORIGIN, + readonly lineType: HelperLineType = HelperLineType.Left + ) { + super(); + this.id = uuid(); + this.type = HELPER_LINE; + } + + get isLeft(): boolean { + return this.lineType === HelperLineType.Left || this.lineType === HelperLineType.LeftRight; + } + + get isRight(): boolean { + return this.lineType === HelperLineType.Right || this.lineType === HelperLineType.RightLeft; + } + + get isTop(): boolean { + return this.lineType === HelperLineType.Top || this.lineType === HelperLineType.TopBottom; + } + + get isBottom(): boolean { + return this.lineType === HelperLineType.Bottom || this.lineType === HelperLineType.BottomTop; + } + + get isMiddle(): boolean { + return this.lineType === HelperLineType.Middle; + } + + get isCenter(): boolean { + return this.lineType === HelperLineType.Center; + } +} + +export function isHelperLine(element: GModelElement): element is HelperLine { + return element.type === HELPER_LINE; +} + +export const SELECTION_BOUNDS = 'selection-bounds'; + +export class SelectionBounds extends GShapeElement implements ArgsAware { + args?: Args; + + constructor(bounds?: Bounds) { + super(); + this.id = uuid(); + this.type = SELECTION_BOUNDS; + if (bounds) { + this.bounds = bounds; + } + } +} + +export function isSelectionBounds(element: GModelElement): element is SelectionBounds { + return element.type === SELECTION_BOUNDS; +} + +export const Direction = { + Left: 'left', + Right: 'right', + Up: 'up', + Down: 'down' +} as const; + +// allow any string to be set as helper line type to support customization +export type Direction = (typeof Direction)[keyof typeof Direction]; + +export function getDirectionOf(point: Point): Direction[] { + const directions: Direction[] = []; + if (point.x < 0) { + directions.push(Direction.Left); + } else if (point.x > 0) { + directions.push(Direction.Right); + } + if (point.y < 0) { + directions.push(Direction.Up); + } else if (point.y > 0) { + directions.push(Direction.Down); + } + return directions; +} + +export function getDirectionFrom(resize?: ResizeHandleLocation): Direction[] { + if (resize === ResizeHandleLocation.TopLeft) { + return [Direction.Up, Direction.Left]; + } + if (resize === ResizeHandleLocation.TopRight) { + return [Direction.Up, Direction.Right]; + } + if (resize === ResizeHandleLocation.BottomLeft) { + return [Direction.Down, Direction.Left]; + } + if (resize === ResizeHandleLocation.BottomRight) { + return [Direction.Down, Direction.Right]; + } + return []; +} diff --git a/packages/client/src/features/helper-lines/view.tsx b/packages/client/src/features/helper-lines/view.tsx new file mode 100644 index 00000000..b47b6d6f --- /dev/null +++ b/packages/client/src/features/helper-lines/view.tsx @@ -0,0 +1,60 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { IViewArgs, RenderingContext, ShapeView, svg } from '@eclipse-glsp/sprotty'; +import { injectable } from 'inversify'; +import { VNode } from 'snabbdom'; +import { HelperLine, SelectionBounds } from './model'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const JSX = { createElement: svg }; + +@injectable() +export class HelperLineView extends ShapeView { + override render(model: HelperLine, _context: RenderingContext): VNode | undefined { + return ( + + + + ); + } +} + +@injectable() +export class SelectionBoundsView extends ShapeView { + override render(model: SelectionBounds, context: RenderingContext, args?: IViewArgs | undefined): VNode | undefined { + if (!this.isVisible(model, context)) { + return undefined; + } + return ( + + + + ); + } +} diff --git a/packages/client/src/features/hints/index.ts b/packages/client/src/features/hints/index.ts new file mode 100644 index 00000000..2effe7c2 --- /dev/null +++ b/packages/client/src/features/hints/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './model'; +export * from './type-hint-provider'; +export * from './type-hints-module'; diff --git a/packages/client/src/features/hover/index.ts b/packages/client/src/features/hover/index.ts new file mode 100644 index 00000000..948df0b6 --- /dev/null +++ b/packages/client/src/features/hover/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './hover-module'; +export * from './hover'; diff --git a/packages/client/src/features/index.ts b/packages/client/src/features/index.ts new file mode 100644 index 00000000..e58d15db --- /dev/null +++ b/packages/client/src/features/index.ts @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './accessibility'; +export * from './bounds'; +export * from './change-bounds'; +export * from './command-palette'; +export * from './context-menu'; +export * from './copy-paste'; +export * from './decoration'; +export * from './element-template'; +export * from './export'; +export * from './helper-lines'; +export * from './hints'; +export * from './hover'; +export * from './label-edit'; +export * from './layout'; +export * from './navigation'; +export * from './reconnect'; +export * from './routing'; +export * from './save'; +export * from './select'; +export * from './source-model-watcher'; +export * from './status'; +export * from './svg-metadata'; +export * from './tool-palette'; +export * from './tools'; +export * from './undo-redo'; +export * from './validation'; +export * from './viewport'; diff --git a/packages/client/src/features/label-edit/index.ts b/packages/client/src/features/label-edit/index.ts new file mode 100644 index 00000000..340592dc --- /dev/null +++ b/packages/client/src/features/label-edit/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './edit-label-tool'; +export * from './edit-label-validator'; +export * from './label-edit-module'; diff --git a/packages/client/src/features/layout/index.ts b/packages/client/src/features/layout/index.ts new file mode 100644 index 00000000..03966eb4 --- /dev/null +++ b/packages/client/src/features/layout/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './layout-elements-action'; +export * from './layout-module'; diff --git a/packages/client/src/features/navigation/index.ts b/packages/client/src/features/navigation/index.ts new file mode 100644 index 00000000..93c96cc8 --- /dev/null +++ b/packages/client/src/features/navigation/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './navigation-action-handler'; +export * from './navigation-module'; +export * from './navigation-target-resolver'; diff --git a/packages/client/src/features/reconnect/index.ts b/packages/client/src/features/reconnect/index.ts new file mode 100644 index 00000000..0886faad --- /dev/null +++ b/packages/client/src/features/reconnect/index.ts @@ -0,0 +1,16 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './model'; diff --git a/packages/client/src/features/routing/index.ts b/packages/client/src/features/routing/index.ts new file mode 100644 index 00000000..452038f5 --- /dev/null +++ b/packages/client/src/features/routing/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './glsp-manhattan-edge-router'; +export * from './routing-module'; diff --git a/packages/client/src/features/save/index.ts b/packages/client/src/features/save/index.ts new file mode 100644 index 00000000..d9c79be0 --- /dev/null +++ b/packages/client/src/features/save/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './save-keylistener'; +export * from './save-module'; diff --git a/packages/client/src/features/select/index.ts b/packages/client/src/features/select/index.ts new file mode 100644 index 00000000..00304482 --- /dev/null +++ b/packages/client/src/features/select/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './select-feedback-command'; +export * from './select-module'; +export * from './select-mouse-listener'; diff --git a/packages/client/src/features/source-model-watcher/index.ts b/packages/client/src/features/source-model-watcher/index.ts new file mode 100644 index 00000000..caa3abda --- /dev/null +++ b/packages/client/src/features/source-model-watcher/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './source-model-changed-action-handler'; +export * from './source-model-watcher-module'; diff --git a/packages/client/src/features/source-model-watcher/source-model-wacher-module.ts b/packages/client/src/features/source-model-watcher/source-model-watcher-module.ts similarity index 100% rename from packages/client/src/features/source-model-watcher/source-model-wacher-module.ts rename to packages/client/src/features/source-model-watcher/source-model-watcher-module.ts diff --git a/packages/client/src/features/status/index.ts b/packages/client/src/features/status/index.ts new file mode 100644 index 00000000..51e2efed --- /dev/null +++ b/packages/client/src/features/status/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './status-module'; +export * from './status-overlay'; diff --git a/packages/client/src/features/svg-metadata/index.ts b/packages/client/src/features/svg-metadata/index.ts new file mode 100644 index 00000000..db153a79 --- /dev/null +++ b/packages/client/src/features/svg-metadata/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './metadata-placer'; +export * from './svg-metadata-module'; diff --git a/packages/client/src/features/tool-palette/index.ts b/packages/client/src/features/tool-palette/index.ts new file mode 100644 index 00000000..008dc760 --- /dev/null +++ b/packages/client/src/features/tool-palette/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './tool-palette-module'; +export * from './tool-palette'; diff --git a/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts b/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts index cb711d4c..f6984c36 100644 --- a/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts +++ b/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts @@ -22,24 +22,24 @@ import { GChildElement, GModelElement, GModelRoot, - MouseListener, MoveAction, Point, TYPES, findParentByFeature, hasStringProp, isMoveable, - isSelectable, - isViewport + isSelectable } from '@eclipse-glsp/sprotty'; import { inject, injectable } from 'inversify'; -import { VNode } from 'snabbdom'; +import { DebouncedFunc, debounce } from 'lodash'; +import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener'; import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback'; import { FeedbackCommand } from '../../../base/feedback/feedback-command'; import { forEachElement } from '../../../utils/gmodel-util'; import { SResizeHandle, addResizeHandles, isResizable, removeResizeHandles } from '../../change-bounds/model'; import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from '../../change-bounds/movement-restrictor'; +import { PointPositionUpdater } from '../../change-bounds/point-position-updater'; import { ChangeBoundsTool } from './change-bounds-tool'; export interface ShowChangeBoundsToolResizeFeedbackAction extends Action { @@ -113,6 +113,38 @@ export class HideChangeBoundsToolResizeFeedbackCommand extends FeedbackCommand { } } +export interface MoveInitializedEventAction extends Action { + kind: typeof MoveInitializedEventAction.KIND; +} + +export namespace MoveInitializedEventAction { + export const KIND = 'move-initialized-event'; + + export function is(object: any): object is MoveInitializedEventAction { + return Action.hasKind(object, KIND); + } + + export function create(): MoveInitializedEventAction { + return { kind: KIND }; + } +} + +export interface MoveFinishedEventAction extends Action { + kind: typeof MoveFinishedEventAction.KIND; +} + +export namespace MoveFinishedEventAction { + export const KIND = 'move-finished-event'; + + export function is(object: any): object is MoveFinishedEventAction { + return Action.hasKind(object, KIND); + } + + export function create(): MoveFinishedEventAction { + return { kind: KIND }; + } +} + /** * This mouse listener provides visual feedback for moving by sending client-side * `MoveAction`s while elements are selected and dragged. This will also update @@ -120,45 +152,57 @@ export class HideChangeBoundsToolResizeFeedbackCommand extends FeedbackCommand { * the visual feedback but also the basis for sending the change to the server * (see also `tools/MoveTool`). */ -export class FeedbackMoveMouseListener extends MouseListener implements Disposable { - protected hasDragged = false; +export class FeedbackMoveMouseListener extends DragAwareMouseListener implements Disposable { protected rootElement?: GModelRoot; - protected startDragPosition?: Point; + protected positionUpdater; protected elementId2startPos = new Map(); + protected pendingMoveInitialized?: DebouncedFunc<() => void>; constructor(protected tool: ChangeBoundsTool) { super(); + this.positionUpdater = new PointPositionUpdater(tool.positionSnapper); } override mouseDown(target: GModelElement, event: MouseEvent): Action[] { if (event.button === 0 && !(target instanceof SResizeHandle)) { const moveable = findParentByFeature(target, isMoveable); - if (moveable !== undefined) { - this.startDragPosition = { x: event.pageX, y: event.pageY }; + if (moveable !== undefined && !(target instanceof SResizeHandle)) { + this.positionUpdater.updateLastDragPosition(event); + this.scheduleMoveInitialized(); } else { - this.startDragPosition = undefined; + this.positionUpdater.resetPosition(); } - this.hasDragged = false; + this._isMouseDrag = false; } return []; } + protected scheduleMoveInitialized(): void { + this.pendingMoveInitialized?.cancel(); + this.pendingMoveInitialized = debounce(() => { + this.tool.registerFeedback([MoveInitializedEventAction.create()], this); + this.pendingMoveInitialized = undefined; + }, 750); + this.pendingMoveInitialized(); + } + override mouseMove(target: GModelElement, event: MouseEvent): Action[] { const result: Action[] = []; if (event.buttons === 0) { this.mouseUp(target, event); - } else if (this.startDragPosition) { + } else if (!this.positionUpdater.isLastDragPositionUndefined()) { + this.pendingMoveInitialized?.cancel(); if (this.elementId2startPos.size === 0) { this.collectStartPositions(target.root); } - this.hasDragged = true; + this._isMouseDrag = true; const moveAction = this.getElementMoves(target, event, false); if (moveAction) { result.push(moveAction); result.push(cursorFeedbackAction(CursorCSS.MOVE)); + this.tool.registerFeedback(result, this); } } - this.tool.registerFeedback(result, this); return []; } @@ -186,18 +230,11 @@ export class FeedbackMoveMouseListener extends MouseListener implements Disposab } protected getElementMoves(target: GModelElement, event: MouseEvent, finished: boolean): MoveAction | undefined { - if (!this.startDragPosition) { + const delta = this.positionUpdater.updatePosition(target, event); + if (!delta) { return undefined; } - - const viewport = findParentByFeature(target, isViewport); - const zoom = viewport ? viewport.zoom : 1; - const delta = { - x: (event.pageX - this.startDragPosition.x) / zoom, - y: (event.pageY - this.startDragPosition.y) / zoom - }; - - const elementMoves: ElementMove[] = this.getElementMovesForDelta(target, delta, !event.altKey, finished); + const elementMoves: ElementMove[] = this.getElementMovesForDelta(target, delta, finished); if (elementMoves.length > 0) { return MoveAction.create(elementMoves, { animate: false, finished }); } else { @@ -205,35 +242,15 @@ export class FeedbackMoveMouseListener extends MouseListener implements Disposab } } - protected getElementMovesForDelta( - target: GModelElement, - delta: { x: number; y: number }, - isSnap: boolean, - finished: boolean - ): ElementMove[] { + protected getElementMovesForDelta(target: GModelElement, delta: Point, finished: boolean): ElementMove[] { const elementMoves: ElementMove[] = []; this.elementId2startPos.forEach((startPosition, elementId) => { const element = target.root.index.getById(elementId); if (element) { - let toPosition = this.snap( - { - x: startPosition.x + delta.x, - y: startPosition.y + delta.y - }, - element, - isSnap - ); - if (isMoveable(element)) { - toPosition = this.validateMove(startPosition, toPosition, element, finished); - elementMoves.push({ - elementId: element.id, - fromPosition: { - x: element.position.x, - y: element.position.y - }, - toPosition - }); + const targetPosition = Point.add(element.position, delta); + const toPosition = this.validateMove(startPosition, targetPosition, element, finished); + elementMoves.push({ elementId: element.id, fromPosition: element.position, toPosition }); } } }); @@ -247,7 +264,6 @@ export class FeedbackMoveMouseListener extends MouseListener implements Disposab let action; if (!valid) { action = createMovementRestrictionFeedback(element, this.tool.movementRestrictor); - if (isFinished) { newPosition = startPosition; } @@ -259,24 +275,24 @@ export class FeedbackMoveMouseListener extends MouseListener implements Disposab return newPosition; } - protected snap(position: Point, element: GModelElement, isSnap: boolean): Point { - if (isSnap && this.tool.snapper) { - return this.tool.snapper.snap(position, element); - } else { - return position; - } - } - override mouseEnter(target: GModelElement, event: MouseEvent): Action[] { - if (target instanceof GModelRoot && event.buttons === 0 && !this.startDragPosition) { + if (target instanceof GModelRoot && event.buttons === 0 && this.positionUpdater.isLastDragPositionUndefined()) { this.mouseUp(target, event); } return []; } - override mouseUp(target: GModelElement, event: MouseEvent): Action[] { + override nonDraggingMouseUp(element: GModelElement, event: MouseEvent): Action[] { + this.reset(true); + return []; + } + + override draggingMouseUp(target: GModelElement, event: MouseEvent): Action[] { const result: Action[] = []; - if (this.startDragPosition) { + if (this.positionUpdater.isLastDragPositionUndefined()) { + this.reset(true); + return result; + } else { const moveAction = this.getElementMoves(target, event, true); if (moveAction) { result.push(moveAction); @@ -292,22 +308,36 @@ export class FeedbackMoveMouseListener extends MouseListener implements Disposab return result; } + protected resetMoveFeedback(): ElementMove[] { + const elementMoves: ElementMove[] = []; + this.elementId2startPos.forEach((startPosition, elementId) => { + const element = this.rootElement!.index.getById(elementId); + if (element) { + if (isMoveable(element)) { + elementMoves.push({ elementId: element.id, fromPosition: element.position, toPosition: startPosition }); + } + } + }); + return elementMoves; + } + protected reset(resetFeedback = false): void { + this.pendingMoveInitialized?.cancel(); if (this.rootElement && resetFeedback) { - const elementMoves: ElementMove[] = this.getElementMovesForDelta(this.rootElement, { x: 0, y: 0 }, true, true); - const moveAction = MoveAction.create(elementMoves, { animate: false, finished: true }); - this.tool.deregisterFeedback(this, [moveAction]); + const elementMoves: ElementMove[] = this.resetMoveFeedback(); + if (elementMoves.length > 0) { + const moveAction = MoveAction.create(elementMoves, { animate: false, finished: true }); + this.tool.deregisterFeedback(this, [moveAction]); + } + } else if (resetFeedback) { + this.tool.deregisterFeedback(this, [MoveFinishedEventAction.create()]); } - this.hasDragged = false; - this.startDragPosition = undefined; + this.positionUpdater.resetPosition(); + this._isMouseDrag = false; this.rootElement = undefined; this.elementId2startPos.clear(); } - override decorate(vnode: VNode, _element: GModelElement): VNode { - return vnode; - } - dispose(): void { this.reset(true); } diff --git a/packages/client/src/features/tools/change-bounds/change-boounds-tool-module.ts b/packages/client/src/features/tools/change-bounds/change-bounds-tool-module.ts similarity index 100% rename from packages/client/src/features/tools/change-bounds/change-boounds-tool-module.ts rename to packages/client/src/features/tools/change-bounds/change-bounds-tool-module.ts diff --git a/packages/client/src/features/tools/change-bounds/change-bounds-tool.ts b/packages/client/src/features/tools/change-bounds/change-bounds-tool.ts index 672c7677..6be5e57f 100644 --- a/packages/client/src/features/tools/change-bounds/change-bounds-tool.ts +++ b/packages/client/src/features/tools/change-bounds/change-bounds-tool.ts @@ -32,7 +32,6 @@ import { GModelElement, GModelRoot, GParentElement, - ISnapper, MouseListener, Operation, Point, @@ -43,7 +42,7 @@ import { import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener'; import { CursorCSS, applyCssClasses, cursorFeedbackAction, deleteCssClasses } from '../../../base/feedback/css-feedback'; import { ISelectionListener, SelectionService } from '../../../base/selection-service'; -import { PointPositionUpdater } from '../../../features/change-bounds/snap'; +import { PointPositionUpdater } from '../../../features/change-bounds/point-position-updater'; import { calcElementAndRoutingPoints, forEachElement, @@ -58,6 +57,8 @@ import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from '../../change-bounds/movement-restrictor'; +import { PositionSnapper } from '../../change-bounds/position-snapper'; +import { getDirectionFrom } from '../../helper-lines/model'; import { BaseEditTool } from '../base-tools'; import { FeedbackMoveMouseListener, @@ -84,8 +85,8 @@ export class ChangeBoundsTool extends BaseEditTool { @inject(SelectionService) protected selectionService: SelectionService; @inject(EdgeRouterRegistry) @optional() readonly edgeRouterRegistry?: EdgeRouterRegistry; - @inject(TYPES.ISnapper) @optional() readonly snapper?: ISnapper; @inject(TYPES.IMovementRestrictor) @optional() readonly movementRestrictor?: IMovementRestrictor; + @inject(PositionSnapper) readonly positionSnapper: PositionSnapper; get id(): string { return ChangeBoundsTool.ID; @@ -135,7 +136,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel constructor(protected tool: ChangeBoundsTool) { super(); - this.pointPositionUpdater = new PointPositionUpdater(tool.snapper); + this.pointPositionUpdater = new PointPositionUpdater(tool.positionSnapper); } override mouseDown(target: GModelElement, event: MouseEvent): Action[] { @@ -168,7 +169,11 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel cursorFeedbackAction(this.activeResizeHandle.isNwSeResize() ? CursorCSS.RESIZE_NWSE : CursorCSS.RESIZE_NESW), applyCssClasses(this.activeResizeHandle, ChangeBoundsListener.CSS_CLASS_ACTIVE) ]; - const positionUpdate = this.pointPositionUpdater.updatePosition(target, { x: event.pageX, y: event.pageY }, !event.altKey); + const positionUpdate = this.pointPositionUpdater.updatePosition( + target, + event, + getDirectionFrom(this.activeResizeHandle.location) + ); if (positionUpdate) { const resizeActions = this.handleResizeOnClient(positionUpdate); actions.push(...resizeActions); @@ -242,7 +247,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel const newRoutingPoints: ElementAndRoutingPoints[] = []; const routerRegistry = this.tool.edgeRouterRegistry; if (routerRegistry) { - // If client routing is enabled -> delegate routingpoints of connected edges to server + // If client routing is enabled -> delegate routing points of connected edges to server forEachElement(target.index, isNonRoutableSelectedMovableBoundsAware, element => { if (element instanceof GConnectableElement) { element.incomingEdges @@ -308,7 +313,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel } protected initPosition(event: MouseEvent): void { - this.pointPositionUpdater.updateLastDragPosition({ x: event.pageX, y: event.pageY }); + this.pointPositionUpdater.updateLastDragPosition(event); if (this.activeResizeHandle) { const resizeElement = findParentByFeature(this.activeResizeHandle, isResizable); this.initialBounds = { @@ -459,10 +464,6 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel return result; } - protected snap(position: Point, element: GModelElement, isSnap: boolean): Point { - return isSnap && this.tool.snapper ? this.tool.snapper.snap(position, element) : { x: position.x, y: position.y }; - } - protected isValidBoundChange(element: GModelElement & BoundsAware, newPosition: Point, newSize: Dimension): boolean { return this.isValidSize(element, newSize) && this.isValidMove(element, newPosition); } diff --git a/packages/client/src/features/tools/change-bounds/index.ts b/packages/client/src/features/tools/change-bounds/index.ts new file mode 100644 index 00000000..8b50ec72 --- /dev/null +++ b/packages/client/src/features/tools/change-bounds/index.ts @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './change-bounds-tool-feedback'; +export * from './change-bounds-tool-module'; +export * from './change-bounds-tool'; +export * from './view'; diff --git a/packages/client/src/features/tools/deletion/index.ts b/packages/client/src/features/tools/deletion/index.ts new file mode 100644 index 00000000..f33c825f --- /dev/null +++ b/packages/client/src/features/tools/deletion/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './delete-tool'; +export * from './deletion-tool-module'; diff --git a/packages/client/src/features/tools/edge-creation/index.ts b/packages/client/src/features/tools/edge-creation/index.ts new file mode 100644 index 00000000..c16e26d4 --- /dev/null +++ b/packages/client/src/features/tools/edge-creation/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './dangling-edge-feedback'; +export * from './edege-creation-module'; +export * from './edge-creation-tool-feedback'; +export * from './edge-creation-tool'; +export * from './view'; diff --git a/packages/client/src/features/tools/edge-edit/edge-edit-tool-feedback.ts b/packages/client/src/features/tools/edge-edit/edge-edit-tool-feedback.ts index b7fe2271..d32850de 100644 --- a/packages/client/src/features/tools/edge-edit/edge-edit-tool-feedback.ts +++ b/packages/client/src/features/tools/edge-edit/edge-edit-tool-feedback.ts @@ -13,8 +13,6 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from 'inversify'; -import { VNode } from 'snabbdom'; import { Action, AnchorComputerRegistry, @@ -24,14 +22,13 @@ import { Disposable, EdgeRouterRegistry, ElementMove, - ISnapper, + GConnectableElement, + GModelElement, + GRoutingHandle, MouseListener, MoveAction, Point, PolylineEdgeRouter, - GConnectableElement, - GModelElement, - GRoutingHandle, SwitchEditModeAction, SwitchEditModeCommand, TYPES, @@ -42,14 +39,17 @@ import { isConnectable, isSelected } from '@eclipse-glsp/sprotty'; +import { inject, injectable } from 'inversify'; import { IFeedbackActionDispatcher } from '../../../base/feedback/feedback-action-dispatcher'; import { FeedbackCommand } from '../../../base/feedback/feedback-command'; import { forEachElement, isRoutable, isRoutingHandle } from '../../../utils/gmodel-util'; import { getAbsolutePosition, toAbsoluteBounds } from '../../../utils/viewpoint-util'; -import { PointPositionUpdater } from '../../change-bounds/snap'; +import { PositionSnapper } from '../../change-bounds/position-snapper'; +import { useSnap } from '../../change-bounds/snap'; import { addReconnectHandles, removeReconnectHandles } from '../../reconnect/model'; import { FeedbackEdgeEnd, feedbackEdgeEndId, feedbackEdgeId } from '../edge-creation/dangling-edge-feedback'; import { FeedbackEdgeEndMovingMouseListener } from '../edge-creation/edge-creation-tool-feedback'; +import { PointPositionUpdater } from '../../change-bounds/point-position-updater'; /** * RECONNECT HANDLES FEEDBACK @@ -198,7 +198,10 @@ export class FeedbackEdgeTargetMovingMouseListener extends FeedbackEdgeEndMoving } export class FeedbackEdgeSourceMovingMouseListener extends MouseListener implements Disposable { - constructor(protected anchorRegistry: AnchorComputerRegistry, protected feedbackDispatcher: IFeedbackActionDispatcher) { + constructor( + protected anchorRegistry: AnchorComputerRegistry, + protected feedbackDispatcher: IFeedbackActionDispatcher + ) { super(); } @@ -254,9 +257,12 @@ export class FeedbackEdgeSourceMovingMouseListener extends MouseListener impleme export class FeedbackEdgeRouteMovingMouseListener extends MouseListener { protected pointPositionUpdater: PointPositionUpdater; - constructor(protected edgeRouterRegistry?: EdgeRouterRegistry, protected snapper?: ISnapper) { + constructor( + protected positionSnapper: PositionSnapper, + protected edgeRouterRegistry?: EdgeRouterRegistry + ) { super(); - this.pointPositionUpdater = new PointPositionUpdater(snapper); + this.pointPositionUpdater = new PointPositionUpdater(positionSnapper); } override mouseDown(target: GModelElement, event: MouseEvent): Action[] { @@ -265,7 +271,7 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener { const routingHandle = findParentByFeature(target, isRoutingHandle); if (routingHandle !== undefined) { result.push(SwitchRoutingModeAction.create({ elementsToActivate: [target.id] })); - this.pointPositionUpdater.updateLastDragPosition({ x: event.pageX, y: event.pageY }); + this.pointPositionUpdater.updateLastDragPosition(event); } else { this.pointPositionUpdater.resetPosition(); } @@ -278,9 +284,9 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener { if (event.buttons === 0) { return this.mouseUp(target, event); } - const positionUpdate = this.pointPositionUpdater.updatePosition(target, { x: event.pageX, y: event.pageY }, !event.altKey); + const positionUpdate = this.pointPositionUpdater.updatePosition(target, event); if (positionUpdate) { - const moveActions = this.handleMoveOnClient(target, positionUpdate, !event.altKey); + const moveActions = this.handleMoveOnClient(target, positionUpdate, useSnap(event)); result.push(...moveActions); } return result; @@ -322,10 +328,7 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener { } protected getSnappedHandlePosition(element: GRoutingHandle, point: Point, isSnap: boolean): Point { - if (this.snapper && isSnap) { - return this.snapper.snap(point, element); - } - return point; + return this.positionSnapper?.snapPosition(point, element, isSnap); } protected getHandlePosition(handle: GRoutingHandle): Point | undefined { @@ -345,10 +348,6 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener { this.pointPositionUpdater.resetPosition(); return []; } - - override decorate(vnode: VNode, _element: GModelElement): VNode { - return vnode; - } } /** diff --git a/packages/client/src/features/tools/edge-edit/edge-edit-tool.ts b/packages/client/src/features/tools/edge-edit/edge-edit-tool.ts index caf9877c..018122d3 100644 --- a/packages/client/src/features/tools/edge-edit/edge-edit-tool.ts +++ b/packages/client/src/features/tools/edge-edit/edge-edit-tool.ts @@ -13,7 +13,6 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, optional } from 'inversify'; import { Action, AnchorComputerRegistry, @@ -21,22 +20,22 @@ import { Connectable, Disposable, EdgeRouterRegistry, - ISnapper, - ReconnectEdgeOperation, GModelElement, GModelRoot, GRoutableElement, GRoutingHandle, - TYPES, + ReconnectEdgeOperation, canEditRouting, findParentByFeature, isConnectable, isSelected } from '@eclipse-glsp/sprotty'; +import { inject, injectable, optional } from 'inversify'; import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener'; import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback'; import { ISelectionListener, SelectionService } from '../../../base/selection-service'; import { calcElementAndRoutingPoints, isRoutable, isRoutingHandle } from '../../../utils/gmodel-util'; +import { PositionSnapper } from '../../change-bounds/position-snapper'; import { GReconnectHandle, isReconnectHandle, isReconnectable, isSourceRoutingHandle, isTargetRoutingHandle } from '../../reconnect/model'; import { BaseEditTool } from '../base-tools'; import { DrawFeedbackEdgeAction, RemoveFeedbackEdgeAction, feedbackEdgeId } from '../edge-creation/dangling-edge-feedback'; @@ -57,7 +56,7 @@ export class EdgeEditTool extends BaseEditTool { @inject(SelectionService) protected selectionService: SelectionService; @inject(AnchorComputerRegistry) protected anchorRegistry: AnchorComputerRegistry; @inject(EdgeRouterRegistry) @optional() readonly edgeRouterRegistry?: EdgeRouterRegistry; - @inject(TYPES.ISnapper) @optional() readonly snapper?: ISnapper; + @inject(PositionSnapper) readonly positionSnapper: PositionSnapper; protected feedbackEdgeSourceMovingListener: FeedbackEdgeSourceMovingMouseListener; protected feedbackEdgeTargetMovingListener: FeedbackEdgeTargetMovingMouseListener; @@ -74,7 +73,7 @@ export class EdgeEditTool extends BaseEditTool { // install feedback move mouse listener for client-side move updates this.feedbackEdgeSourceMovingListener = new FeedbackEdgeSourceMovingMouseListener(this.anchorRegistry, this.feedbackDispatcher); this.feedbackEdgeTargetMovingListener = new FeedbackEdgeTargetMovingMouseListener(this.anchorRegistry, this.feedbackDispatcher); - this.feedbackMovingListener = new FeedbackEdgeRouteMovingMouseListener(this.edgeRouterRegistry, this.snapper); + this.feedbackMovingListener = new FeedbackEdgeRouteMovingMouseListener(this.positionSnapper, this.edgeRouterRegistry); this.toDisposeOnDisable.push( Disposable.create(() => this.edgeEditListener.reset()), diff --git a/packages/client/src/features/tools/edge-edit/index.ts b/packages/client/src/features/tools/edge-edit/index.ts new file mode 100644 index 00000000..a1bd73ec --- /dev/null +++ b/packages/client/src/features/tools/edge-edit/index.ts @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './edge-edit-module'; +export * from './edge-edit-tool-feedback'; +export * from './edge-edit-tool'; diff --git a/packages/client/src/features/tools/index.ts b/packages/client/src/features/tools/index.ts new file mode 100644 index 00000000..9b343891 --- /dev/null +++ b/packages/client/src/features/tools/index.ts @@ -0,0 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './base-tools'; +export * from './change-bounds'; +export * from './deletion'; +export * from './edge-creation'; +export * from './edge-edit'; +export * from './marquee-selection'; +export * from './node-creation'; +export * from './tool-focus-loss-module'; diff --git a/packages/client/src/features/tools/marquee-selection/index.ts b/packages/client/src/features/tools/marquee-selection/index.ts new file mode 100644 index 00000000..6eab570b --- /dev/null +++ b/packages/client/src/features/tools/marquee-selection/index.ts @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './marquee-behavior'; +export * from './marquee-mouse-tool'; +export * from './marquee-selection-module'; +export * from './marquee-tool-feedback'; +export * from './marquee-tool'; +export * from './model'; +export * from './view'; diff --git a/packages/client/src/features/tools/marquee-selection/marquee-tool.ts b/packages/client/src/features/tools/marquee-selection/marquee-tool.ts index 06b2fa1f..d52af1fd 100644 --- a/packages/client/src/features/tools/marquee-selection/marquee-tool.ts +++ b/packages/client/src/features/tools/marquee-selection/marquee-tool.ts @@ -13,8 +13,9 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable } from 'inversify'; -import { Action, KeyListener, GModelElement } from '@eclipse-glsp/sprotty'; +import { Action, GModelElement, KeyListener } from '@eclipse-glsp/sprotty'; +import { inject, injectable } from 'inversify'; +import { SelectionService } from '../../../base/selection-service'; import { EnableToolsAction } from '../../../base/tool-manager/tool'; import { BaseEditTool } from '../base-tools'; import { MarqueeMouseTool } from './marquee-mouse-tool'; @@ -23,21 +24,30 @@ import { MarqueeMouseTool } from './marquee-mouse-tool'; export class MarqueeTool extends BaseEditTool { static ID = 'glsp.marquee-tool'; - protected marqueeKeyListener: MarqueeKeyListener = new MarqueeKeyListener(); + @inject(SelectionService) protected selectionService: SelectionService; + + protected marqueeKeyListener: MarqueeKeyListener; get id(): string { return MarqueeTool.ID; } enable(): void { + if (!this.marqueeKeyListener) { + this.marqueeKeyListener = new MarqueeKeyListener(this.selectionService); + } this.toDisposeOnDisable.push(this.keyTool.registerListener(this.marqueeKeyListener)); } } @injectable() export class MarqueeKeyListener extends KeyListener { + constructor(protected selectionService: SelectionService) { + super(); + } + override keyDown(_element: GModelElement, event: KeyboardEvent): Action[] { - if (event.shiftKey) { + if (event.shiftKey && !this.selectionService.hasSelectedElements()) { return [EnableToolsAction.create([MarqueeMouseTool.ID])]; } return []; diff --git a/packages/client/src/features/tools/node-creation/index.ts b/packages/client/src/features/tools/node-creation/index.ts new file mode 100644 index 00000000..4cf07d26 --- /dev/null +++ b/packages/client/src/features/tools/node-creation/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './node-creation-module'; +export * from './node-creation-tool'; diff --git a/packages/client/src/features/tools/node-creation/node-creation-tool.ts b/packages/client/src/features/tools/node-creation/node-creation-tool.ts index edefe885..bd8a8ed7 100644 --- a/packages/client/src/features/tools/node-creation/node-creation-tool.ts +++ b/packages/client/src/features/tools/node-creation/node-creation-tool.ts @@ -18,7 +18,6 @@ import { CreateNodeOperation, GModelElement, GNode, - ISnapper, Point, TYPES, TriggerNodeCreationAction, @@ -32,9 +31,9 @@ import { CSS_GHOST_ELEMENT, CSS_HIDDEN, CursorCSS, cursorFeedbackAction } from ' import { EnableDefaultToolsAction } from '../../../base/tool-manager/tool'; import { getAbsolutePosition } from '../../../utils/viewpoint-util'; import { IMovementRestrictor } from '../../change-bounds/movement-restrictor'; +import { PositionSnapper } from '../../change-bounds/position-snapper'; import { AddTemplateElementsAction, getTemplateElementId } from '../../element-template/add-template-element'; import { MouseTrackingElementPositionListener, PositioningTool } from '../../element-template/mouse-tracking-element-position-listener'; -import { RemoveTemplateElementsAction } from '../../element-template/remove-template-element'; import { Containable, isContainable } from '../../hints/model'; import { BaseCreationTool } from '../base-tools'; @@ -44,8 +43,8 @@ export class NodeCreationTool extends BaseCreationTool= 0 ? bounds.width * 0.5 : 0); +} + +export function right(bounds: Bounds): number { + return bounds.x + bounds.width; +} + +export function top(bounds: Bounds): number { + return bounds.y; +} + +export function middle(bounds: Bounds): number { + return bounds.y + (bounds.height >= 0 ? bounds.height * 0.5 : 0); +} + +export function bottom(bounds: Bounds): number { + return bounds.y + bounds.height; +} + +export function topLeft(bounds: Bounds): Point { + return { x: left(bounds), y: top(bounds) }; +} + +export function topCenter(bounds: Bounds): Point { + return { x: center(bounds), y: top(bounds) }; +} + +export function topRight(bounds: Bounds): Point { + return { x: right(bounds), y: top(bounds) }; +} + +export function middleLeft(bounds: Bounds): Point { + return { x: left(bounds), y: middle(bounds) }; +} + +export function middleCenter(bounds: Bounds): Point { + return { x: center(bounds), y: middle(bounds) }; +} + +export function middleRight(bounds: Bounds): Point { + return { x: right(bounds), y: middle(bounds) }; +} + +export function bottomLeft(bounds: Bounds): Point { + return { x: left(bounds), y: bottom(bounds) }; +} + +export function bottomCenter(bounds: Bounds): Point { + return { x: center(bounds), y: bottom(bounds) }; +} + +export function bottomRight(bounds: Bounds): Point { + return { x: right(bounds), y: bottom(bounds) }; +} + +export function sortBy(rankFunc: (elem: Bounds) => number, ...points: Bounds[]): Bounds[] { + return points.sort(compareFunction(rankFunc)); +} + +export function compareFunction(rankFunc: (elem: T) => number): (left: T, right: T) => number { + return (elemLeft, elemRight) => rankFunc(elemLeft) - rankFunc(elemRight); +} + +export function isAbove(leftBounds: Bounds, rightBounds: Bounds): boolean { + return top(leftBounds) <= top(rightBounds); +} + +export function isBelow(leftBounds: Bounds, rightBounds: Bounds): boolean { + return top(leftBounds) >= top(rightBounds); +} + +export function isBefore(leftBounds: Bounds, rightBounds: Bounds): boolean { + return left(leftBounds) < left(rightBounds); +} + +export function isAfter(leftBounds: Bounds, rightBounds: Bounds): boolean { + return left(leftBounds) >= left(rightBounds); +} diff --git a/packages/client/src/utils/gmodel-util.ts b/packages/client/src/utils/gmodel-util.ts index 1b0b1dc1..e42e8de6 100644 --- a/packages/client/src/utils/gmodel-util.ts +++ b/packages/client/src/utils/gmodel-util.ts @@ -15,25 +15,28 @@ ********************************************************************************/ import { BoundsAware, - distinctAdd, EdgeRouterRegistry, ElementAndBounds, ElementAndRoutingPoints, FluentIterable, - isBoundsAware, - isMoveable, - isSelectable, - isSelected, + GChildElement, + GModelElement, + GModelElementSchema, + GRoutableElement, + GRoutingHandle, ModelIndexImpl, Point, - remove, RoutedPoint, Selectable, - GModelElement, - GRoutableElement, - GRoutingHandle, TypeGuard, - GModelElementSchema + distinctAdd, + findParentByFeature, + isBoundsAware, + isMoveable, + isSelectable, + isSelected, + isViewport, + remove } from '@eclipse-glsp/sprotty'; /** @@ -319,3 +322,31 @@ export function getElementTypeId(input: GModelElement | GModelElementSchema | st return input.type; } } + +export function findTopLevelElementByFeature( + element: GModelElement, + predicate: (t: GModelElement) => t is GModelElement & T, + skip: (t: GModelElement) => boolean = _t => false +): (GModelElement & T) | undefined { + let match: (GModelElement & T) | undefined; + let current: GModelElement | undefined = element; + while (current !== undefined) { + if (!skip(current) && predicate(current)) { + match = current; + } + if (current instanceof GChildElement) { + current = current.parent; + } else { + current = undefined; + } + } + return match; +} + +export function calculateDeltaBetweenPoints(target: Point, source: Point, element: GModelElement): Point { + const delta = Point.subtract(target, source); + const viewport = findParentByFeature(element, isViewport); + const zoom = viewport?.zoom ?? 1; + const adaptedDelta = { x: delta.x / zoom, y: delta.y / zoom }; + return adaptedDelta; +} diff --git a/packages/client/src/utils/html-utils.ts b/packages/client/src/utils/html-utils.ts index 7b5c066d..801cc49d 100644 --- a/packages/client/src/utils/html-utils.ts +++ b/packages/client/src/utils/html-utils.ts @@ -13,6 +13,8 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import { AnyObject, hasNumberProp } from '@eclipse-glsp/sprotty'; + export function createElementFromHTML(html: string): HTMLElement | undefined { const template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result @@ -23,3 +25,7 @@ export function createElementFromHTML(html: string): HTMLElement | undefined { } return undefined; } + +export function isMouseEvent(object: unknown): object is MouseEvent { + return AnyObject.is(object) && hasNumberProp(object, 'pageX') && hasNumberProp(object, 'pageY'); +} diff --git a/packages/client/src/utils/index.ts b/packages/client/src/utils/index.ts new file mode 100644 index 00000000..ffccc99a --- /dev/null +++ b/packages/client/src/utils/index.ts @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './argument-utils'; +export * from './geometry-util'; +export * from './gmodel-util'; +export * from './html-utils'; +export * from './layout-utils'; +export * from './marker'; +export * from './viewpoint-util'; diff --git a/packages/client/src/utils/viewpoint-util.ts b/packages/client/src/utils/viewpoint-util.ts index 9df75461..8ec28fe5 100644 --- a/packages/client/src/utils/viewpoint-util.ts +++ b/packages/client/src/utils/viewpoint-util.ts @@ -29,6 +29,7 @@ import { isViewport, translateBounds } from '@eclipse-glsp/sprotty'; +import { bottomRight, topLeft } from './geometry-util'; import { BoundsAwareModelElement } from './gmodel-util'; /** @@ -47,6 +48,7 @@ import { BoundsAwareModelElement } from './gmodel-util'; export function getAbsolutePosition(target: GModelElement, mouseEvent: MouseEvent): Point { return getAbsolutePositionByPoint(target, { x: mouseEvent.pageX, y: mouseEvent.pageY }); } + export function getAbsolutePositionByPoint(target: GModelElement, point: Point): Point { let xPos = point.x; let yPos = point.y; @@ -71,6 +73,12 @@ export function getAbsolutePositionByPoint(target: GModelElement, point: Point): }; } +export function getViewportBounds(target: GModelElement, bounds: Bounds): Bounds { + const start = getAbsolutePositionByPoint(target, topLeft(bounds)); + const end = getAbsolutePositionByPoint(target, bottomRight(bounds)); + return { ...start, width: end.x - start.x, height: end.y - start.y }; +} + /** * Translates the bounds of the diagram element (local coordinates) into the diagram coordinates system * (i.e. relative to the Diagram's 0;0 point) diff --git a/packages/client/src/views/index.ts b/packages/client/src/views/index.ts index 845fd97a..a10f7495 100644 --- a/packages/client/src/views/index.ts +++ b/packages/client/src/views/index.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2019-2023 EclipseSource and others. + * Copyright (c) 2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -18,5 +18,5 @@ export * from './compartments'; export * from './gedge-view'; export * from './glsp-projection-view'; export * from './issue-marker-view'; -export * from './rounded-corner'; export * from './rounded-corner-view'; +export * from './rounded-corner'; diff --git a/packages/glsp-sprotty/src/index.ts b/packages/glsp-sprotty/src/index.ts index 5f72a68b..f24046c9 100644 --- a/packages/glsp-sprotty/src/index.ts +++ b/packages/glsp-sprotty/src/index.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2022-2023 STMicroelectronics and others. + * Copyright (c) 2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,310 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - -/** - * Selective custom reexport of the sprotty API. Also provides augmentations/adaptions - * of certain type definitions for GLSP. Using this custom reexport allows us to gracefully - * update to newer sprotty versions without hard API breaks. - */ - -/** - * @eclipse-glsp/protocol - */ -export * from '@eclipse-glsp/protocol'; - export * from './action-override'; - -/* - * sprotty - */ -// ------------------ Base ------------------ - -// Exclude definition for labeled actions -// export * from 'sprotty/lib/base/actions/action'; -export * from 'sprotty/lib/base/actions/action-dispatcher'; -export { - ActionHandlerRegistration, - ActionHandlerRegistry, - IActionHandlerInitializer, - configureActionHandler, - onAction -} from 'sprotty/lib/base/actions/action-handler'; -export * from 'sprotty/lib/base/actions/diagram-locker'; - -export * from 'sprotty/lib/base/animations/animation'; -export * from 'sprotty/lib/base/animations/animation-frame-syncer'; -export * from 'sprotty/lib/base/animations/easing'; - -export * from 'sprotty/lib/base/commands/command'; -export * from 'sprotty/lib/base/commands/command-registration'; -export * from 'sprotty/lib/base/commands/command-stack'; -export * from 'sprotty/lib/base/commands/command-stack-options'; - -export * from 'sprotty/lib/base/features/initialize-canvas'; -export * from 'sprotty/lib/base/features/set-model'; -// Exclude SModelElementImpl as it as exported with augmentation module -export { - FeatureSet, - SChildElementImpl as GChildElement, - SModelElementImpl as GModelElement, - SModelRootImpl as GModelRoot, - SParentElementImpl as GParentElement, - IModelIndex, - ModelIndexImpl, - createRandomId, - isParent -} from 'sprotty/lib/base/model/smodel'; - -export { - CustomFeatures, - EMPTY_ROOT, - SModelElementConstructor as GModelElementConstructor, - SModelElementRegistration as GModelElementRegistration, - SModelFactory as GModelFactory, - IModelFactory, - // exported without alias we extend it in glsp-client to `GModelRegistry` - SModelRegistry, - createFeatureSet -} from 'sprotty/lib/base/model/smodel-factory'; -export * from 'sprotty/lib/base/model/smodel-utils'; - -export * from 'sprotty/lib/base/ui-extensions/ui-extension'; -export * from 'sprotty/lib/base/ui-extensions/ui-extension-registry'; - -export * from 'sprotty/lib/base/views/dom-helper'; -export { KeyTool } from 'sprotty/lib/base/views/key-tool'; -export { MouseEventKind, MousePositionTracker, MouseTool, PopupMouseTool } from 'sprotty/lib/base/views/mouse-tool'; -export * from 'sprotty/lib/base/views/thunk-view'; -export * from 'sprotty/lib/base/views/view'; -export * from 'sprotty/lib/base/views/viewer'; -export * from 'sprotty/lib/base/views/viewer-cache'; -export * from 'sprotty/lib/base/views/viewer-options'; -export * from 'sprotty/lib/base/views/vnode-postprocessor'; -export * from 'sprotty/lib/base/views/vnode-utils'; - -// Exclude sprotty types and export augmented GLSP types instead -// export * from 'sprotty/lib/base/types'; -export * from './types'; - -// ------------------ Features ------------------ -export * from 'sprotty/lib/features/bounds/abstract-layout'; -export * from 'sprotty/lib/features/bounds/bounds-manipulation'; -export * from 'sprotty/lib/features/bounds/hbox-layout'; -export * from 'sprotty/lib/features/bounds/hidden-bounds-updater'; -export * from 'sprotty/lib/features/bounds/layout'; -export * from 'sprotty/lib/features/bounds/layout-options'; -export { - Alignable, - BoundsAware, - SShapeElementImpl as GShapeElement, - LayoutContainer, - LayoutableChild, - ModelLayoutOptions, - boundsFeature, - findChildrenAtPosition, - getAbsoluteBounds, - getAbsoluteClientBounds, - isAlignable, - isBoundsAware, - isLayoutContainer, - isLayoutableChild, - isSizeable, - layoutContainerFeature, - layoutableChildFeature -} from 'sprotty/lib/features/bounds/model'; -// exclude stack layout as its not supported in GLSP -// export * from 'sprotty/lib/features/bounds/stack-layout'; -export * from 'sprotty/lib/features/bounds/vbox-layout'; -export * from 'sprotty/lib/features/bounds/views'; - -export { ButtonHandlerRegistry, IButtonHandlerRegistration, configureButtonHandler } from 'sprotty/lib/features/button/button-handler'; -export { SButtonImpl as GButton, SButtonSchema as GButtonSchema } from 'sprotty/lib/features/button/model'; - -export { - CommandPaletteActionProviderRegistry, - RevealNamedElementActionProvider -} from 'sprotty/lib/features/command-palette/action-providers'; -export * from 'sprotty/lib/features/command-palette/command-palette'; - -// Exclude menu item. Aready provided by glsp-protocol -export { Anchor, IContextMenuService, IContextMenuServiceProvider } from 'sprotty/lib/features/context-menu/context-menu-service'; -export { ContextMenuProviderRegistry, DeleteContextMenuItemProvider } from 'sprotty/lib/features/context-menu/menu-providers'; -export * from 'sprotty/lib/features/context-menu/mouse-listener'; - -export * from 'sprotty/lib/features/edge-layout/di.config'; -export * from 'sprotty/lib/features/edge-layout/edge-layout'; -export * from 'sprotty/lib/features/edge-layout/model'; -// Exclude client-side creation features (not supported in GLSP) -// export * from 'sprotty/lib/features/edit/create'; -// export * from 'sprotty/lib/features/edit/create-on-drag'; -export * from 'sprotty/lib/features/edit/delete'; -export * from 'sprotty/lib/features/edit/edit-label'; -export * from 'sprotty/lib/features/edit/edit-label-ui'; -export * from 'sprotty/lib/features/edit/edit-routing'; -export * from 'sprotty/lib/features/edit/model'; -// export * from 'sprotty/lib/features/edit/reconnect'; - -export * from 'sprotty/lib/features/expand/expand'; -export * from 'sprotty/lib/features/expand/model'; -export * from 'sprotty/lib/features/expand/views'; -// Exclude RequestExportSvgAction. Already provided by glsp-protocol -export { ExportSvgCommand, ExportSvgKeyListener, ExportSvgPostprocessor } from 'sprotty/lib/features/export/export'; -export * from 'sprotty/lib/features/export/model'; -// Exclude ExportSvgAction. Already provided by glsp-protocol -export { SvgExporter } from 'sprotty/lib/features/export/svg-exporter'; - -export * from 'sprotty/lib/features/fade/fade'; -export * from 'sprotty/lib/features/fade/model'; - -export * from 'sprotty/lib/features/hover/hover'; -export * from 'sprotty/lib/features/hover/model'; -export * from 'sprotty/lib/features/hover/popup-position-updater'; - -// Alias SModel types -export * from 'sprotty/lib/features/decoration/decoration-placer'; -export { - Decoration, - SDecoration as GDecoration, - SIssue as GIssue, - SIssueSeverity as GIssueSeverity, - // Export as is, we extend it glsp-client to `GIssueMarker` - SIssueMarker -} from 'sprotty/lib/features/decoration/model'; -export * from 'sprotty/lib/features/decoration/views'; - -export * from 'sprotty/lib/features/edge-intersection/intersection-finder'; -export * from 'sprotty/lib/features/edge-intersection/sweepline'; - -export * from 'sprotty/lib/features/move/model'; -export * from 'sprotty/lib/features/move/move'; -export * from 'sprotty/lib/features/move/snap'; - -export * from 'sprotty/lib/features/nameable/model'; - -export * from 'sprotty/lib/features/open/model'; -export * from 'sprotty/lib/features/open/open'; - -export * from 'sprotty/lib/features/projection/model'; -export * from 'sprotty/lib/features/projection/views'; - -export * from 'sprotty/lib/features/routing/abstract-edge-router'; -export * from 'sprotty/lib/features/routing/anchor'; -export * from 'sprotty/lib/features/routing/bezier-anchors'; -export * from 'sprotty/lib/features/routing/bezier-edge-router'; -export * from 'sprotty/lib/features/routing/manhattan-anchors'; -export * from 'sprotty/lib/features/routing/manhattan-edge-router'; -// Alias SModel types -export { - Connectable, - SConnectableElementImpl as GConnectableElement, - SDanglingAnchorImpl as GDanglingAnchor, - SRoutableElementImpl as GRoutableElement, - SRoutingHandleImpl as GRoutingHandle, - RoutingHandleKind, - connectableFeature, - edgeInProgressID, - edgeInProgressTargetHandleID, - getAbsoluteRouteBounds, - getRouteBounds, - isConnectable -} from 'sprotty/lib/features/routing/model'; -export * from 'sprotty/lib/features/routing/polyline-anchors'; -export * from 'sprotty/lib/features/routing/polyline-edge-router'; -export * from 'sprotty/lib/features/routing/routing'; -export * from 'sprotty/lib/features/routing/views'; - -export * from 'sprotty/lib/features/select/model'; -// Alias Select commands with sprotty prefix to avoid clash with glsp-client -export { - GetSelectionCommand, - SelectKeyboardListener, - SelectMouseListener, - SelectAllCommand as SprottySelectAllCommand, - SelectCommand as SprottySelectCommand -} from 'sprotty/lib/features/select/select'; - -// Exclude undo-redo as the client provides its own implementation -// export * from 'sprotty/lib/features/undo-redo/undo-redo'; - -export * from 'sprotty/lib/features/update/model-matching'; -export * from 'sprotty/lib/features/update/update-model'; - -export * from 'sprotty/lib/features/viewport/center-fit'; -export * from 'sprotty/lib/features/viewport/model'; -export * from 'sprotty/lib/features/viewport/scroll'; -// Alias SModel types -export * from 'sprotty/lib/features/viewport/viewport'; -export { ViewportRootElementImpl as GViewportRootElement } from 'sprotty/lib/features/viewport/viewport-root'; -export * from 'sprotty/lib/features/viewport/zoom'; - -export * from 'sprotty/lib/features/zorder/zorder'; - -// ------------------ Graph ------------------ -// Alias SModel types -export { - SCompartmentImpl as GCompartment, - SGraphIndex as GGraphIndex, - SLabelImpl as GLabel, - SNodeImpl as GNode, - SPortImpl as GPort, - // Exported as is, we extend it in glsp-client to GEdge - SEdgeImpl, - // Exported as is, we extend it in glsp-client to GGraph - SGraphImpl -} from 'sprotty/lib/graph/sgraph'; -// Alias SModel types -export { - BezierCurveEdgeView, - SBezierControlHandleView as GBezierControlHandleView, - SBezierCreateHandleView as GBezierCreateHandleView, - SCompartmentView as GCompartmentView, - SGraphView as GGraphView, - SLabelView as GLabelView, - SRoutingHandleView as GRoutingHandleView, - JumpingPolylineEdgeView, - PolylineEdgeView, - PolylineEdgeViewWithGapsOnIntersections -} from 'sprotty/lib/graph/views'; - export * from './feature-modules'; - -// ------------------ Library ------------------ - -export * from 'sprotty/lib/lib/generic-views'; -export * from 'sprotty/lib/lib/html-views'; -export * from 'sprotty/lib/lib/jsx'; -// Alias SModel types -export { - CircularNode, - CircularPort, - DiamondNode, - ForeignObjectElementImpl as GForeignObjectElement, - HtmlRootImpl as GHtmlRoot, - PreRenderedElementImpl as GPreRenderedElement, - ShapedPreRenderedElementImpl as GShapedPreRenderedElement, - RectangularNode, - RectangularPort -} from 'sprotty/lib/lib/model'; -export * from 'sprotty/lib/lib/modules'; -export * from 'sprotty/lib/lib/svg-views'; - -// ------------------ Model Source ------------------ -export * from 'sprotty/lib/model-source/commit-model'; -// Exclude as not supported in GLSP -// export * from 'sprotty/lib/model-source/diagram-server'; -// export * from 'sprotty/lib/model-source/local-model-source'; -export * from 'sprotty/lib/model-source/logging'; -export * from 'sprotty/lib/model-source/model-source'; -// export * from 'sprotty/lib/model-source/websocket'; - -// ------------------ Utilities ------------------ -export * from 'sprotty/lib/utils/browser'; -export * from 'sprotty/lib/utils/codicon'; -export * from 'sprotty/lib/utils/color'; -export * from 'sprotty/lib/utils/geometry'; -export * from 'sprotty/lib/utils/inversify'; -export * from 'sprotty/lib/utils/iterable'; -export * from 'sprotty/lib/utils/keyboard'; -export * from 'sprotty/lib/utils/logging'; -export * from 'sprotty/lib/utils/registry'; +export * from './re-exports'; +export * from './types'; diff --git a/packages/glsp-sprotty/src/re-exports.ts b/packages/glsp-sprotty/src/re-exports.ts new file mode 100644 index 00000000..7623566d --- /dev/null +++ b/packages/glsp-sprotty/src/re-exports.ts @@ -0,0 +1,311 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +/** + * @eclipse-glsp/protocol + */ +export * from '@eclipse-glsp/protocol'; + +/* + * sprotty + */ +// ------------------ Base ------------------ + +// Exclude definition for labeled actions +// export * from 'sprotty/lib/base/actions/action'; +export * from 'sprotty/lib/base/actions/action-dispatcher'; +export { + ActionHandlerRegistration, + ActionHandlerRegistry, + IActionHandlerInitializer, + configureActionHandler, + onAction +} from 'sprotty/lib/base/actions/action-handler'; +export * from 'sprotty/lib/base/actions/diagram-locker'; + +export * from 'sprotty/lib/base/animations/animation'; +export * from 'sprotty/lib/base/animations/animation-frame-syncer'; +export * from 'sprotty/lib/base/animations/easing'; + +export * from 'sprotty/lib/base/commands/command'; +export * from 'sprotty/lib/base/commands/command-registration'; +export * from 'sprotty/lib/base/commands/command-stack'; +export * from 'sprotty/lib/base/commands/command-stack-options'; + +export * from 'sprotty/lib/base/features/initialize-canvas'; +export * from 'sprotty/lib/base/features/set-model'; +// Exclude SModelElementImpl as it as exported with augmentation module +export { + FeatureSet, + SChildElementImpl as GChildElement, + SModelElementImpl as GModelElement, + SModelRootImpl as GModelRoot, + SParentElementImpl as GParentElement, + IModelIndex, + ModelIndexImpl, + createRandomId, + isParent +} from 'sprotty/lib/base/model/smodel'; + +export { + CustomFeatures, + EMPTY_ROOT, + SModelElementConstructor as GModelElementConstructor, + SModelElementRegistration as GModelElementRegistration, + SModelFactory as GModelFactory, + IModelFactory, + // exported without alias we extend it in glsp-client to `GModelRegistry` + SModelRegistry, + createFeatureSet +} from 'sprotty/lib/base/model/smodel-factory'; +export * from 'sprotty/lib/base/model/smodel-utils'; + +export * from 'sprotty/lib/base/ui-extensions/ui-extension'; +export * from 'sprotty/lib/base/ui-extensions/ui-extension-registry'; + +export * from 'sprotty/lib/base/views/dom-helper'; +export { KeyTool } from 'sprotty/lib/base/views/key-tool'; +export { MouseEventKind, MousePositionTracker, MouseTool, PopupMouseTool } from 'sprotty/lib/base/views/mouse-tool'; +export * from 'sprotty/lib/base/views/thunk-view'; +export * from 'sprotty/lib/base/views/view'; +export * from 'sprotty/lib/base/views/viewer'; +export * from 'sprotty/lib/base/views/viewer-cache'; +export * from 'sprotty/lib/base/views/viewer-options'; +export * from 'sprotty/lib/base/views/vnode-postprocessor'; +export * from 'sprotty/lib/base/views/vnode-utils'; + +// Exclude sprotty types and export augmented GLSP types instead +// export * from 'sprotty/lib/base/types'; + +// ------------------ Features ------------------ +export * from 'sprotty/lib/features/bounds/abstract-layout'; +export * from 'sprotty/lib/features/bounds/bounds-manipulation'; +export * from 'sprotty/lib/features/bounds/hbox-layout'; +export * from 'sprotty/lib/features/bounds/hidden-bounds-updater'; +export * from 'sprotty/lib/features/bounds/layout'; +export * from 'sprotty/lib/features/bounds/layout-options'; +export { + Alignable, + BoundsAware, + SShapeElementImpl as GShapeElement, + LayoutContainer, + LayoutableChild, + ModelLayoutOptions, + boundsFeature, + findChildrenAtPosition, + getAbsoluteBounds, + getAbsoluteClientBounds, + isAlignable, + isBoundsAware, + isLayoutContainer, + isLayoutableChild, + isSizeable, + layoutContainerFeature, + layoutableChildFeature +} from 'sprotty/lib/features/bounds/model'; +// exclude stack layout as its not supported in GLSP +// export * from 'sprotty/lib/features/bounds/stack-layout'; +export * from 'sprotty/lib/features/bounds/vbox-layout'; +export * from 'sprotty/lib/features/bounds/views'; + +export { ButtonHandlerRegistry, IButtonHandlerRegistration, configureButtonHandler } from 'sprotty/lib/features/button/button-handler'; +export { SButtonImpl as GButton, SButtonSchema as GButtonSchema } from 'sprotty/lib/features/button/model'; + +export { + CommandPaletteActionProviderRegistry, + RevealNamedElementActionProvider +} from 'sprotty/lib/features/command-palette/action-providers'; +export * from 'sprotty/lib/features/command-palette/command-palette'; + +// Exclude menu item. Aready provided by glsp-protocol +export { Anchor, IContextMenuService, IContextMenuServiceProvider } from 'sprotty/lib/features/context-menu/context-menu-service'; +export { ContextMenuProviderRegistry, DeleteContextMenuItemProvider } from 'sprotty/lib/features/context-menu/menu-providers'; +export * from 'sprotty/lib/features/context-menu/mouse-listener'; + +export * from 'sprotty/lib/features/edge-layout/di.config'; +export * from 'sprotty/lib/features/edge-layout/edge-layout'; +export * from 'sprotty/lib/features/edge-layout/model'; +// Exclude client-side creation features (not supported in GLSP) +// export * from 'sprotty/lib/features/edit/create'; +// export * from 'sprotty/lib/features/edit/create-on-drag'; +export * from 'sprotty/lib/features/edit/delete'; +export * from 'sprotty/lib/features/edit/edit-label'; +export * from 'sprotty/lib/features/edit/edit-label-ui'; +export * from 'sprotty/lib/features/edit/edit-routing'; +export * from 'sprotty/lib/features/edit/model'; +// export * from 'sprotty/lib/features/edit/reconnect'; + +export * from 'sprotty/lib/features/expand/expand'; +export * from 'sprotty/lib/features/expand/model'; +export * from 'sprotty/lib/features/expand/views'; +// Exclude RequestExportSvgAction. Already provided by glsp-protocol +export { ExportSvgCommand, ExportSvgKeyListener, ExportSvgPostprocessor } from 'sprotty/lib/features/export/export'; +export * from 'sprotty/lib/features/export/model'; +// Exclude ExportSvgAction. Already provided by glsp-protocol +export { SvgExporter } from 'sprotty/lib/features/export/svg-exporter'; + +export * from 'sprotty/lib/features/fade/fade'; +export * from 'sprotty/lib/features/fade/model'; + +export * from 'sprotty/lib/features/hover/hover'; +export * from 'sprotty/lib/features/hover/model'; +export * from 'sprotty/lib/features/hover/popup-position-updater'; + +// Alias SModel types +export * from 'sprotty/lib/features/decoration/decoration-placer'; +export { + Decoration, + SDecoration as GDecoration, + SIssue as GIssue, + SIssueSeverity as GIssueSeverity, + // Export as is, we extend it glsp-client to `GIssueMarker` + SIssueMarker +} from 'sprotty/lib/features/decoration/model'; +export * from 'sprotty/lib/features/decoration/views'; + +export * from 'sprotty/lib/features/edge-intersection/intersection-finder'; +export * from 'sprotty/lib/features/edge-intersection/sweepline'; + +export * from 'sprotty/lib/features/move/model'; +export * from 'sprotty/lib/features/move/move'; +export * from 'sprotty/lib/features/move/snap'; + +export * from 'sprotty/lib/features/nameable/model'; + +export * from 'sprotty/lib/features/open/model'; +export * from 'sprotty/lib/features/open/open'; + +export * from 'sprotty/lib/features/projection/model'; +export * from 'sprotty/lib/features/projection/views'; + +export * from 'sprotty/lib/features/routing/abstract-edge-router'; +export * from 'sprotty/lib/features/routing/anchor'; +export * from 'sprotty/lib/features/routing/bezier-anchors'; +export * from 'sprotty/lib/features/routing/bezier-edge-router'; +export * from 'sprotty/lib/features/routing/manhattan-anchors'; +export * from 'sprotty/lib/features/routing/manhattan-edge-router'; +// Alias SModel types +export { + Connectable, + SConnectableElementImpl as GConnectableElement, + SDanglingAnchorImpl as GDanglingAnchor, + SRoutableElementImpl as GRoutableElement, + SRoutingHandleImpl as GRoutingHandle, + RoutingHandleKind, + connectableFeature, + edgeInProgressID, + edgeInProgressTargetHandleID, + getAbsoluteRouteBounds, + getRouteBounds, + isConnectable +} from 'sprotty/lib/features/routing/model'; +export * from 'sprotty/lib/features/routing/polyline-anchors'; +export * from 'sprotty/lib/features/routing/polyline-edge-router'; +export * from 'sprotty/lib/features/routing/routing'; +export * from 'sprotty/lib/features/routing/views'; + +export * from 'sprotty/lib/features/select/model'; +// Alias Select commands with sprotty prefix to avoid clash with glsp-client +export { + GetSelectionCommand, + SelectKeyboardListener, + SelectMouseListener, + SelectAllCommand as SprottySelectAllCommand, + SelectCommand as SprottySelectCommand +} from 'sprotty/lib/features/select/select'; + +// Exclude undo-redo as the client provides its own implementation +// export * from 'sprotty/lib/features/undo-redo/undo-redo'; + +export * from 'sprotty/lib/features/update/model-matching'; +export * from 'sprotty/lib/features/update/update-model'; + +export * from 'sprotty/lib/features/viewport/center-fit'; +export * from 'sprotty/lib/features/viewport/model'; +export * from 'sprotty/lib/features/viewport/scroll'; +// Alias SModel types +export * from 'sprotty/lib/features/viewport/viewport'; +export { ViewportRootElementImpl as GViewportRootElement } from 'sprotty/lib/features/viewport/viewport-root'; +export * from 'sprotty/lib/features/viewport/zoom'; + +export * from 'sprotty/lib/features/zorder/zorder'; + +// ------------------ Graph ------------------ +// Alias SModel types +export { + SCompartmentImpl as GCompartment, + SGraphIndex as GGraphIndex, + SLabelImpl as GLabel, + SNodeImpl as GNode, + SPortImpl as GPort, + // Exported as is, we extend it in glsp-client to GEdge + SEdgeImpl, + // Exported as is, we extend it in glsp-client to GGraph + SGraphImpl +} from 'sprotty/lib/graph/sgraph'; +// Alias SModel types +export { + BezierCurveEdgeView, + SBezierControlHandleView as GBezierControlHandleView, + SBezierCreateHandleView as GBezierCreateHandleView, + SCompartmentView as GCompartmentView, + SGraphView as GGraphView, + SLabelView as GLabelView, + SRoutingHandleView as GRoutingHandleView, + JumpingPolylineEdgeView, + PolylineEdgeView, + PolylineEdgeViewWithGapsOnIntersections +} from 'sprotty/lib/graph/views'; + +// ------------------ Library ------------------ + +export * from 'sprotty/lib/lib/generic-views'; +export * from 'sprotty/lib/lib/html-views'; +export * from 'sprotty/lib/lib/jsx'; +// Alias SModel types +export { + CircularNode, + CircularPort, + DiamondNode, + ForeignObjectElementImpl as GForeignObjectElement, + HtmlRootImpl as GHtmlRoot, + PreRenderedElementImpl as GPreRenderedElement, + ShapedPreRenderedElementImpl as GShapedPreRenderedElement, + RectangularNode, + RectangularPort +} from 'sprotty/lib/lib/model'; +export * from 'sprotty/lib/lib/modules'; +export * from 'sprotty/lib/lib/svg-views'; + +// ------------------ Model Source ------------------ +export * from 'sprotty/lib/model-source/commit-model'; +// Exclude as not supported in GLSP +// export * from 'sprotty/lib/model-source/diagram-server'; +// export * from 'sprotty/lib/model-source/local-model-source'; +export * from 'sprotty/lib/model-source/logging'; +export * from 'sprotty/lib/model-source/model-source'; +// export * from 'sprotty/lib/model-source/websocket'; + +// ------------------ Utilities ------------------ +export * from 'sprotty/lib/utils/browser'; +export * from 'sprotty/lib/utils/codicon'; +export * from 'sprotty/lib/utils/color'; +export * from 'sprotty/lib/utils/geometry'; +export * from 'sprotty/lib/utils/inversify'; +export * from 'sprotty/lib/utils/iterable'; +export * from 'sprotty/lib/utils/keyboard'; +export * from 'sprotty/lib/utils/logging'; +export * from 'sprotty/lib/utils/registry'; diff --git a/packages/glsp-sprotty/src/types.ts b/packages/glsp-sprotty/src/types.ts index a8267553..79376602 100644 --- a/packages/glsp-sprotty/src/types.ts +++ b/packages/glsp-sprotty/src/types.ts @@ -40,6 +40,7 @@ export const TYPES = { IDefaultTool: Symbol('IDefaultTool'), IEditModeListener: Symbol('IEditModeListener'), IMarqueeBehavior: Symbol('IMarqueeBehavior'), + IHelperLineManager: Symbol('IHelperLineManager'), IElementNavigator: Symbol('IElementNavigator'), ILocalElementNavigator: Symbol('ILocalElementNavigator'), IDiagramOptions: Symbol('IDiagramOptions'), diff --git a/packages/protocol/src/client-server-protocol/index.ts b/packages/protocol/src/client-server-protocol/index.ts new file mode 100644 index 00000000..dd8fa481 --- /dev/null +++ b/packages/protocol/src/client-server-protocol/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './base-glsp-client'; +export * from './glsp-client'; +export * from './glsp-server'; +export * from './jsonrpc'; +export * from './types'; diff --git a/packages/protocol/src/client-server-protocol/jsonrpc/index.ts b/packages/protocol/src/client-server-protocol/jsonrpc/index.ts new file mode 100644 index 00000000..b4669937 --- /dev/null +++ b/packages/protocol/src/client-server-protocol/jsonrpc/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './base-jsonrpc-glsp-client'; +export * from './glsp-jsonrpc-client'; +export * from './glsp-jsonrpc-server'; +export * from './websocket-connection'; +export * from './ws-connection-provider'; diff --git a/packages/protocol/src/index.ts b/packages/protocol/src/index.ts index d47a1d92..8dc607af 100644 --- a/packages/protocol/src/index.ts +++ b/packages/protocol/src/index.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2020-2023 EclipseSource and others. + * Copyright (c) 2023 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -13,29 +13,9 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ - -// Partial reexport of sprotty-protocol -export { Viewport } from 'sprotty-protocol/lib/model'; -export * from 'sprotty-protocol/lib/utils/async'; -export * from 'sprotty-protocol/lib/utils/geometry'; -export * from 'sprotty-protocol/lib/utils/json'; -export { applyBounds, cloneModel, findElement, getBasicType, getSubType } from 'sprotty-protocol/lib/utils/model-utils'; -// Default export of @eclipse-glsp/protocol export * from './action-protocol'; -export * from './client-server-protocol/base-glsp-client'; -export * from './client-server-protocol/glsp-client'; -export * from './client-server-protocol/glsp-server'; -export * from './client-server-protocol/jsonrpc/base-jsonrpc-glsp-client'; -export * from './client-server-protocol/jsonrpc/glsp-jsonrpc-client'; -export * from './client-server-protocol/jsonrpc/glsp-jsonrpc-server'; -export * from './client-server-protocol/jsonrpc/websocket-connection'; -export * from './client-server-protocol/jsonrpc/ws-connection-provider'; -export * from './client-server-protocol/types'; -export * from './model/default-types'; -export * from './model/model-schema'; +export * from './client-server-protocol'; +export * from './model'; +export * from './re-exports'; export * from './sprotty-actions'; -export * from './utils/array-util'; -export * from './utils/di-util'; -export * from './utils/disposable'; -export * from './utils/event'; -export * from './utils/type-util'; +export * from './utils'; diff --git a/packages/protocol/src/model/index.ts b/packages/protocol/src/model/index.ts new file mode 100644 index 00000000..d372746e --- /dev/null +++ b/packages/protocol/src/model/index.ts @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './default-types'; +export * from './model-schema'; diff --git a/packages/protocol/src/re-exports.ts b/packages/protocol/src/re-exports.ts new file mode 100644 index 00000000..84d91f5a --- /dev/null +++ b/packages/protocol/src/re-exports.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export { Viewport } from 'sprotty-protocol/lib/model'; +export * from 'sprotty-protocol/lib/utils/async'; +export * from 'sprotty-protocol/lib/utils/geometry'; +export * from 'sprotty-protocol/lib/utils/json'; +export { applyBounds, cloneModel, findElement, getBasicType, getSubType } from 'sprotty-protocol/lib/utils/model-utils'; diff --git a/packages/protocol/src/utils/array-util.ts b/packages/protocol/src/utils/array-util.ts index 501d524b..0d6f8266 100644 --- a/packages/protocol/src/utils/array-util.ts +++ b/packages/protocol/src/utils/array-util.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { Constructor, Primitive } from './type-util'; +import { Constructor, Primitive, TypeGuard } from './type-util'; /** * A union type for for objects that can either be a single element or and array of elements. @@ -180,3 +180,16 @@ export function isStringArray(object: unknown, supportEmpty = false): object is export function isArrayMatching(object: unknown, predicate: (elem: unknown) => boolean, supportEmpty = false): boolean { return Array.isArray(object) && object.every(predicate) && (supportEmpty || object.length > 0); } + +export function partition(source: T[], matchGuard: TypeGuard): { match: T[]; rest: T[] } { + const match: T[] = []; + const rest: T[] = []; + source.forEach(element => { + if (matchGuard(element)) { + match.push(element); + } else { + rest.push(element); + } + }); + return { match, rest }; +} diff --git a/packages/protocol/src/utils/index.ts b/packages/protocol/src/utils/index.ts new file mode 100644 index 00000000..0bb9e30c --- /dev/null +++ b/packages/protocol/src/utils/index.ts @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +export * from './array-util'; +export * from './di-util'; +export * from './disposable'; +export * from './event'; +export * from './test-util'; +export * from './type-util';