@eclipse-glsp/client 2.2.0-next.329 → 2.2.0-next.330

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 (174) hide show
  1. package/css/change-bounds.css +15 -0
  2. package/css/glsp-sprotty.css +17 -0
  3. package/lib/base/drag-aware-mouse-listener.d.ts +5 -2
  4. package/lib/base/drag-aware-mouse-listener.d.ts.map +1 -1
  5. package/lib/base/drag-aware-mouse-listener.js +11 -0
  6. package/lib/base/drag-aware-mouse-listener.js.map +1 -1
  7. package/lib/base/feedback/css-feedback.d.ts +4 -0
  8. package/lib/base/feedback/css-feedback.d.ts.map +1 -1
  9. package/lib/base/feedback/css-feedback.js +4 -0
  10. package/lib/base/feedback/css-feedback.js.map +1 -1
  11. package/lib/base/feedback/feeback-emitter.d.ts +67 -0
  12. package/lib/base/feedback/feeback-emitter.d.ts.map +1 -0
  13. package/lib/base/feedback/feeback-emitter.js +109 -0
  14. package/lib/base/feedback/feeback-emitter.js.map +1 -0
  15. package/lib/base/feedback/feedback-action-dispatcher.d.ts +17 -5
  16. package/lib/base/feedback/feedback-action-dispatcher.d.ts.map +1 -1
  17. package/lib/base/feedback/feedback-action-dispatcher.js +23 -3
  18. package/lib/base/feedback/feedback-action-dispatcher.js.map +1 -1
  19. package/lib/base/feedback/index.d.ts +1 -0
  20. package/lib/base/feedback/index.d.ts.map +1 -1
  21. package/lib/base/feedback/index.js +1 -0
  22. package/lib/base/feedback/index.js.map +1 -1
  23. package/lib/base/selection-service.d.ts +3 -2
  24. package/lib/base/selection-service.d.ts.map +1 -1
  25. package/lib/base/selection-service.js.map +1 -1
  26. package/lib/features/accessibility/element-navigation/local-element-navigator.d.ts +3 -3
  27. package/lib/features/accessibility/element-navigation/local-element-navigator.d.ts.map +1 -1
  28. package/lib/features/accessibility/element-navigation/local-element-navigator.js.map +1 -1
  29. package/lib/features/accessibility/move-zoom/move-handler.d.ts +4 -2
  30. package/lib/features/accessibility/move-zoom/move-handler.d.ts.map +1 -1
  31. package/lib/features/accessibility/move-zoom/move-handler.js +10 -4
  32. package/lib/features/accessibility/move-zoom/move-handler.js.map +1 -1
  33. package/lib/features/accessibility/resize-key-tool/resize-key-handler.d.ts +4 -2
  34. package/lib/features/accessibility/resize-key-tool/resize-key-handler.d.ts.map +1 -1
  35. package/lib/features/accessibility/resize-key-tool/resize-key-handler.js +11 -3
  36. package/lib/features/accessibility/resize-key-tool/resize-key-handler.js.map +1 -1
  37. package/lib/features/change-bounds/model.d.ts +8 -8
  38. package/lib/features/change-bounds/model.d.ts.map +1 -1
  39. package/lib/features/change-bounds/model.js +17 -5
  40. package/lib/features/change-bounds/model.js.map +1 -1
  41. package/lib/features/change-bounds/movement-restrictor.d.ts +3 -2
  42. package/lib/features/change-bounds/movement-restrictor.d.ts.map +1 -1
  43. package/lib/features/change-bounds/movement-restrictor.js.map +1 -1
  44. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts +15 -5
  45. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts.map +1 -1
  46. package/lib/features/element-template/mouse-tracking-element-position-listener.js +27 -38
  47. package/lib/features/element-template/mouse-tracking-element-position-listener.js.map +1 -1
  48. package/lib/features/helper-lines/helper-line-manager-default.d.ts +4 -2
  49. package/lib/features/helper-lines/helper-line-manager-default.d.ts.map +1 -1
  50. package/lib/features/helper-lines/helper-line-manager-default.js +8 -12
  51. package/lib/features/helper-lines/helper-line-manager-default.js.map +1 -1
  52. package/lib/features/hints/type-hint-provider.d.ts +3 -0
  53. package/lib/features/hints/type-hint-provider.d.ts.map +1 -1
  54. package/lib/features/hints/type-hint-provider.js +10 -1
  55. package/lib/features/hints/type-hint-provider.js.map +1 -1
  56. package/lib/features/select/select-mouse-listener.d.ts +4 -3
  57. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  58. package/lib/features/select/select-mouse-listener.js.map +1 -1
  59. package/lib/features/tools/base-tools.d.ts +23 -4
  60. package/lib/features/tools/base-tools.d.ts.map +1 -1
  61. package/lib/features/tools/base-tools.js +11 -6
  62. package/lib/features/tools/base-tools.js.map +1 -1
  63. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts +1 -35
  64. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts.map +1 -1
  65. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js +5 -206
  66. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js.map +1 -1
  67. package/lib/features/tools/change-bounds/change-bounds-tool-module.d.ts +1 -0
  68. package/lib/features/tools/change-bounds/change-bounds-tool-module.d.ts.map +1 -1
  69. package/lib/features/tools/change-bounds/change-bounds-tool-module.js +1 -0
  70. package/lib/features/tools/change-bounds/change-bounds-tool-module.js.map +1 -1
  71. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts +67 -0
  72. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts.map +1 -0
  73. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js +227 -0
  74. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js.map +1 -0
  75. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts +34 -24
  76. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts.map +1 -1
  77. package/lib/features/tools/change-bounds/change-bounds-tool.js +172 -180
  78. package/lib/features/tools/change-bounds/change-bounds-tool.js.map +1 -1
  79. package/lib/features/tools/change-bounds/index.d.ts +1 -0
  80. package/lib/features/tools/change-bounds/index.d.ts.map +1 -1
  81. package/lib/features/tools/change-bounds/index.js +1 -0
  82. package/lib/features/tools/change-bounds/index.js.map +1 -1
  83. package/lib/features/tools/change-bounds/view.d.ts +1 -1
  84. package/lib/features/tools/change-bounds/view.d.ts.map +1 -1
  85. package/lib/features/tools/change-bounds/view.js +3 -0
  86. package/lib/features/tools/change-bounds/view.js.map +1 -1
  87. package/lib/features/tools/deletion/delete-tool.d.ts +16 -1
  88. package/lib/features/tools/deletion/delete-tool.d.ts.map +1 -1
  89. package/lib/features/tools/deletion/delete-tool.js +5 -2
  90. package/lib/features/tools/deletion/delete-tool.js.map +1 -1
  91. package/lib/features/tools/edge-creation/dangling-edge-feedback.d.ts +17 -1
  92. package/lib/features/tools/edge-creation/dangling-edge-feedback.d.ts.map +1 -1
  93. package/lib/features/tools/edge-creation/dangling-edge-feedback.js +3 -1
  94. package/lib/features/tools/edge-creation/dangling-edge-feedback.js.map +1 -1
  95. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.d.ts +3 -1
  96. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.d.ts.map +1 -1
  97. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.js +4 -7
  98. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.js.map +1 -1
  99. package/lib/features/tools/edge-creation/edge-creation-tool.d.ts +20 -2
  100. package/lib/features/tools/edge-creation/edge-creation-tool.d.ts.map +1 -1
  101. package/lib/features/tools/edge-creation/edge-creation-tool.js +23 -18
  102. package/lib/features/tools/edge-creation/edge-creation-tool.js.map +1 -1
  103. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts +3 -1
  104. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts.map +1 -1
  105. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js +5 -8
  106. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js.map +1 -1
  107. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts +4 -4
  108. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts.map +1 -1
  109. package/lib/features/tools/edge-edit/edge-edit-tool.js +27 -44
  110. package/lib/features/tools/edge-edit/edge-edit-tool.js.map +1 -1
  111. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts +3 -2
  112. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts.map +1 -1
  113. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js +4 -2
  114. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js.map +1 -1
  115. package/lib/features/tools/node-creation/node-creation-tool.d.ts +14 -5
  116. package/lib/features/tools/node-creation/node-creation-tool.d.ts.map +1 -1
  117. package/lib/features/tools/node-creation/node-creation-tool.js +58 -24
  118. package/lib/features/tools/node-creation/node-creation-tool.js.map +1 -1
  119. package/lib/features/validation/marker-navigator.d.ts +7 -6
  120. package/lib/features/validation/marker-navigator.d.ts.map +1 -1
  121. package/lib/features/validation/marker-navigator.js.map +1 -1
  122. package/lib/features/validation/validate.d.ts +2 -5
  123. package/lib/features/validation/validate.d.ts.map +1 -1
  124. package/lib/features/validation/validate.js +4 -16
  125. package/lib/features/validation/validate.js.map +1 -1
  126. package/lib/utils/gmodel-util.d.ts +27 -2
  127. package/lib/utils/gmodel-util.d.ts.map +1 -1
  128. package/lib/utils/gmodel-util.js +30 -1
  129. package/lib/utils/gmodel-util.js.map +1 -1
  130. package/lib/utils/layout-utils.d.ts +8 -7
  131. package/lib/utils/layout-utils.d.ts.map +1 -1
  132. package/lib/utils/layout-utils.js +2 -4
  133. package/lib/utils/layout-utils.js.map +1 -1
  134. package/lib/utils/viewpoint-util.d.ts +4 -4
  135. package/lib/utils/viewpoint-util.d.ts.map +1 -1
  136. package/lib/utils/viewpoint-util.js.map +1 -1
  137. package/package.json +3 -3
  138. package/src/base/drag-aware-mouse-listener.ts +16 -2
  139. package/src/base/feedback/css-feedback.ts +4 -0
  140. package/src/base/feedback/feeback-emitter.ts +112 -0
  141. package/src/base/feedback/feedback-action-dispatcher.ts +41 -6
  142. package/src/base/feedback/index.ts +1 -0
  143. package/src/base/selection-service.spec.ts +5 -0
  144. package/src/base/selection-service.ts +2 -3
  145. package/src/features/accessibility/element-navigation/local-element-navigator.ts +4 -4
  146. package/src/features/accessibility/move-zoom/move-handler.ts +10 -5
  147. package/src/features/accessibility/resize-key-tool/resize-key-handler.ts +11 -7
  148. package/src/features/change-bounds/model.ts +25 -13
  149. package/src/features/change-bounds/movement-restrictor.ts +5 -4
  150. package/src/features/element-template/mouse-tracking-element-position-listener.ts +44 -44
  151. package/src/features/helper-lines/helper-line-manager-default.ts +12 -21
  152. package/src/features/hints/type-hint-provider.spec.ts +4 -1
  153. package/src/features/hints/type-hint-provider.ts +9 -2
  154. package/src/features/select/select-mouse-listener.ts +4 -3
  155. package/src/features/tools/base-tools.ts +15 -8
  156. package/src/features/tools/change-bounds/change-bounds-tool-feedback.ts +6 -232
  157. package/src/features/tools/change-bounds/change-bounds-tool-module.ts +1 -0
  158. package/src/features/tools/change-bounds/change-bounds-tool-move-feedback.ts +254 -0
  159. package/src/features/tools/change-bounds/change-bounds-tool.ts +217 -226
  160. package/src/features/tools/change-bounds/index.ts +1 -0
  161. package/src/features/tools/change-bounds/view.tsx +4 -1
  162. package/src/features/tools/deletion/delete-tool.ts +6 -6
  163. package/src/features/tools/edge-creation/dangling-edge-feedback.ts +7 -4
  164. package/src/features/tools/edge-creation/edge-creation-tool-feedback.ts +9 -9
  165. package/src/features/tools/edge-creation/edge-creation-tool.ts +33 -23
  166. package/src/features/tools/edge-edit/edge-edit-tool-feedback.ts +8 -8
  167. package/src/features/tools/edge-edit/edge-edit-tool.ts +42 -49
  168. package/src/features/tools/marquee-selection/marquee-mouse-tool.ts +7 -6
  169. package/src/features/tools/node-creation/node-creation-tool.ts +69 -25
  170. package/src/features/validation/marker-navigator.ts +6 -8
  171. package/src/features/validation/validate.ts +5 -20
  172. package/src/utils/gmodel-util.ts +44 -1
  173. package/src/utils/layout-utils.ts +9 -20
  174. package/src/utils/viewpoint-util.ts +3 -4
@@ -17,7 +17,6 @@ import { inject, injectable, optional } from 'inversify';
17
17
 
18
18
  import {
19
19
  Action,
20
- Bounds,
21
20
  BoundsAware,
22
21
  ChangeBoundsOperation,
23
22
  ChangeRoutingPointsOperation,
@@ -36,35 +35,39 @@ import {
36
35
  Operation,
37
36
  Point,
38
37
  TYPES,
39
- findParentByFeature,
40
- isSelected
38
+ findParentByFeature
41
39
  } from '@eclipse-glsp/sprotty';
42
40
  import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener';
43
41
  import { CursorCSS, applyCssClasses, cursorFeedbackAction, deleteCssClasses } from '../../../base/feedback/css-feedback';
42
+ import { FeedbackEmitter } from '../../../base/feedback/feeback-emitter';
44
43
  import { ISelectionListener, SelectionService } from '../../../base/selection-service';
45
- import { PointPositionUpdater } from '../../../features/change-bounds/point-position-updater';
44
+ import { isValidMove, isValidSize } from '../../../utils';
46
45
  import {
46
+ BoundsAwareModelElement,
47
+ ResizableModelElement,
47
48
  calcElementAndRoutingPoints,
48
49
  forEachElement,
50
+ getMatchingElements,
49
51
  isNonRoutableSelectedMovableBoundsAware,
50
52
  toElementAndBounds
51
53
  } from '../../../utils/gmodel-util';
52
- import { isValidMove, isValidSize } from '../../../utils/layout-utils';
53
- import { SetBoundsFeedbackAction } from '../../bounds/set-bounds-feedback-command';
54
- import { Resizable, ResizeHandleLocation, SResizeHandle, isBoundsAwareMoveable, isResizable } from '../../change-bounds/model';
54
+ import { SetBoundsFeedbackAction } from '../../bounds';
55
+ import { PointPositionUpdater } from '../../change-bounds';
56
+ import { ResizeHandleLocation, SResizeHandle, isResizable } from '../../change-bounds/model';
55
57
  import {
56
58
  IMovementRestrictor,
57
59
  createMovementRestrictionFeedback,
58
60
  removeMovementRestrictionFeedback
59
61
  } from '../../change-bounds/movement-restrictor';
60
62
  import { PositionSnapper } from '../../change-bounds/position-snapper';
61
- import { getDirectionFrom } from '../../helper-lines/model';
63
+ import { getDirectionFrom } from '../../helper-lines';
62
64
  import { BaseEditTool } from '../base-tools';
63
65
  import {
64
- FeedbackMoveMouseListener,
65
66
  HideChangeBoundsToolResizeFeedbackAction,
67
+ MoveFinishedEventAction,
66
68
  ShowChangeBoundsToolResizeFeedbackAction
67
69
  } from './change-bounds-tool-feedback';
70
+ import { FeedbackMoveMouseListener } from './change-bounds-tool-move-feedback';
68
71
 
69
72
  /**
70
73
  * The change bounds tool has the license to move multiple elements or resize a single element by implementing the ChangeBounds operation.
@@ -108,8 +111,6 @@ export class ChangeBoundsTool extends BaseEditTool {
108
111
  this.toDisposeOnDisable.push(
109
112
  this.mouseTool.registerListener(feedbackMoveMouseListener),
110
113
  this.mouseTool.registerListener(changeBoundsListener),
111
- Disposable.create(() => this.deregisterFeedback(feedbackMoveMouseListener)),
112
- Disposable.create(() => this.deregisterFeedback(changeBoundsListener, [HideChangeBoundsToolResizeFeedbackAction.create()])),
113
114
  this.selectionService.onSelectionChanged(change => changeBoundsListener.selectionChanged(change.root, change.selectedElements))
114
115
  );
115
116
  }
@@ -123,20 +124,34 @@ export class ChangeBoundsTool extends BaseEditTool {
123
124
  }
124
125
  }
125
126
 
127
+ export interface ValidatedElementAndBounds extends ElementAndBounds {
128
+ valid: boolean;
129
+ }
130
+
131
+ export namespace ValidatedElementAndBounds {
132
+ export function isValid(move: ElementAndBounds): boolean {
133
+ return (move as ValidatedElementAndBounds).valid ?? true;
134
+ }
135
+ }
136
+
126
137
  export class ChangeBoundsListener extends DragAwareMouseListener implements ISelectionListener, Disposable {
127
138
  static readonly CSS_CLASS_ACTIVE = 'active';
128
139
 
129
140
  // members for calculating the correct position change
130
- protected initialBounds: Bounds | undefined;
131
- protected pointPositionUpdater: PointPositionUpdater;
141
+ protected initialBounds: ElementAndBounds | undefined;
142
+ protected positionUpdater: PointPositionUpdater;
132
143
 
133
144
  // members for resize mode
134
- protected activeResizeElement?: GModelElement;
145
+ protected activeResizeElement?: ResizableModelElement;
135
146
  protected activeResizeHandle?: SResizeHandle;
147
+ protected handleFeedback: FeedbackEmitter;
148
+ protected resizeFeedback: FeedbackEmitter;
136
149
 
137
150
  constructor(protected tool: ChangeBoundsTool) {
138
151
  super();
139
- this.pointPositionUpdater = new PointPositionUpdater(tool.positionSnapper);
152
+ this.positionUpdater = new PointPositionUpdater(tool.positionSnapper);
153
+ this.handleFeedback = tool.createFeedbackEmitter();
154
+ this.resizeFeedback = tool.createFeedbackEmitter();
140
155
  }
141
156
 
142
157
  override mouseDown(target: GModelElement, event: MouseEvent): Action[] {
@@ -147,93 +162,157 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
147
162
  return [];
148
163
  }
149
164
  // check if we have a resize handle (only single-selection)
150
- if (this.activeResizeElement && target instanceof SResizeHandle) {
151
- this.activeResizeHandle = target;
152
- } else {
153
- this.setActiveResizeElement(target);
154
- }
165
+ this.updateResizeElement(target, event);
166
+ return [];
167
+ }
168
+
169
+ protected updateResizeElement(target: GModelElement, event?: MouseEvent): boolean {
170
+ this.activeResizeHandle = target instanceof SResizeHandle ? target : undefined;
171
+ this.activeResizeElement = this.activeResizeHandle?.parent ?? this.findResizeElement(target);
155
172
  if (this.activeResizeElement) {
156
- this.initPosition(event);
173
+ if (event) {
174
+ this.positionUpdater.updateLastDragPosition(event);
175
+ }
176
+ this.initialBounds = {
177
+ newSize: this.activeResizeElement.bounds,
178
+ newPosition: this.activeResizeElement.bounds,
179
+ elementId: this.activeResizeElement.id
180
+ };
181
+ this.handleFeedback.add(
182
+ ShowChangeBoundsToolResizeFeedbackAction.create(this.activeResizeElement.id),
183
+ HideChangeBoundsToolResizeFeedbackAction.create()
184
+ );
185
+ this.handleFeedback.submit();
186
+ return true;
157
187
  } else {
158
- this.reset();
188
+ this.positionUpdater.resetPosition();
189
+ this.handleFeedback.dispose();
190
+ return false;
159
191
  }
160
- return [];
161
192
  }
162
193
 
163
- override mouseMove(target: GModelElement, event: MouseEvent): Action[] {
164
- super.mouseMove(target, event);
165
- if (this.isMouseDrag && this.activeResizeHandle) {
166
- // rely on the FeedbackMoveMouseListener to update the element bounds of selected elements
167
- // consider resize handles ourselves
168
- const actions: Action[] = [
169
- cursorFeedbackAction(this.activeResizeHandle.isNwSeResize() ? CursorCSS.RESIZE_NWSE : CursorCSS.RESIZE_NESW),
170
- applyCssClasses(this.activeResizeHandle, ChangeBoundsListener.CSS_CLASS_ACTIVE)
171
- ];
172
- const positionUpdate = this.pointPositionUpdater.updatePosition(
173
- target,
174
- event,
175
- getDirectionFrom(this.activeResizeHandle.location)
176
- );
194
+ protected findResizeElement(target: GModelElement): ResizableModelElement | undefined {
195
+ // check if we have a selected, moveable element (multi-selection allowed)
196
+ // but only allow one element to have the element resize handles
197
+ return findParentByFeature(target, isResizable);
198
+ }
199
+
200
+ protected override draggingMouseMove(target: GModelElement, event: MouseEvent): Action[] {
201
+ // rely on the FeedbackMoveMouseListener to update the element bounds of selected elements
202
+ // consider resize handles ourselves
203
+ if (this.activeResizeHandle && !this.positionUpdater.isLastDragPositionUndefined()) {
204
+ if (!this.initialBounds) {
205
+ const element = this.activeResizeHandle.parent;
206
+ this.initialBounds = { elementId: element.id, newSize: element.bounds, newPosition: element.bounds };
207
+ }
208
+ const positionUpdate = this.positionUpdater.updatePosition(target, event, getDirectionFrom(this.activeResizeHandle.location));
177
209
  if (positionUpdate) {
178
- const resizeActions = this.handleResizeOnClient(positionUpdate);
179
- actions.push(...resizeActions);
210
+ const resizeActions = this.handleResizeOnClient(positionUpdate).filter(action => action);
211
+ if (resizeActions.length > 0) {
212
+ this.resizeFeedback.add(SetBoundsFeedbackAction.create(resizeActions), () => this.resetBoundsAction());
213
+ this.addResizeFeedback(resizeActions, this.activeResizeHandle, target, event);
214
+ this.resizeFeedback.submit();
215
+ }
180
216
  }
181
- this.tool.registerFeedback(actions, this);
182
217
  }
183
- return [];
218
+ return super.draggingMouseMove(target, event);
219
+ }
220
+
221
+ protected filterResizeOnClient(resize: ValidatedElementAndBounds, positionUpdate: Point): boolean {
222
+ return true;
223
+ }
224
+
225
+ protected addResizeFeedback(
226
+ resizeActions: ValidatedElementAndBounds[],
227
+ handle: SResizeHandle,
228
+ target: GModelElement,
229
+ event: MouseEvent
230
+ ): void {
231
+ // handle feedback
232
+ this.resizeFeedback.add(
233
+ applyCssClasses(handle, ChangeBoundsListener.CSS_CLASS_ACTIVE),
234
+ deleteCssClasses(handle, ChangeBoundsListener.CSS_CLASS_ACTIVE)
235
+ );
236
+
237
+ // cursor feedback
238
+ const cursorClass = handle.isNeResize()
239
+ ? CursorCSS.RESIZE_NE
240
+ : handle.isNwResize()
241
+ ? CursorCSS.RESIZE_NW
242
+ : handle.isSeResize()
243
+ ? CursorCSS.RESIZE_SE
244
+ : CursorCSS.RESIZE_SW;
245
+ this.resizeFeedback.add(cursorFeedbackAction(cursorClass), cursorFeedbackAction(CursorCSS.DEFAULT));
246
+
247
+ // restriction feedback
248
+ resizeActions.forEach(elementResize => {
249
+ const element = handle.root.index.getById(elementResize.elementId);
250
+ if (element && this.tool.movementRestrictor) {
251
+ if (!elementResize.valid) {
252
+ this.resizeFeedback.add(createMovementRestrictionFeedback(element, this.tool.movementRestrictor), () =>
253
+ removeMovementRestrictionFeedback(element, this.tool.movementRestrictor!)
254
+ );
255
+ } else {
256
+ this.resizeFeedback.add(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor!));
257
+ }
258
+ }
259
+ });
260
+ }
261
+
262
+ protected resetBoundsAction(): Action[] {
263
+ // reset the bounds to the initial bounds and ensure that we do not show helper line feedback anymore (MoveFinishedEventAction)
264
+ return this.initialBounds
265
+ ? [SetBoundsFeedbackAction.create([this.initialBounds]), MoveFinishedEventAction.create()]
266
+ : [MoveFinishedEventAction.create()];
184
267
  }
185
268
 
186
269
  override draggingMouseUp(target: GModelElement, _event: MouseEvent): Action[] {
187
- if (this.pointPositionUpdater.isLastDragPositionUndefined()) {
188
- this.resetPosition();
270
+ if (this.positionUpdater.isLastDragPositionUndefined()) {
189
271
  return [];
190
272
  }
191
273
  const actions: Action[] = [];
192
-
193
274
  if (this.activeResizeHandle) {
194
- // Resize
195
- actions.push(...this.handleResize(this.activeResizeHandle));
275
+ actions.push(...this.handleResizeOnServer(this.activeResizeHandle));
196
276
  } else {
197
- // Move
198
277
  actions.push(...this.handleMoveOnServer(target));
199
278
  }
200
- this.resetPosition();
201
- this.tool.deregisterFeedback(this, [cursorFeedbackAction(CursorCSS.DEFAULT)]);
279
+ this.disposeAllButHandles();
202
280
  return actions;
203
281
  }
204
282
 
283
+ override nonDraggingMouseUp(element: GModelElement, event: MouseEvent): Action[] {
284
+ this.disposeAllButHandles();
285
+ return super.nonDraggingMouseUp(element, event);
286
+ }
287
+
205
288
  protected handleMoveOnServer(target: GModelElement): Action[] {
206
289
  const operations: Operation[] = [];
207
-
208
290
  operations.push(...this.handleMoveElementsOnServer(target));
209
291
  operations.push(...this.handleMoveRoutingPointsOnServer(target));
210
- if (operations.length > 0) {
211
- return [CompoundOperation.create(operations)];
212
- }
213
- return operations;
292
+ return operations.length > 0 ? [CompoundOperation.create(operations)] : [];
214
293
  }
215
294
 
216
295
  protected handleMoveElementsOnServer(target: GModelElement): Operation[] {
217
- const result: Operation[] = [];
218
- const newBounds: ElementAndBounds[] = [];
219
- const selectedElements: (GModelElement & BoundsAware)[] = [];
220
- forEachElement(target.index, isNonRoutableSelectedMovableBoundsAware, element => {
221
- selectedElements.push(element);
222
- });
223
-
224
- const selectionSet: Set<GModelElement & BoundsAware> = new Set(selectedElements);
225
- selectedElements
226
- .filter(element => !this.isChildOfSelected(selectionSet, element))
227
- .map(element => this.createElementAndBounds(element))
228
- .forEach(bounds => newBounds.push(...bounds));
229
-
230
- if (newBounds.length > 0) {
231
- result.push(ChangeBoundsOperation.create(newBounds));
232
- }
233
- return result;
296
+ const selectedElements = getMatchingElements(target.index, isNonRoutableSelectedMovableBoundsAware);
297
+ const selectionSet: Set<BoundsAwareModelElement> = new Set(selectedElements);
298
+ const newBounds: ElementAndBounds[] = selectedElements
299
+ .filter(element => this.isValidElement(element, selectionSet))
300
+ .map(toElementAndBounds);
301
+ return newBounds.length > 0 ? [ChangeBoundsOperation.create(newBounds)] : [];
302
+ }
303
+
304
+ protected isValidElement(element: BoundsAwareModelElement, selectedElements: Set<BoundsAwareModelElement> = new Set()): boolean {
305
+ return (
306
+ this.isValidMove(element, element.bounds) &&
307
+ this.isValidSize(element, element.bounds) &&
308
+ !this.isChildOfSelected(selectedElements, element)
309
+ );
234
310
  }
235
311
 
236
312
  protected isChildOfSelected(selectedElements: Set<GModelElement>, element: GModelElement): boolean {
313
+ if (selectedElements.size === 0) {
314
+ return false;
315
+ }
237
316
  while (element instanceof GChildElement) {
238
317
  element = element.parent;
239
318
  if (selectedElements.has(element)) {
@@ -262,217 +341,129 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
262
341
  return newRoutingPoints.length > 0 ? [ChangeRoutingPointsOperation.create(newRoutingPoints)] : [];
263
342
  }
264
343
 
265
- protected handleResize(activeResizeHandle: SResizeHandle): Action[] {
266
- const actions: Action[] = [];
267
- actions.push(cursorFeedbackAction(CursorCSS.DEFAULT));
268
- actions.push(deleteCssClasses(activeResizeHandle, ChangeBoundsListener.CSS_CLASS_ACTIVE));
269
- const resizeElement = findParentByFeature(activeResizeHandle, isResizable);
270
- if (this.isActiveResizeElement(resizeElement)) {
271
- this.createChangeBoundsAction(resizeElement).forEach(action => actions.push(action));
344
+ protected handleResizeOnServer(activeResizeHandle: SResizeHandle): Action[] {
345
+ if (this.isValidElement(activeResizeHandle.parent) && this.initialBounds) {
346
+ const elementAndBounds = toElementAndBounds(activeResizeHandle.parent);
347
+ if (!this.initialBounds.newPosition || !elementAndBounds.newPosition) {
348
+ return [];
349
+ }
350
+ if (
351
+ !Point.equals(this.initialBounds.newPosition, elementAndBounds.newPosition) ||
352
+ !Dimension.equals(this.initialBounds.newSize, elementAndBounds.newSize)
353
+ ) {
354
+ // UX: we do not want the element positions to be reset to their start as they will be moved to their start and
355
+ // only afterwards moved by the move action again, leading to a ping-pong movement.
356
+ // We therefore clear our element map so that they cannot be reset.
357
+ this.initialBounds = undefined;
358
+ return [ChangeBoundsOperation.create([elementAndBounds])];
359
+ }
272
360
  }
273
- return actions;
361
+ return [];
274
362
  }
275
363
 
276
364
  selectionChanged(root: GModelRoot, selectedElements: string[]): void {
277
- if (this.activeResizeElement) {
278
- if (selectedElements.includes(this.activeResizeElement.id)) {
279
- // our active element is still selected, nothing to do
280
- return;
281
- }
282
-
283
- // try to find some other selected element and mark that active
284
- for (const elementId of selectedElements.reverse()) {
285
- const element = root.index.getById(elementId);
286
- if (element && this.setActiveResizeElement(element)) {
287
- return;
288
- }
289
- }
290
- this.reset();
365
+ if (this.activeResizeElement && selectedElements.includes(this.activeResizeElement.id)) {
366
+ // our active element is still selected, nothing to do
367
+ return;
291
368
  }
292
- }
293
369
 
294
- protected setActiveResizeElement(target: GModelElement): boolean {
295
- // check if we have a selected, moveable element (multi-selection allowed)
296
- const moveableElement = findParentByFeature(target, isBoundsAwareMoveable);
297
- if (isSelected(moveableElement)) {
298
- // only allow one element to have the element resize handles
299
- this.activeResizeElement = moveableElement;
300
- if (isResizable(this.activeResizeElement)) {
301
- this.tool.registerFeedback(
302
- [ShowChangeBoundsToolResizeFeedbackAction.create(this.activeResizeElement.id)],
303
- this.activeResizeElement
304
- );
370
+ // try to find some other selected element and mark that active
371
+ for (const elementId of selectedElements.reverse()) {
372
+ const element = root.index.getById(elementId);
373
+ if (element && this.updateResizeElement(element)) {
374
+ return;
305
375
  }
306
- return true;
307
376
  }
308
- return false;
377
+ this.updateResizeElement(root);
378
+ this.disposeAllButHandles();
309
379
  }
310
380
 
311
381
  protected isActiveResizeElement(element?: GModelElement): element is GParentElement & BoundsAware {
312
382
  return element !== undefined && this.activeResizeElement !== undefined && element.id === this.activeResizeElement.id;
313
383
  }
314
384
 
315
- protected initPosition(event: MouseEvent): void {
316
- this.pointPositionUpdater.updateLastDragPosition(event);
317
- if (this.activeResizeHandle) {
318
- const resizeElement = findParentByFeature(this.activeResizeHandle, isResizable);
319
- this.initialBounds = {
320
- x: resizeElement!.bounds.x,
321
- y: resizeElement!.bounds.y,
322
- width: resizeElement!.bounds.width,
323
- height: resizeElement!.bounds.height
324
- };
325
- }
326
- }
327
-
328
- dispose(): void {
329
- this.reset(true);
330
- }
331
-
332
- protected reset(resetBounds = false): void {
333
- this.resetFeedback(resetBounds);
334
- this.resetPosition();
335
- }
336
-
337
- protected resetFeedback(resetBounds = false): void {
338
- const resetFeedback: Action[] = [];
339
- if (this.activeResizeElement && isResizable(this.activeResizeElement)) {
340
- if (this.initialBounds && this.activeResizeHandle && resetBounds) {
341
- // we only reset the bounds if an active resize operation was cancelled due to the tool being disabled
342
- resetFeedback.push(
343
- SetBoundsFeedbackAction.create([
344
- {
345
- elementId: this.activeResizeElement.id,
346
- newPosition: this.initialBounds,
347
- newSize: this.initialBounds
348
- }
349
- ])
350
- );
351
- }
352
- this.tool.deregisterFeedback(this.activeResizeElement, [HideChangeBoundsToolResizeFeedbackAction.create()]);
353
- }
354
- resetFeedback.push(cursorFeedbackAction(CursorCSS.DEFAULT));
355
- this.tool.deregisterFeedback(this, resetFeedback);
356
- }
357
-
358
- protected resetPosition(): void {
359
- this.activeResizeHandle = undefined;
360
- this.pointPositionUpdater.resetPosition();
361
- }
362
-
363
- protected handleResizeOnClient(positionUpdate: Point): Action[] {
385
+ protected handleResizeOnClient(positionUpdate: Point): ValidatedElementAndBounds[] {
364
386
  if (!this.activeResizeHandle) {
365
387
  return [];
366
388
  }
367
389
 
368
- const resizeElement = findParentByFeature(this.activeResizeHandle, isResizable);
369
- if (this.isActiveResizeElement(resizeElement)) {
390
+ if (this.isActiveResizeElement(this.activeResizeHandle.parent)) {
370
391
  switch (this.activeResizeHandle.location) {
371
392
  case ResizeHandleLocation.TopLeft:
372
- return this.handleTopLeftResize(resizeElement, positionUpdate);
393
+ return this.handleTopLeftResize(this.activeResizeHandle.parent, positionUpdate);
373
394
  case ResizeHandleLocation.TopRight:
374
- return this.handleTopRightResize(resizeElement, positionUpdate);
395
+ return this.handleTopRightResize(this.activeResizeHandle.parent, positionUpdate);
375
396
  case ResizeHandleLocation.BottomLeft:
376
- return this.handleBottomLeftResize(resizeElement, positionUpdate);
397
+ return this.handleBottomLeftResize(this.activeResizeHandle.parent, positionUpdate);
377
398
  case ResizeHandleLocation.BottomRight:
378
- return this.handleBottomRightResize(resizeElement, positionUpdate);
399
+ return this.handleBottomRightResize(this.activeResizeHandle.parent, positionUpdate);
379
400
  }
380
401
  }
381
402
  return [];
382
403
  }
383
404
 
384
- protected handleTopLeftResize(resizeElement: GParentElement & Resizable, positionUpdate: Point): Action[] {
405
+ protected handleTopLeftResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
385
406
  return this.createSetBoundsAction(
386
407
  resizeElement,
387
- resizeElement.bounds.x + positionUpdate.x,
388
- resizeElement.bounds.y + positionUpdate.y,
389
- resizeElement.bounds.width - positionUpdate.x,
390
- resizeElement.bounds.height - positionUpdate.y
408
+ { x: resizeElement.bounds.x + positionUpdate.x, y: resizeElement.bounds.y + positionUpdate.y },
409
+ { width: resizeElement.bounds.width - positionUpdate.x, height: resizeElement.bounds.height - positionUpdate.y }
391
410
  );
392
411
  }
393
412
 
394
- protected handleTopRightResize(resizeElement: GParentElement & Resizable, positionUpdate: Point): Action[] {
413
+ protected handleTopRightResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
395
414
  return this.createSetBoundsAction(
396
415
  resizeElement,
397
- resizeElement.bounds.x,
398
- resizeElement.bounds.y + positionUpdate.y,
399
- resizeElement.bounds.width + positionUpdate.x,
400
- resizeElement.bounds.height - positionUpdate.y
416
+ { x: resizeElement.bounds.x, y: resizeElement.bounds.y + positionUpdate.y },
417
+ { width: resizeElement.bounds.width + positionUpdate.x, height: resizeElement.bounds.height - positionUpdate.y }
401
418
  );
402
419
  }
403
420
 
404
- protected handleBottomLeftResize(resizeElement: GParentElement & Resizable, positionUpdate: Point): Action[] {
421
+ protected handleBottomLeftResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
405
422
  return this.createSetBoundsAction(
406
423
  resizeElement,
407
- resizeElement.bounds.x + positionUpdate.x,
408
- resizeElement.bounds.y,
409
- resizeElement.bounds.width - positionUpdate.x,
410
- resizeElement.bounds.height + positionUpdate.y
424
+ { x: resizeElement.bounds.x + positionUpdate.x, y: resizeElement.bounds.y },
425
+ { width: resizeElement.bounds.width - positionUpdate.x, height: resizeElement.bounds.height + positionUpdate.y }
411
426
  );
412
427
  }
413
428
 
414
- protected handleBottomRightResize(resizeElement: GParentElement & Resizable, positionUpdate: Point): Action[] {
429
+ protected handleBottomRightResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
415
430
  return this.createSetBoundsAction(
416
431
  resizeElement,
417
- resizeElement.bounds.x,
418
- resizeElement.bounds.y,
419
- resizeElement.bounds.width + positionUpdate.x,
420
- resizeElement.bounds.height + positionUpdate.y
432
+ { x: resizeElement.bounds.x, y: resizeElement.bounds.y },
433
+ { width: resizeElement.bounds.width + positionUpdate.x, height: resizeElement.bounds.height + positionUpdate.y }
421
434
  );
422
435
  }
423
436
 
424
- protected createChangeBoundsAction(element: GModelElement & BoundsAware): Action[] {
425
- if (this.isValidBoundChange(element, element.bounds, element.bounds)) {
426
- return [ChangeBoundsOperation.create([toElementAndBounds(element)])];
427
- } else if (this.initialBounds) {
428
- const actions: Action[] = [];
429
- if (this.tool.movementRestrictor) {
430
- actions.push(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor));
431
- }
432
- actions.push(
433
- SetBoundsFeedbackAction.create([{ elementId: element.id, newPosition: this.initialBounds, newSize: this.initialBounds }])
434
- );
435
- return actions;
436
- }
437
- return [];
438
- }
439
-
440
- protected createElementAndBounds(element: GModelElement & BoundsAware): ElementAndBounds[] {
441
- if (this.isValidBoundChange(element, element.bounds, element.bounds)) {
442
- return [toElementAndBounds(element)];
437
+ protected createSetBoundsAction(element: BoundsAwareModelElement, newPosition: Point, newSize: Dimension): ValidatedElementAndBounds[] {
438
+ if (!isValidSize(element, newSize)) {
439
+ // we are not allowing any invalid sizes (breaking min size), not even during client feedback
440
+ return [];
443
441
  }
444
- return [];
442
+ const valid = this.isValidMove(element, newPosition);
443
+ return [{ elementId: element.id, newPosition, newSize, valid }];
445
444
  }
446
445
 
447
- protected createSetBoundsAction(element: GModelElement & BoundsAware, x: number, y: number, width: number, height: number): Action[] {
448
- const newPosition = { x, y };
449
- const newSize = { width, height };
450
- const result: Action[] = [];
451
-
452
- if (this.isValidBoundChange(element, newPosition, newSize)) {
453
- if (this.tool.movementRestrictor) {
454
- result.push(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor));
455
- }
456
- result.push(SetBoundsFeedbackAction.create([{ elementId: element.id, newPosition, newSize }]));
457
- } else if (this.isValidSize(element, newSize)) {
458
- if (this.tool.movementRestrictor) {
459
- result.push(createMovementRestrictionFeedback(element, this.tool.movementRestrictor));
460
- }
461
- result.push(SetBoundsFeedbackAction.create([{ elementId: element.id, newPosition, newSize }]));
462
- }
463
-
464
- return result;
446
+ protected isValidSize(element: BoundsAwareModelElement, size: Dimension): boolean {
447
+ return isValidSize(element, size);
465
448
  }
466
449
 
467
- protected isValidBoundChange(element: GModelElement & BoundsAware, newPosition: Point, newSize: Dimension): boolean {
468
- return this.isValidSize(element, newSize) && this.isValidMove(element, newPosition);
450
+ protected isValidMove(element: BoundsAwareModelElement, newPosition: Point): boolean {
451
+ return isValidMove(element, newPosition, this.tool.movementRestrictor);
469
452
  }
470
453
 
471
- protected isValidSize(element: GModelElement & BoundsAware, size: Dimension): boolean {
472
- return isValidSize(element, size);
454
+ protected disposeAllButHandles(): void {
455
+ // We do not dispose the handle feedback as we want to keep showing the handles on selected elements
456
+ // this.handleFeedback.dispose();
457
+ this.resizeFeedback.dispose();
458
+ this.positionUpdater.resetPosition();
459
+ this.activeResizeElement = undefined;
460
+ this.activeResizeHandle = undefined;
461
+ this.initialBounds = undefined;
462
+ super.dispose();
473
463
  }
474
464
 
475
- protected isValidMove(element: GModelElement & BoundsAware, newPosition: Point): boolean {
476
- return isValidMove(element, newPosition, this.tool.movementRestrictor);
465
+ override dispose(): void {
466
+ this.handleFeedback.dispose();
467
+ this.disposeAllButHandles();
477
468
  }
478
469
  }
@@ -16,4 +16,5 @@
16
16
  export * from './change-bounds-tool';
17
17
  export * from './change-bounds-tool-feedback';
18
18
  export * from './change-bounds-tool-module';
19
+ export * from './change-bounds-tool-move-feedback';
19
20
  export * from './view';
@@ -23,7 +23,10 @@ const JSX = { createElement: svg };
23
23
 
24
24
  @injectable()
25
25
  export class SResizeHandleView implements IView {
26
- render(handle: SResizeHandle, context: RenderingContext): VNode {
26
+ render(handle: SResizeHandle, context: RenderingContext): VNode | undefined {
27
+ if (context.targetKind === 'hidden') {
28
+ return undefined;
29
+ }
27
30
  const position = this.getPosition(handle);
28
31
  if (position !== undefined) {
29
32
  const node = (
@@ -13,20 +13,20 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { inject, injectable } from 'inversify';
17
16
  import {
18
17
  Action,
19
18
  DeleteElementOperation,
19
+ GModelElement,
20
20
  KeyListener,
21
21
  KeyTool,
22
22
  MouseListener,
23
- GModelElement,
24
23
  findParentByFeature,
25
24
  isCtrlOrCmd,
26
25
  isDeletable,
27
26
  isSelectable,
28
27
  matchesKeystroke
29
28
  } from '@eclipse-glsp/sprotty';
29
+ import { inject, injectable } from 'inversify';
30
30
  import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback';
31
31
  import { EnableDefaultToolsAction, Tool } from '../../../base/tool-manager/tool';
32
32
  import { BaseEditTool } from '../base-tools';
@@ -89,10 +89,10 @@ export class MouseDeleteTool extends BaseEditTool {
89
89
  }
90
90
 
91
91
  enable(): void {
92
- this.toDisposeOnDisable.push(
93
- this.mouseTool.registerListener(this.deleteToolMouseListener),
94
- this.registerFeedback([cursorFeedbackAction(CursorCSS.ELEMENT_DELETION)], this, [cursorFeedbackAction()])
95
- );
92
+ const cursorFeedback = this.createFeedbackEmitter()
93
+ .add(cursorFeedbackAction(CursorCSS.ELEMENT_DELETION), cursorFeedbackAction())
94
+ .submit();
95
+ this.toDisposeOnDisable.push(this.mouseTool.registerListener(this.deleteToolMouseListener), cursorFeedback);
96
96
  }
97
97
  }
98
98