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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/css/change-bounds.css +15 -0
  2. package/css/glsp-sprotty.css +17 -0
  3. package/lib/base/drag-aware-mouse-listener.d.ts +5 -2
  4. package/lib/base/drag-aware-mouse-listener.d.ts.map +1 -1
  5. package/lib/base/drag-aware-mouse-listener.js +11 -0
  6. package/lib/base/drag-aware-mouse-listener.js.map +1 -1
  7. package/lib/base/feedback/css-feedback.d.ts +4 -0
  8. package/lib/base/feedback/css-feedback.d.ts.map +1 -1
  9. package/lib/base/feedback/css-feedback.js +4 -0
  10. package/lib/base/feedback/css-feedback.js.map +1 -1
  11. package/lib/base/feedback/feeback-emitter.d.ts +67 -0
  12. package/lib/base/feedback/feeback-emitter.d.ts.map +1 -0
  13. package/lib/base/feedback/feeback-emitter.js +109 -0
  14. package/lib/base/feedback/feeback-emitter.js.map +1 -0
  15. package/lib/base/feedback/feedback-action-dispatcher.d.ts +17 -5
  16. package/lib/base/feedback/feedback-action-dispatcher.d.ts.map +1 -1
  17. package/lib/base/feedback/feedback-action-dispatcher.js +23 -3
  18. package/lib/base/feedback/feedback-action-dispatcher.js.map +1 -1
  19. package/lib/base/feedback/index.d.ts +1 -0
  20. package/lib/base/feedback/index.d.ts.map +1 -1
  21. package/lib/base/feedback/index.js +1 -0
  22. package/lib/base/feedback/index.js.map +1 -1
  23. package/lib/base/selection-service.d.ts +3 -2
  24. package/lib/base/selection-service.d.ts.map +1 -1
  25. package/lib/base/selection-service.js.map +1 -1
  26. package/lib/features/accessibility/element-navigation/local-element-navigator.d.ts +3 -3
  27. package/lib/features/accessibility/element-navigation/local-element-navigator.d.ts.map +1 -1
  28. package/lib/features/accessibility/element-navigation/local-element-navigator.js.map +1 -1
  29. package/lib/features/accessibility/move-zoom/move-handler.d.ts +4 -2
  30. package/lib/features/accessibility/move-zoom/move-handler.d.ts.map +1 -1
  31. package/lib/features/accessibility/move-zoom/move-handler.js +10 -4
  32. package/lib/features/accessibility/move-zoom/move-handler.js.map +1 -1
  33. package/lib/features/accessibility/resize-key-tool/resize-key-handler.d.ts +4 -2
  34. package/lib/features/accessibility/resize-key-tool/resize-key-handler.d.ts.map +1 -1
  35. package/lib/features/accessibility/resize-key-tool/resize-key-handler.js +11 -3
  36. package/lib/features/accessibility/resize-key-tool/resize-key-handler.js.map +1 -1
  37. package/lib/features/change-bounds/model.d.ts +8 -8
  38. package/lib/features/change-bounds/model.d.ts.map +1 -1
  39. package/lib/features/change-bounds/model.js +17 -5
  40. package/lib/features/change-bounds/model.js.map +1 -1
  41. package/lib/features/change-bounds/movement-restrictor.d.ts +3 -2
  42. package/lib/features/change-bounds/movement-restrictor.d.ts.map +1 -1
  43. package/lib/features/change-bounds/movement-restrictor.js.map +1 -1
  44. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts +15 -5
  45. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts.map +1 -1
  46. package/lib/features/element-template/mouse-tracking-element-position-listener.js +27 -38
  47. package/lib/features/element-template/mouse-tracking-element-position-listener.js.map +1 -1
  48. package/lib/features/helper-lines/helper-line-manager-default.d.ts +4 -2
  49. package/lib/features/helper-lines/helper-line-manager-default.d.ts.map +1 -1
  50. package/lib/features/helper-lines/helper-line-manager-default.js +8 -12
  51. package/lib/features/helper-lines/helper-line-manager-default.js.map +1 -1
  52. package/lib/features/hints/type-hint-provider.d.ts +3 -0
  53. package/lib/features/hints/type-hint-provider.d.ts.map +1 -1
  54. package/lib/features/hints/type-hint-provider.js +10 -1
  55. package/lib/features/hints/type-hint-provider.js.map +1 -1
  56. package/lib/features/select/select-mouse-listener.d.ts +4 -3
  57. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  58. package/lib/features/select/select-mouse-listener.js.map +1 -1
  59. package/lib/features/tools/base-tools.d.ts +23 -4
  60. package/lib/features/tools/base-tools.d.ts.map +1 -1
  61. package/lib/features/tools/base-tools.js +11 -6
  62. package/lib/features/tools/base-tools.js.map +1 -1
  63. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts +1 -35
  64. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.d.ts.map +1 -1
  65. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js +5 -206
  66. package/lib/features/tools/change-bounds/change-bounds-tool-feedback.js.map +1 -1
  67. package/lib/features/tools/change-bounds/change-bounds-tool-module.d.ts +1 -0
  68. package/lib/features/tools/change-bounds/change-bounds-tool-module.d.ts.map +1 -1
  69. package/lib/features/tools/change-bounds/change-bounds-tool-module.js +1 -0
  70. package/lib/features/tools/change-bounds/change-bounds-tool-module.js.map +1 -1
  71. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts +67 -0
  72. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts.map +1 -0
  73. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js +227 -0
  74. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js.map +1 -0
  75. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts +34 -24
  76. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts.map +1 -1
  77. package/lib/features/tools/change-bounds/change-bounds-tool.js +172 -180
  78. package/lib/features/tools/change-bounds/change-bounds-tool.js.map +1 -1
  79. package/lib/features/tools/change-bounds/index.d.ts +1 -0
  80. package/lib/features/tools/change-bounds/index.d.ts.map +1 -1
  81. package/lib/features/tools/change-bounds/index.js +1 -0
  82. package/lib/features/tools/change-bounds/index.js.map +1 -1
  83. package/lib/features/tools/change-bounds/view.d.ts +1 -1
  84. package/lib/features/tools/change-bounds/view.d.ts.map +1 -1
  85. package/lib/features/tools/change-bounds/view.js +3 -0
  86. package/lib/features/tools/change-bounds/view.js.map +1 -1
  87. package/lib/features/tools/deletion/delete-tool.d.ts +16 -1
  88. package/lib/features/tools/deletion/delete-tool.d.ts.map +1 -1
  89. package/lib/features/tools/deletion/delete-tool.js +5 -2
  90. package/lib/features/tools/deletion/delete-tool.js.map +1 -1
  91. package/lib/features/tools/edge-creation/dangling-edge-feedback.d.ts +17 -1
  92. package/lib/features/tools/edge-creation/dangling-edge-feedback.d.ts.map +1 -1
  93. package/lib/features/tools/edge-creation/dangling-edge-feedback.js +3 -1
  94. package/lib/features/tools/edge-creation/dangling-edge-feedback.js.map +1 -1
  95. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.d.ts +3 -1
  96. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.d.ts.map +1 -1
  97. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.js +4 -7
  98. package/lib/features/tools/edge-creation/edge-creation-tool-feedback.js.map +1 -1
  99. package/lib/features/tools/edge-creation/edge-creation-tool.d.ts +20 -2
  100. package/lib/features/tools/edge-creation/edge-creation-tool.d.ts.map +1 -1
  101. package/lib/features/tools/edge-creation/edge-creation-tool.js +23 -18
  102. package/lib/features/tools/edge-creation/edge-creation-tool.js.map +1 -1
  103. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts +3 -1
  104. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts.map +1 -1
  105. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js +5 -8
  106. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js.map +1 -1
  107. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts +4 -4
  108. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts.map +1 -1
  109. package/lib/features/tools/edge-edit/edge-edit-tool.js +27 -44
  110. package/lib/features/tools/edge-edit/edge-edit-tool.js.map +1 -1
  111. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts +3 -2
  112. package/lib/features/tools/marquee-selection/marquee-mouse-tool.d.ts.map +1 -1
  113. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js +4 -2
  114. package/lib/features/tools/marquee-selection/marquee-mouse-tool.js.map +1 -1
  115. package/lib/features/tools/node-creation/node-creation-tool.d.ts +14 -5
  116. package/lib/features/tools/node-creation/node-creation-tool.d.ts.map +1 -1
  117. package/lib/features/tools/node-creation/node-creation-tool.js +58 -24
  118. package/lib/features/tools/node-creation/node-creation-tool.js.map +1 -1
  119. package/lib/features/validation/marker-navigator.d.ts +7 -6
  120. package/lib/features/validation/marker-navigator.d.ts.map +1 -1
  121. package/lib/features/validation/marker-navigator.js.map +1 -1
  122. package/lib/features/validation/validate.d.ts +2 -5
  123. package/lib/features/validation/validate.d.ts.map +1 -1
  124. package/lib/features/validation/validate.js +4 -16
  125. package/lib/features/validation/validate.js.map +1 -1
  126. package/lib/utils/gmodel-util.d.ts +27 -2
  127. package/lib/utils/gmodel-util.d.ts.map +1 -1
  128. package/lib/utils/gmodel-util.js +30 -1
  129. package/lib/utils/gmodel-util.js.map +1 -1
  130. package/lib/utils/layout-utils.d.ts +8 -7
  131. package/lib/utils/layout-utils.d.ts.map +1 -1
  132. package/lib/utils/layout-utils.js +2 -4
  133. package/lib/utils/layout-utils.js.map +1 -1
  134. package/lib/utils/viewpoint-util.d.ts +4 -4
  135. package/lib/utils/viewpoint-util.d.ts.map +1 -1
  136. package/lib/utils/viewpoint-util.js.map +1 -1
  137. package/package.json +3 -3
  138. package/src/base/drag-aware-mouse-listener.ts +16 -2
  139. package/src/base/feedback/css-feedback.ts +4 -0
  140. package/src/base/feedback/feeback-emitter.ts +112 -0
  141. package/src/base/feedback/feedback-action-dispatcher.ts +41 -6
  142. package/src/base/feedback/index.ts +1 -0
  143. package/src/base/selection-service.spec.ts +5 -0
  144. package/src/base/selection-service.ts +2 -3
  145. package/src/features/accessibility/element-navigation/local-element-navigator.ts +4 -4
  146. package/src/features/accessibility/move-zoom/move-handler.ts +10 -5
  147. package/src/features/accessibility/resize-key-tool/resize-key-handler.ts +11 -7
  148. package/src/features/change-bounds/model.ts +25 -13
  149. package/src/features/change-bounds/movement-restrictor.ts +5 -4
  150. package/src/features/element-template/mouse-tracking-element-position-listener.ts +44 -44
  151. package/src/features/helper-lines/helper-line-manager-default.ts +12 -21
  152. package/src/features/hints/type-hint-provider.spec.ts +4 -1
  153. package/src/features/hints/type-hint-provider.ts +9 -2
  154. package/src/features/select/select-mouse-listener.ts +4 -3
  155. package/src/features/tools/base-tools.ts +15 -8
  156. package/src/features/tools/change-bounds/change-bounds-tool-feedback.ts +6 -232
  157. package/src/features/tools/change-bounds/change-bounds-tool-module.ts +1 -0
  158. package/src/features/tools/change-bounds/change-bounds-tool-move-feedback.ts +254 -0
  159. package/src/features/tools/change-bounds/change-bounds-tool.ts +217 -226
  160. package/src/features/tools/change-bounds/index.ts +1 -0
  161. package/src/features/tools/change-bounds/view.tsx +4 -1
  162. package/src/features/tools/deletion/delete-tool.ts +6 -6
  163. package/src/features/tools/edge-creation/dangling-edge-feedback.ts +7 -4
  164. package/src/features/tools/edge-creation/edge-creation-tool-feedback.ts +9 -9
  165. package/src/features/tools/edge-creation/edge-creation-tool.ts +33 -23
  166. package/src/features/tools/edge-edit/edge-edit-tool-feedback.ts +8 -8
  167. package/src/features/tools/edge-edit/edge-edit-tool.ts +42 -49
  168. package/src/features/tools/marquee-selection/marquee-mouse-tool.ts +7 -6
  169. package/src/features/tools/node-creation/node-creation-tool.ts +69 -25
  170. package/src/features/validation/marker-navigator.ts +6 -8
  171. package/src/features/validation/validate.ts +5 -20
  172. package/src/utils/gmodel-util.ts +44 -1
  173. package/src/utils/layout-utils.ts +9 -20
  174. package/src/utils/viewpoint-util.ts +3 -4
@@ -18,8 +18,8 @@ import {
18
18
  Action,
19
19
  Dimension,
20
20
  Disposable,
21
+ ElementMove,
21
22
  GModelElement,
22
- Locateable,
23
23
  MoveAction,
24
24
  Point,
25
25
  isBoundsAware,
@@ -28,8 +28,10 @@ import {
28
28
  import { injectable } from 'inversify';
29
29
  import { DragAwareMouseListener } from '../../base/drag-aware-mouse-listener';
30
30
  import { CSS_HIDDEN, ModifyCSSFeedbackAction } from '../../base/feedback/css-feedback';
31
+ import { FeedbackEmitter } from '../../base/feedback/feeback-emitter';
31
32
  import { IFeedbackEmitter } from '../../base/feedback/feedback-action-dispatcher';
32
33
  import { Tool } from '../../base/tool-manager/tool';
34
+ import { MoveableElement } from '../../utils';
33
35
  import { getAbsolutePosition } from '../../utils/viewpoint-util';
34
36
  import {
35
37
  IMovementRestrictor,
@@ -39,20 +41,28 @@ import {
39
41
  import { PointPositionUpdater } from '../change-bounds/point-position-updater';
40
42
  import { PositionSnapper } from '../change-bounds/position-snapper';
41
43
  import { useSnap } from '../change-bounds/snap';
44
+ import { MoveFinishedEventAction } from '../tools';
42
45
 
43
46
  export interface PositioningTool extends Tool {
44
47
  readonly positionSnapper: PositionSnapper;
45
48
  readonly movementRestrictor?: IMovementRestrictor;
46
49
 
50
+ createFeedbackEmitter(): FeedbackEmitter;
51
+ /**
52
+ * @deprecated It is recommended to create a {@link createFeedbackEmitter dedicated emitter} per feedback instead of using the tool.
53
+ */
47
54
  registerFeedback(feedbackActions: Action[], feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): Disposable;
55
+ /**
56
+ * @deprecated It is recommended to create a {@link createFeedbackEmitter dedicated emitter} per feedback and dispose it like that.
57
+ */
48
58
  deregisterFeedback(feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): void;
49
59
  }
50
60
 
51
61
  @injectable()
52
62
  export class MouseTrackingElementPositionListener extends DragAwareMouseListener {
53
- currentPosition?: Point;
54
-
55
63
  protected positionUpdater: PointPositionUpdater;
64
+ protected moveGhostFeedback: FeedbackEmitter;
65
+ protected currentPosition?: Point;
56
66
 
57
67
  constructor(
58
68
  protected elementId: string,
@@ -61,6 +71,7 @@ export class MouseTrackingElementPositionListener extends DragAwareMouseListener
61
71
  ) {
62
72
  super();
63
73
  this.positionUpdater = new PointPositionUpdater(this.tool.positionSnapper);
74
+ this.moveGhostFeedback = this.tool.createFeedbackEmitter();
64
75
  }
65
76
 
66
77
  override mouseMove(target: GModelElement, event: MouseEvent): Action[] {
@@ -69,59 +80,48 @@ export class MouseTrackingElementPositionListener extends DragAwareMouseListener
69
80
  if (!element || !isMoveable(element)) {
70
81
  return [];
71
82
  }
72
- let targetPosition = this.getTargetPosition(target, event, element);
73
83
  if (this.positionUpdater.isLastDragPositionUndefined()) {
74
- this.positionUpdater.updateLastDragPosition(targetPosition);
84
+ this.positionUpdater.updateLastDragPosition(element.position);
75
85
  }
76
- const delta = this.positionUpdater.updatePosition(element, targetPosition, useSnap(event));
86
+ const mousePosition = getAbsolutePosition(target, event);
87
+ const delta = this.positionUpdater.updatePosition(element, mousePosition, useSnap(event));
77
88
  if (!delta) {
78
89
  return [];
79
90
  }
80
- targetPosition = this.validateMove(this.currentPosition ?? targetPosition, targetPosition, element, false);
81
- const moveGhostElement = MoveAction.create(
82
- [
83
- {
84
- elementId: element.id,
85
- fromPosition: this.currentPosition,
86
- toPosition: targetPosition
87
- }
88
- ],
89
- { animate: false, finished: false }
90
- );
91
- this.currentPosition = targetPosition;
92
- this.tool.registerFeedback([moveGhostElement], this);
93
- return element.cssClasses?.includes(CSS_HIDDEN)
94
- ? [ModifyCSSFeedbackAction.create({ elements: [element.id], remove: [CSS_HIDDEN] })]
95
- : [];
96
- }
97
-
98
- protected getTargetPosition(target: GModelElement, event: MouseEvent, element: GModelElement & Locateable): Point {
99
- let targetPosition = getAbsolutePosition(target, event);
100
- if (this.cursorPosition === 'middle' && isBoundsAware(element)) {
101
- targetPosition = Point.subtract(targetPosition, Dimension.center(element.bounds));
102
- }
103
- return targetPosition;
91
+ const toPosition = this.getElementTargetPosition(mousePosition, element, event);
92
+ const elementMove = { elementId: element.id, toPosition: toPosition };
93
+ this.addMoveFeeback(element, elementMove);
94
+ this.currentPosition = toPosition;
95
+ // since we are moving a ghost element that is feedback-only and will be removed anyway,
96
+ // we just send a MoveFinishedEventAction instead of reseting the position with a MoveAction and the finished flag set to true.
97
+ this.moveGhostFeedback.add(MoveAction.create([elementMove], { animate: false }), MoveFinishedEventAction.create()).submit();
98
+ return [];
104
99
  }
105
100
 
106
- protected snap(position: Point, element: GModelElement, isSnap = true): Point {
107
- return this.tool.positionSnapper.snapPosition(position, element, isSnap);
101
+ protected getElementTargetPosition(mousePosition: Point, element: MoveableElement, event: MouseEvent): Point {
102
+ const unsnappedPosition =
103
+ this.cursorPosition === 'middle' && isBoundsAware(element)
104
+ ? Point.subtract(mousePosition, Dimension.center(element.bounds))
105
+ : mousePosition;
106
+ return this.tool.positionSnapper.snapPosition(unsnappedPosition, element, useSnap(event));
108
107
  }
109
108
 
110
- protected validateMove(startPosition: Point, toPosition: Point, element: GModelElement, isFinished: boolean): Point {
111
- let newPosition = toPosition;
109
+ protected addMoveFeeback(element: MoveableElement, elementMove: ElementMove): void {
112
110
  if (this.tool.movementRestrictor) {
113
- const valid = this.tool.movementRestrictor.validate(element, toPosition);
114
- let action;
115
- if (!valid) {
116
- action = createMovementRestrictionFeedback(element, this.tool.movementRestrictor);
117
- if (isFinished) {
118
- newPosition = startPosition;
119
- }
111
+ if (!this.tool.movementRestrictor.validate(element, elementMove.toPosition)) {
112
+ this.moveGhostFeedback.add(
113
+ createMovementRestrictionFeedback(element, this.tool.movementRestrictor),
114
+ removeMovementRestrictionFeedback(element, this.tool.movementRestrictor)
115
+ );
120
116
  } else {
121
- action = removeMovementRestrictionFeedback(element, this.tool.movementRestrictor);
117
+ this.moveGhostFeedback.add(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor));
122
118
  }
123
- this.tool.registerFeedback([action], this, [removeMovementRestrictionFeedback(element, this.tool.movementRestrictor)]);
124
119
  }
125
- return newPosition;
120
+ this.moveGhostFeedback.add(ModifyCSSFeedbackAction.create({ elements: [element.id], remove: [CSS_HIDDEN] }));
121
+ }
122
+
123
+ override dispose(): void {
124
+ this.moveGhostFeedback.dispose();
125
+ super.dispose();
126
126
  }
127
127
  }
@@ -13,18 +13,9 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import {
17
- Action,
18
- DisposableCollection,
19
- GModelElement,
20
- GModelRoot,
21
- IActionHandler,
22
- MoveAction,
23
- Point,
24
- SetBoundsAction,
25
- TYPES
26
- } from '@eclipse-glsp/sprotty';
16
+ import { Action, GModelElement, GModelRoot, IActionHandler, MoveAction, Point, SetBoundsAction, TYPES } from '@eclipse-glsp/sprotty';
27
17
  import { inject, injectable, optional, postConstruct } from 'inversify';
18
+ import { FeedbackEmitter } from '../../base';
28
19
  import { IFeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher';
29
20
  import { ISelectionListener, SelectionService } from '../../base/selection-service';
30
21
  import { SetBoundsFeedbackAction } from '../bounds/set-bounds-feedback-command';
@@ -95,10 +86,11 @@ export class HelperLineManager implements IActionHandler, ISelectionListener, IH
95
86
  @optional() @inject(TYPES.IHelperLineOptions) protected userOptions?: IHelperLineOptions;
96
87
 
97
88
  protected options: Required<IHelperLineOptions>;
98
- protected feedback: DisposableCollection = new DisposableCollection();
89
+ protected feedback: FeedbackEmitter;
99
90
 
100
91
  @postConstruct()
101
92
  protected init(): void {
93
+ this.feedback = this.feedbackDispatcher.createEmitter();
102
94
  this.options = { ...DEFAULT_HELPER_LINE_OPTIONS, ...this.userOptions };
103
95
  this.selectionService.onSelectionChanged(change =>
104
96
  this.selectionChanged(change.root, change.selectedElements, change.deselectedElements)
@@ -118,9 +110,7 @@ export class HelperLineManager implements IActionHandler, ISelectionListener, IH
118
110
  }
119
111
 
120
112
  protected handleMoveInitializedAction(_action: MoveInitializedEventAction): void {
121
- this.feedback.dispose();
122
- const feedback = this.createHelperLineFeedback(this.selectionService.getSelectedElementIDs());
123
- this.feedback.push(this.feedbackDispatcher.registerFeedback(this, [feedback], [RemoveHelperLinesFeedbackAction.create()]));
113
+ this.submitHelperLineFeedback();
124
114
  }
125
115
 
126
116
  protected handleMoveFinishedAction(_action: MoveFinishedEventAction): void {
@@ -129,22 +119,23 @@ export class HelperLineManager implements IActionHandler, ISelectionListener, IH
129
119
 
130
120
  protected handleMoveAction(action: MoveAction): void {
131
121
  if (!action.finished) {
132
- const elementIds = action.moves.map(move => move.elementId);
133
- const feedback = this.createHelperLineFeedback(elementIds);
134
- this.feedback.push(this.feedbackDispatcher.registerFeedback(this, [feedback], [RemoveHelperLinesFeedbackAction.create()]));
122
+ this.submitHelperLineFeedback(action.moves.map(move => move.elementId));
135
123
  } else {
136
124
  this.feedback.dispose();
137
125
  }
138
126
  }
139
127
 
128
+ protected submitHelperLineFeedback(elementIds: string[] = this.selectionService.getSelectedElementIDs()): void {
129
+ const feedback = this.createHelperLineFeedback(elementIds);
130
+ this.feedback.add(feedback, [RemoveHelperLinesFeedbackAction.create()]).submit();
131
+ }
132
+
140
133
  protected createHelperLineFeedback(elementIds: string[]): DrawHelperLinesFeedbackAction {
141
134
  return DrawHelperLinesFeedbackAction.create({ elementIds, ...this.options });
142
135
  }
143
136
 
144
137
  protected handleSetBoundsAction(action: SetBoundsAction | SetBoundsFeedbackAction): void {
145
- const elementIds = action.bounds.map(bound => bound.elementId);
146
- const feedback = this.createHelperLineFeedback(elementIds);
147
- this.feedback.push(this.feedbackDispatcher.registerFeedback(this, [feedback], [RemoveHelperLinesFeedbackAction.create()]));
138
+ this.submitHelperLineFeedback(action.bounds.map(bound => bound.elementId));
148
139
  }
149
140
 
150
141
  selectionChanged(root: Readonly<GModelRoot>, selectedElements: string[], deselectedElements?: string[] | undefined): void {
@@ -35,6 +35,7 @@ import {
35
35
  import { expect } from 'chai';
36
36
  import { Container } from 'inversify';
37
37
  import * as sinon from 'sinon';
38
+ import { FeedbackEmitter } from '../../base';
38
39
  import { GLSPActionDispatcher } from '../../base/action-dispatcher';
39
40
  import { FeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher';
40
41
  import { GEdge } from '../../model';
@@ -45,7 +46,9 @@ import { ApplyTypeHintsAction, ApplyTypeHintsCommand, ITypeHintProvider, TypeHin
45
46
  describe('TypeHintProvider', () => {
46
47
  const container = new Container();
47
48
  container.bind(GLSPActionDispatcher).toConstantValue(sinon.createStubInstance(GLSPActionDispatcher));
48
- container.bind(TYPES.IFeedbackActionDispatcher).toConstantValue(sinon.createStubInstance(FeedbackActionDispatcher));
49
+ const stub = sinon.createStubInstance(FeedbackActionDispatcher);
50
+ stub.createEmitter.returns(new FeedbackEmitter(stub));
51
+ container.bind(TYPES.IFeedbackActionDispatcher).toConstantValue(stub);
49
52
  const typeHintProvider = container.resolve(TypeHintProvider);
50
53
 
51
54
  describe('getShapeTypeHint', () => {
@@ -35,8 +35,9 @@ import {
35
35
  isConnectable,
36
36
  moveFeature
37
37
  } from '@eclipse-glsp/sprotty';
38
- import { inject, injectable } from 'inversify';
38
+ import { inject, injectable, postConstruct } from 'inversify';
39
39
 
40
+ import { FeedbackEmitter } from '../../base';
40
41
  import { GLSPActionDispatcher } from '../../base/action-dispatcher';
41
42
  import { IFeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher';
42
43
  import { FeedbackCommand } from '../../base/feedback/feedback-command';
@@ -204,15 +205,21 @@ export class TypeHintProvider implements IActionHandler, ITypeHintProvider, IDia
204
205
  @inject(GLSPActionDispatcher)
205
206
  protected actionDispatcher: GLSPActionDispatcher;
206
207
 
208
+ protected typeHintsFeedback: FeedbackEmitter;
207
209
  protected shapeHints: Map<string, ShapeTypeHint> = new Map();
208
210
  protected edgeHints: Map<string, EdgeTypeHint> = new Map();
209
211
 
212
+ @postConstruct()
213
+ protected init(): void {
214
+ this.typeHintsFeedback = this.feedbackActionDispatcher.createEmitter();
215
+ }
216
+
210
217
  handle(action: SetTypeHintsAction): void {
211
218
  this.shapeHints.clear();
212
219
  this.edgeHints.clear();
213
220
  action.shapeHints.forEach(hint => this.shapeHints.set(hint.elementTypeId, hint));
214
221
  action.edgeHints.forEach(hint => this.edgeHints.set(hint.elementTypeId, hint));
215
- this.feedbackActionDispatcher.registerFeedback(this, [ApplyTypeHintsAction.create()]);
222
+ this.typeHintsFeedback.add(ApplyTypeHintsAction.create()).submit();
216
223
  }
217
224
 
218
225
  getShapeTypeHint(input: GModelElement | GModelElementSchema | string): ShapeTypeHint | undefined {
@@ -13,8 +13,9 @@
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, Selectable } from '@eclipse-glsp/sprotty';
16
+ import { Action, BringToFrontAction, GModelElement, SelectAction, SelectMouseListener } from '@eclipse-glsp/sprotty';
17
17
  import { Ranked } from '../../base/ranked';
18
+ import { SelectableElement } from '../../utils';
18
19
 
19
20
  /**
20
21
  * Ranked select mouse listener that is executed before default mouse listeners when using the RankedMouseTool.
@@ -24,7 +25,7 @@ export class RankedSelectMouseListener extends SelectMouseListener implements Ra
24
25
  rank: number = Ranked.DEFAULT_RANK - 100; /* we want to be executed before all default mouse listeners */
25
26
 
26
27
  protected override handleSelectTarget(
27
- selectableTarget: GModelElement & Selectable,
28
+ selectableTarget: SelectableElement,
28
29
  deselectedElements: GModelElement[],
29
30
  event: MouseEvent
30
31
  ): (Action | Promise<Action>)[] {
@@ -40,7 +41,7 @@ export class RankedSelectMouseListener extends SelectMouseListener implements Ra
40
41
  return result;
41
42
  }
42
43
 
43
- protected override handleDeselectTarget(selectableTarget: GModelElement & Selectable, event: MouseEvent): (Action | Promise<Action>)[] {
44
+ protected override handleDeselectTarget(selectableTarget: SelectableElement, event: MouseEvent): (Action | Promise<Action>)[] {
44
45
  const result: Action[] = [];
45
46
  result.push(SelectAction.create({ selectedElementsIDs: [], deselectedElementsIDs: [selectableTarget.id] }));
46
47
  return result;
@@ -13,11 +13,12 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { inject, injectable } from 'inversify';
17
16
  import { Action, Disposable, DisposableCollection, IActionHandler, TYPES } from '@eclipse-glsp/sprotty';
17
+ import { inject, injectable } from 'inversify';
18
18
  import { GLSPActionDispatcher } from '../../base/action-dispatcher';
19
19
  import { EditorContextService } from '../../base/editor-context-service';
20
- import { IFeedbackActionDispatcher, IFeedbackEmitter } from '../../base/feedback/feedback-action-dispatcher';
20
+ import { FeedbackEmitter } from '../../base/feedback/feeback-emitter';
21
+ import { IFeedbackActionDispatcher, IFeedbackEmitter, MaybeActions } from '../../base/feedback/feedback-action-dispatcher';
21
22
  import { EnableToolsAction, Tool } from '../../base/tool-manager/tool';
22
23
  import { GLSPKeyTool } from '../../base/view/key-tool';
23
24
  import { GLSPMouseTool } from '../../base/view/mouse-tool';
@@ -47,8 +48,12 @@ export abstract class BaseEditTool implements Tool {
47
48
  return true;
48
49
  }
49
50
 
50
- dispatchActions(actions: Action[]): void {
51
- this.actionDispatcher.dispatchAll(actions);
51
+ dispatchActions(actions: Action[]): Promise<void> {
52
+ return this.actionDispatcher.dispatchAll(actions);
53
+ }
54
+
55
+ createFeedbackEmitter(): FeedbackEmitter {
56
+ return this.feedbackDispatcher.createEmitter();
52
57
  }
53
58
 
54
59
  /**
@@ -59,9 +64,10 @@ export abstract class BaseEditTool implements Tool {
59
64
  * @param feedbackEmitter the emitter sending out feedback actions (this tool by default).
60
65
  * @param cleanupActions the actions to be sent out when the feedback is de-registered through the returned Disposable.
61
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.
62
68
  */
63
- registerFeedback(feedbackActions: Action[], feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): Disposable {
64
- return this.feedbackDispatcher.registerFeedback(feedbackEmitter ?? this, feedbackActions, cleanupActions);
69
+ registerFeedback(feedbackActions: Action[], feedbackEmitter: IFeedbackEmitter = this, cleanupActions?: MaybeActions): Disposable {
70
+ return this.feedbackDispatcher.registerFeedback(feedbackEmitter, feedbackActions, cleanupActions);
65
71
  }
66
72
 
67
73
  /**
@@ -70,9 +76,10 @@ export abstract class BaseEditTool implements Tool {
70
76
  *
71
77
  * @param feedbackEmitter the emitter to be deregistered (this tool by default).
72
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.
73
80
  */
74
- deregisterFeedback(feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): void {
75
- this.feedbackDispatcher.deregisterFeedback(feedbackEmitter ?? this, cleanupActions);
81
+ deregisterFeedback(feedbackEmitter: IFeedbackEmitter = this, cleanupActions?: MaybeActions): void {
82
+ this.feedbackDispatcher.deregisterFeedback(feedbackEmitter, cleanupActions);
76
83
  }
77
84
  }
78
85
 
@@ -13,35 +13,13 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import {
17
- Action,
18
- CommandExecutionContext,
19
- CommandReturn,
20
- Disposable,
21
- ElementMove,
22
- GChildElement,
23
- GModelElement,
24
- GModelRoot,
25
- MoveAction,
26
- Point,
27
- TYPES,
28
- findParentByFeature,
29
- hasStringProp,
30
- isMoveable,
31
- isSelectable
32
- } from '@eclipse-glsp/sprotty';
16
+ import { Action, CommandExecutionContext, CommandReturn, TYPES, hasStringProp } from '@eclipse-glsp/sprotty';
33
17
  import { inject, injectable } from 'inversify';
34
18
 
35
- import { DebouncedFunc, debounce } from 'lodash';
36
- import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener';
37
- import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback';
38
19
  import { FeedbackCommand } from '../../../base/feedback/feedback-command';
39
20
  import { OptionalAction } from '../../../base/model/glsp-model-source';
40
21
  import { forEachElement } from '../../../utils/gmodel-util';
41
- import { SResizeHandle, addResizeHandles, isResizable, removeResizeHandles } from '../../change-bounds/model';
42
- import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from '../../change-bounds/movement-restrictor';
43
- import { PointPositionUpdater } from '../../change-bounds/point-position-updater';
44
- import { ChangeBoundsTool } from './change-bounds-tool';
22
+ import { addResizeHandles, isResizable, removeResizeHandles } from '../../change-bounds/model';
45
23
 
46
24
  export interface ShowChangeBoundsToolResizeFeedbackAction extends Action {
47
25
  kind: typeof ShowChangeBoundsToolResizeFeedbackAction.KIND;
@@ -89,13 +67,11 @@ export class ShowChangeBoundsToolResizeFeedbackCommand extends FeedbackCommand {
89
67
  execute(context: CommandExecutionContext): CommandReturn {
90
68
  const index = context.root.index;
91
69
 
92
- forEachElement(index, isResizable, removeResizeHandles);
70
+ forEachElement(index, isResizable, element => element.id !== this.action.elementId && removeResizeHandles(element));
93
71
 
94
- if (this.action.elementId) {
95
- const resizeElement = index.getById(this.action.elementId);
96
- if (resizeElement && isResizable(resizeElement)) {
97
- addResizeHandles(resizeElement);
98
- }
72
+ const resizeElement = index.getById(this.action.elementId);
73
+ if (resizeElement && isResizable(resizeElement)) {
74
+ addResizeHandles(resizeElement);
99
75
  }
100
76
  return context.root;
101
77
  }
@@ -145,205 +121,3 @@ export namespace MoveFinishedEventAction {
145
121
  return OptionalAction.mark({ kind: KIND });
146
122
  }
147
123
  }
148
-
149
- /**
150
- * This mouse listener provides visual feedback for moving by sending client-side
151
- * `MoveAction`s while elements are selected and dragged. This will also update
152
- * their bounds, which is important, as it is not only required for rendering
153
- * the visual feedback but also the basis for sending the change to the server
154
- * (see also `tools/MoveTool`).
155
- */
156
- export class FeedbackMoveMouseListener extends DragAwareMouseListener implements Disposable {
157
- protected rootElement?: GModelRoot;
158
- protected positionUpdater;
159
- protected elementId2startPos = new Map<string, Point>();
160
- protected pendingMoveInitialized?: DebouncedFunc<() => void>;
161
- protected moveInitialized = false;
162
-
163
- constructor(protected tool: ChangeBoundsTool) {
164
- super();
165
- this.positionUpdater = new PointPositionUpdater(tool.positionSnapper);
166
- }
167
-
168
- override mouseDown(target: GModelElement, event: MouseEvent): Action[] {
169
- if (event.button === 0 && !(target instanceof SResizeHandle)) {
170
- const moveable = findParentByFeature(target, isMoveable);
171
- if (moveable !== undefined && !(target instanceof SResizeHandle)) {
172
- this.positionUpdater.updateLastDragPosition(event);
173
- this.scheduleMoveInitialized();
174
- } else {
175
- this.positionUpdater.resetPosition();
176
- }
177
- this._isMouseDrag = false;
178
- }
179
- return [];
180
- }
181
-
182
- protected scheduleMoveInitialized(): void {
183
- this.moveInitialized = false;
184
- this.pendingMoveInitialized?.cancel();
185
- this.pendingMoveInitialized = debounce(() => {
186
- this.tool.registerFeedback([MoveInitializedEventAction.create()], this);
187
- this.pendingMoveInitialized = undefined;
188
- this.moveInitialized = true;
189
- }, 750);
190
- this.pendingMoveInitialized();
191
- }
192
-
193
- override mouseMove(target: GModelElement, event: MouseEvent): Action[] {
194
- const result: Action[] = [];
195
- if (event.buttons === 0) {
196
- this.mouseUp(target, event);
197
- } else if (!this.positionUpdater.isLastDragPositionUndefined()) {
198
- if (this.elementId2startPos.size === 0) {
199
- this.collectStartPositions(target.root);
200
- }
201
- this._isMouseDrag = true;
202
- const moveAction = this.getElementMoves(target, event, false);
203
- if (moveAction) {
204
- this.pendingMoveInitialized?.cancel();
205
- result.push(moveAction);
206
- result.push(cursorFeedbackAction(CursorCSS.MOVE));
207
- this.tool.registerFeedback(result, this);
208
- }
209
- }
210
- return [];
211
- }
212
-
213
- protected collectStartPositions(root: GModelRoot): void {
214
- this.rootElement = root;
215
- const selectedElements = root.index.all().filter(element => isSelectable(element) && element.selected);
216
- const elementsSet = new Set(selectedElements);
217
- selectedElements
218
- .filter(element => !this.isChildOfSelected(elementsSet, element))
219
- .forEach(element => {
220
- if (isMoveable(element)) {
221
- this.elementId2startPos.set(element.id, element.position);
222
- }
223
- });
224
- }
225
-
226
- protected isChildOfSelected(selectedElements: Set<GModelElement>, element: GModelElement): boolean {
227
- while (element instanceof GChildElement) {
228
- element = element.parent;
229
- if (selectedElements.has(element)) {
230
- return true;
231
- }
232
- }
233
- return false;
234
- }
235
-
236
- protected getElementMoves(target: GModelElement, event: MouseEvent, finished: boolean): MoveAction | undefined {
237
- const delta = this.positionUpdater.updatePosition(target, event);
238
- if (!delta) {
239
- return undefined;
240
- }
241
- const elementMoves: ElementMove[] = this.getElementMovesForDelta(target, delta, finished);
242
- if (elementMoves.length > 0) {
243
- return MoveAction.create(elementMoves, { animate: false, finished });
244
- } else {
245
- return undefined;
246
- }
247
- }
248
-
249
- protected getElementMovesForDelta(target: GModelElement, delta: Point, finished: boolean): ElementMove[] {
250
- const elementMoves: ElementMove[] = [];
251
- this.elementId2startPos.forEach((startPosition, elementId) => {
252
- const element = target.root.index.getById(elementId);
253
- if (element) {
254
- if (isMoveable(element)) {
255
- const targetPosition = Point.add(element.position, delta);
256
- const toPosition = this.validateMove(startPosition, targetPosition, element, finished);
257
- elementMoves.push({ elementId: element.id, fromPosition: element.position, toPosition });
258
- }
259
- }
260
- });
261
- return elementMoves;
262
- }
263
-
264
- protected validateMove(startPosition: Point, toPosition: Point, element: GModelElement, isFinished: boolean): Point {
265
- let newPosition = toPosition;
266
- if (this.tool.movementRestrictor) {
267
- const valid = this.tool.movementRestrictor.validate(element, toPosition);
268
- let action;
269
- if (!valid) {
270
- action = createMovementRestrictionFeedback(element, this.tool.movementRestrictor);
271
- if (isFinished) {
272
- newPosition = startPosition;
273
- }
274
- } else {
275
- action = removeMovementRestrictionFeedback(element, this.tool.movementRestrictor);
276
- }
277
- this.tool.registerFeedback([action], this);
278
- }
279
- return newPosition;
280
- }
281
-
282
- override mouseEnter(target: GModelElement, event: MouseEvent): Action[] {
283
- if (target instanceof GModelRoot && event.buttons === 0 && this.positionUpdater.isLastDragPositionUndefined()) {
284
- this.mouseUp(target, event);
285
- }
286
- return [];
287
- }
288
-
289
- override nonDraggingMouseUp(element: GModelElement, event: MouseEvent): Action[] {
290
- this.reset(true);
291
- return [];
292
- }
293
-
294
- override draggingMouseUp(target: GModelElement, event: MouseEvent): Action[] {
295
- const result: Action[] = [];
296
- if (this.positionUpdater.isLastDragPositionUndefined()) {
297
- this.reset(true);
298
- return result;
299
- } else {
300
- const moveAction = this.getElementMoves(target, event, true);
301
- if (moveAction) {
302
- result.push(moveAction);
303
- }
304
- const resetFeedback: Action[] = [];
305
- if (this.tool.movementRestrictor) {
306
- resetFeedback.push(removeMovementRestrictionFeedback(target, this.tool.movementRestrictor));
307
- }
308
- resetFeedback.push(cursorFeedbackAction(CursorCSS.DEFAULT));
309
- this.tool.deregisterFeedback(this, resetFeedback);
310
- }
311
- this.reset();
312
- return result;
313
- }
314
-
315
- protected resetMoveFeedback(): ElementMove[] {
316
- const elementMoves: ElementMove[] = [];
317
- this.elementId2startPos.forEach((startPosition, elementId) => {
318
- const element = this.rootElement!.index.getById(elementId);
319
- if (element) {
320
- if (isMoveable(element)) {
321
- elementMoves.push({ elementId: element.id, fromPosition: element.position, toPosition: startPosition });
322
- }
323
- }
324
- });
325
- return elementMoves;
326
- }
327
-
328
- protected reset(resetFeedback = false): void {
329
- this.pendingMoveInitialized?.cancel();
330
- if (this.rootElement && resetFeedback) {
331
- const elementMoves: ElementMove[] = this.resetMoveFeedback();
332
- if (elementMoves.length > 0) {
333
- const moveAction = MoveAction.create(elementMoves, { animate: false, finished: true });
334
- this.tool.deregisterFeedback(this, [moveAction]);
335
- }
336
- } else if (resetFeedback && this.moveInitialized) {
337
- this.tool.deregisterFeedback(this, [MoveFinishedEventAction.create()]);
338
- }
339
- this.positionUpdater.resetPosition();
340
- this._isMouseDrag = false;
341
- this.moveInitialized = false;
342
- this.rootElement = undefined;
343
- this.elementId2startPos.clear();
344
- }
345
-
346
- dispose(): void {
347
- this.reset(true);
348
- }
349
- }
@@ -14,6 +14,7 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
16
  import { FeatureModule, TYPES, bindAsService, configureCommand, configureView } from '@eclipse-glsp/sprotty';
17
+ import '../../../../css/change-bounds.css';
17
18
  import { SResizeHandle } from '../../change-bounds/model';
18
19
  import { ChangeBoundsTool } from './change-bounds-tool';
19
20
  import { HideChangeBoundsToolResizeFeedbackCommand, ShowChangeBoundsToolResizeFeedbackCommand } from './change-bounds-tool-feedback';