@eclipse-glsp/client 2.2.0-next.333 → 2.2.0-next.335

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 (214) 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/mouse-position-tracker.d.ts +22 -0
  20. package/lib/base/mouse-position-tracker.d.ts.map +1 -0
  21. package/lib/base/mouse-position-tracker.js +40 -0
  22. package/lib/base/mouse-position-tracker.js.map +1 -0
  23. package/lib/features/accessibility/view-key-tools/movement-key-tool.d.ts +2 -0
  24. package/lib/features/accessibility/view-key-tools/movement-key-tool.d.ts.map +1 -1
  25. package/lib/features/accessibility/view-key-tools/movement-key-tool.js +15 -7
  26. package/lib/features/accessibility/view-key-tools/movement-key-tool.js.map +1 -1
  27. package/lib/features/bounds/bounds-module.d.ts.map +1 -1
  28. package/lib/features/bounds/bounds-module.js +2 -0
  29. package/lib/features/bounds/bounds-module.js.map +1 -1
  30. package/lib/features/bounds/freeform-layout.d.ts +17 -1
  31. package/lib/features/bounds/freeform-layout.d.ts.map +1 -1
  32. package/lib/features/bounds/freeform-layout.js +14 -5
  33. package/lib/features/bounds/freeform-layout.js.map +1 -1
  34. package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts +5 -4
  35. package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts.map +1 -1
  36. package/lib/features/bounds/glsp-hidden-bounds-updater.js +51 -39
  37. package/lib/features/bounds/glsp-hidden-bounds-updater.js.map +1 -1
  38. package/lib/features/bounds/hbox-layout.d.ts +18 -2
  39. package/lib/features/bounds/hbox-layout.d.ts.map +1 -1
  40. package/lib/features/bounds/hbox-layout.js +14 -5
  41. package/lib/features/bounds/hbox-layout.js.map +1 -1
  42. package/lib/features/bounds/index.d.ts +1 -0
  43. package/lib/features/bounds/index.d.ts.map +1 -1
  44. package/lib/features/bounds/index.js +1 -0
  45. package/lib/features/bounds/index.js.map +1 -1
  46. package/lib/features/bounds/layout-data.d.ts +27 -0
  47. package/lib/features/bounds/layout-data.d.ts.map +1 -0
  48. package/lib/features/bounds/layout-data.js +48 -0
  49. package/lib/features/bounds/layout-data.js.map +1 -0
  50. package/lib/features/bounds/local-bounds.d.ts +3 -1
  51. package/lib/features/bounds/local-bounds.d.ts.map +1 -1
  52. package/lib/features/bounds/local-bounds.js +14 -0
  53. package/lib/features/bounds/local-bounds.js.map +1 -1
  54. package/lib/features/bounds/vbox-layout.d.ts +18 -2
  55. package/lib/features/bounds/vbox-layout.d.ts.map +1 -1
  56. package/lib/features/bounds/vbox-layout.js +14 -5
  57. package/lib/features/bounds/vbox-layout.js.map +1 -1
  58. package/lib/features/change-bounds/index.d.ts +1 -0
  59. package/lib/features/change-bounds/index.d.ts.map +1 -1
  60. package/lib/features/change-bounds/index.js +1 -0
  61. package/lib/features/change-bounds/index.js.map +1 -1
  62. package/lib/features/change-bounds/model.d.ts +19 -3
  63. package/lib/features/change-bounds/model.d.ts.map +1 -1
  64. package/lib/features/change-bounds/model.js +92 -4
  65. package/lib/features/change-bounds/model.js.map +1 -1
  66. package/lib/features/change-bounds/movement-restrictor.d.ts +1 -0
  67. package/lib/features/change-bounds/movement-restrictor.d.ts.map +1 -1
  68. package/lib/features/change-bounds/movement-restrictor.js +7 -1
  69. package/lib/features/change-bounds/movement-restrictor.js.map +1 -1
  70. package/lib/features/change-bounds/point-position-updater.d.ts +3 -0
  71. package/lib/features/change-bounds/point-position-updater.d.ts.map +1 -1
  72. package/lib/features/change-bounds/point-position-updater.js +7 -2
  73. package/lib/features/change-bounds/point-position-updater.js.map +1 -1
  74. package/lib/features/change-bounds/position-snapper.d.ts +4 -0
  75. package/lib/features/change-bounds/position-snapper.d.ts.map +1 -1
  76. package/lib/features/change-bounds/position-snapper.js +4 -0
  77. package/lib/features/change-bounds/position-snapper.js.map +1 -1
  78. package/lib/features/change-bounds/snap.d.ts +6 -0
  79. package/lib/features/change-bounds/snap.d.ts.map +1 -1
  80. package/lib/features/change-bounds/snap.js +6 -0
  81. package/lib/features/change-bounds/snap.js.map +1 -1
  82. package/lib/features/change-bounds/tracker.d.ts +32 -0
  83. package/lib/features/change-bounds/tracker.d.ts.map +1 -0
  84. package/lib/features/change-bounds/tracker.js +60 -0
  85. package/lib/features/change-bounds/tracker.js.map +1 -0
  86. package/lib/features/debug/debug-bounds-decorator.d.ts +4 -1
  87. package/lib/features/debug/debug-bounds-decorator.d.ts.map +1 -1
  88. package/lib/features/debug/debug-bounds-decorator.js +14 -7
  89. package/lib/features/debug/debug-bounds-decorator.js.map +1 -1
  90. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts +9 -22
  91. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts.map +1 -1
  92. package/lib/features/element-template/mouse-tracking-element-position-listener.js +30 -46
  93. package/lib/features/element-template/mouse-tracking-element-position-listener.js.map +1 -1
  94. package/lib/features/helper-lines/helper-line-feedback.d.ts +2 -2
  95. package/lib/features/helper-lines/helper-line-feedback.d.ts.map +1 -1
  96. package/lib/features/helper-lines/helper-line-feedback.js +1 -1
  97. package/lib/features/helper-lines/helper-line-feedback.js.map +1 -1
  98. package/lib/features/helper-lines/helper-line-manager-default.d.ts +2 -1
  99. package/lib/features/helper-lines/helper-line-manager-default.d.ts.map +1 -1
  100. package/lib/features/helper-lines/helper-line-manager-default.js +23 -0
  101. package/lib/features/helper-lines/helper-line-manager-default.js.map +1 -1
  102. package/lib/features/helper-lines/helper-line-manager.d.ts +10 -2
  103. package/lib/features/helper-lines/helper-line-manager.d.ts.map +1 -1
  104. package/lib/features/routing/edge-router.d.ts +4 -0
  105. package/lib/features/routing/edge-router.d.ts.map +1 -1
  106. package/lib/features/routing/edge-router.js +37 -0
  107. package/lib/features/routing/edge-router.js.map +1 -1
  108. package/lib/features/select/select-mouse-listener.d.ts +3 -0
  109. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  110. package/lib/features/select/select-mouse-listener.js +30 -2
  111. package/lib/features/select/select-mouse-listener.js.map +1 -1
  112. package/lib/features/tools/base-tools.d.ts +24 -15
  113. package/lib/features/tools/base-tools.d.ts.map +1 -1
  114. package/lib/features/tools/base-tools.js +0 -18
  115. package/lib/features/tools/base-tools.js.map +1 -1
  116. package/lib/features/tools/change-bounds/change-bounds-manager.d.ts +42 -0
  117. package/lib/features/tools/change-bounds/change-bounds-manager.d.ts.map +1 -0
  118. package/lib/features/tools/change-bounds/change-bounds-manager.js +136 -0
  119. package/lib/features/tools/change-bounds/change-bounds-manager.js.map +1 -0
  120. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts +6 -0
  121. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts.map +1 -1
  122. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js +14 -8
  123. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js.map +1 -1
  124. package/lib/features/tools/change-bounds/change-bounds-tool-module.d.ts.map +1 -1
  125. package/lib/features/tools/change-bounds/change-bounds-tool-module.js +2 -0
  126. package/lib/features/tools/change-bounds/change-bounds-tool-module.js.map +1 -1
  127. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts +6 -15
  128. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts.map +1 -1
  129. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js +24 -72
  130. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js.map +1 -1
  131. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts +14 -25
  132. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts.map +1 -1
  133. package/lib/features/tools/change-bounds/change-bounds-tool.js +63 -109
  134. package/lib/features/tools/change-bounds/change-bounds-tool.js.map +1 -1
  135. package/lib/features/tools/change-bounds/change-bounds-tracker.d.ts +136 -0
  136. package/lib/features/tools/change-bounds/change-bounds-tracker.d.ts.map +1 -0
  137. package/lib/features/tools/change-bounds/change-bounds-tracker.js +301 -0
  138. package/lib/features/tools/change-bounds/change-bounds-tracker.js.map +1 -0
  139. package/lib/features/tools/change-bounds/index.d.ts +2 -0
  140. package/lib/features/tools/change-bounds/index.d.ts.map +1 -1
  141. package/lib/features/tools/change-bounds/index.js +2 -0
  142. package/lib/features/tools/change-bounds/index.js.map +1 -1
  143. package/lib/features/tools/change-bounds/view.d.ts.map +1 -1
  144. package/lib/features/tools/change-bounds/view.js +1 -16
  145. package/lib/features/tools/change-bounds/view.js.map +1 -1
  146. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts +7 -9
  147. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts.map +1 -1
  148. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js +27 -47
  149. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js.map +1 -1
  150. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts +2 -2
  151. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts.map +1 -1
  152. package/lib/features/tools/edge-edit/edge-edit-tool.js +5 -5
  153. package/lib/features/tools/edge-edit/edge-edit-tool.js.map +1 -1
  154. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts +1 -0
  155. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts.map +1 -1
  156. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js +3 -0
  157. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js.map +1 -1
  158. package/lib/features/tools/node-creation/node-creation-tool.d.ts +2 -4
  159. package/lib/features/tools/node-creation/node-creation-tool.d.ts.map +1 -1
  160. package/lib/features/tools/node-creation/node-creation-tool.js +8 -11
  161. package/lib/features/tools/node-creation/node-creation-tool.js.map +1 -1
  162. package/lib/utils/gmodel-util.d.ts +20 -1
  163. package/lib/utils/gmodel-util.d.ts.map +1 -1
  164. package/lib/utils/gmodel-util.js +38 -16
  165. package/lib/utils/gmodel-util.js.map +1 -1
  166. package/lib/utils/layout-utils.d.ts +1 -0
  167. package/lib/utils/layout-utils.d.ts.map +1 -1
  168. package/lib/utils/layout-utils.js +5 -1
  169. package/lib/utils/layout-utils.js.map +1 -1
  170. package/package.json +3 -3
  171. package/src/base/default.module.ts +4 -1
  172. package/src/base/feedback/css-feedback.ts +10 -2
  173. package/src/base/feedback/feeback-emitter.ts +6 -3
  174. package/src/base/index.ts +1 -0
  175. package/src/base/mouse-position-tracker.ts +25 -0
  176. package/src/features/accessibility/view-key-tools/movement-key-tool.ts +12 -7
  177. package/src/features/bounds/bounds-module.ts +2 -0
  178. package/src/features/bounds/freeform-layout.ts +16 -6
  179. package/src/features/bounds/glsp-hidden-bounds-updater.ts +63 -46
  180. package/src/features/bounds/hbox-layout.ts +18 -9
  181. package/src/features/bounds/index.ts +1 -0
  182. package/src/features/bounds/layout-data.ts +48 -0
  183. package/src/features/bounds/local-bounds.ts +20 -4
  184. package/src/features/bounds/vbox-layout.ts +18 -10
  185. package/src/features/change-bounds/index.ts +1 -0
  186. package/src/features/change-bounds/model.ts +104 -4
  187. package/src/features/change-bounds/movement-restrictor.ts +10 -0
  188. package/src/features/change-bounds/point-position-updater.spec.ts +2 -0
  189. package/src/features/change-bounds/point-position-updater.ts +6 -0
  190. package/src/features/change-bounds/position-snapper.ts +4 -0
  191. package/src/features/change-bounds/snap.ts +6 -0
  192. package/src/features/change-bounds/tracker.ts +63 -0
  193. package/src/features/debug/debug-bounds-decorator.tsx +17 -7
  194. package/src/features/element-template/mouse-tracking-element-position-listener.ts +49 -71
  195. package/src/features/helper-lines/helper-line-feedback.ts +2 -1
  196. package/src/features/helper-lines/helper-line-manager-default.ts +37 -2
  197. package/src/features/helper-lines/helper-line-manager.ts +11 -2
  198. package/src/features/routing/edge-router.ts +42 -0
  199. package/src/features/select/select-mouse-listener.ts +15 -1
  200. package/src/features/tools/base-tools.ts +31 -19
  201. package/src/features/tools/change-bounds/change-bounds-manager.ts +145 -0
  202. package/src/features/tools/change-bounds/change-bounds-tool-feedback.ts +17 -6
  203. package/src/features/tools/change-bounds/change-bounds-tool-module.ts +2 -0
  204. package/src/features/tools/change-bounds/change-bounds-tool-move-feedback.ts +31 -81
  205. package/src/features/tools/change-bounds/change-bounds-tool.ts +89 -148
  206. package/src/features/tools/change-bounds/change-bounds-tracker.ts +455 -0
  207. package/src/features/tools/change-bounds/index.ts +2 -0
  208. package/src/features/tools/change-bounds/view.tsx +2 -14
  209. package/src/features/tools/edge-edit/edge-edit-tool-feedback.ts +34 -52
  210. package/src/features/tools/edge-edit/edge-edit-tool.ts +4 -3
  211. package/src/features/tools/marquee-selection/marquee-mouse-tool.ts +4 -0
  212. package/src/features/tools/node-creation/node-creation-tool.ts +7 -7
  213. package/src/utils/gmodel-util.ts +41 -5
  214. package/src/utils/layout-utils.ts +4 -0
@@ -41,6 +41,13 @@ export abstract class GLSPAbstractEdgeRouter extends AbstractEdgeRouter {
41
41
  const anchor = super.getTranslatedAnchor(connectable, refPoint, refContainer, edge, anchorCorrection);
42
42
  return Point.isValid(anchor) ? anchor : refPoint;
43
43
  }
44
+
45
+ override cleanupRoutingPoints(edge: GRoutableElement, routingPoints: Point[], updateHandles: boolean, addRoutingPoints: boolean): void {
46
+ // sometimes it might happen that the source or target has the bounds not properly set when using the feedback edge
47
+ if (ensureBounds(edge.source) && ensureBounds(edge.target)) {
48
+ super.cleanupRoutingPoints(edge, routingPoints, updateHandles, addRoutingPoints);
49
+ }
50
+ }
44
51
  }
45
52
 
46
53
  @injectable()
@@ -56,6 +63,13 @@ export class GLSPPolylineEdgeRouter extends PolylineEdgeRouter {
56
63
  const anchor = super.getTranslatedAnchor(connectable, refPoint, refContainer, edge, anchorCorrection);
57
64
  return Point.isValid(anchor) ? anchor : refPoint;
58
65
  }
66
+
67
+ override cleanupRoutingPoints(edge: GRoutableElement, routingPoints: Point[], updateHandles: boolean, addRoutingPoints: boolean): void {
68
+ // sometimes it might happen that the source or target has the bounds not properly set when using the feedback edge
69
+ if (ensureBounds(edge.source) && ensureBounds(edge.target)) {
70
+ super.cleanupRoutingPoints(edge, routingPoints, updateHandles, addRoutingPoints);
71
+ }
72
+ }
59
73
  }
60
74
 
61
75
  @injectable()
@@ -111,6 +125,13 @@ export class GLSPManhattanEdgeRouter extends ManhattanEdgeRouter {
111
125
  }
112
126
  });
113
127
  }
128
+
129
+ override cleanupRoutingPoints(edge: GRoutableElement, routingPoints: Point[], updateHandles: boolean, addRoutingPoints: boolean): void {
130
+ // sometimes it might happen that the source or target has the bounds not properly set when using the feedback edge
131
+ if (ensureBounds(edge.source) && ensureBounds(edge.target)) {
132
+ super.cleanupRoutingPoints(edge, routingPoints, updateHandles, addRoutingPoints);
133
+ }
134
+ }
114
135
  }
115
136
 
116
137
  @injectable()
@@ -126,4 +147,25 @@ export class GLSPBezierEdgeRouter extends BezierEdgeRouter {
126
147
  const anchor = super.getTranslatedAnchor(connectable, refPoint, refContainer, edge, anchorCorrection);
127
148
  return Point.isValid(anchor) ? anchor : refPoint;
128
149
  }
150
+
151
+ override cleanupRoutingPoints(edge: GRoutableElement, routingPoints: Point[], updateHandles: boolean, addRoutingPoints: boolean): void {
152
+ // sometimes it might happen that the source or target has the bounds not properly set when using the feedback edge
153
+ if (ensureBounds(edge.source) && ensureBounds(edge.target)) {
154
+ super.cleanupRoutingPoints(edge, routingPoints, updateHandles, addRoutingPoints);
155
+ }
156
+ }
157
+ }
158
+
159
+ function ensureBounds(element?: GConnectableElement): boolean {
160
+ if (!element) {
161
+ return false;
162
+ }
163
+ if (element.bounds) {
164
+ return true;
165
+ }
166
+ if (element.position && element.size) {
167
+ element.bounds = { ...element.position, ...element.size };
168
+ return true;
169
+ }
170
+ return false;
129
171
  }
@@ -13,17 +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 { Action, BringToFrontAction, GModelElement, SelectAction, SelectMouseListener } from '@eclipse-glsp/sprotty';
16
+ import { Action, BringToFrontAction, GModelElement, SelectAction, SelectMouseListener, TYPES } from '@eclipse-glsp/sprotty';
17
+ import { inject, injectable, optional } from 'inversify';
17
18
  import { Ranked } from '../../base/ranked';
18
19
  import { SelectableElement } from '../../utils';
20
+ import { SResizeHandle } from '../change-bounds';
21
+ import { ChangeBoundsManager } from '../tools';
19
22
 
20
23
  /**
21
24
  * Ranked select mouse listener that is executed before default mouse listeners when using the RankedMouseTool.
22
25
  * This ensures that default mouse listeners are working on a model that has selection changes already applied.
23
26
  */
27
+ @injectable()
24
28
  export class RankedSelectMouseListener extends SelectMouseListener implements Ranked {
25
29
  rank: number = Ranked.DEFAULT_RANK - 100; /* we want to be executed before all default mouse listeners */
26
30
 
31
+ @inject(TYPES.IChangeBoundsManager) @optional() readonly changeBoundsManager?: ChangeBoundsManager;
32
+
27
33
  protected override handleSelectTarget(
28
34
  selectableTarget: SelectableElement,
29
35
  deselectedElements: GModelElement[],
@@ -52,4 +58,12 @@ export class RankedSelectMouseListener extends SelectMouseListener implements Ra
52
58
  result.push(SelectAction.create({ selectedElementsIDs: [], deselectedElementsIDs: deselectedElements.map(e => e.id) }));
53
59
  return result;
54
60
  }
61
+
62
+ protected override handleButton(target: GModelElement, event: MouseEvent): (Action | Promise<Action>)[] | undefined {
63
+ if (target instanceof SResizeHandle && this.changeBoundsManager?.useSymmetricResize(event)) {
64
+ // avoid de-selecting elements when resizing with a modifier key
65
+ return [];
66
+ }
67
+ return super.handleButton(target, event);
68
+ }
55
69
  }
@@ -23,11 +23,41 @@ import { EnableToolsAction, Tool } from '../../base/tool-manager/tool';
23
23
  import { GLSPKeyTool } from '../../base/view/key-tool';
24
24
  import { GLSPMouseTool } from '../../base/view/mouse-tool';
25
25
 
26
+ export interface FeedbackAwareTool extends Tool {
27
+ /**
28
+ * Creates a new feedback emitter helper object. While anything can serve as a feedback emitter,
29
+ * this method ensures that the emitter is stable and does not change between model updates.
30
+ */
31
+ createFeedbackEmitter(): FeedbackEmitter;
32
+
33
+ /**
34
+ * Registers `actions` to be sent out as feedback, i.e., changes that are re-established whenever the `GModelRoot`
35
+ * has been set or updated.
36
+ *
37
+ * @param feedbackActions the actions to be sent out.
38
+ * @param feedbackEmitter the emitter sending out feedback actions (this tool by default).
39
+ * @param cleanupActions the actions to be sent out when the feedback is de-registered through the returned Disposable.
40
+ * @returns A 'Disposable' that de-registers the feedback and cleans up any pending feedback with the given `cleanupActions`.
41
+ * @deprecated It is recommended to create a {@link createFeedbackEmitter dedicated emitter} per feedback instead of using the tool.
42
+ */
43
+ registerFeedback(feedbackActions: Action[], feedbackEmitter?: IFeedbackEmitter, cleanupActions?: MaybeActions): Disposable;
44
+
45
+ /**
46
+ * De-registers all feedback from the given `feedbackEmitter` (this tool by default) and cleans up any pending feedback with the
47
+ * given `cleanupActions`.
48
+ *
49
+ * @param feedbackEmitter the emitter to be deregistered (this tool by default).
50
+ * @param cleanupActions the actions to be dispatched right after the deregistration to clean up any pending feedback.
51
+ * @deprecated It is recommended to create a {@link createFeedbackEmitter dedicated emitter} per feedback and dispose it like that.
52
+ */
53
+ deregisterFeedback(feedbackEmitter?: IFeedbackEmitter, cleanupActions?: MaybeActions): void;
54
+ }
55
+
26
56
  /**
27
57
  * A reusable base implementation for edit {@link Tool}s.
28
58
  */
29
59
  @injectable()
30
- export abstract class BaseEditTool implements Tool {
60
+ export abstract class BaseEditTool implements FeedbackAwareTool {
31
61
  @inject(TYPES.IFeedbackActionDispatcher) protected feedbackDispatcher: IFeedbackActionDispatcher;
32
62
  @inject(TYPES.IActionDispatcher) protected actionDispatcher: GLSPActionDispatcher;
33
63
  @inject(GLSPMouseTool) protected mouseTool: GLSPMouseTool;
@@ -56,28 +86,10 @@ export abstract class BaseEditTool implements Tool {
56
86
  return this.feedbackDispatcher.createEmitter();
57
87
  }
58
88
 
59
- /**
60
- * Registers `actions` to be sent out as feedback, i.e., changes that are re-established whenever the `GModelRoot`
61
- * has been set or updated.
62
- *
63
- * @param feedbackActions the actions to be sent out.
64
- * @param feedbackEmitter the emitter sending out feedback actions (this tool by default).
65
- * @param cleanupActions the actions to be sent out when the feedback is de-registered through the returned Disposable.
66
- * @returns A 'Disposable' that de-registers the feedback and cleans up any pending feedback with the given `cleanupActions`.
67
- * @deprecated It is recommended to create a {@link createFeedbackEmitter dedicated emitter} per feedback instead of using the tool.
68
- */
69
89
  registerFeedback(feedbackActions: Action[], feedbackEmitter: IFeedbackEmitter = this, cleanupActions?: MaybeActions): Disposable {
70
90
  return this.feedbackDispatcher.registerFeedback(feedbackEmitter, feedbackActions, cleanupActions);
71
91
  }
72
92
 
73
- /**
74
- * De-registers all feedback from the given `feedbackEmitter` (this tool by default) and cleans up any pending feedback with the
75
- * given `cleanupActions`.
76
- *
77
- * @param feedbackEmitter the emitter to be deregistered (this tool by default).
78
- * @param cleanupActions the actions to be dispatched right after the deregistration to clean up any pending feedback.
79
- * @deprecated It is recommended to create a {@link createFeedbackEmitter dedicated emitter} per feedback and dispose it like that.
80
- */
81
93
  deregisterFeedback(feedbackEmitter: IFeedbackEmitter = this, cleanupActions?: MaybeActions): void {
82
94
  this.feedbackDispatcher.deregisterFeedback(feedbackEmitter, cleanupActions);
83
95
  }
@@ -0,0 +1,145 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2024 Axon Ivy AG 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
+
17
+ import {
18
+ Dimension,
19
+ GModelElement,
20
+ ISnapper,
21
+ KeyboardModifier,
22
+ MousePositionTracker,
23
+ Movement,
24
+ Point,
25
+ TYPES,
26
+ Vector,
27
+ isBoundsAware,
28
+ isLocateable
29
+ } from '@eclipse-glsp/sprotty';
30
+ import { inject, injectable, optional } from 'inversify';
31
+ import { FeedbackEmitter } from '../../../base';
32
+ import { isValidMove, minDimensions } from '../../../utils';
33
+ import { LayoutAware } from '../../bounds/layout-data';
34
+ import {
35
+ IMovementRestrictor,
36
+ ResizeHandleLocation,
37
+ movementRestrictionFeedback,
38
+ removeMovementRestrictionFeedback
39
+ } from '../../change-bounds';
40
+ import { IHelperLineManager } from '../../helper-lines';
41
+ import { ChangeBoundsTracker, TrackedElementMove } from './change-bounds-tracker';
42
+
43
+ export const CSS_RESIZE_MODE = 'resize-mode';
44
+
45
+ @injectable()
46
+ export class ChangeBoundsManager {
47
+ constructor(
48
+ @inject(MousePositionTracker) readonly positionTracker: MousePositionTracker,
49
+ @optional() @inject(TYPES.IMovementRestrictor) readonly movementRestrictor?: IMovementRestrictor,
50
+ @optional() @inject(TYPES.ISnapper) readonly snapper?: ISnapper,
51
+ @optional() @inject(TYPES.IHelperLineManager) readonly helperLineManager?: IHelperLineManager
52
+ ) {}
53
+
54
+ unsnapModifier(): KeyboardModifier | undefined {
55
+ return 'shift';
56
+ }
57
+
58
+ usePositionSnap(arg: MouseEvent | KeyboardEvent | any): boolean {
59
+ return typeof arg === 'boolean' ? arg : !(arg as MouseEvent | KeyboardEvent).shiftKey;
60
+ }
61
+
62
+ snapPosition(element: GModelElement, position: Point): Point {
63
+ return this.snapper?.snap(position, element) ?? position;
64
+ }
65
+
66
+ isValid(element: GModelElement): boolean {
67
+ return this.hasValidPosition(element) && this.hasValidSize(element);
68
+ }
69
+
70
+ hasValidPosition(element: GModelElement, position?: Point): boolean {
71
+ return !isLocateable(element) || isValidMove(element, position ?? element.position, this.movementRestrictor);
72
+ }
73
+
74
+ hasValidSize(element: GModelElement, size?: Dimension): boolean {
75
+ if (!isBoundsAware(element)) {
76
+ return true;
77
+ }
78
+ const dimension: Dimension = size ?? element.bounds;
79
+ const minimum = this.getMinimumSize(element);
80
+ if (dimension.width < minimum.width || dimension.height < minimum.height) {
81
+ return false;
82
+ }
83
+ return true;
84
+ }
85
+
86
+ getMinimumSize(element: GModelElement): Dimension {
87
+ if (!isBoundsAware(element)) {
88
+ return Dimension.EMPTY;
89
+ }
90
+ const definedMinimum = minDimensions(element);
91
+ const computedMinimum = LayoutAware.getComputedDimensions(element);
92
+ return computedMinimum
93
+ ? {
94
+ width: Math.max(definedMinimum.width, computedMinimum.width),
95
+ height: Math.max(definedMinimum.height, computedMinimum.height)
96
+ }
97
+ : definedMinimum;
98
+ }
99
+
100
+ useMovementRestriction(arg: MouseEvent | KeyboardEvent | any): boolean {
101
+ return this.usePositionSnap(arg);
102
+ }
103
+
104
+ restrictMovement(element: GModelElement, movement: Movement): Movement {
105
+ const minimumMovement = this.helperLineManager?.getMinimumMoveVector(element, true, movement.direction);
106
+ if (!minimumMovement) {
107
+ return movement;
108
+ }
109
+ // minimum is given in absolute coordinates
110
+ // if minimum is not reached, reset to original position for that coordinate
111
+ const absVector = Vector.abs(movement.vector);
112
+ const targetPosition: Point = {
113
+ x: absVector.x < minimumMovement.x ? movement.from.x : movement.to.x,
114
+ y: absVector.y < minimumMovement.y ? movement.from.y : movement.to.y
115
+ };
116
+ return Point.move(movement.from, targetPosition);
117
+ }
118
+
119
+ addRestrictionFeedback(feedback: FeedbackEmitter, move: TrackedElementMove): FeedbackEmitter {
120
+ if (this.movementRestrictor) {
121
+ feedback.add(
122
+ movementRestrictionFeedback(move.element, this.movementRestrictor!, move.valid),
123
+ removeMovementRestrictionFeedback(move.element, this.movementRestrictor!)
124
+ );
125
+ }
126
+ return feedback;
127
+ }
128
+
129
+ defaultResizeLocations(): ResizeHandleLocation[] {
130
+ return [
131
+ ResizeHandleLocation.TopLeft,
132
+ ResizeHandleLocation.TopRight,
133
+ ResizeHandleLocation.BottomRight,
134
+ ResizeHandleLocation.BottomLeft
135
+ ];
136
+ }
137
+
138
+ useSymmetricResize(arg: MouseEvent | KeyboardEvent | any): boolean {
139
+ return typeof arg === 'boolean' ? arg : (arg as MouseEvent | KeyboardEvent).ctrlKey;
140
+ }
141
+
142
+ createTracker(): ChangeBoundsTracker {
143
+ return new ChangeBoundsTracker(this);
144
+ }
145
+ }
@@ -19,12 +19,14 @@ import { inject, injectable } from 'inversify';
19
19
  import { FeedbackCommand } from '../../../base/feedback/feedback-command';
20
20
  import { OptionalAction } from '../../../base/model/glsp-model-source';
21
21
  import { forEachElement } from '../../../utils/gmodel-util';
22
- import { addResizeHandles, isResizable, removeResizeHandles } from '../../change-bounds/model';
22
+ import { ResizeHandleLocation, addResizeHandles, isResizable, removeResizeHandles } from '../../change-bounds/model';
23
+ import { ChangeBoundsManager } from './change-bounds-manager';
23
24
 
24
25
  export interface ShowChangeBoundsToolResizeFeedbackAction extends Action {
25
26
  kind: typeof ShowChangeBoundsToolResizeFeedbackAction.KIND;
26
27
 
27
28
  elementId: string;
29
+ resizeLocations?: ResizeHandleLocation[];
28
30
  }
29
31
 
30
32
  export namespace ShowChangeBoundsToolResizeFeedbackAction {
@@ -34,10 +36,16 @@ export namespace ShowChangeBoundsToolResizeFeedbackAction {
34
36
  return Action.hasKind(object, KIND) && hasStringProp(object, 'elementId');
35
37
  }
36
38
 
37
- export function create(elementId: string): ShowChangeBoundsToolResizeFeedbackAction {
39
+ /** @deprecated Use the create method with the options object parameter instead and set the 'elementId' parameter. */
40
+ export function create(elementId: string): ShowChangeBoundsToolResizeFeedbackAction;
41
+ export function create(options: Omit<ShowChangeBoundsToolResizeFeedbackAction, 'kind'>): ShowChangeBoundsToolResizeFeedbackAction;
42
+ export function create(
43
+ options: Omit<ShowChangeBoundsToolResizeFeedbackAction, 'kind'> | string
44
+ ): ShowChangeBoundsToolResizeFeedbackAction {
45
+ const opts = typeof options === 'string' ? { elementId: options } : options;
38
46
  return {
39
47
  kind: KIND,
40
- elementId
48
+ ...opts
41
49
  };
42
50
  }
43
51
  }
@@ -63,15 +71,18 @@ export class ShowChangeBoundsToolResizeFeedbackCommand extends FeedbackCommand {
63
71
  static readonly KIND = ShowChangeBoundsToolResizeFeedbackAction.KIND;
64
72
 
65
73
  @inject(TYPES.Action) protected action: ShowChangeBoundsToolResizeFeedbackAction;
74
+ @inject(TYPES.IChangeBoundsManager) protected changeBoundsManager: ChangeBoundsManager;
66
75
 
67
76
  execute(context: CommandExecutionContext): CommandReturn {
68
77
  const index = context.root.index;
69
78
 
70
79
  forEachElement(index, isResizable, element => element.id !== this.action.elementId && removeResizeHandles(element));
71
80
 
72
- const resizeElement = index.getById(this.action.elementId);
73
- if (resizeElement && isResizable(resizeElement)) {
74
- addResizeHandles(resizeElement);
81
+ if (this.action.elementId) {
82
+ const resizeElement = index.getById(this.action.elementId);
83
+ if (resizeElement && isResizable(resizeElement)) {
84
+ addResizeHandles(resizeElement, this.action.resizeLocations ?? this.changeBoundsManager.defaultResizeLocations());
85
+ }
75
86
  }
76
87
  return context.root;
77
88
  }
@@ -16,12 +16,14 @@
16
16
  import { FeatureModule, TYPES, bindAsService, configureCommand, configureView } from '@eclipse-glsp/sprotty';
17
17
  import '../../../../css/change-bounds.css';
18
18
  import { SResizeHandle } from '../../change-bounds/model';
19
+ import { ChangeBoundsManager } from './change-bounds-manager';
19
20
  import { ChangeBoundsTool } from './change-bounds-tool';
20
21
  import { HideChangeBoundsToolResizeFeedbackCommand, ShowChangeBoundsToolResizeFeedbackCommand } from './change-bounds-tool-feedback';
21
22
  import { SResizeHandleView } from './view';
22
23
 
23
24
  export const changeBoundsToolModule = new FeatureModule((bind, unbind, isBound, rebind) => {
24
25
  const context = { bind, unbind, isBound, rebind };
26
+ bindAsService(context, TYPES.IChangeBoundsManager, ChangeBoundsManager);
25
27
  bindAsService(context, TYPES.IDefaultTool, ChangeBoundsTool);
26
28
  configureCommand(context, ShowChangeBoundsToolResizeFeedbackCommand);
27
29
  configureCommand(context, HideChangeBoundsToolResizeFeedbackCommand);
@@ -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
  }