@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
@@ -27,6 +27,7 @@ import {
27
27
  Point,
28
28
  TYPES,
29
29
  Viewport,
30
+ equalUpTo,
30
31
  findParentByFeature,
31
32
  isBoundsAware,
32
33
  isDecoration,
@@ -293,7 +294,7 @@ export class DrawHelperLinesFeedbackCommand extends FeedbackCommand {
293
294
  }
294
295
 
295
296
  protected isMatch(leftCoordinate: number, rightCoordinate: number, epsilon: number): boolean {
296
- return Math.abs(leftCoordinate - rightCoordinate) <= epsilon;
297
+ return equalUpTo(leftCoordinate, rightCoordinate, epsilon);
297
298
  }
298
299
 
299
300
  protected log(message: string, ...params: any[]): void {
@@ -13,7 +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, GModelElement, GModelRoot, IActionHandler, MoveAction, Point, SetBoundsAction, TYPES } from '@eclipse-glsp/sprotty';
16
+ import {
17
+ Action,
18
+ GModelElement,
19
+ GModelRoot,
20
+ IActionHandler,
21
+ MoveAction,
22
+ Point,
23
+ SetBoundsAction,
24
+ TYPES,
25
+ Vector,
26
+ Writable
27
+ } from '@eclipse-glsp/sprotty';
17
28
  import { inject, injectable, optional, postConstruct } from 'inversify';
18
29
  import { FeedbackEmitter } from '../../base';
19
30
  import { IFeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher';
@@ -33,7 +44,7 @@ import {
33
44
  ViewportLineType
34
45
  } from './helper-line-feedback';
35
46
  import { IHelperLineManager } from './helper-line-manager';
36
- import { Direction, HelperLineType } from './model';
47
+ import { Direction, HelperLine, HelperLineType, isHelperLine } from './model';
37
48
 
38
49
  export interface IHelperLineOptions {
39
50
  /**
@@ -158,4 +169,28 @@ export class HelperLineManager implements IActionHandler, ISelectionListener, IH
158
169
  ? this.options.minimumMoveDelta.x
159
170
  : this.options.minimumMoveDelta.y;
160
171
  }
172
+
173
+ getMinimumMoveVector(element: GModelElement, isSnap: boolean, directions: Direction[]): Vector | undefined {
174
+ if (!isSnap) {
175
+ return undefined;
176
+ }
177
+
178
+ const helperLines = element.root.children.filter(child => isHelperLine(child)) as HelperLine[];
179
+ if (helperLines.length === 0) {
180
+ return undefined;
181
+ }
182
+
183
+ const minimum: Writable<Vector> = { ...Vector.ZERO };
184
+ if (directions.includes(Direction.Left) && helperLines.some(line => line.isLeft || line.isCenter)) {
185
+ minimum.x = this.getMinimumMoveDelta(element, isSnap, Direction.Left);
186
+ } else if (directions.includes(Direction.Right) && helperLines.some(line => line.isRight || line.isCenter)) {
187
+ minimum.x = this.getMinimumMoveDelta(element, isSnap, Direction.Right);
188
+ }
189
+ if (directions.includes(Direction.Up) && helperLines.some(line => line.isTop || line.isMiddle)) {
190
+ minimum.y = this.getMinimumMoveDelta(element, isSnap, Direction.Up);
191
+ } else if (directions.includes(Direction.Down) && helperLines.some(line => line.isBottom || line.isMiddle)) {
192
+ minimum.y = this.getMinimumMoveDelta(element, isSnap, Direction.Down);
193
+ }
194
+ return Vector.isZero(minimum) ? undefined : minimum;
195
+ }
161
196
  }
@@ -13,16 +13,25 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { GModelElement } from '@eclipse-glsp/sprotty';
16
+ import { GModelElement, Vector } from '@eclipse-glsp/sprotty';
17
17
  import { Direction } from './model';
18
18
 
19
19
  export interface IHelperLineManager {
20
20
  /**
21
- * Calculates the minimum move delta that is necessary to break through a helper line.
21
+ * Calculates the minimum move delta on one axis that is necessary to break through a helper line.
22
22
  *
23
23
  * @param element element that is being moved
24
24
  * @param isSnap whether snapping is active or not
25
25
  * @param direction direction in which the target element is moving
26
26
  */
27
27
  getMinimumMoveDelta(element: GModelElement, isSnap: boolean, direction: Direction): number;
28
+
29
+ /**
30
+ * Calculates the minimum move vector that is necessary to break through a helper line.
31
+ *
32
+ * @param element element that is being moved
33
+ * @param isSnap whether snapping is active or not
34
+ * @param directions directions in which the target element is moving
35
+ */
36
+ getMinimumMoveVector(element: GModelElement, isSnap: boolean, directions: Direction[]): Vector | undefined;
28
37
  }
@@ -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);