@eclipse-glsp/client 2.2.0-next.332 → 2.2.0-next.334

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 (223) hide show
  1. package/css/change-bounds.css +36 -2
  2. package/css/glsp-sprotty.css +18 -2
  3. package/css/helper-lines.css +3 -3
  4. package/lib/base/default.module.d.ts.map +1 -1
  5. package/lib/base/default.module.js +3 -0
  6. package/lib/base/default.module.js.map +1 -1
  7. package/lib/base/feedback/css-feedback.d.ts +7 -2
  8. package/lib/base/feedback/css-feedback.d.ts.map +1 -1
  9. package/lib/base/feedback/css-feedback.js +10 -2
  10. package/lib/base/feedback/css-feedback.js.map +1 -1
  11. package/lib/base/feedback/feeback-emitter.d.ts +2 -2
  12. package/lib/base/feedback/feeback-emitter.d.ts.map +1 -1
  13. package/lib/base/feedback/feeback-emitter.js +5 -2
  14. package/lib/base/feedback/feeback-emitter.js.map +1 -1
  15. package/lib/base/index.d.ts +1 -0
  16. package/lib/base/index.d.ts.map +1 -1
  17. package/lib/base/index.js +1 -0
  18. package/lib/base/index.js.map +1 -1
  19. package/lib/base/model/diagram-loader.d.ts.map +1 -1
  20. package/lib/base/model/diagram-loader.js +6 -1
  21. package/lib/base/model/diagram-loader.js.map +1 -1
  22. package/lib/base/mouse-position-tracker.d.ts +22 -0
  23. package/lib/base/mouse-position-tracker.d.ts.map +1 -0
  24. package/lib/base/mouse-position-tracker.js +40 -0
  25. package/lib/base/mouse-position-tracker.js.map +1 -0
  26. package/lib/features/accessibility/view-key-tools/movement-key-tool.d.ts +2 -0
  27. package/lib/features/accessibility/view-key-tools/movement-key-tool.d.ts.map +1 -1
  28. package/lib/features/accessibility/view-key-tools/movement-key-tool.js +15 -7
  29. package/lib/features/accessibility/view-key-tools/movement-key-tool.js.map +1 -1
  30. package/lib/features/bounds/bounds-module.d.ts.map +1 -1
  31. package/lib/features/bounds/bounds-module.js +2 -0
  32. package/lib/features/bounds/bounds-module.js.map +1 -1
  33. package/lib/features/bounds/freeform-layout.d.ts +17 -1
  34. package/lib/features/bounds/freeform-layout.d.ts.map +1 -1
  35. package/lib/features/bounds/freeform-layout.js +14 -5
  36. package/lib/features/bounds/freeform-layout.js.map +1 -1
  37. package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts +5 -4
  38. package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts.map +1 -1
  39. package/lib/features/bounds/glsp-hidden-bounds-updater.js +51 -39
  40. package/lib/features/bounds/glsp-hidden-bounds-updater.js.map +1 -1
  41. package/lib/features/bounds/hbox-layout.d.ts +18 -2
  42. package/lib/features/bounds/hbox-layout.d.ts.map +1 -1
  43. package/lib/features/bounds/hbox-layout.js +14 -5
  44. package/lib/features/bounds/hbox-layout.js.map +1 -1
  45. package/lib/features/bounds/index.d.ts +1 -0
  46. package/lib/features/bounds/index.d.ts.map +1 -1
  47. package/lib/features/bounds/index.js +1 -0
  48. package/lib/features/bounds/index.js.map +1 -1
  49. package/lib/features/bounds/layout-data.d.ts +27 -0
  50. package/lib/features/bounds/layout-data.d.ts.map +1 -0
  51. package/lib/features/bounds/layout-data.js +48 -0
  52. package/lib/features/bounds/layout-data.js.map +1 -0
  53. package/lib/features/bounds/local-bounds.d.ts +3 -1
  54. package/lib/features/bounds/local-bounds.d.ts.map +1 -1
  55. package/lib/features/bounds/local-bounds.js +14 -0
  56. package/lib/features/bounds/local-bounds.js.map +1 -1
  57. package/lib/features/bounds/vbox-layout.d.ts +18 -2
  58. package/lib/features/bounds/vbox-layout.d.ts.map +1 -1
  59. package/lib/features/bounds/vbox-layout.js +14 -5
  60. package/lib/features/bounds/vbox-layout.js.map +1 -1
  61. package/lib/features/change-bounds/index.d.ts +1 -0
  62. package/lib/features/change-bounds/index.d.ts.map +1 -1
  63. package/lib/features/change-bounds/index.js +1 -0
  64. package/lib/features/change-bounds/index.js.map +1 -1
  65. package/lib/features/change-bounds/model.d.ts +19 -3
  66. package/lib/features/change-bounds/model.d.ts.map +1 -1
  67. package/lib/features/change-bounds/model.js +92 -4
  68. package/lib/features/change-bounds/model.js.map +1 -1
  69. package/lib/features/change-bounds/movement-restrictor.d.ts +1 -0
  70. package/lib/features/change-bounds/movement-restrictor.d.ts.map +1 -1
  71. package/lib/features/change-bounds/movement-restrictor.js +7 -1
  72. package/lib/features/change-bounds/movement-restrictor.js.map +1 -1
  73. package/lib/features/change-bounds/point-position-updater.d.ts +3 -0
  74. package/lib/features/change-bounds/point-position-updater.d.ts.map +1 -1
  75. package/lib/features/change-bounds/point-position-updater.js +7 -2
  76. package/lib/features/change-bounds/point-position-updater.js.map +1 -1
  77. package/lib/features/change-bounds/position-snapper.d.ts +4 -0
  78. package/lib/features/change-bounds/position-snapper.d.ts.map +1 -1
  79. package/lib/features/change-bounds/position-snapper.js +4 -0
  80. package/lib/features/change-bounds/position-snapper.js.map +1 -1
  81. package/lib/features/change-bounds/snap.d.ts +6 -0
  82. package/lib/features/change-bounds/snap.d.ts.map +1 -1
  83. package/lib/features/change-bounds/snap.js +6 -0
  84. package/lib/features/change-bounds/snap.js.map +1 -1
  85. package/lib/features/change-bounds/tracker.d.ts +32 -0
  86. package/lib/features/change-bounds/tracker.d.ts.map +1 -0
  87. package/lib/features/change-bounds/tracker.js +60 -0
  88. package/lib/features/change-bounds/tracker.js.map +1 -0
  89. package/lib/features/debug/debug-bounds-decorator.d.ts +4 -1
  90. package/lib/features/debug/debug-bounds-decorator.d.ts.map +1 -1
  91. package/lib/features/debug/debug-bounds-decorator.js +14 -7
  92. package/lib/features/debug/debug-bounds-decorator.js.map +1 -1
  93. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts +9 -22
  94. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts.map +1 -1
  95. package/lib/features/element-template/mouse-tracking-element-position-listener.js +30 -46
  96. package/lib/features/element-template/mouse-tracking-element-position-listener.js.map +1 -1
  97. package/lib/features/helper-lines/helper-line-feedback.d.ts +2 -2
  98. package/lib/features/helper-lines/helper-line-feedback.d.ts.map +1 -1
  99. package/lib/features/helper-lines/helper-line-feedback.js +1 -1
  100. package/lib/features/helper-lines/helper-line-feedback.js.map +1 -1
  101. package/lib/features/helper-lines/helper-line-manager-default.d.ts +2 -1
  102. package/lib/features/helper-lines/helper-line-manager-default.d.ts.map +1 -1
  103. package/lib/features/helper-lines/helper-line-manager-default.js +23 -0
  104. package/lib/features/helper-lines/helper-line-manager-default.js.map +1 -1
  105. package/lib/features/helper-lines/helper-line-manager.d.ts +10 -2
  106. package/lib/features/helper-lines/helper-line-manager.d.ts.map +1 -1
  107. package/lib/features/routing/edge-router.d.ts +4 -0
  108. package/lib/features/routing/edge-router.d.ts.map +1 -1
  109. package/lib/features/routing/edge-router.js +37 -0
  110. package/lib/features/routing/edge-router.js.map +1 -1
  111. package/lib/features/select/select-mouse-listener.d.ts +3 -0
  112. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  113. package/lib/features/select/select-mouse-listener.js +30 -2
  114. package/lib/features/select/select-mouse-listener.js.map +1 -1
  115. package/lib/features/tools/base-tools.d.ts +24 -15
  116. package/lib/features/tools/base-tools.d.ts.map +1 -1
  117. package/lib/features/tools/base-tools.js +0 -18
  118. package/lib/features/tools/base-tools.js.map +1 -1
  119. package/lib/features/tools/change-bounds/change-bounds-manager.d.ts +42 -0
  120. package/lib/features/tools/change-bounds/change-bounds-manager.d.ts.map +1 -0
  121. package/lib/features/tools/change-bounds/change-bounds-manager.js +136 -0
  122. package/lib/features/tools/change-bounds/change-bounds-manager.js.map +1 -0
  123. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts +6 -0
  124. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts.map +1 -1
  125. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js +14 -8
  126. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js.map +1 -1
  127. package/lib/features/tools/change-bounds/change-bounds-tool-module.d.ts.map +1 -1
  128. package/lib/features/tools/change-bounds/change-bounds-tool-module.js +2 -0
  129. package/lib/features/tools/change-bounds/change-bounds-tool-module.js.map +1 -1
  130. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts +6 -15
  131. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts.map +1 -1
  132. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js +24 -72
  133. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js.map +1 -1
  134. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts +14 -25
  135. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts.map +1 -1
  136. package/lib/features/tools/change-bounds/change-bounds-tool.js +63 -109
  137. package/lib/features/tools/change-bounds/change-bounds-tool.js.map +1 -1
  138. package/lib/features/tools/change-bounds/change-bounds-tracker.d.ts +136 -0
  139. package/lib/features/tools/change-bounds/change-bounds-tracker.d.ts.map +1 -0
  140. package/lib/features/tools/change-bounds/change-bounds-tracker.js +301 -0
  141. package/lib/features/tools/change-bounds/change-bounds-tracker.js.map +1 -0
  142. package/lib/features/tools/change-bounds/index.d.ts +2 -0
  143. package/lib/features/tools/change-bounds/index.d.ts.map +1 -1
  144. package/lib/features/tools/change-bounds/index.js +2 -0
  145. package/lib/features/tools/change-bounds/index.js.map +1 -1
  146. package/lib/features/tools/change-bounds/view.d.ts.map +1 -1
  147. package/lib/features/tools/change-bounds/view.js +1 -16
  148. package/lib/features/tools/change-bounds/view.js.map +1 -1
  149. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts +7 -9
  150. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts.map +1 -1
  151. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js +27 -47
  152. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js.map +1 -1
  153. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts +2 -2
  154. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts.map +1 -1
  155. package/lib/features/tools/edge-edit/edge-edit-tool.js +5 -5
  156. package/lib/features/tools/edge-edit/edge-edit-tool.js.map +1 -1
  157. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts +1 -0
  158. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts.map +1 -1
  159. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js +3 -0
  160. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js.map +1 -1
  161. package/lib/features/tools/node-creation/node-creation-tool.d.ts +2 -4
  162. package/lib/features/tools/node-creation/node-creation-tool.d.ts.map +1 -1
  163. package/lib/features/tools/node-creation/node-creation-tool.js +8 -11
  164. package/lib/features/tools/node-creation/node-creation-tool.js.map +1 -1
  165. package/lib/features/viewport/viewport-handler.d.ts +6 -4
  166. package/lib/features/viewport/viewport-handler.d.ts.map +1 -1
  167. package/lib/features/viewport/viewport-handler.js +13 -8
  168. package/lib/features/viewport/viewport-handler.js.map +1 -1
  169. package/lib/utils/gmodel-util.d.ts +20 -1
  170. package/lib/utils/gmodel-util.d.ts.map +1 -1
  171. package/lib/utils/gmodel-util.js +38 -16
  172. package/lib/utils/gmodel-util.js.map +1 -1
  173. package/lib/utils/layout-utils.d.ts +1 -0
  174. package/lib/utils/layout-utils.d.ts.map +1 -1
  175. package/lib/utils/layout-utils.js +5 -1
  176. package/lib/utils/layout-utils.js.map +1 -1
  177. package/package.json +3 -3
  178. package/src/base/default.module.ts +4 -1
  179. package/src/base/feedback/css-feedback.ts +10 -2
  180. package/src/base/feedback/feeback-emitter.ts +6 -3
  181. package/src/base/index.ts +1 -0
  182. package/src/base/model/diagram-loader.ts +5 -1
  183. package/src/base/mouse-position-tracker.ts +25 -0
  184. package/src/features/accessibility/view-key-tools/movement-key-tool.ts +12 -7
  185. package/src/features/bounds/bounds-module.ts +2 -0
  186. package/src/features/bounds/freeform-layout.ts +16 -6
  187. package/src/features/bounds/glsp-hidden-bounds-updater.ts +63 -46
  188. package/src/features/bounds/hbox-layout.ts +18 -9
  189. package/src/features/bounds/index.ts +1 -0
  190. package/src/features/bounds/layout-data.ts +48 -0
  191. package/src/features/bounds/local-bounds.ts +20 -4
  192. package/src/features/bounds/vbox-layout.ts +18 -10
  193. package/src/features/change-bounds/index.ts +1 -0
  194. package/src/features/change-bounds/model.ts +104 -4
  195. package/src/features/change-bounds/movement-restrictor.ts +10 -0
  196. package/src/features/change-bounds/point-position-updater.spec.ts +2 -0
  197. package/src/features/change-bounds/point-position-updater.ts +6 -0
  198. package/src/features/change-bounds/position-snapper.ts +4 -0
  199. package/src/features/change-bounds/snap.ts +6 -0
  200. package/src/features/change-bounds/tracker.ts +63 -0
  201. package/src/features/debug/debug-bounds-decorator.tsx +17 -7
  202. package/src/features/element-template/mouse-tracking-element-position-listener.ts +49 -71
  203. package/src/features/helper-lines/helper-line-feedback.ts +2 -1
  204. package/src/features/helper-lines/helper-line-manager-default.ts +37 -2
  205. package/src/features/helper-lines/helper-line-manager.ts +11 -2
  206. package/src/features/routing/edge-router.ts +42 -0
  207. package/src/features/select/select-mouse-listener.ts +15 -1
  208. package/src/features/tools/base-tools.ts +31 -19
  209. package/src/features/tools/change-bounds/change-bounds-manager.ts +145 -0
  210. package/src/features/tools/change-bounds/change-bounds-tool-feedback.ts +17 -6
  211. package/src/features/tools/change-bounds/change-bounds-tool-module.ts +2 -0
  212. package/src/features/tools/change-bounds/change-bounds-tool-move-feedback.ts +31 -81
  213. package/src/features/tools/change-bounds/change-bounds-tool.ts +89 -148
  214. package/src/features/tools/change-bounds/change-bounds-tracker.ts +455 -0
  215. package/src/features/tools/change-bounds/index.ts +2 -0
  216. package/src/features/tools/change-bounds/view.tsx +2 -14
  217. package/src/features/tools/edge-edit/edge-edit-tool-feedback.ts +34 -52
  218. package/src/features/tools/edge-edit/edge-edit-tool.ts +4 -3
  219. package/src/features/tools/marquee-selection/marquee-mouse-tool.ts +4 -0
  220. package/src/features/tools/node-creation/node-creation-tool.ts +7 -7
  221. package/src/features/viewport/viewport-handler.ts +13 -7
  222. package/src/utils/gmodel-util.ts +41 -5
  223. package/src/utils/layout-utils.ts +4 -0
@@ -13,29 +13,18 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Action, Disposable, ElementMove, GModelElement, GModelRoot, MoveAction, Point, findParentByFeature } from '@eclipse-glsp/sprotty';
16
+ import { Action, ElementMove, GModelElement, GModelRoot, MoveAction, Point, findParentByFeature } from '@eclipse-glsp/sprotty';
17
17
 
18
18
  import { DebouncedFunc, debounce } from 'lodash';
19
+ import { ChangeBoundsTracker, TrackedMove } from '.';
19
20
  import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener';
20
21
  import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback';
21
22
  import { FeedbackEmitter } from '../../../base/feedback/feeback-emitter';
22
23
  import { MoveableElement, filter, getElements, isNonRoutableSelectedMovableBoundsAware, removeDescendants } from '../../../utils';
23
- import { PointPositionUpdater } from '../../change-bounds';
24
24
  import { SResizeHandle } from '../../change-bounds/model';
25
- import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from '../../change-bounds/movement-restrictor';
26
25
  import { ChangeBoundsTool } from './change-bounds-tool';
27
26
  import { MoveFinishedEventAction, MoveInitializedEventAction } from './change-bounds-tool-feedback';
28
27
 
29
- export interface ValidatedElementMove extends ElementMove {
30
- valid: boolean;
31
- }
32
-
33
- export namespace ValidatedElementMove {
34
- export function isValid(move: ElementMove): boolean {
35
- return (move as ValidatedElementMove).valid ?? true;
36
- }
37
- }
38
-
39
28
  /**
40
29
  * This mouse listener provides visual feedback for moving by sending client-side
41
30
  * `MoveAction`s while elements are selected and dragged. This will also update
@@ -43,9 +32,9 @@ export namespace ValidatedElementMove {
43
32
  * the visual feedback but also the basis for sending the change to the server
44
33
  * (see also `tools/MoveTool`).
45
34
  */
46
- export class FeedbackMoveMouseListener extends DragAwareMouseListener implements Disposable {
35
+ export class FeedbackMoveMouseListener extends DragAwareMouseListener {
47
36
  protected rootElement?: GModelRoot;
48
- protected positionUpdater;
37
+ protected tracker: ChangeBoundsTracker;
49
38
  protected elementId2startPos = new Map<string, Point>();
50
39
 
51
40
  protected pendingMoveInitialized?: DebouncedFunc<() => void>;
@@ -55,7 +44,7 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
55
44
 
56
45
  constructor(protected tool: ChangeBoundsTool) {
57
46
  super();
58
- this.positionUpdater = new PointPositionUpdater(tool.positionSnapper);
47
+ this.tracker = tool.createChangeBoundsTracker();
59
48
  this.moveInitializedFeedback = tool.createFeedbackEmitter();
60
49
  this.moveFeedback = tool.createFeedbackEmitter();
61
50
  }
@@ -66,7 +55,7 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
66
55
  this.initializeMove(target, event);
67
56
  return [];
68
57
  }
69
- this.positionUpdater.resetPosition();
58
+ this.tracker.stopTracking();
70
59
  return [];
71
60
  }
72
61
 
@@ -77,10 +66,10 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
77
66
  }
78
67
  const moveable = findParentByFeature(target, this.isValidMoveable);
79
68
  if (moveable !== undefined) {
80
- this.positionUpdater.updateLastDragPosition(event);
69
+ this.tracker.startTracking();
81
70
  this.scheduleMoveInitialized();
82
71
  } else {
83
- this.positionUpdater.resetPosition();
72
+ this.tracker.stopTracking();
84
73
  }
85
74
  }
86
75
 
@@ -113,7 +102,7 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
113
102
  override nonDraggingMouseUp(element: GModelElement, event: MouseEvent): Action[] {
114
103
  // should reset everything that may have happend on mouse down
115
104
  this.moveInitializedFeedback.dispose();
116
- this.positionUpdater.resetPosition();
105
+ this.tracker.stopTracking();
117
106
  return [];
118
107
  }
119
108
 
@@ -121,7 +110,7 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
121
110
  super.mouseMove(target, event);
122
111
  if (event.buttons === 0) {
123
112
  return this.mouseUp(target, event);
124
- } else if (!this.positionUpdater.isLastDragPositionUndefined()) {
113
+ } else if (this.tracker.isTracking()) {
125
114
  return this.moveElements(target, event);
126
115
  }
127
116
  return [];
@@ -131,35 +120,34 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
131
120
  if (this.elementId2startPos.size === 0) {
132
121
  this.initializeElementsToMove(target.root);
133
122
  }
134
- const moveAction = this.getElementMoves(target, event, false);
135
- if (!moveAction) {
123
+ const elementsToMove = this.getElementsToMove(target);
124
+ const move = this.tracker.moveElements(elementsToMove, { snap: event, restrict: event });
125
+ if (move.elementMoves.length === 0) {
136
126
  return [];
137
127
  }
138
128
  // cancel any pending move
139
129
  this.pendingMoveInitialized?.cancel();
140
- this.moveFeedback.add(moveAction, () => this.resetElementPositions(target));
141
- this.addMovementFeedback(moveAction, target);
130
+ this.moveFeedback.add(this.createMoveAction(move), () => this.resetElementPositions(target));
131
+ this.addMovementFeedback(move, target, event);
132
+ this.tracker.updateTrackingPosition(move);
142
133
  this.moveFeedback.submit();
143
134
  return [];
144
135
  }
145
136
 
146
- protected addMovementFeedback(movement: MoveAction, ctx: GModelElement): void {
137
+ protected createMoveAction(trackedMove: TrackedMove): Action {
138
+ // we never want to animate the move action as this interferes with the move feedback
139
+ return MoveAction.create(
140
+ trackedMove.elementMoves.map(move => ({ elementId: move.element.id, toPosition: move.toPosition })),
141
+ { animate: false }
142
+ );
143
+ }
144
+
145
+ protected addMovementFeedback(trackedMove: TrackedMove, ctx: GModelElement, event: MouseEvent): void {
147
146
  // cursor feedback
148
147
  this.moveFeedback.add(cursorFeedbackAction(CursorCSS.MOVE), cursorFeedbackAction(CursorCSS.DEFAULT));
149
148
 
150
149
  // restriction feedback
151
- movement.moves.forEach(move => {
152
- const element = ctx.root.index.getById(move.elementId);
153
- if (element && this.tool.movementRestrictor) {
154
- if (!ValidatedElementMove.isValid(move)) {
155
- this.moveFeedback.add(createMovementRestrictionFeedback(element, this.tool.movementRestrictor), () =>
156
- removeMovementRestrictionFeedback(element, this.tool.movementRestrictor!)
157
- );
158
- } else {
159
- this.moveFeedback.add(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor));
160
- }
161
- }
162
- });
150
+ trackedMove.elementMoves.forEach(move => this.tool.changeBoundsManager.addRestrictionFeedback(this.moveFeedback, move));
163
151
  }
164
152
 
165
153
  protected initializeElementsToMove(root: GModelRoot): void {
@@ -177,45 +165,6 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
177
165
  return getElements(context.root.index, Array.from(this.elementId2startPos.keys()), this.isValidMoveable);
178
166
  }
179
167
 
180
- protected getElementMoves(target: GModelElement, event: MouseEvent, finished: boolean): MoveAction | undefined {
181
- const delta = this.positionUpdater.updatePosition(target, event);
182
- if (!delta) {
183
- return undefined;
184
- }
185
- const elementMoves: ElementMove[] = this.getElementMovesForDelta(target, delta, finished).filter(move =>
186
- this.filterElementMove(move)
187
- );
188
- if (elementMoves.length > 0) {
189
- // we never want to animate the move action as this interferes with the move feedback
190
- return MoveAction.create(elementMoves, { animate: false, finished });
191
- } else {
192
- return undefined;
193
- }
194
- }
195
-
196
- protected filterElementMove(elementMove: ValidatedElementMove): boolean {
197
- return !!elementMove.fromPosition && !Point.equals(elementMove.fromPosition, elementMove.toPosition);
198
- }
199
-
200
- protected getElementMovesForDelta(target: GModelElement, delta: Point, finished: boolean): ValidatedElementMove[] {
201
- return this.getElementsToMove(target).flatMap<ValidatedElementMove>(element => {
202
- const startPosition = this.elementId2startPos.get(element.id);
203
- if (!startPosition) {
204
- return [];
205
- }
206
- const targetPosition = Point.add(element.position, delta);
207
- const valid = this.tool.movementRestrictor?.validate(element, targetPosition) ?? true;
208
- return [this.createElementMove(element, targetPosition, valid, finished)];
209
- });
210
- }
211
-
212
- protected createElementMove(element: MoveableElement, toPosition: Point, valid: boolean, finished: boolean): ValidatedElementMove {
213
- // if we are finished and have an invalid move, we want to move the element back to its start position
214
- return !valid && finished
215
- ? { elementId: element.id, fromPosition: element.position, toPosition: element.position, valid }
216
- : { elementId: element.id, fromPosition: element.position, toPosition, valid };
217
- }
218
-
219
168
  protected resetElementPositions(context: GModelElement): MoveAction | undefined {
220
169
  const elementMoves: ElementMove[] = this.revertElementMoves(context);
221
170
  return MoveAction.create(elementMoves, { animate: false, finished: true });
@@ -233,12 +182,13 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
233
182
  }
234
183
 
235
184
  override draggingMouseUp(target: GModelElement, event: MouseEvent): Action[] {
236
- if (this.positionUpdater.isLastDragPositionUndefined()) {
185
+ if (!this.tracker.isTracking()) {
237
186
  return [];
238
187
  }
239
188
  // only reset the move of invalid elements, the others will be handled by the change bounds tool itself
240
- const moves = this.getElementMovesForDelta(target, Point.ORIGIN, false);
241
- moves.filter(move => move.valid).forEach(move => this.elementId2startPos.delete(move.elementId));
189
+ this.getElementsToMove(target)
190
+ .filter(element => this.tool.changeBoundsManager.isValid(element))
191
+ .forEach(element => this.elementId2startPos.delete(element.id));
242
192
  this.dispose();
243
193
  return [];
244
194
  }
@@ -247,7 +197,7 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements
247
197
  this.pendingMoveInitialized?.cancel();
248
198
  this.moveInitializedFeedback.dispose();
249
199
  this.moveFeedback.dispose();
250
- this.positionUpdater.resetPosition();
200
+ this.tracker.dispose();
251
201
  this.elementId2startPos.clear();
252
202
  super.dispose();
253
203
  }
@@ -37,11 +37,18 @@ import {
37
37
  TYPES,
38
38
  findParentByFeature
39
39
  } from '@eclipse-glsp/sprotty';
40
+ import { CSS_RESIZE_MODE, ChangeBoundsManager, ChangeBoundsTracker, TrackedElementResize, TrackedResize } from '..';
40
41
  import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener';
41
- import { CursorCSS, applyCssClasses, cursorFeedbackAction, deleteCssClasses } from '../../../base/feedback/css-feedback';
42
+ import {
43
+ CursorCSS,
44
+ ModifyCSSFeedbackAction,
45
+ applyCssClasses,
46
+ cursorFeedbackAction,
47
+ deleteCssClasses,
48
+ toggleCssClasses
49
+ } from '../../../base/feedback/css-feedback';
42
50
  import { FeedbackEmitter } from '../../../base/feedback/feeback-emitter';
43
51
  import { ISelectionListener, SelectionService } from '../../../base/selection-service';
44
- import { isValidMove, isValidSize } from '../../../utils';
45
52
  import {
46
53
  BoundsAwareModelElement,
47
54
  ResizableModelElement,
@@ -51,16 +58,13 @@ import {
51
58
  isNonRoutableSelectedMovableBoundsAware,
52
59
  toElementAndBounds
53
60
  } from '../../../utils/gmodel-util';
54
- import { SetBoundsFeedbackAction } from '../../bounds';
55
- import { PointPositionUpdater } from '../../change-bounds';
56
- import { ResizeHandleLocation, SResizeHandle, isResizable } from '../../change-bounds/model';
61
+ import { LocalRequestBoundsAction, SetBoundsFeedbackAction } from '../../bounds';
62
+ import { SResizeHandle, isResizable } from '../../change-bounds/model';
57
63
  import {
58
64
  IMovementRestrictor,
59
- createMovementRestrictionFeedback,
65
+ movementRestrictionFeedback,
60
66
  removeMovementRestrictionFeedback
61
67
  } from '../../change-bounds/movement-restrictor';
62
- import { PositionSnapper } from '../../change-bounds/position-snapper';
63
- import { getDirectionFrom } from '../../helper-lines';
64
68
  import { BaseEditTool } from '../base-tools';
65
69
  import {
66
70
  HideChangeBoundsToolResizeFeedbackAction,
@@ -89,7 +93,7 @@ export class ChangeBoundsTool extends BaseEditTool {
89
93
  @inject(SelectionService) protected selectionService: SelectionService;
90
94
  @inject(EdgeRouterRegistry) @optional() readonly edgeRouterRegistry?: EdgeRouterRegistry;
91
95
  @inject(TYPES.IMovementRestrictor) @optional() readonly movementRestrictor?: IMovementRestrictor;
92
- @inject(PositionSnapper) readonly positionSnapper: PositionSnapper;
96
+ @inject(TYPES.IChangeBoundsManager) readonly changeBoundsManager: ChangeBoundsManager;
93
97
 
94
98
  get id(): string {
95
99
  return ChangeBoundsTool.ID;
@@ -115,6 +119,10 @@ export class ChangeBoundsTool extends BaseEditTool {
115
119
  );
116
120
  }
117
121
 
122
+ createChangeBoundsTracker(): ChangeBoundsTracker {
123
+ return this.changeBoundsManager.createTracker();
124
+ }
125
+
118
126
  protected createMoveMouseListener(): MouseListener {
119
127
  return new FeedbackMoveMouseListener(this);
120
128
  }
@@ -124,22 +132,12 @@ export class ChangeBoundsTool extends BaseEditTool {
124
132
  }
125
133
  }
126
134
 
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
-
137
- export class ChangeBoundsListener extends DragAwareMouseListener implements ISelectionListener, Disposable {
135
+ export class ChangeBoundsListener extends DragAwareMouseListener implements ISelectionListener {
138
136
  static readonly CSS_CLASS_ACTIVE = 'active';
139
137
 
140
138
  // members for calculating the correct position change
141
139
  protected initialBounds: ElementAndBounds | undefined;
142
- protected positionUpdater: PointPositionUpdater;
140
+ protected tracker: ChangeBoundsTracker;
143
141
 
144
142
  // members for resize mode
145
143
  protected activeResizeElement?: ResizableModelElement;
@@ -149,7 +147,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
149
147
 
150
148
  constructor(protected tool: ChangeBoundsTool) {
151
149
  super();
152
- this.positionUpdater = new PointPositionUpdater(tool.positionSnapper);
150
+ this.tracker = tool.createChangeBoundsTracker();
153
151
  this.handleFeedback = tool.createFeedbackEmitter();
154
152
  this.resizeFeedback = tool.createFeedbackEmitter();
155
153
  }
@@ -171,22 +169,28 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
171
169
  this.activeResizeElement = this.activeResizeHandle?.parent ?? this.findResizeElement(target);
172
170
  if (this.activeResizeElement) {
173
171
  if (event) {
174
- this.positionUpdater.updateLastDragPosition(event);
172
+ this.tracker.startTracking();
175
173
  }
176
174
  this.initialBounds = {
177
175
  newSize: this.activeResizeElement.bounds,
178
176
  newPosition: this.activeResizeElement.bounds,
179
177
  elementId: this.activeResizeElement.id
180
178
  };
179
+ // we trigger the local bounds calculation once to get the correct layout information for reszing
180
+ // for any sub-sequent calls the layout information will be updated automatically
181
+ this.tool
182
+ .createFeedbackEmitter()
183
+ .add(LocalRequestBoundsAction.create(target.root, [this.activeResizeElement.id]))
184
+ .submit()
185
+ .dispose();
181
186
  this.handleFeedback.add(
182
- ShowChangeBoundsToolResizeFeedbackAction.create(this.activeResizeElement.id),
187
+ ShowChangeBoundsToolResizeFeedbackAction.create({ elementId: this.activeResizeElement.id }),
183
188
  HideChangeBoundsToolResizeFeedbackAction.create()
184
189
  );
185
190
  this.handleFeedback.submit();
186
191
  return true;
187
192
  } else {
188
- this.positionUpdater.resetPosition();
189
- this.handleFeedback.dispose();
193
+ this.dispose();
190
194
  return false;
191
195
  }
192
196
  }
@@ -200,74 +204,81 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
200
204
  protected override draggingMouseMove(target: GModelElement, event: MouseEvent): Action[] {
201
205
  // rely on the FeedbackMoveMouseListener to update the element bounds of selected elements
202
206
  // 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));
209
- if (positionUpdate) {
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
- }
207
+ if (this.activeResizeHandle && this.tracker.isTracking()) {
208
+ const resize = this.tracker.resizeElements(this.activeResizeHandle, { snap: event, symmetric: event, restrict: event });
209
+ this.addResizeFeedback(resize, target, event);
210
+ const resizeAction = this.resizeBoundsAction(resize);
211
+ if (resizeAction.bounds.length > 0) {
212
+ this.resizeFeedback.add(resizeAction, () => this.resetBounds());
213
+ this.tracker.updateTrackingPosition(resize.handleMove.moveVector);
214
+ } else {
215
+ this.resizeFeedback.add(undefined, () => this.resetBounds());
216
216
  }
217
+ this.resizeFeedback.submit();
217
218
  }
218
219
  return super.draggingMouseMove(target, event);
219
220
  }
220
221
 
221
- protected filterResizeOnClient(resize: ValidatedElementAndBounds, positionUpdate: Point): boolean {
222
- return true;
222
+ protected resizeBoundsAction(resize: TrackedResize): SetBoundsFeedbackAction {
223
+ // we do not want to resize elements beyond their valid size, not even for feedback, as the next layout cycle usually corrects this
224
+ const elementResizes = resize.elementResizes.filter(elementResize => elementResize.valid.size);
225
+ return SetBoundsFeedbackAction.create(elementResizes.map(elementResize => this.toElementAndBounds(elementResize)));
223
226
  }
224
227
 
225
- protected addResizeFeedback(
226
- resizeActions: ValidatedElementAndBounds[],
227
- handle: SResizeHandle,
228
- target: GModelElement,
229
- event: MouseEvent
230
- ): void {
228
+ protected toElementAndBounds(elementResize: TrackedElementResize): ElementAndBounds {
229
+ return {
230
+ elementId: elementResize.element.id,
231
+ newSize: elementResize.toBounds,
232
+ newPosition: elementResize.toBounds
233
+ };
234
+ }
235
+
236
+ protected addResizeFeedback(resize: TrackedResize, target: GModelElement, event: MouseEvent): void {
237
+ const handle = resize.handleMove.element;
231
238
  // handle feedback
232
239
  this.resizeFeedback.add(
233
240
  applyCssClasses(handle, ChangeBoundsListener.CSS_CLASS_ACTIVE),
234
241
  deleteCssClasses(handle, ChangeBoundsListener.CSS_CLASS_ACTIVE)
235
242
  );
236
243
 
244
+ // graph feedback
245
+ this.resizeFeedback.add(
246
+ ModifyCSSFeedbackAction.create({ add: [CSS_RESIZE_MODE] }),
247
+ ModifyCSSFeedbackAction.create({ remove: [CSS_RESIZE_MODE] })
248
+ );
249
+
237
250
  // 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;
251
+ const cursorClass = SResizeHandle.getCursorCss(resize.handleMove.element);
245
252
  this.resizeFeedback.add(cursorFeedbackAction(cursorClass), cursorFeedbackAction(CursorCSS.DEFAULT));
246
253
 
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
- }
254
+ const movementRestrictor = this.tool.changeBoundsManager.movementRestrictor;
255
+ resize.elementResizes.forEach(elementResize => {
256
+ if (movementRestrictor) {
257
+ this.resizeFeedback.add(
258
+ movementRestrictionFeedback(elementResize.element, movementRestrictor, elementResize.valid.move),
259
+ removeMovementRestrictionFeedback(elementResize.element, movementRestrictor)
260
+ );
258
261
  }
262
+ this.resizeFeedback.add(
263
+ toggleCssClasses(elementResize.element, !elementResize.valid.size, 'resize-not-allowed'),
264
+ deleteCssClasses(elementResize.element, 'resize-not-allowed')
265
+ );
259
266
  });
267
+ this.resizeFeedback.add(
268
+ toggleCssClasses(resize.handleMove.element, !resize.valid.size, 'resize-not-allowed'),
269
+ deleteCssClasses(resize.handleMove.element, 'resize-not-allowed')
270
+ );
260
271
  }
261
272
 
262
- protected resetBoundsAction(): Action[] {
273
+ protected resetBounds(): Action[] {
263
274
  // reset the bounds to the initial bounds and ensure that we do not show helper line feedback anymore (MoveFinishedEventAction)
264
275
  return this.initialBounds
265
276
  ? [SetBoundsFeedbackAction.create([this.initialBounds]), MoveFinishedEventAction.create()]
266
277
  : [MoveFinishedEventAction.create()];
267
278
  }
268
279
 
269
- override draggingMouseUp(target: GModelElement, _event: MouseEvent): Action[] {
270
- if (this.positionUpdater.isLastDragPositionUndefined()) {
280
+ override draggingMouseUp(target: GModelElement, event: MouseEvent): Action[] {
281
+ if (!this.tracker.isTracking()) {
271
282
  return [];
272
283
  }
273
284
  const actions: Action[] = [];
@@ -296,17 +307,13 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
296
307
  const selectedElements = getMatchingElements(target.index, isNonRoutableSelectedMovableBoundsAware);
297
308
  const selectionSet: Set<BoundsAwareModelElement> = new Set(selectedElements);
298
309
  const newBounds: ElementAndBounds[] = selectedElements
299
- .filter(element => this.isValidElement(element, selectionSet))
310
+ .filter(element => this.isValidMove(element, selectionSet))
300
311
  .map(toElementAndBounds);
301
312
  return newBounds.length > 0 ? [ChangeBoundsOperation.create(newBounds)] : [];
302
313
  }
303
314
 
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
- );
315
+ protected isValidMove(element: BoundsAwareModelElement, selectedElements: Set<BoundsAwareModelElement> = new Set()): boolean {
316
+ return this.tool.changeBoundsManager.hasValidPosition(element) && !this.isChildOfSelected(selectedElements, element);
310
317
  }
311
318
 
312
319
  protected isChildOfSelected(selectedElements: Set<GModelElement>, element: GModelElement): boolean {
@@ -342,7 +349,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
342
349
  }
343
350
 
344
351
  protected handleResizeOnServer(activeResizeHandle: SResizeHandle): Action[] {
345
- if (this.isValidElement(activeResizeHandle.parent) && this.initialBounds) {
352
+ if (this.initialBounds && this.isValidResize(activeResizeHandle.parent)) {
346
353
  const elementAndBounds = toElementAndBounds(activeResizeHandle.parent);
347
354
  if (!this.initialBounds.newPosition || !elementAndBounds.newPosition) {
348
355
  return [];
@@ -361,6 +368,10 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
361
368
  return [];
362
369
  }
363
370
 
371
+ protected isValidResize(element: BoundsAwareModelElement): boolean {
372
+ return this.tool.changeBoundsManager.isValid(element);
373
+ }
374
+
364
375
  selectionChanged(root: GModelRoot, selectedElements: string[]): void {
365
376
  if (this.activeResizeElement && selectedElements.includes(this.activeResizeElement.id)) {
366
377
  // our active element is still selected, nothing to do
@@ -374,88 +385,18 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
374
385
  return;
375
386
  }
376
387
  }
377
- this.updateResizeElement(root);
378
- this.disposeAllButHandles();
388
+ this.dispose();
379
389
  }
380
390
 
381
391
  protected isActiveResizeElement(element?: GModelElement): element is GParentElement & BoundsAware {
382
392
  return element !== undefined && this.activeResizeElement !== undefined && element.id === this.activeResizeElement.id;
383
393
  }
384
394
 
385
- protected handleResizeOnClient(positionUpdate: Point): ValidatedElementAndBounds[] {
386
- if (!this.activeResizeHandle) {
387
- return [];
388
- }
389
-
390
- if (this.isActiveResizeElement(this.activeResizeHandle.parent)) {
391
- switch (this.activeResizeHandle.location) {
392
- case ResizeHandleLocation.TopLeft:
393
- return this.handleTopLeftResize(this.activeResizeHandle.parent, positionUpdate);
394
- case ResizeHandleLocation.TopRight:
395
- return this.handleTopRightResize(this.activeResizeHandle.parent, positionUpdate);
396
- case ResizeHandleLocation.BottomLeft:
397
- return this.handleBottomLeftResize(this.activeResizeHandle.parent, positionUpdate);
398
- case ResizeHandleLocation.BottomRight:
399
- return this.handleBottomRightResize(this.activeResizeHandle.parent, positionUpdate);
400
- }
401
- }
402
- return [];
403
- }
404
-
405
- protected handleTopLeftResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
406
- return this.createSetBoundsAction(
407
- resizeElement,
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 }
410
- );
411
- }
412
-
413
- protected handleTopRightResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
414
- return this.createSetBoundsAction(
415
- resizeElement,
416
- { x: resizeElement.bounds.x, y: resizeElement.bounds.y + positionUpdate.y },
417
- { width: resizeElement.bounds.width + positionUpdate.x, height: resizeElement.bounds.height - positionUpdate.y }
418
- );
419
- }
420
-
421
- protected handleBottomLeftResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
422
- return this.createSetBoundsAction(
423
- resizeElement,
424
- { x: resizeElement.bounds.x + positionUpdate.x, y: resizeElement.bounds.y },
425
- { width: resizeElement.bounds.width - positionUpdate.x, height: resizeElement.bounds.height + positionUpdate.y }
426
- );
427
- }
428
-
429
- protected handleBottomRightResize(resizeElement: ResizableModelElement, positionUpdate: Point): ValidatedElementAndBounds[] {
430
- return this.createSetBoundsAction(
431
- resizeElement,
432
- { x: resizeElement.bounds.x, y: resizeElement.bounds.y },
433
- { width: resizeElement.bounds.width + positionUpdate.x, height: resizeElement.bounds.height + positionUpdate.y }
434
- );
435
- }
436
-
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 [];
441
- }
442
- const valid = this.isValidMove(element, newPosition);
443
- return [{ elementId: element.id, newPosition, newSize, valid }];
444
- }
445
-
446
- protected isValidSize(element: BoundsAwareModelElement, size: Dimension): boolean {
447
- return isValidSize(element, size);
448
- }
449
-
450
- protected isValidMove(element: BoundsAwareModelElement, newPosition: Point): boolean {
451
- return isValidMove(element, newPosition, this.tool.movementRestrictor);
452
- }
453
-
454
395
  protected disposeAllButHandles(): void {
455
396
  // We do not dispose the handle feedback as we want to keep showing the handles on selected elements
456
397
  // this.handleFeedback.dispose();
457
398
  this.resizeFeedback.dispose();
458
- this.positionUpdater.resetPosition();
399
+ this.tracker.dispose();
459
400
  this.activeResizeElement = undefined;
460
401
  this.activeResizeHandle = undefined;
461
402
  this.initialBounds = undefined;