@eclipse-glsp/client 0.10.0-next.e5fcffe.169 → 1.1.0-next.282a632.186

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/css/decoration.css +5 -1
  2. package/css/glsp-sprotty.css +39 -0
  3. package/css/tool-palette.css +2 -2
  4. package/lib/base/container-modules.d.ts.map +1 -1
  5. package/lib/base/container-modules.js +10 -9
  6. package/lib/base/container-modules.js.map +1 -1
  7. package/lib/base/editor-context-service.d.ts +2 -3
  8. package/lib/base/editor-context-service.d.ts.map +1 -1
  9. package/lib/base/editor-context-service.js +11 -12
  10. package/lib/base/editor-context-service.js.map +1 -1
  11. package/lib/base/model/update-model-command.d.ts +14 -7
  12. package/lib/base/model/update-model-command.d.ts.map +1 -1
  13. package/lib/base/model/update-model-command.js +31 -41
  14. package/lib/base/model/update-model-command.js.map +1 -1
  15. package/lib/base/source-uri-aware.d.ts +2 -17
  16. package/lib/base/source-uri-aware.d.ts.map +1 -1
  17. package/lib/base/source-uri-aware.js +4 -6
  18. package/lib/base/source-uri-aware.js.map +1 -1
  19. package/lib/features/change-bounds/movement-restrictor.d.ts +36 -6
  20. package/lib/features/change-bounds/movement-restrictor.d.ts.map +1 -1
  21. package/lib/features/change-bounds/movement-restrictor.js +54 -39
  22. package/lib/features/change-bounds/movement-restrictor.js.map +1 -1
  23. package/lib/features/change-bounds/snap.d.ts +20 -5
  24. package/lib/features/change-bounds/snap.d.ts.map +1 -1
  25. package/lib/features/change-bounds/snap.js +19 -8
  26. package/lib/features/change-bounds/snap.js.map +1 -1
  27. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.d.ts +5 -7
  28. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.d.ts.map +1 -1
  29. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.js +7 -14
  30. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.js.map +1 -1
  31. package/lib/features/hover/hover.d.ts +1 -2
  32. package/lib/features/hover/hover.d.ts.map +1 -1
  33. package/lib/features/hover/hover.js +2 -15
  34. package/lib/features/hover/hover.js.map +1 -1
  35. package/lib/features/layout/di.config.d.ts +2 -2
  36. package/lib/features/layout/di.config.d.ts.map +1 -1
  37. package/lib/features/layout/di.config.js +8 -5
  38. package/lib/features/layout/di.config.js.map +1 -1
  39. package/lib/features/layout/hbox-layout.d.ts +1 -1
  40. package/lib/features/layout/hbox-layout.d.ts.map +1 -1
  41. package/lib/features/layout/hbox-layout.js +19 -7
  42. package/lib/features/layout/hbox-layout.js.map +1 -1
  43. package/lib/features/layout/{layout-commands.d.ts → layout-elements-action.d.ts} +75 -57
  44. package/lib/features/layout/layout-elements-action.d.ts.map +1 -0
  45. package/lib/features/layout/{layout-commands.js → layout-elements-action.js} +153 -197
  46. package/lib/features/layout/layout-elements-action.js.map +1 -0
  47. package/lib/features/layout/layout-elements-action.spec.d.ts +3 -0
  48. package/lib/features/layout/layout-elements-action.spec.d.ts.map +1 -0
  49. package/lib/features/layout/{layout-commands.spec.js → layout-elements-action.spec.js} +63 -56
  50. package/lib/features/layout/layout-elements-action.spec.js.map +1 -0
  51. package/lib/features/layout/layouter.d.ts +37 -0
  52. package/lib/features/layout/layouter.d.ts.map +1 -0
  53. package/lib/features/layout/layouter.js +131 -0
  54. package/lib/features/layout/layouter.js.map +1 -0
  55. package/lib/features/layout/vbox-layout.d.ts +1 -1
  56. package/lib/features/layout/vbox-layout.d.ts.map +1 -1
  57. package/lib/features/layout/vbox-layout.js +17 -5
  58. package/lib/features/layout/vbox-layout.js.map +1 -1
  59. package/lib/features/mouse-tool/mouse-tool.d.ts +0 -1
  60. package/lib/features/mouse-tool/mouse-tool.d.ts.map +1 -1
  61. package/lib/features/mouse-tool/mouse-tool.js +9 -5
  62. package/lib/features/mouse-tool/mouse-tool.js.map +1 -1
  63. package/lib/features/select/select-mouse-listener.d.ts +3 -1
  64. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  65. package/lib/features/select/select-mouse-listener.js +62 -0
  66. package/lib/features/select/select-mouse-listener.js.map +1 -1
  67. package/lib/features/select/selection-service.d.ts +12 -3
  68. package/lib/features/select/selection-service.d.ts.map +1 -1
  69. package/lib/features/select/selection-service.js +31 -45
  70. package/lib/features/select/selection-service.js.map +1 -1
  71. package/lib/features/select/selection-service.spec.js +1 -1
  72. package/lib/features/select/selection-service.spec.js.map +1 -1
  73. package/lib/features/source-model-watcher/di.config.d.ts +4 -0
  74. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.d.ts.map +1 -1
  75. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.js +4 -4
  76. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.js.map +1 -1
  77. package/lib/features/{model-source-watcher/model-source-changed-action-handler.d.ts → source-model-watcher/source-model-changed-action-handler.d.ts} +9 -9
  78. package/lib/features/{model-source-watcher/model-source-changed-action-handler.d.ts.map → source-model-watcher/source-model-changed-action-handler.d.ts.map} +1 -1
  79. package/lib/features/{model-source-watcher/model-source-changed-action-handler.js → source-model-watcher/source-model-changed-action-handler.js} +19 -19
  80. package/lib/features/{model-source-watcher/model-source-changed-action-handler.js.map → source-model-watcher/source-model-changed-action-handler.js.map} +1 -1
  81. package/lib/features/tool-feedback/change-bounds-tool-feedback.js +1 -1
  82. package/lib/features/tool-feedback/change-bounds-tool-feedback.js.map +1 -1
  83. package/lib/features/tool-feedback/edge-edit-tool-feedback.d.ts +3 -2
  84. package/lib/features/tool-feedback/edge-edit-tool-feedback.d.ts.map +1 -1
  85. package/lib/features/tool-feedback/edge-edit-tool-feedback.js +13 -6
  86. package/lib/features/tool-feedback/edge-edit-tool-feedback.js.map +1 -1
  87. package/lib/features/tools/di.config.d.ts +9 -2
  88. package/lib/features/tools/di.config.d.ts.map +1 -1
  89. package/lib/features/tools/di.config.js +14 -3
  90. package/lib/features/tools/di.config.js.map +1 -1
  91. package/lib/features/tools/enable-default-tools-on-focus-loss.d.ts +26 -0
  92. package/lib/features/tools/enable-default-tools-on-focus-loss.d.ts.map +1 -0
  93. package/lib/features/tools/enable-default-tools-on-focus-loss.js +29 -0
  94. package/lib/features/tools/enable-default-tools-on-focus-loss.js.map +1 -0
  95. package/lib/features/validation/di.config.d.ts +0 -15
  96. package/lib/features/validation/di.config.d.ts.map +1 -1
  97. package/lib/features/validation/di.config.js +6 -4
  98. package/lib/features/validation/di.config.js.map +1 -1
  99. package/lib/features/validation/issue-marker.d.ts +11 -5
  100. package/lib/features/validation/issue-marker.d.ts.map +1 -1
  101. package/lib/features/validation/issue-marker.js +25 -5
  102. package/lib/features/validation/issue-marker.js.map +1 -1
  103. package/lib/features/validation/marker-navigator.d.ts +11 -17
  104. package/lib/features/validation/marker-navigator.d.ts.map +1 -1
  105. package/lib/features/validation/marker-navigator.js +32 -61
  106. package/lib/features/validation/marker-navigator.js.map +1 -1
  107. package/lib/features/validation/validate.d.ts +8 -19
  108. package/lib/features/validation/validate.d.ts.map +1 -1
  109. package/lib/features/validation/validate.js +29 -55
  110. package/lib/features/validation/validate.js.map +1 -1
  111. package/lib/index.d.ts +6 -6
  112. package/lib/index.d.ts.map +1 -1
  113. package/lib/index.js +16 -15
  114. package/lib/index.js.map +1 -1
  115. package/lib/lib/model.d.ts +1 -1
  116. package/lib/lib/model.d.ts.map +1 -1
  117. package/lib/lib/model.js +3 -3
  118. package/lib/lib/model.js.map +1 -1
  119. package/lib/model-source/glsp-diagram-server.d.ts +1 -1
  120. package/lib/model-source/glsp-diagram-server.d.ts.map +1 -1
  121. package/lib/model-source/glsp-diagram-server.js +1 -1
  122. package/lib/model-source/glsp-diagram-server.js.map +1 -1
  123. package/lib/utils/layout-utils.d.ts +4 -20
  124. package/lib/utils/layout-utils.d.ts.map +1 -1
  125. package/lib/utils/layout-utils.js +1 -1
  126. package/lib/utils/layout-utils.js.map +1 -1
  127. package/lib/views/base-view-module.d.ts.map +1 -1
  128. package/lib/views/base-view-module.js +3 -1
  129. package/lib/views/base-view-module.js.map +1 -1
  130. package/lib/views/glsp-projection-view.d.ts +30 -0
  131. package/lib/views/glsp-projection-view.d.ts.map +1 -0
  132. package/lib/views/glsp-projection-view.js +142 -0
  133. package/lib/views/glsp-projection-view.js.map +1 -0
  134. package/lib/views/index.d.ts +1 -0
  135. package/lib/views/index.d.ts.map +1 -1
  136. package/lib/views/index.js +1 -0
  137. package/lib/views/index.js.map +1 -1
  138. package/package.json +4 -4
  139. package/src/base/container-modules.ts +6 -5
  140. package/src/base/editor-context-service.ts +15 -7
  141. package/src/base/model/update-model-command.ts +48 -34
  142. package/src/base/source-uri-aware.ts +5 -4
  143. package/src/features/change-bounds/movement-restrictor.ts +73 -43
  144. package/src/features/change-bounds/snap.ts +15 -12
  145. package/src/features/context-menu/selection-service-aware-context-menu-mouse-listener.ts +19 -25
  146. package/src/features/hover/hover.ts +2 -24
  147. package/src/features/layout/di.config.ts +14 -6
  148. package/src/features/layout/hbox-layout.ts +24 -7
  149. package/src/features/layout/{layout-commands.spec.ts → layout-elements-action.spec.ts} +56 -48
  150. package/src/features/layout/{layout-commands.ts → layout-elements-action.ts} +195 -207
  151. package/src/features/layout/layouter.ts +137 -0
  152. package/src/features/layout/vbox-layout.ts +22 -5
  153. package/src/features/mouse-tool/mouse-tool.ts +8 -6
  154. package/src/features/select/select-mouse-listener.ts +81 -1
  155. package/src/features/select/selection-service.spec.ts +1 -1
  156. package/src/features/select/selection-service.ts +37 -50
  157. package/src/features/{model-source-watcher → source-model-watcher}/di.config.ts +5 -5
  158. package/src/features/{model-source-watcher/model-source-changed-action-handler.ts → source-model-watcher/source-model-changed-action-handler.ts} +12 -12
  159. package/src/features/tool-feedback/change-bounds-tool-feedback.ts +1 -1
  160. package/src/features/tool-feedback/edge-edit-tool-feedback.ts +14 -6
  161. package/src/features/tools/di.config.ts +13 -2
  162. package/src/features/tools/enable-default-tools-on-focus-loss.ts +33 -0
  163. package/src/features/validation/di.config.ts +10 -7
  164. package/src/features/validation/issue-marker.ts +34 -12
  165. package/src/features/validation/marker-navigator.ts +29 -66
  166. package/src/features/validation/validate.ts +36 -71
  167. package/src/index.ts +8 -7
  168. package/src/lib/model.ts +1 -1
  169. package/src/model-source/glsp-diagram-server.ts +1 -1
  170. package/src/utils/layout-utils.ts +7 -27
  171. package/src/views/base-view-module.ts +5 -1
  172. package/src/views/glsp-projection-view.tsx +166 -0
  173. package/src/views/index.ts +2 -0
  174. package/lib/features/layout/layout-commands.d.ts.map +0 -1
  175. package/lib/features/layout/layout-commands.js.map +0 -1
  176. package/lib/features/layout/layout-commands.spec.d.ts +0 -3
  177. package/lib/features/layout/layout-commands.spec.d.ts.map +0 -1
  178. package/lib/features/layout/layout-commands.spec.js.map +0 -1
  179. package/lib/features/model-source-watcher/di.config.d.ts +0 -4
@@ -0,0 +1,137 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2022 EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+ import {
17
+ BoundsData,
18
+ ILogger,
19
+ LayoutRegistry,
20
+ StatefulLayouter,
21
+ SModelElement,
22
+ Layouter,
23
+ SParentElement,
24
+ LayoutContainer,
25
+ isLayoutContainer
26
+ } from 'sprotty';
27
+ import { injectable } from 'inversify';
28
+ import { Bounds } from 'sprotty-protocol/lib/utils/geometry';
29
+
30
+ @injectable()
31
+ export class LayouterExt extends Layouter {
32
+ override layout(element2boundsData: Map<SModelElement, BoundsData>): void {
33
+ new StatefulLayouterExt(element2boundsData, this.layoutRegistry, this.logger).layout();
34
+ }
35
+ }
36
+
37
+ // 2-pass layout:
38
+ // Step 1: Find "rendered size" of each element (may take resizeContainer into account)
39
+ // Child-to-parent layout
40
+ // Step 2: Extend parents as necessary, then use the adjusted parent size to properly
41
+ // align children (center/end alignments, hGrab/vGrab)
42
+ // Parent-to-children layout
43
+
44
+ export class StatefulLayouterExt extends StatefulLayouter {
45
+ protected toBeLayouted2: (SParentElement & LayoutContainer)[];
46
+
47
+ /**
48
+ *
49
+ * @param elementToBoundsData The map of element to bounds data. Bounds Data are computed from the hidden
50
+ * SVG rendering pass.
51
+ * @param layoutRegistry2 The registry of available layouts.
52
+ * @param log The log.
53
+ */
54
+ constructor(
55
+ protected readonly elementToBoundsData: Map<SModelElement, BoundsData>,
56
+ protected readonly layoutRegistry2: LayoutRegistry,
57
+ log: ILogger
58
+ ) {
59
+ super(elementToBoundsData, layoutRegistry2, log);
60
+ this.toBeLayouted2 = [];
61
+ elementToBoundsData.forEach((data, element) => {
62
+ if (isLayoutContainer(element)) {
63
+ this.toBeLayouted2.push(element);
64
+ }
65
+ });
66
+ for (const element of this.toBeLayouted2) {
67
+ // Clear previous layout information for dynamic-layout objects
68
+ elementToBoundsData.delete(element);
69
+ }
70
+ }
71
+
72
+ override getBoundsData(element: SModelElement): BoundsData {
73
+ let boundsData = this.elementToBoundsData.get(element);
74
+ let bounds = (element as any).bounds;
75
+ if (isLayoutContainer(element) && this.toBeLayouted2.indexOf(element) >= 0) {
76
+ bounds = this.doLayout(element);
77
+ } else if (isLayoutContainer(element)) {
78
+ bounds = {
79
+ x: 0,
80
+ y: 0,
81
+ width: -1,
82
+ height: -1
83
+ };
84
+ }
85
+ if (!boundsData) {
86
+ boundsData = {
87
+ bounds: bounds,
88
+ boundsChanged: false,
89
+ alignmentChanged: false
90
+ };
91
+ this.elementToBoundsData.set(element, boundsData);
92
+ }
93
+ return boundsData;
94
+ }
95
+
96
+ override layout(): void {
97
+ // First pass: apply layout with cleared container data. Will get
98
+ // preferred size for all elements (Children first, then parents)
99
+ while (this.toBeLayouted2.length > 0) {
100
+ const element = this.toBeLayouted2[0];
101
+ this.doLayout(element);
102
+ }
103
+
104
+ this.toBeLayouted2 = [];
105
+ this.elementToBoundsData.forEach((data, element) => {
106
+ if (isLayoutContainer(element)) {
107
+ this.toBeLayouted2.push(element);
108
+ }
109
+ });
110
+
111
+ // Second pass: apply layout with initial size data for all
112
+ // nodes. Update the position/size of all elements, taking
113
+ // vGrab/hGrab into account (parent -> children).
114
+ while (this.toBeLayouted2.length > 0) {
115
+ const element = this.toBeLayouted2[0];
116
+ this.doLayout(element);
117
+ }
118
+ }
119
+
120
+ protected override doLayout(element: SParentElement & LayoutContainer): Bounds {
121
+ const index = this.toBeLayouted2.indexOf(element);
122
+ if (index >= 0) {
123
+ this.toBeLayouted2.splice(index, 1);
124
+ }
125
+ const layout = this.layoutRegistry2.get(element.layout);
126
+ if (layout) {
127
+ layout.layout(element, this);
128
+ }
129
+ const boundsData = this.elementToBoundsData.get(element);
130
+ if (boundsData !== undefined && boundsData.bounds !== undefined) {
131
+ return boundsData.bounds;
132
+ } else {
133
+ this.log.error(element, 'Layout failed');
134
+ return Bounds.EMPTY;
135
+ }
136
+ }
137
+ }
@@ -49,6 +49,9 @@ export class VBoxLayouterExt extends VBoxLayouter {
49
49
 
50
50
  const fixedSize = this.getFixedContainerBounds(container, options, layouter);
51
51
 
52
+ const currentWidth = (boundsData.bounds?.width || 0) - options.paddingLeft - options.paddingRight;
53
+ const currentHeight = (boundsData.bounds?.height || 0) - options.paddingTop - options.paddingBottom;
54
+
52
55
  const maxWidth =
53
56
  options.paddingFactor *
54
57
  (options.resizeContainer
@@ -60,8 +63,11 @@ export class VBoxLayouterExt extends VBoxLayouter {
60
63
  ? Math.max(fixedSize.height - options.paddingTop - options.paddingBottom, childrenSize.height)
61
64
  : Math.max(0, fixedSize.height - options.paddingTop - options.paddingBottom));
62
65
 
66
+ const width = Math.max(currentWidth, maxWidth);
67
+ const height = Math.max(currentHeight, maxHeight);
68
+
63
69
  // Remaining size that can be grabbed by children with the vGrab option
64
- const grabHeight: number = maxHeight - childrenSize.height;
70
+ const grabHeight: number = height - childrenSize.height;
65
71
  // Number of children that request vGrab
66
72
  // FIXME: This approach works fine when only 1 child uses VGrab, but may cause rounding issues
67
73
  // when the grabHeight can't be equally shared by all children.
@@ -70,7 +76,7 @@ export class VBoxLayouterExt extends VBoxLayouter {
70
76
  .filter(opt => opt.vGrab).length;
71
77
 
72
78
  if (maxWidth > 0 && maxHeight > 0) {
73
- const offset = this.layoutChildren(container, layouter, options, maxWidth, maxHeight, grabHeight, grabbingChildren);
79
+ const offset = this.layoutChildren(container, layouter, options, width, height, grabHeight, grabbingChildren);
74
80
  boundsData.bounds = this.getFinalContainerBounds(container, offset, options, childrenSize.width, childrenSize.height);
75
81
  boundsData.boundsChanged = true;
76
82
  }
@@ -155,7 +161,14 @@ export class VBoxLayouterExt extends VBoxLayouter {
155
161
  grabHeight?: number,
156
162
  grabbingChildren?: number
157
163
  ): Point {
164
+ const hAlign = childOptions.hGrab ? 'left' : childOptions.hAlign;
165
+ const dx = this.getDx(hAlign, bounds, maxWidth);
158
166
  let offset = super.layoutChild(child, boundsData, bounds, childOptions, containerOptions, currentOffset, maxWidth, maxHeight);
167
+ boundsData.bounds = {
168
+ ...boundsData.bounds!,
169
+ x: currentOffset.x + dx,
170
+ y: currentOffset.y
171
+ };
159
172
  if (childOptions.hGrab) {
160
173
  boundsData.bounds = {
161
174
  x: boundsData.bounds!.x,
@@ -197,7 +210,7 @@ export class VBoxLayouterExt extends VBoxLayouter {
197
210
  }
198
211
 
199
212
  protected override getChildLayoutOptions(child: SChildElement, containerOptions: VBoxLayoutOptionsExt): VBoxLayoutOptionsExt {
200
- return super.getChildLayoutOptions(child, containerOptions) as VBoxLayoutOptionsExt;
213
+ return super.getChildLayoutOptions(child, this.filterContainerOptions(containerOptions)) as VBoxLayoutOptionsExt;
201
214
  }
202
215
 
203
216
  protected override getLayoutOptions(element: SModelElement): VBoxLayoutOptionsExt {
@@ -250,7 +263,11 @@ export class VBoxLayouterExt extends VBoxLayouter {
250
263
  };
251
264
  }
252
265
 
253
- protected override spread(a: VBoxLayoutOptionsExt, b: VBoxLayoutOptionsExt): VBoxLayoutOptionsExt {
254
- return { ...a, ...b };
266
+ protected filterContainerOptions(containerOptions: VBoxLayoutOptionsExt): VBoxLayoutOptionsExt {
267
+ // Reset object-specific layout options to default before merging,
268
+ // to make sure they won't be inherited (grab, prefSize)
269
+ // eslint-disable-next-line no-null/no-null
270
+ const localOptions = { vGrab: false, hGrab: false, prefHeight: null, prefWidth: null };
271
+ return { ...containerOptions, ...localOptions };
255
272
  }
256
273
  }
@@ -13,6 +13,7 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
+ import { Action } from '@eclipse-glsp/protocol';
16
17
  import { injectable, multiInject, optional } from 'inversify';
17
18
  import { MouseListener, MouseTool, SModelElement, SModelRoot } from 'sprotty';
18
19
  import { TYPES } from '../../base/types';
@@ -48,10 +49,6 @@ export class RankingMouseTool extends MouseTool implements IMouseTool {
48
49
  this.rankedMouseListeners = groupBy(this.mouseListeners, listener => getRank(listener));
49
50
  }
50
51
 
51
- contextMenu(model: SModelRoot, event: MouseEvent): void {
52
- event.preventDefault();
53
- }
54
-
55
52
  protected override handleEvent<K extends MouseListenerMethods>(methodName: K, model: SModelRoot, event: MouseEvent): void {
56
53
  this.focusOnMouseEvent(methodName, model);
57
54
  const element = this.getTargetElement(model, event);
@@ -82,8 +79,13 @@ export class RankingMouseTool extends MouseTool implements IMouseTool {
82
79
  if (actions.length > 0) {
83
80
  event.preventDefault();
84
81
  for (const actionOrPromise of actions) {
85
- const action = await actionOrPromise;
86
- await this.actionDispatcher.dispatch(action);
82
+ if (Action.is(actionOrPromise)) {
83
+ await this.actionDispatcher.dispatch(actionOrPromise);
84
+ } else {
85
+ actionOrPromise.then((action: Action) => {
86
+ this.actionDispatcher.dispatch(action);
87
+ });
88
+ }
87
89
  }
88
90
  }
89
91
  }
@@ -13,7 +13,21 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { SelectMouseListener } from 'sprotty';
16
+ import { Action, SelectAction } from '@eclipse-glsp/protocol';
17
+ import {
18
+ BringToFrontAction,
19
+ findParentByFeature,
20
+ isCtrlOrCmd,
21
+ isSelectable,
22
+ SButton,
23
+ SelectMouseListener,
24
+ SModelElement,
25
+ SModelRoot,
26
+ SRoutableElement,
27
+ SRoutingHandle,
28
+ SwitchEditModeAction
29
+ } from 'sprotty';
30
+ import { toArray } from 'sprotty/lib/utils/iterable';
17
31
  import { DEFAULT_RANK, Ranked } from '../rank/model';
18
32
 
19
33
  /**
@@ -22,4 +36,70 @@ import { DEFAULT_RANK, Ranked } from '../rank/model';
22
36
  */
23
37
  export class RankedSelectMouseListener extends SelectMouseListener implements Ranked {
24
38
  rank: number = DEFAULT_RANK - 1; /* we want to be executed before all default mouse listeners */
39
+
40
+ override mouseDown(target: SModelElement, event: MouseEvent): Action[] {
41
+ const result: Action[] = [];
42
+ if (this.buttonHandlerRegistry !== undefined && target instanceof SButton && target.enabled) {
43
+ const buttonHandler = this.buttonHandlerRegistry.get(target.type);
44
+ if (buttonHandler !== undefined) {
45
+ return buttonHandler.buttonPressed(target);
46
+ }
47
+ }
48
+ const selectableTarget = findParentByFeature(target, isSelectable);
49
+ if (selectableTarget !== undefined || target instanceof SModelRoot) {
50
+ this.hasDragged = false;
51
+ let deselect: SModelElement[] = [];
52
+ // multi-selection?
53
+ if (!isCtrlOrCmd(event)) {
54
+ deselect = toArray(
55
+ target.root.index
56
+ .all()
57
+ .filter(
58
+ element =>
59
+ isSelectable(element) &&
60
+ element.selected &&
61
+ !(selectableTarget instanceof SRoutingHandle && element === (selectableTarget.parent as SModelElement))
62
+ )
63
+ );
64
+ }
65
+ if (selectableTarget !== undefined) {
66
+ if (!selectableTarget.selected) {
67
+ this.wasSelected = false;
68
+ result.push(
69
+ SelectAction.create({
70
+ selectedElementsIDs: [selectableTarget.id],
71
+ deselectedElementsIDs: deselect.map(e => e.id)
72
+ })
73
+ );
74
+ result.push(BringToFrontAction.create([selectableTarget.id]));
75
+ const routableDeselect = deselect.filter(e => e instanceof SRoutableElement).map(e => e.id);
76
+ if (selectableTarget instanceof SRoutableElement) {
77
+ result.push(
78
+ SwitchEditModeAction.create({
79
+ elementsToActivate: [selectableTarget.id],
80
+ elementsToDeactivate: routableDeselect
81
+ })
82
+ );
83
+ } else if (routableDeselect.length > 0) {
84
+ result.push(SwitchEditModeAction.create({ elementsToDeactivate: routableDeselect }));
85
+ }
86
+ } else if (isCtrlOrCmd(event)) {
87
+ this.wasSelected = false;
88
+ result.push(SelectAction.create({ deselectedElementsIDs: [selectableTarget.id] }));
89
+ if (selectableTarget instanceof SRoutableElement) {
90
+ result.push(SwitchEditModeAction.create({ elementsToDeactivate: [selectableTarget.id] }));
91
+ }
92
+ } else {
93
+ this.wasSelected = true;
94
+ }
95
+ } else {
96
+ result.push(SelectAction.create({ deselectedElementsIDs: deselect.map(e => e.id) }));
97
+ const routableDeselect = deselect.filter(e => e instanceof SRoutableElement).map(e => e.id);
98
+ if (routableDeselect.length > 0) {
99
+ result.push(SwitchEditModeAction.create({ elementsToDeactivate: routableDeselect }));
100
+ }
101
+ }
102
+ }
103
+ return result;
104
+ }
25
105
  }
@@ -347,7 +347,7 @@ describe('SelectionService', () => {
347
347
  expect(selectionService.hasSelectedElements()).to.equal(expectedSelection.length > 0);
348
348
  expect(selectionService.getSelectedElementIDs()).to.have.lengthOf(expectedSelection.length);
349
349
  if (expectedSelection.length > 0) {
350
- expect(selectionService.getSelectedElementIDs()).to.have.all.keys(...expectedSelection);
350
+ expect(selectionService.getSelectedElementIDs()).to.have.ordered.members(expectedSelection);
351
351
  }
352
352
  }
353
353
 
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Action, distinctAdd, remove, SelectAction, SelectAllAction } from '@eclipse-glsp/protocol';
16
+ import { Action, distinctAdd, pluck, remove, SelectAction, SelectAllAction } from '@eclipse-glsp/protocol';
17
17
  import { inject, injectable, multiInject, optional } from 'inversify';
18
18
  import {
19
19
  Command,
@@ -25,12 +25,11 @@ import {
25
25
  SelectAllCommand as SprottySelectAllCommand,
26
26
  SelectCommand as SprottySelectCommand,
27
27
  SModelElement,
28
- SModelRoot,
29
- SParentElement
28
+ SModelRoot
30
29
  } from 'sprotty';
31
30
  import { SModelRootListener } from '../../base/model/update-model-command';
32
31
  import { TYPES } from '../../base/types';
33
- import { getElements } from '../../utils/smodel-util';
32
+ import { getElements, getMatchingElements } from '../../utils/smodel-util';
34
33
  import { IFeedbackActionDispatcher } from '../tool-feedback/feedback-action-dispatcher';
35
34
  import { SelectFeedbackAction } from './select-feedback-action';
36
35
 
@@ -135,8 +134,8 @@ export class SelectionService implements SModelRootListener {
135
134
  * QUERY METHODS
136
135
  */
137
136
 
138
- getSelectedElementIDs(): Set<string> {
139
- return this.selectedElementIDs;
137
+ getSelectedElementIDs(): string[] {
138
+ return [...this.selectedElementIDs];
140
139
  }
141
140
 
142
141
  hasSelectedElements(): boolean {
@@ -152,6 +151,11 @@ export class SelectionService implements SModelRootListener {
152
151
  }
153
152
  }
154
153
 
154
+ /**
155
+ * Handles a {@link SelectAction} and propagates the new selection to the {@link SelectionService}.
156
+ * Other tools might be selection-sensitive which means {@link SelectAction}s must be processed as fast as possible.
157
+ * Handling the action with a command ensures that the action is processed before the next render tick.
158
+ */
155
159
  @injectable()
156
160
  export class SelectCommand extends Command {
157
161
  static readonly KIND = SprottySelectCommand.KIND;
@@ -168,36 +172,30 @@ export class SelectCommand extends Command {
168
172
 
169
173
  execute(context: CommandExecutionContext): SModelRoot {
170
174
  const model = context.root;
171
- this.action.selectedElementsIDs.forEach(id => {
172
- const element = model.index.getById(id);
173
- if (element instanceof SChildElement && isSelectable(element)) {
174
- this.selected.push(element);
175
- }
176
- });
177
- this.action.deselectedElementsIDs.forEach(id => {
178
- const element = model.index.getById(id);
179
- if (element instanceof SChildElement && isSelectable(element)) {
180
- this.deselected.push(element);
181
- }
182
- });
183
- return this.redo(context);
175
+ const selectionGuard = (element: any): element is SModelElement => element instanceof SChildElement && isSelectable(element);
176
+ const selectedElements = getElements(model.index, this.action.selectedElementsIDs, selectionGuard);
177
+ const deselectedElements = getElements(model.index, this.action.deselectedElementsIDs, selectionGuard);
178
+
179
+ this.selectionService.updateSelection(model, pluck(selectedElements, 'id'), pluck(deselectedElements, 'id'));
180
+ return model;
184
181
  }
185
182
 
183
+ // Basically no-op since client-side undo is not supported in GLSP.
186
184
  undo(context: CommandExecutionContext): SModelRoot {
187
- const select = this.deselected.map(element => element.id);
188
- const deselect = this.selected.map(element => element.id);
189
- this.selectionService.updateSelection(context.root, select, deselect);
190
185
  return context.root;
191
186
  }
192
187
 
188
+ // Basically no-op since client-side redo is not supported in GLSP.
193
189
  redo(context: CommandExecutionContext): SModelRoot {
194
- const select = this.selected.map(element => element.id);
195
- const deselect = this.deselected.map(element => element.id);
196
- this.selectionService.updateSelection(context.root, select, deselect);
197
190
  return context.root;
198
191
  }
199
192
  }
200
193
 
194
+ /**
195
+ * Handles a {@link SelectAllAction} and propagates the new selection to the {@link SelectionService}.
196
+ * Other tools might be selection-sensitive which means {@link SelectionAllAction}s must be processed as fast as possible.
197
+ * Handling the action with a command ensures that the action is processed before the next render tick.
198
+ */
201
199
  @injectable()
202
200
  export class SelectAllCommand extends Command {
203
201
  static readonly KIND = SprottySelectAllCommand.KIND;
@@ -211,38 +209,27 @@ export class SelectAllCommand extends Command {
211
209
  }
212
210
 
213
211
  execute(context: CommandExecutionContext): SModelRoot {
214
- return this.redo(context);
212
+ const model = context.root;
213
+ const selectionGuard = (element: any): element is SModelElement => element instanceof SChildElement && isSelectable(element);
214
+
215
+ const selectables = getMatchingElements(model.index, selectionGuard);
216
+ const selectableIds = pluck(selectables, 'id');
217
+ if (this.action.select) {
218
+ this.selectionService.updateSelection(model, selectableIds, []);
219
+ } else {
220
+ this.selectionService.updateSelection(model, [], selectableIds);
221
+ }
222
+
223
+ return model;
215
224
  }
216
225
 
226
+ // Basically no-op since client-side undo is not supported in GLSP.
217
227
  undo(context: CommandExecutionContext): SModelRoot {
218
- const index = context.root.index;
219
- for (const previousState of this.previousSelection) {
220
- const element = index.getById(previousState[0]);
221
- if (element !== undefined && isSelectable(element)) {
222
- element.selected = previousState[1];
223
- }
224
- }
225
228
  return context.root;
226
229
  }
227
230
 
231
+ // Basically no-op since client-side redo is not supported in GLSP.
228
232
  redo(context: CommandExecutionContext): SModelRoot {
229
- const selectables: string[] = [];
230
- this.selectAll(context.root, this.action.select, selectables);
231
- if (this.action.select) {
232
- this.selectionService.updateSelection(context.root, selectables, []);
233
- } else {
234
- this.selectionService.updateSelection(context.root, [], selectables);
235
- }
236
233
  return context.root;
237
234
  }
238
-
239
- protected selectAll(element: SParentElement, newState: boolean, selected: string[]): void {
240
- if (isSelectable(element)) {
241
- this.previousSelection.set(element.id, element.selected);
242
- selected.push(element.id);
243
- }
244
- for (const child of element.children) {
245
- this.selectAll(child, newState, selected);
246
- }
247
- }
248
235
  }
@@ -13,13 +13,13 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { ModelSourceChangedAction } from '@eclipse-glsp/protocol';
16
+ import { SourceModelChangedAction } from '@eclipse-glsp/protocol';
17
17
  import { ContainerModule } from 'inversify';
18
18
  import { configureActionHandler } from 'sprotty';
19
- import { ModelSourceChangedActionHandler } from './model-source-changed-action-handler';
19
+ import { SourceModelChangedActionHandler } from './source-model-changed-action-handler';
20
20
 
21
- const modelSourceWatcherModule = new ContainerModule((bind, _unbind, isBound) => {
22
- configureActionHandler({ bind, isBound }, ModelSourceChangedAction.KIND, ModelSourceChangedActionHandler);
21
+ const sourceModelWatcherModule = new ContainerModule((bind, _unbind, isBound) => {
22
+ configureActionHandler({ bind, isBound }, SourceModelChangedAction.KIND, SourceModelChangedActionHandler);
23
23
  });
24
24
 
25
- export default modelSourceWatcherModule;
25
+ export default sourceModelWatcherModule;
@@ -13,43 +13,43 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Action, ModelSourceChangedAction, ServerMessageAction, ServerStatusAction } from '@eclipse-glsp/protocol';
16
+ import { Action, ServerMessageAction, ServerStatusAction, SourceModelChangedAction } from '@eclipse-glsp/protocol';
17
17
  import { inject, injectable, optional } from 'inversify';
18
18
  import { IActionDispatcher, IActionHandler, ViewerOptions } from 'sprotty';
19
19
  import { TYPES } from '../../base/types';
20
20
 
21
21
  /**
22
- * An external handler of the model source change event.
22
+ * An external handler of the source model change event.
23
23
  *
24
24
  * This allows external applications to react specifically to this event, e.g. by bringing up the diagram,
25
25
  * check their dirty state, show a dialog, etc.
26
26
  */
27
27
  @injectable()
28
- export abstract class ExternalModelSourceChangedHandler {
28
+ export abstract class ExternalSourceModelChangedHandler {
29
29
  /**
30
- * Notifies about a change of the model source.
30
+ * Notifies about a change of the source model.
31
31
  * @returns actions to be dispatched after the notification.
32
32
  */
33
- abstract notifyModelSourceChange(modelSourceName: string, options: ViewerOptions): Promise<Action[]>;
33
+ abstract notifySourceModelChange(sourceModelName: string, options: ViewerOptions): Promise<Action[]>;
34
34
  }
35
35
 
36
36
  @injectable()
37
- export class ModelSourceChangedActionHandler implements IActionHandler {
37
+ export class SourceModelChangedActionHandler implements IActionHandler {
38
38
  @inject(TYPES.IActionDispatcher)
39
39
  protected dispatcher: IActionDispatcher;
40
40
 
41
41
  @inject(TYPES.ViewerOptions)
42
42
  protected options: ViewerOptions;
43
43
 
44
- @inject(ExternalModelSourceChangedHandler)
44
+ @inject(ExternalSourceModelChangedHandler)
45
45
  @optional()
46
- protected externalModelSourceChangedHandler?: ExternalModelSourceChangedHandler;
46
+ protected externalModelSourceChangedHandler?: ExternalSourceModelChangedHandler;
47
47
 
48
48
  handle(action: Action): void {
49
- if (ModelSourceChangedAction.is(action)) {
49
+ if (SourceModelChangedAction.is(action)) {
50
50
  if (this.externalModelSourceChangedHandler) {
51
51
  this.externalModelSourceChangedHandler
52
- .notifyModelSourceChange(action.modelSourceName, this.options)
52
+ .notifySourceModelChange(action.sourceModelName, this.options)
53
53
  .then(actions => this.dispatcher.dispatchAll(actions));
54
54
  return;
55
55
  }
@@ -57,8 +57,8 @@ export class ModelSourceChangedActionHandler implements IActionHandler {
57
57
  }
58
58
  }
59
59
 
60
- protected showSimpleNotification(action: ModelSourceChangedAction): void {
61
- const message = `The model source ${action.modelSourceName} has changed. You might want to consider reloading.`;
60
+ protected showSimpleNotification(action: SourceModelChangedAction): void {
61
+ const message = `The source model ${action.sourceModelName} has changed. You might want to consider reloading.`;
62
62
  const timeout = 0;
63
63
  const severity = 'WARNING';
64
64
  this.dispatcher.dispatchAll([
@@ -224,7 +224,7 @@ export class FeedbackMoveMouseListener extends MouseListener {
224
224
  protected validateMove(startPostion: Point, toPosition: Point, element: SModelElement, isFinished: boolean): Point {
225
225
  let newPosition = toPosition;
226
226
  if (this.tool.movementRestrictor) {
227
- const valid = this.tool.movementRestrictor.validate(toPosition, element);
227
+ const valid = this.tool.movementRestrictor.validate(element, toPosition);
228
228
  let action;
229
229
  if (!valid) {
230
230
  action = createMovementRestrictionFeedback(element, this.tool.movementRestrictor);
@@ -265,20 +265,20 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener {
265
265
  }
266
266
  const positionUpdate = this.pointPositionUpdater.updatePosition(target, { x: event.pageX, y: event.pageY }, !event.altKey);
267
267
  if (positionUpdate) {
268
- const moveActions = this.handleMoveOnClient(target, positionUpdate);
268
+ const moveActions = this.handleMoveOnClient(target, positionUpdate, !event.altKey);
269
269
  result.push(...moveActions);
270
270
  }
271
271
  return result;
272
272
  }
273
273
 
274
- protected handleMoveOnClient(target: SModelElement, positionUpdate: Point): Action[] {
274
+ protected handleMoveOnClient(target: SModelElement, positionUpdate: Point, isSnap: boolean): Action[] {
275
275
  const handleMoves: ElementMove[] = [];
276
276
  target.root.index
277
277
  .all()
278
278
  .filter(element => isSelected(element))
279
279
  .forEach(element => {
280
280
  if (isRoutingHandle(element)) {
281
- const elementMove = this.toElementMove(element, positionUpdate);
281
+ const elementMove = this.toElementMove(element, positionUpdate, isSnap);
282
282
  if (elementMove) {
283
283
  handleMoves.push(elementMove);
284
284
  }
@@ -290,21 +290,29 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener {
290
290
  return [];
291
291
  }
292
292
 
293
- protected toElementMove(element: SRoutingHandle, positionDelta: Point): ElementMove | undefined {
293
+ protected toElementMove(element: SRoutingHandle, positionDelta: Point, isSnap: boolean): ElementMove | undefined {
294
294
  const point = this.getHandlePosition(element);
295
295
  if (point !== undefined) {
296
+ const snappedPoint = this.getSnappedHandlePosition(element, point, isSnap);
296
297
  return {
297
298
  elementId: element.id,
298
299
  fromPosition: point,
299
300
  toPosition: {
300
- x: point.x + positionDelta.x,
301
- y: point.y + positionDelta.y
301
+ x: snappedPoint.x + positionDelta.x,
302
+ y: snappedPoint.y + positionDelta.y
302
303
  }
303
304
  };
304
305
  }
305
306
  return undefined;
306
307
  }
307
308
 
309
+ protected getSnappedHandlePosition(element: SRoutingHandle, point: Point, isSnap: boolean): Point {
310
+ if (this.snapper && isSnap) {
311
+ return this.snapper.snap(point, element);
312
+ }
313
+ return point;
314
+ }
315
+
308
316
  protected getHandlePosition(handle: SRoutingHandle): Point | undefined {
309
317
  if (this.edgeRouterRegistry) {
310
318
  const parent = handle.parent;