@eclipse-glsp/client 0.10.0-next.e5fcffe.169 → 1.1.0-next.fe0b297.182

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 (164) 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 +6 -5
  38. package/lib/features/layout/di.config.js.map +1 -1
  39. package/lib/features/layout/{layout-commands.d.ts → layout-elements-action.d.ts} +75 -57
  40. package/lib/features/layout/layout-elements-action.d.ts.map +1 -0
  41. package/lib/features/layout/{layout-commands.js → layout-elements-action.js} +153 -197
  42. package/lib/features/layout/layout-elements-action.js.map +1 -0
  43. package/lib/features/layout/layout-elements-action.spec.d.ts +3 -0
  44. package/lib/features/layout/layout-elements-action.spec.d.ts.map +1 -0
  45. package/lib/features/layout/{layout-commands.spec.js → layout-elements-action.spec.js} +63 -56
  46. package/lib/features/layout/layout-elements-action.spec.js.map +1 -0
  47. package/lib/features/mouse-tool/mouse-tool.d.ts +0 -1
  48. package/lib/features/mouse-tool/mouse-tool.d.ts.map +1 -1
  49. package/lib/features/mouse-tool/mouse-tool.js +9 -5
  50. package/lib/features/mouse-tool/mouse-tool.js.map +1 -1
  51. package/lib/features/select/select-mouse-listener.d.ts +3 -1
  52. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  53. package/lib/features/select/select-mouse-listener.js +62 -0
  54. package/lib/features/select/select-mouse-listener.js.map +1 -1
  55. package/lib/features/select/selection-service.d.ts +12 -3
  56. package/lib/features/select/selection-service.d.ts.map +1 -1
  57. package/lib/features/select/selection-service.js +31 -45
  58. package/lib/features/select/selection-service.js.map +1 -1
  59. package/lib/features/select/selection-service.spec.js +1 -1
  60. package/lib/features/select/selection-service.spec.js.map +1 -1
  61. package/lib/features/source-model-watcher/di.config.d.ts +4 -0
  62. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.d.ts.map +1 -1
  63. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.js +4 -4
  64. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.js.map +1 -1
  65. 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
  66. 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
  67. package/lib/features/{model-source-watcher/model-source-changed-action-handler.js → source-model-watcher/source-model-changed-action-handler.js} +19 -19
  68. 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
  69. package/lib/features/tool-feedback/change-bounds-tool-feedback.js +1 -1
  70. package/lib/features/tool-feedback/change-bounds-tool-feedback.js.map +1 -1
  71. package/lib/features/tool-feedback/edge-edit-tool-feedback.d.ts +3 -2
  72. package/lib/features/tool-feedback/edge-edit-tool-feedback.d.ts.map +1 -1
  73. package/lib/features/tool-feedback/edge-edit-tool-feedback.js +13 -6
  74. package/lib/features/tool-feedback/edge-edit-tool-feedback.js.map +1 -1
  75. package/lib/features/tools/di.config.d.ts +9 -2
  76. package/lib/features/tools/di.config.d.ts.map +1 -1
  77. package/lib/features/tools/di.config.js +14 -3
  78. package/lib/features/tools/di.config.js.map +1 -1
  79. package/lib/features/tools/enable-default-tools-on-focus-loss.d.ts +26 -0
  80. package/lib/features/tools/enable-default-tools-on-focus-loss.d.ts.map +1 -0
  81. package/lib/features/tools/enable-default-tools-on-focus-loss.js +29 -0
  82. package/lib/features/tools/enable-default-tools-on-focus-loss.js.map +1 -0
  83. package/lib/features/validation/di.config.d.ts +0 -15
  84. package/lib/features/validation/di.config.d.ts.map +1 -1
  85. package/lib/features/validation/di.config.js +6 -4
  86. package/lib/features/validation/di.config.js.map +1 -1
  87. package/lib/features/validation/issue-marker.d.ts +11 -5
  88. package/lib/features/validation/issue-marker.d.ts.map +1 -1
  89. package/lib/features/validation/issue-marker.js +25 -5
  90. package/lib/features/validation/issue-marker.js.map +1 -1
  91. package/lib/features/validation/marker-navigator.d.ts +11 -17
  92. package/lib/features/validation/marker-navigator.d.ts.map +1 -1
  93. package/lib/features/validation/marker-navigator.js +32 -61
  94. package/lib/features/validation/marker-navigator.js.map +1 -1
  95. package/lib/features/validation/validate.d.ts +8 -19
  96. package/lib/features/validation/validate.d.ts.map +1 -1
  97. package/lib/features/validation/validate.js +29 -55
  98. package/lib/features/validation/validate.js.map +1 -1
  99. package/lib/index.d.ts +6 -6
  100. package/lib/index.d.ts.map +1 -1
  101. package/lib/index.js +16 -15
  102. package/lib/index.js.map +1 -1
  103. package/lib/lib/model.d.ts +1 -1
  104. package/lib/lib/model.d.ts.map +1 -1
  105. package/lib/lib/model.js +3 -3
  106. package/lib/lib/model.js.map +1 -1
  107. package/lib/model-source/glsp-diagram-server.d.ts +1 -1
  108. package/lib/model-source/glsp-diagram-server.d.ts.map +1 -1
  109. package/lib/model-source/glsp-diagram-server.js +1 -1
  110. package/lib/model-source/glsp-diagram-server.js.map +1 -1
  111. package/lib/utils/layout-utils.d.ts +4 -20
  112. package/lib/utils/layout-utils.d.ts.map +1 -1
  113. package/lib/utils/layout-utils.js +1 -1
  114. package/lib/utils/layout-utils.js.map +1 -1
  115. package/lib/views/base-view-module.d.ts.map +1 -1
  116. package/lib/views/base-view-module.js +3 -1
  117. package/lib/views/base-view-module.js.map +1 -1
  118. package/lib/views/glsp-projection-view.d.ts +30 -0
  119. package/lib/views/glsp-projection-view.d.ts.map +1 -0
  120. package/lib/views/glsp-projection-view.js +142 -0
  121. package/lib/views/glsp-projection-view.js.map +1 -0
  122. package/lib/views/index.d.ts +1 -0
  123. package/lib/views/index.d.ts.map +1 -1
  124. package/lib/views/index.js +1 -0
  125. package/lib/views/index.js.map +1 -1
  126. package/package.json +4 -4
  127. package/src/base/container-modules.ts +6 -5
  128. package/src/base/editor-context-service.ts +15 -7
  129. package/src/base/model/update-model-command.ts +48 -34
  130. package/src/base/source-uri-aware.ts +5 -4
  131. package/src/features/change-bounds/movement-restrictor.ts +73 -43
  132. package/src/features/change-bounds/snap.ts +15 -12
  133. package/src/features/context-menu/selection-service-aware-context-menu-mouse-listener.ts +19 -25
  134. package/src/features/hover/hover.ts +2 -24
  135. package/src/features/layout/di.config.ts +12 -6
  136. package/src/features/layout/{layout-commands.spec.ts → layout-elements-action.spec.ts} +56 -48
  137. package/src/features/layout/{layout-commands.ts → layout-elements-action.ts} +195 -207
  138. package/src/features/mouse-tool/mouse-tool.ts +8 -6
  139. package/src/features/select/select-mouse-listener.ts +81 -1
  140. package/src/features/select/selection-service.spec.ts +1 -1
  141. package/src/features/select/selection-service.ts +37 -50
  142. package/src/features/{model-source-watcher → source-model-watcher}/di.config.ts +5 -5
  143. package/src/features/{model-source-watcher/model-source-changed-action-handler.ts → source-model-watcher/source-model-changed-action-handler.ts} +12 -12
  144. package/src/features/tool-feedback/change-bounds-tool-feedback.ts +1 -1
  145. package/src/features/tool-feedback/edge-edit-tool-feedback.ts +14 -6
  146. package/src/features/tools/di.config.ts +13 -2
  147. package/src/features/tools/enable-default-tools-on-focus-loss.ts +33 -0
  148. package/src/features/validation/di.config.ts +10 -7
  149. package/src/features/validation/issue-marker.ts +34 -12
  150. package/src/features/validation/marker-navigator.ts +29 -66
  151. package/src/features/validation/validate.ts +36 -71
  152. package/src/index.ts +8 -7
  153. package/src/lib/model.ts +1 -1
  154. package/src/model-source/glsp-diagram-server.ts +1 -1
  155. package/src/utils/layout-utils.ts +7 -27
  156. package/src/views/base-view-module.ts +5 -1
  157. package/src/views/glsp-projection-view.tsx +166 -0
  158. package/src/views/index.ts +2 -0
  159. package/lib/features/layout/layout-commands.d.ts.map +0 -1
  160. package/lib/features/layout/layout-commands.js.map +0 -1
  161. package/lib/features/layout/layout-commands.spec.d.ts +0 -3
  162. package/lib/features/layout/layout-commands.spec.d.ts.map +0 -1
  163. package/lib/features/layout/layout-commands.spec.js.map +0 -1
  164. package/lib/features/model-source-watcher/di.config.d.ts +0 -4
@@ -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;
@@ -16,19 +16,25 @@
16
16
  import { TriggerEdgeCreationAction, TriggerNodeCreationAction } from '@eclipse-glsp/protocol';
17
17
  import { ContainerModule, interfaces } from 'inversify';
18
18
  import { configureActionHandler, configureModelElement } from 'sprotty';
19
+ import { FocusStateChangedAction } from '../../base/actions/focus-change-action';
19
20
  import { TYPES } from '../../base/types';
20
21
  import { MARQUEE } from '../tool-feedback/marquee-tool-feedback';
21
22
  import { ChangeBoundsTool } from './change-bounds-tool';
22
23
  import { DelKeyDeleteTool, MouseDeleteTool } from './delete-tool';
23
24
  import { EdgeCreationTool } from './edge-creation-tool';
24
25
  import { EdgeEditTool } from './edge-edit-tool';
26
+ import { EnableDefaultToolsOnFocusLossHandler } from './enable-default-tools-on-focus-loss';
25
27
  import { MarqueeMouseTool } from './marquee-mouse-tool';
26
28
  import { MarqueeTool } from './marquee-tool';
27
29
  import { MarqueeNode } from './model';
28
30
  import { NodeCreationTool } from './node-creation-tool';
29
31
  import { MarqueeView } from './view';
30
32
 
31
- const toolsModule = new ContainerModule((bind, _unbind, isBound) => {
33
+ /**
34
+ * Registers the default tools of GLSP (node and edge creation, changing bounds, edge editing, deletion)
35
+ * and adds the marquee selection tool.
36
+ */
37
+ export const toolsModule = new ContainerModule((bind, _unbind, isBound) => {
32
38
  // Register default tools
33
39
  bind(TYPES.IDefaultTool).to(ChangeBoundsTool);
34
40
  bind(TYPES.IDefaultTool).to(EdgeEditTool);
@@ -52,4 +58,9 @@ export function configureMarqueeTool(context: { bind: interfaces.Bind; isBound:
52
58
  context.bind(TYPES.ITool).to(MarqueeMouseTool);
53
59
  }
54
60
 
55
- export default toolsModule;
61
+ /**
62
+ * Enables the default tools in the tool manager if the diagram looses focus.
63
+ */
64
+ export const enableDefaultToolsOnFocusLossModule = new ContainerModule((bind, _unbind, isBound) => {
65
+ configureActionHandler({ bind, isBound }, FocusStateChangedAction.KIND, EnableDefaultToolsOnFocusLossHandler);
66
+ });
@@ -0,0 +1,33 @@
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 { Action } from '@eclipse-glsp/protocol';
17
+ import { injectable } from 'inversify';
18
+ import { EnableDefaultToolsAction, IActionHandler, ICommand } from 'sprotty';
19
+ import { FocusStateChangedAction } from '../../base/actions/focus-change-action';
20
+
21
+ /**
22
+ * Action handler that enables the default tools when the diagram lost focus.
23
+ * @see FocusTracker
24
+ * @see FocusStateChangedAction
25
+ */
26
+ @injectable()
27
+ export class EnableDefaultToolsOnFocusLossHandler implements IActionHandler {
28
+ handle(action: Action): void | Action | ICommand {
29
+ if (FocusStateChangedAction.is(action) && !action.hasFocus) {
30
+ return EnableDefaultToolsAction.create();
31
+ }
32
+ }
33
+ }
@@ -13,30 +13,33 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
+ import { SetMarkersAction } from '@eclipse-glsp/protocol';
16
17
  import { ContainerModule } from 'inversify';
17
- import { configureCommand } from 'sprotty';
18
+ import { configureActionHandler, configureCommand } from 'sprotty';
18
19
  import { TYPES } from '../../base/types';
19
20
  import {
20
21
  LeftToRightTopToBottomComparator,
21
22
  MarkerNavigator,
22
23
  MarkerNavigatorContextMenuItemProvider,
23
24
  MarkerNavigatorKeyListener,
24
- NavigateToMarkerCommand,
25
+ NavigateToMarkerAction,
26
+ NavigateToMarkerActionHandler,
25
27
  SModelElementComparator
26
28
  } from './marker-navigator';
27
- import { ApplyMarkersCommand, DeleteMarkersCommand, SetMarkersCommand, ValidationFeedbackEmitter } from './validate';
29
+ import { ApplyMarkersCommand, DeleteMarkersCommand, SetMarkersActionHandler, ValidationFeedbackEmitter } from './validate';
28
30
 
29
31
  export const validationModule = new ContainerModule((bind, _unbind, isBound) => {
30
- configureCommand({ bind, isBound }, SetMarkersCommand);
31
- configureCommand({ bind, isBound }, ApplyMarkersCommand);
32
- configureCommand({ bind, isBound }, DeleteMarkersCommand);
32
+ const context = { bind, isBound };
33
+ configureActionHandler(context, SetMarkersAction.KIND, SetMarkersActionHandler);
34
+ configureCommand(context, ApplyMarkersCommand);
35
+ configureCommand(context, DeleteMarkersCommand);
33
36
  bind(ValidationFeedbackEmitter).toSelf().inSingletonScope();
34
37
  });
35
38
 
36
39
  export const markerNavigatorModule = new ContainerModule((bind, _unbind, isBound) => {
37
40
  bind(SModelElementComparator).to(LeftToRightTopToBottomComparator).inSingletonScope();
38
41
  bind(MarkerNavigator).toSelf().inSingletonScope();
39
- configureCommand({ bind, isBound }, NavigateToMarkerCommand);
42
+ configureActionHandler({ bind, isBound }, NavigateToMarkerAction.KIND, NavigateToMarkerActionHandler);
40
43
  });
41
44
 
42
45
  /**
@@ -13,14 +13,23 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Marker, MarkerKind } from '@eclipse-glsp/protocol';
17
- import { SDecoration, SIssue, SIssueMarker, SParentElement } from 'sprotty';
16
+ import { Bounds, Marker, MarkerKind } from '@eclipse-glsp/protocol';
17
+ import { isBoundsAware, Projectable, SDecoration, SIssue, SIssueMarker, SIssueSeverity, SParentElement } from 'sprotty';
18
18
 
19
- export class GIssueMarker extends SIssueMarker {
19
+ export class GIssueMarker extends SIssueMarker implements Projectable {
20
20
  constructor() {
21
21
  super();
22
22
  this.features = new Set<symbol>(SDecoration.DEFAULT_FEATURES);
23
23
  }
24
+ projectionCssClasses: string[];
25
+ projectedBounds?: Bounds;
26
+ override issues: SIssue[] = [];
27
+ override type = 'marker';
28
+
29
+ computeProjectionCssClasses(): void {
30
+ const severityCss = getSeverity(this);
31
+ this.projectionCssClasses = ['sprotty-issue', 'sprotty-' + severityCss];
32
+ }
24
33
  }
25
34
 
26
35
  /**
@@ -31,14 +40,15 @@ export class GIssueMarker extends SIssueMarker {
31
40
  * @returns the child `SIssueMarker` or a new `SIssueMarker` if no such child exists.
32
41
  */
33
42
  export function getOrCreateSIssueMarker(modelElement: SParentElement): SIssueMarker {
34
- let issueMarker: SIssueMarker | undefined;
43
+ let issueMarker: GIssueMarker | undefined;
35
44
 
36
45
  issueMarker = getSIssueMarker(modelElement);
37
46
 
38
47
  if (issueMarker === undefined) {
39
48
  issueMarker = new GIssueMarker();
40
- issueMarker.type = 'marker';
41
- issueMarker.issues = new Array<SIssue>();
49
+ if (isBoundsAware(modelElement)) {
50
+ issueMarker.projectedBounds = modelElement.parentToLocal(modelElement.bounds);
51
+ }
42
52
  modelElement.add(issueMarker);
43
53
  }
44
54
 
@@ -51,11 +61,11 @@ export function getOrCreateSIssueMarker(modelElement: SParentElement): SIssueMar
51
61
  * @param modelElement for which the `SIssueMarker` should be retrieved.
52
62
  * @returns the child `SIssueMarker` or `undefined` if no such child exists.
53
63
  */
54
- export function getSIssueMarker(modelElement: SParentElement): SIssueMarker | undefined {
55
- let issueMarker: SIssueMarker | undefined;
64
+ export function getSIssueMarker(modelElement: SParentElement): GIssueMarker | undefined {
65
+ let issueMarker: GIssueMarker | undefined;
56
66
 
57
67
  for (const child of modelElement.children) {
58
- if (child instanceof SIssueMarker) {
68
+ if (child instanceof GIssueMarker) {
59
69
  issueMarker = child;
60
70
  }
61
71
  }
@@ -69,8 +79,8 @@ export function getSIssueMarker(modelElement: SParentElement): SIssueMarker | un
69
79
  * @param marker `Marker` for that an `SIssue` should be created.
70
80
  * @returns the created `SIssue`.
71
81
  */
72
- export function createSIssue(marker: Marker): SIssue {
73
- const issue: SIssue = new SIssue();
82
+ export function createSIssue(marker: Marker, parent?: SParentElement): SIssue {
83
+ const issue = new SIssue();
74
84
  issue.message = marker.description;
75
85
 
76
86
  switch (marker.kind) {
@@ -87,6 +97,18 @@ export function createSIssue(marker: Marker): SIssue {
87
97
  break;
88
98
  }
89
99
  }
90
-
91
100
  return issue;
92
101
  }
102
+
103
+ export function getSeverity(marker: SIssueMarker): SIssueSeverity {
104
+ let currentSeverity: SIssueSeverity = 'info';
105
+ for (const severity of marker.issues.map(s => s.severity)) {
106
+ if (severity === 'error') {
107
+ return severity;
108
+ }
109
+ if (severity === 'warning' && currentSeverity === 'info') {
110
+ currentSeverity = severity;
111
+ }
112
+ }
113
+ return currentSeverity;
114
+ }