@eclipse-glsp/client 0.10.0-next.e5fcffe.169 → 1.1.0-next.282a632.186

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 (179) hide show
  1. package/css/decoration.css +5 -1
  2. package/css/glsp-sprotty.css +39 -0
  3. package/css/tool-palette.css +2 -2
  4. package/lib/base/container-modules.d.ts.map +1 -1
  5. package/lib/base/container-modules.js +10 -9
  6. package/lib/base/container-modules.js.map +1 -1
  7. package/lib/base/editor-context-service.d.ts +2 -3
  8. package/lib/base/editor-context-service.d.ts.map +1 -1
  9. package/lib/base/editor-context-service.js +11 -12
  10. package/lib/base/editor-context-service.js.map +1 -1
  11. package/lib/base/model/update-model-command.d.ts +14 -7
  12. package/lib/base/model/update-model-command.d.ts.map +1 -1
  13. package/lib/base/model/update-model-command.js +31 -41
  14. package/lib/base/model/update-model-command.js.map +1 -1
  15. package/lib/base/source-uri-aware.d.ts +2 -17
  16. package/lib/base/source-uri-aware.d.ts.map +1 -1
  17. package/lib/base/source-uri-aware.js +4 -6
  18. package/lib/base/source-uri-aware.js.map +1 -1
  19. package/lib/features/change-bounds/movement-restrictor.d.ts +36 -6
  20. package/lib/features/change-bounds/movement-restrictor.d.ts.map +1 -1
  21. package/lib/features/change-bounds/movement-restrictor.js +54 -39
  22. package/lib/features/change-bounds/movement-restrictor.js.map +1 -1
  23. package/lib/features/change-bounds/snap.d.ts +20 -5
  24. package/lib/features/change-bounds/snap.d.ts.map +1 -1
  25. package/lib/features/change-bounds/snap.js +19 -8
  26. package/lib/features/change-bounds/snap.js.map +1 -1
  27. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.d.ts +5 -7
  28. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.d.ts.map +1 -1
  29. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.js +7 -14
  30. package/lib/features/context-menu/selection-service-aware-context-menu-mouse-listener.js.map +1 -1
  31. package/lib/features/hover/hover.d.ts +1 -2
  32. package/lib/features/hover/hover.d.ts.map +1 -1
  33. package/lib/features/hover/hover.js +2 -15
  34. package/lib/features/hover/hover.js.map +1 -1
  35. package/lib/features/layout/di.config.d.ts +2 -2
  36. package/lib/features/layout/di.config.d.ts.map +1 -1
  37. package/lib/features/layout/di.config.js +8 -5
  38. package/lib/features/layout/di.config.js.map +1 -1
  39. package/lib/features/layout/hbox-layout.d.ts +1 -1
  40. package/lib/features/layout/hbox-layout.d.ts.map +1 -1
  41. package/lib/features/layout/hbox-layout.js +19 -7
  42. package/lib/features/layout/hbox-layout.js.map +1 -1
  43. package/lib/features/layout/{layout-commands.d.ts → layout-elements-action.d.ts} +75 -57
  44. package/lib/features/layout/layout-elements-action.d.ts.map +1 -0
  45. package/lib/features/layout/{layout-commands.js → layout-elements-action.js} +153 -197
  46. package/lib/features/layout/layout-elements-action.js.map +1 -0
  47. package/lib/features/layout/layout-elements-action.spec.d.ts +3 -0
  48. package/lib/features/layout/layout-elements-action.spec.d.ts.map +1 -0
  49. package/lib/features/layout/{layout-commands.spec.js → layout-elements-action.spec.js} +63 -56
  50. package/lib/features/layout/layout-elements-action.spec.js.map +1 -0
  51. package/lib/features/layout/layouter.d.ts +37 -0
  52. package/lib/features/layout/layouter.d.ts.map +1 -0
  53. package/lib/features/layout/layouter.js +131 -0
  54. package/lib/features/layout/layouter.js.map +1 -0
  55. package/lib/features/layout/vbox-layout.d.ts +1 -1
  56. package/lib/features/layout/vbox-layout.d.ts.map +1 -1
  57. package/lib/features/layout/vbox-layout.js +17 -5
  58. package/lib/features/layout/vbox-layout.js.map +1 -1
  59. package/lib/features/mouse-tool/mouse-tool.d.ts +0 -1
  60. package/lib/features/mouse-tool/mouse-tool.d.ts.map +1 -1
  61. package/lib/features/mouse-tool/mouse-tool.js +9 -5
  62. package/lib/features/mouse-tool/mouse-tool.js.map +1 -1
  63. package/lib/features/select/select-mouse-listener.d.ts +3 -1
  64. package/lib/features/select/select-mouse-listener.d.ts.map +1 -1
  65. package/lib/features/select/select-mouse-listener.js +62 -0
  66. package/lib/features/select/select-mouse-listener.js.map +1 -1
  67. package/lib/features/select/selection-service.d.ts +12 -3
  68. package/lib/features/select/selection-service.d.ts.map +1 -1
  69. package/lib/features/select/selection-service.js +31 -45
  70. package/lib/features/select/selection-service.js.map +1 -1
  71. package/lib/features/select/selection-service.spec.js +1 -1
  72. package/lib/features/select/selection-service.spec.js.map +1 -1
  73. package/lib/features/source-model-watcher/di.config.d.ts +4 -0
  74. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.d.ts.map +1 -1
  75. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.js +4 -4
  76. package/lib/features/{model-source-watcher → source-model-watcher}/di.config.js.map +1 -1
  77. package/lib/features/{model-source-watcher/model-source-changed-action-handler.d.ts → source-model-watcher/source-model-changed-action-handler.d.ts} +9 -9
  78. package/lib/features/{model-source-watcher/model-source-changed-action-handler.d.ts.map → source-model-watcher/source-model-changed-action-handler.d.ts.map} +1 -1
  79. package/lib/features/{model-source-watcher/model-source-changed-action-handler.js → source-model-watcher/source-model-changed-action-handler.js} +19 -19
  80. package/lib/features/{model-source-watcher/model-source-changed-action-handler.js.map → source-model-watcher/source-model-changed-action-handler.js.map} +1 -1
  81. package/lib/features/tool-feedback/change-bounds-tool-feedback.js +1 -1
  82. package/lib/features/tool-feedback/change-bounds-tool-feedback.js.map +1 -1
  83. package/lib/features/tool-feedback/edge-edit-tool-feedback.d.ts +3 -2
  84. package/lib/features/tool-feedback/edge-edit-tool-feedback.d.ts.map +1 -1
  85. package/lib/features/tool-feedback/edge-edit-tool-feedback.js +13 -6
  86. package/lib/features/tool-feedback/edge-edit-tool-feedback.js.map +1 -1
  87. package/lib/features/tools/di.config.d.ts +9 -2
  88. package/lib/features/tools/di.config.d.ts.map +1 -1
  89. package/lib/features/tools/di.config.js +14 -3
  90. package/lib/features/tools/di.config.js.map +1 -1
  91. package/lib/features/tools/enable-default-tools-on-focus-loss.d.ts +26 -0
  92. package/lib/features/tools/enable-default-tools-on-focus-loss.d.ts.map +1 -0
  93. package/lib/features/tools/enable-default-tools-on-focus-loss.js +29 -0
  94. package/lib/features/tools/enable-default-tools-on-focus-loss.js.map +1 -0
  95. package/lib/features/validation/di.config.d.ts +0 -15
  96. package/lib/features/validation/di.config.d.ts.map +1 -1
  97. package/lib/features/validation/di.config.js +6 -4
  98. package/lib/features/validation/di.config.js.map +1 -1
  99. package/lib/features/validation/issue-marker.d.ts +11 -5
  100. package/lib/features/validation/issue-marker.d.ts.map +1 -1
  101. package/lib/features/validation/issue-marker.js +25 -5
  102. package/lib/features/validation/issue-marker.js.map +1 -1
  103. package/lib/features/validation/marker-navigator.d.ts +11 -17
  104. package/lib/features/validation/marker-navigator.d.ts.map +1 -1
  105. package/lib/features/validation/marker-navigator.js +32 -61
  106. package/lib/features/validation/marker-navigator.js.map +1 -1
  107. package/lib/features/validation/validate.d.ts +8 -19
  108. package/lib/features/validation/validate.d.ts.map +1 -1
  109. package/lib/features/validation/validate.js +29 -55
  110. package/lib/features/validation/validate.js.map +1 -1
  111. package/lib/index.d.ts +6 -6
  112. package/lib/index.d.ts.map +1 -1
  113. package/lib/index.js +16 -15
  114. package/lib/index.js.map +1 -1
  115. package/lib/lib/model.d.ts +1 -1
  116. package/lib/lib/model.d.ts.map +1 -1
  117. package/lib/lib/model.js +3 -3
  118. package/lib/lib/model.js.map +1 -1
  119. package/lib/model-source/glsp-diagram-server.d.ts +1 -1
  120. package/lib/model-source/glsp-diagram-server.d.ts.map +1 -1
  121. package/lib/model-source/glsp-diagram-server.js +1 -1
  122. package/lib/model-source/glsp-diagram-server.js.map +1 -1
  123. package/lib/utils/layout-utils.d.ts +4 -20
  124. package/lib/utils/layout-utils.d.ts.map +1 -1
  125. package/lib/utils/layout-utils.js +1 -1
  126. package/lib/utils/layout-utils.js.map +1 -1
  127. package/lib/views/base-view-module.d.ts.map +1 -1
  128. package/lib/views/base-view-module.js +3 -1
  129. package/lib/views/base-view-module.js.map +1 -1
  130. package/lib/views/glsp-projection-view.d.ts +30 -0
  131. package/lib/views/glsp-projection-view.d.ts.map +1 -0
  132. package/lib/views/glsp-projection-view.js +142 -0
  133. package/lib/views/glsp-projection-view.js.map +1 -0
  134. package/lib/views/index.d.ts +1 -0
  135. package/lib/views/index.d.ts.map +1 -1
  136. package/lib/views/index.js +1 -0
  137. package/lib/views/index.js.map +1 -1
  138. package/package.json +4 -4
  139. package/src/base/container-modules.ts +6 -5
  140. package/src/base/editor-context-service.ts +15 -7
  141. package/src/base/model/update-model-command.ts +48 -34
  142. package/src/base/source-uri-aware.ts +5 -4
  143. package/src/features/change-bounds/movement-restrictor.ts +73 -43
  144. package/src/features/change-bounds/snap.ts +15 -12
  145. package/src/features/context-menu/selection-service-aware-context-menu-mouse-listener.ts +19 -25
  146. package/src/features/hover/hover.ts +2 -24
  147. package/src/features/layout/di.config.ts +14 -6
  148. package/src/features/layout/hbox-layout.ts +24 -7
  149. package/src/features/layout/{layout-commands.spec.ts → layout-elements-action.spec.ts} +56 -48
  150. package/src/features/layout/{layout-commands.ts → layout-elements-action.ts} +195 -207
  151. package/src/features/layout/layouter.ts +137 -0
  152. package/src/features/layout/vbox-layout.ts +22 -5
  153. package/src/features/mouse-tool/mouse-tool.ts +8 -6
  154. package/src/features/select/select-mouse-listener.ts +81 -1
  155. package/src/features/select/selection-service.spec.ts +1 -1
  156. package/src/features/select/selection-service.ts +37 -50
  157. package/src/features/{model-source-watcher → source-model-watcher}/di.config.ts +5 -5
  158. package/src/features/{model-source-watcher/model-source-changed-action-handler.ts → source-model-watcher/source-model-changed-action-handler.ts} +12 -12
  159. package/src/features/tool-feedback/change-bounds-tool-feedback.ts +1 -1
  160. package/src/features/tool-feedback/edge-edit-tool-feedback.ts +14 -6
  161. package/src/features/tools/di.config.ts +13 -2
  162. package/src/features/tools/enable-default-tools-on-focus-loss.ts +33 -0
  163. package/src/features/validation/di.config.ts +10 -7
  164. package/src/features/validation/issue-marker.ts +34 -12
  165. package/src/features/validation/marker-navigator.ts +29 -66
  166. package/src/features/validation/validate.ts +36 -71
  167. package/src/index.ts +8 -7
  168. package/src/lib/model.ts +1 -1
  169. package/src/model-source/glsp-diagram-server.ts +1 -1
  170. package/src/utils/layout-utils.ts +7 -27
  171. package/src/views/base-view-module.ts +5 -1
  172. package/src/views/glsp-projection-view.tsx +166 -0
  173. package/src/views/index.ts +2 -0
  174. package/lib/features/layout/layout-commands.d.ts.map +0 -1
  175. package/lib/features/layout/layout-commands.js.map +0 -1
  176. package/lib/features/layout/layout-commands.spec.d.ts +0 -3
  177. package/lib/features/layout/layout-commands.spec.d.ts.map +0 -1
  178. package/lib/features/layout/layout-commands.spec.js.map +0 -1
  179. package/lib/features/model-source-watcher/di.config.d.ts +0 -4
@@ -13,25 +13,31 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Action, SetModelAction, UpdateModelAction } from '@eclipse-glsp/protocol';
17
- import { inject, injectable, multiInject, optional, postConstruct } from 'inversify';
16
+ import { Action, SetModelAction, toTypeGuard, UpdateModelAction } from '@eclipse-glsp/protocol';
17
+ import { inject, injectable, multiInject, optional } from 'inversify';
18
18
  import {
19
19
  ActionHandlerRegistry,
20
+ Animation,
20
21
  Command,
21
22
  CommandActionHandler,
22
23
  CommandExecutionContext,
23
24
  CommandReturn,
24
25
  IActionHandler,
25
26
  ILogger,
27
+ MorphEdgesAnimation,
26
28
  SModelRoot,
29
+ UpdateAnimationData,
27
30
  UpdateModelCommand
28
31
  } from 'sprotty';
29
32
  import { IFeedbackActionDispatcher } from '../../features/tool-feedback/feedback-action-dispatcher';
30
33
  import { FeedbackCommand } from '../../features/tool-feedback/model';
31
34
  import { TYPES } from '../types';
32
35
 
33
- /* ActionHandler that transforms a SetModelAction into an (feedback-aware) UpdateModelAction. This can be done because in sprotty
34
- * UpdateModel behaves the same as SetModel if no model is present yet.*/
36
+ /**
37
+ * ActionHandler that transforms a {@link SetModelAction} into an {@link UpdateModelAction} that can be handled
38
+ * by the {@link FeedbackAwareUpdateModelCommand}. This can be done because in sprotty an {@link UpdateModelCommand} and
39
+ * a {@link SetModelCommand} have the same behavior of no model is present yet.
40
+ */
35
41
  @injectable()
36
42
  export class SetModelActionHandler implements IActionHandler {
37
43
  handle(action: Action): Action | void {
@@ -46,38 +52,41 @@ export interface SModelRootListener {
46
52
  }
47
53
 
48
54
  /**
49
- * A special`UpdateModelCommand` that retrieves all registered `actions` from the `IFeedbackActionDispatcher` (if present) and applies their
50
- * feedback to the `newRoot` before performing the update
55
+ * A special {@link UpdateModelCommand} that retrieves all registered {@link Action}s from the {@link IFeedbackActionDispatcher}
56
+ * (if present) and applies their feedback to the `newRoot` before performing the update. This enables persistent client-side feedback
57
+ * across model updates initiated by the GLSP server.
51
58
  */
52
59
  @injectable()
53
60
  export class FeedbackAwareUpdateModelCommand extends UpdateModelCommand {
54
- @inject(TYPES.ILogger) protected logger: ILogger;
55
- @inject(TYPES.IFeedbackActionDispatcher) @optional() protected readonly feedbackActionDispatcher: IFeedbackActionDispatcher;
56
- @inject(TYPES.ActionHandlerRegistryProvider) protected actionHandlerRegistryProvider: () => Promise<ActionHandlerRegistry>;
57
- @multiInject(TYPES.SModelRootListener) @optional() protected modelRootListeners: SModelRootListener[] = [];
61
+ @inject(TYPES.ILogger)
62
+ protected logger: ILogger;
63
+
64
+ @inject(TYPES.IFeedbackActionDispatcher)
65
+ @optional()
66
+ protected feedbackActionDispatcher: IFeedbackActionDispatcher;
67
+
68
+ @multiInject(TYPES.SModelRootListener)
69
+ @optional()
70
+ protected modelRootListeners: SModelRootListener[] = [];
58
71
 
59
72
  protected actionHandlerRegistry?: ActionHandlerRegistry;
60
73
 
61
- constructor(@inject(TYPES.Action) action: UpdateModelAction) {
74
+ constructor(
75
+ @inject(TYPES.Action) action: UpdateModelAction,
76
+ @inject(TYPES.ActionHandlerRegistryProvider)
77
+ actionHandlerRegistryProvider: () => Promise<ActionHandlerRegistry>
78
+ ) {
62
79
  super({ animate: true, ...action });
63
- }
64
-
65
- @postConstruct()
66
- protected initialize(): void {
67
- this.actionHandlerRegistryProvider().then(registry => (this.actionHandlerRegistry = registry));
80
+ actionHandlerRegistryProvider().then(registry => (this.actionHandlerRegistry = registry));
68
81
  }
69
82
 
70
83
  protected override performUpdate(oldRoot: SModelRoot, newRoot: SModelRoot, context: CommandExecutionContext): CommandReturn {
71
84
  if (this.feedbackActionDispatcher && this.actionHandlerRegistry) {
72
- // Create a temporary context wich defines the `newRoot` as `root`
85
+ // Create a temporary context which defines the `newRoot` as `root`
73
86
  // This way we do not corrupt the redo/undo behavior of the super class
74
87
  const tempContext: CommandExecutionContext = {
75
- root: newRoot,
76
- duration: context.duration,
77
- logger: context.logger,
78
- modelChanged: context.modelChanged,
79
- modelFactory: context.modelFactory,
80
- syncer: context.syncer
88
+ ...context,
89
+ root: newRoot
81
90
  };
82
91
 
83
92
  const feedbackCommands = this.getFeedbackCommands(this.actionHandlerRegistry);
@@ -91,19 +100,24 @@ export class FeedbackAwareUpdateModelCommand extends UpdateModelCommand {
91
100
  protected getFeedbackCommands(registry: ActionHandlerRegistry): Command[] {
92
101
  const result: Command[] = [];
93
102
  this.feedbackActionDispatcher.getRegisteredFeedback().forEach(action => {
94
- const handler = registry.get(action.kind).find(h => h instanceof CommandActionHandler);
95
- if (handler instanceof CommandActionHandler) {
96
- result.push(handler.handle(action));
97
- }
103
+ const commands = registry
104
+ .get(action.kind)
105
+ .filter<CommandActionHandler>(toTypeGuard(CommandActionHandler))
106
+ .map(handler => handler.handle(action));
107
+ result.push(...commands);
98
108
  });
99
- // sort commands descanding by priority
100
- return result.sort((a, b) => getPriority(b) - getPriority(a));
109
+ // sort commands descanting by priority
110
+ return result.sort((a, b) => this.getPriority(b) - this.getPriority(a));
111
+ }
112
+
113
+ protected getPriority(command: Partial<FeedbackCommand>): number {
114
+ return command.priority ?? 0;
101
115
  }
102
- }
103
116
 
104
- function getPriority(command: Command): number {
105
- if (command instanceof FeedbackCommand) {
106
- return command.priority;
117
+ // Override the `createAnimations` implementation and remove the animation for edge morphing. Otherwise routing & reconnect
118
+ // handles flicker after each server update.
119
+ protected override createAnimations(data: UpdateAnimationData, root: SModelRoot, context: CommandExecutionContext): Animation[] {
120
+ const animations = super.createAnimations(data, root, context);
121
+ return animations.filter(animation => !(animation instanceof MorphEdgesAnimation));
107
122
  }
108
- return 0;
109
123
  }
@@ -1,5 +1,5 @@
1
1
  /********************************************************************************
2
- * Copyright (c) 2020 EclipseSource and others.
2
+ * Copyright (c) 2020-2022 EclipseSource and others.
3
3
  *
4
4
  * This program and the accompanying materials are made available under the
5
5
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -13,10 +13,11 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- export abstract class SourceUriAware {
17
- abstract getSourceURI(): string;
16
+ import { AnyObject, hasStringProp } from '@eclipse-glsp/protocol';
17
+ export interface SourceUriAware {
18
+ sourceURI: string;
18
19
  }
19
20
 
20
21
  export function isSourceUriAware(obj: any): obj is SourceUriAware {
21
- return obj !== undefined && typeof obj.getSourceURI === 'function';
22
+ return AnyObject.is(obj) && hasStringProp(obj, 'sourceURI');
22
23
  }
@@ -20,40 +20,35 @@ import { toAbsoluteBounds } from '../../utils/viewpoint-util';
20
20
  import { ModifyCSSFeedbackAction } from '../tool-feedback/css-feedback';
21
21
  import { isBoundsAwareMoveable, SResizeHandle } from './model';
22
22
 
23
+ /**
24
+ * A `MovementRestrictor` is an optional service that can be used by tools to validate
25
+ * whether a certain move operation (e.g. `ChangeBoundsOperation`) in the diagram is valid.
26
+ */
23
27
  export interface IMovementRestrictor {
24
- validate(newLocation: Point, element: SModelElement): boolean;
28
+ /**
29
+ * Validate whether moving the given element to a new given location is allowed.
30
+ * @param element The element that should be moved.
31
+ * @param newLocation The new location of the element.
32
+ * @returns `true` if the the element is movable and moving to the given location is allowed, `false` otherwise.
33
+ * Should also return `false` if the newLocation is `undefined`:
34
+ */
35
+ validate(element: SModelElement, newLocation?: Point): boolean;
36
+ /**
37
+ * Feedback css-classes. Can be applied to elements that did fail the validation.
38
+ */
25
39
  cssClasses?: string[];
26
40
  }
27
41
 
28
- export function createMovementRestrictionFeedback(
29
- element: SModelElement,
30
- movementRestrictor: IMovementRestrictor
31
- ): ModifyCSSFeedbackAction {
32
- const elements: SModelElement[] = [element];
33
- if (element instanceof SParentElement) {
34
- element.children.filter(child => child instanceof SResizeHandle).forEach(e => elements.push(e));
35
- }
36
- return ModifyCSSFeedbackAction.create({ elements, add: movementRestrictor.cssClasses });
37
- }
38
-
39
- export function removeMovementRestrictionFeedback(
40
- element: SModelElement,
41
- movementRestrictor: IMovementRestrictor
42
- ): ModifyCSSFeedbackAction {
43
- const elements: SModelElement[] = [element];
44
- if (element instanceof SParentElement) {
45
- element.children.filter(child => child instanceof SResizeHandle).forEach(e => elements.push(e));
46
- }
47
-
48
- return ModifyCSSFeedbackAction.create({ elements, remove: movementRestrictor.cssClasses });
49
- }
50
-
42
+ /**
43
+ * A `IMovementRestrictor` that checks for overlapping elements. Move operations
44
+ * are only valid if the element does not collide with another element after moving.
45
+ */
51
46
  @injectable()
52
- export class NoOverlapMovmentRestrictor implements IMovementRestrictor {
47
+ export class NoOverlapMovementRestrictor implements IMovementRestrictor {
53
48
  cssClasses = ['movement-not-allowed'];
54
49
 
55
- validate(newLocation: Point, element: SModelElement): boolean {
56
- if (!isBoundsAwareMoveable(element)) {
50
+ validate(element: SModelElement, newLocation?: Point): boolean {
51
+ if (!isBoundsAwareMoveable(element) || !newLocation) {
57
52
  return false;
58
53
  }
59
54
  // Create ghost element at the newLocation
@@ -71,27 +66,62 @@ export class NoOverlapMovmentRestrictor implements IMovementRestrictor {
71
66
  .all()
72
67
  .filter(e => e.id !== ghostElement.id && e !== ghostElement.root && e instanceof SNode)
73
68
  .map(e => e as SModelElement & BoundsAware)
74
- ).some(e => areOverlapping(e, ghostElement));
69
+ ).some(e => this.areOverlapping(e, ghostElement));
75
70
  }
76
- }
77
71
 
78
- export function areOverlapping(element1: SModelElement & BoundsAware, element2: SModelElement & BoundsAware): boolean {
79
- const b1 = toAbsoluteBounds(element1);
80
- const b2 = toAbsoluteBounds(element2);
81
- const r1TopLeft: Point = b1;
82
- const r1BottomRight = { x: b1.x + b1.width, y: b1.y + b1.height };
83
- const r2TopLeft: Point = b2;
84
- const r2BottomRight = { x: b2.x + b2.width, y: b2.y + b2.height };
72
+ protected areOverlapping(element1: SModelElement & BoundsAware, element2: SModelElement & BoundsAware): boolean {
73
+ const b1 = toAbsoluteBounds(element1);
74
+ const b2 = toAbsoluteBounds(element2);
75
+ const r1TopLeft: Point = b1;
76
+ const r1BottomRight = { x: b1.x + b1.width, y: b1.y + b1.height };
77
+ const r2TopLeft: Point = b2;
78
+ const r2BottomRight = { x: b2.x + b2.width, y: b2.y + b2.height };
79
+
80
+ // If one rectangle is on left side of other
81
+ if (r1TopLeft.x > r2BottomRight.x || r2TopLeft.x > r1BottomRight.x) {
82
+ return false;
83
+ }
85
84
 
86
- // If one rectangle is on left side of other
87
- if (r1TopLeft.x > r2BottomRight.x || r2TopLeft.x > r1BottomRight.x) {
88
- return false;
85
+ // If one rectangle is above other
86
+ if (r1BottomRight.y < r2TopLeft.y || r2BottomRight.y < r1TopLeft.y) {
87
+ return false;
88
+ }
89
+
90
+ return true;
89
91
  }
92
+ }
90
93
 
91
- // If one rectangle is above other
92
- if (r1BottomRight.y < r2TopLeft.y || r2BottomRight.y < r1TopLeft.y) {
93
- return false;
94
+ /**
95
+ * Utility function to create an action that applies the given {@link IMovementRestrictor.cssClasses} to the given element.
96
+ * @param element The element on which the css classes should be applied.
97
+ * @param movementRestrictor The movement restrictor whose cssClasses should be applied.
98
+ * @returns The corresponding {@link ModifyCSSFeedbackAction}
99
+ */
100
+ export function createMovementRestrictionFeedback(
101
+ element: SModelElement,
102
+ movementRestrictor: IMovementRestrictor
103
+ ): ModifyCSSFeedbackAction {
104
+ const elements: SModelElement[] = [element];
105
+ if (element instanceof SParentElement) {
106
+ element.children.filter(child => child instanceof SResizeHandle).forEach(e => elements.push(e));
107
+ }
108
+ return ModifyCSSFeedbackAction.create({ elements, add: movementRestrictor.cssClasses });
109
+ }
110
+
111
+ /**
112
+ * Utility function to create an action that removes the given {@link IMovementRestrictor.cssClasses} from the given element.
113
+ * @param element The element from which the css classes should be removed.
114
+ * @param movementRestrictor The movement restrictor whose cssClasses should be removed.
115
+ * @returns The corresponding {@link ModifyCSSFeedbackAction}
116
+ */
117
+ export function removeMovementRestrictionFeedback(
118
+ element: SModelElement,
119
+ movementRestrictor: IMovementRestrictor
120
+ ): ModifyCSSFeedbackAction {
121
+ const elements: SModelElement[] = [element];
122
+ if (element instanceof SParentElement) {
123
+ element.children.filter(child => child instanceof SResizeHandle).forEach(e => elements.push(e));
94
124
  }
95
125
 
96
- return true;
126
+ return ModifyCSSFeedbackAction.create({ elements, remove: movementRestrictor.cssClasses });
97
127
  }
@@ -13,25 +13,28 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Point } from '@eclipse-glsp/protocol';
16
+ import { Point, Writable } from '@eclipse-glsp/protocol';
17
17
  import { injectable } from 'inversify';
18
18
  import { findParentByFeature, ISnapper, isViewport, SModelElement } from 'sprotty';
19
- import { WriteablePoint } from '../../utils/layout-utils';
20
19
 
20
+ /**
21
+ * A {@link ISnapper} implementation that snaps all elements onto a fixed gride size.
22
+ * The default grid size is 10x10 pixel.
23
+ * To configure a custom grid size bind the `TYPES.ISnapper` service identifier
24
+ * to constant value, e.g:
25
+ *
26
+ * ```ts
27
+ * bind(TYPES.ISnapper).toConstantValue(new GridSnapper({x:25 ,y:25 }));
28
+ * ```
29
+ */
21
30
  @injectable()
22
31
  export class GridSnapper implements ISnapper {
23
- get gridX(): number {
24
- return 10;
25
- }
26
-
27
- get gridY(): number {
28
- return 10;
29
- }
32
+ constructor(public grid: { x: number; y: number } = { x: 10, y: 10 }) {}
30
33
 
31
34
  snap(position: Point, element: SModelElement): Point {
32
35
  return {
33
- x: Math.round(position.x / this.gridX) * this.gridX,
34
- y: Math.round(position.y / this.gridY) * this.gridY
36
+ x: Math.round(position.x / this.grid.x) * this.grid.x,
37
+ y: Math.round(position.y / this.grid.y) * this.grid.y
35
38
  };
36
39
  }
37
40
  }
@@ -46,7 +49,7 @@ export class GridSnapper implements ISnapper {
46
49
  */
47
50
  export class PointPositionUpdater {
48
51
  protected lastDragPosition?: Point;
49
- protected positionDelta: WriteablePoint = { x: 0, y: 0 };
52
+ protected positionDelta: Writable<Point> = { x: 0, y: 0 };
50
53
 
51
54
  constructor(protected snapper?: ISnapper) {}
52
55
 
@@ -15,53 +15,47 @@
15
15
  ********************************************************************************/
16
16
  import { Action } from '@eclipse-glsp/protocol';
17
17
  import { inject, injectable, optional } from 'inversify';
18
- import {
19
- ContextMenuProviderRegistry,
20
- findParentByFeature,
21
- IContextMenuServiceProvider,
22
- isSelectable,
23
- MouseListener,
24
- SModelElement
25
- } from 'sprotty';
18
+ import { ContextMenuProviderRegistry, IContextMenuServiceProvider, MouseListener, SModelElement } from 'sprotty';
26
19
  import { FocusStateChangedAction } from '../../base/actions/focus-change-action';
27
20
  import { TYPES } from '../../base/types';
28
21
  import { SelectionService } from '../select/selection-service';
29
22
 
30
23
  @injectable()
31
24
  export class SelectionServiceAwareContextMenuMouseListener extends MouseListener {
32
- @inject(TYPES.IContextMenuServiceProvider) @optional() protected readonly contextMenuService: IContextMenuServiceProvider;
33
- @inject(TYPES.IContextMenuProviderRegistry) @optional() protected readonly menuProvider: ContextMenuProviderRegistry;
34
- @inject(TYPES.SelectionService) protected selectionService: SelectionService;
25
+ @inject(TYPES.IContextMenuServiceProvider)
26
+ @optional()
27
+ protected readonly contextMenuService?: IContextMenuServiceProvider;
28
+
29
+ @inject(TYPES.IContextMenuProviderRegistry)
30
+ @optional()
31
+ protected readonly menuProvider?: ContextMenuProviderRegistry;
32
+
33
+ @inject(TYPES.SelectionService)
34
+ protected selectionService: SelectionService;
35
35
 
36
36
  /**
37
- * Opens the context menu on right-click.
37
+ * Opens the context menu.
38
38
  */
39
- override mouseDown(target: SModelElement, event: MouseEvent): (Action | Promise<Action>)[] {
40
- if (event.button === 2 && this.contextMenuService && this.menuProvider) {
41
- return this.openContextMenu(event, target);
42
- }
43
- return [];
39
+ override contextMenu(target: SModelElement, event: MouseEvent): (Action | Promise<Action>)[] {
40
+ return this.openContextMenu(target, event);
44
41
  }
45
42
 
46
43
  /**
47
44
  * Opens the context menu.
48
45
  *
49
- * - query the element on the click-target
50
- * - select the element
51
46
  * - query the context menu service and the context menu elements
52
47
  * - show the context menu
53
48
  * - send a focus state change to indicate that the diagram becomes inactive, once the context menu is shown
54
49
  *
55
50
  * When the context menu is closed, we focus the diagram element again.
56
51
  */
57
- protected openContextMenu(event: MouseEvent, target: SModelElement): Promise<Action>[] {
58
- const mousePosition = { x: event.x, y: event.y };
59
- const selectableTarget = findParentByFeature(target, isSelectable);
60
- if (selectableTarget) {
61
- selectableTarget.selected = true;
62
- this.selectionService.updateSelection(target.root, [selectableTarget.id], []);
52
+ protected openContextMenu(target: SModelElement, event: MouseEvent): Promise<Action>[] {
53
+ if (!this.contextMenuService || !this.menuProvider) {
54
+ return [];
63
55
  }
64
56
 
57
+ const mousePosition = { x: event.x, y: event.y };
58
+
65
59
  const result = Promise.all([this.contextMenuService(), this.menuProvider.getItems(target.root, mousePosition)])
66
60
  .then(([menuService, menuItems]) => menuService.show(menuItems, mousePosition, () => this.focusEventTarget(event)))
67
61
  .then((): Action => FocusStateChangedAction.create(false));
@@ -23,20 +23,11 @@ import {
23
23
  SModelRootSchema
24
24
  } from '@eclipse-glsp/protocol';
25
25
  import { injectable } from 'inversify';
26
- import {
27
- EMPTY_ROOT,
28
- HoverFeedbackAction,
29
- HoverMouseListener,
30
- IActionHandler,
31
- ICommand,
32
- SIssueMarker,
33
- SIssueSeverity,
34
- SModelElement
35
- } from 'sprotty';
26
+ import { EMPTY_ROOT, HoverFeedbackAction, HoverMouseListener, IActionHandler, ICommand, SModelElement } from 'sprotty';
36
27
  import { FocusStateChangedAction } from '../../base/actions/focus-change-action';
37
28
  import { EnableDefaultToolsAction, EnableToolsAction } from '../../base/tool-manager/tool-actions';
38
29
  import { EdgeCreationTool } from '../tools/edge-creation-tool';
39
- import { GIssueMarker } from '../validation/issue-marker';
30
+ import { getSeverity, GIssueMarker } from '../validation/issue-marker';
40
31
  @injectable()
41
32
  export class GlspHoverMouseListener extends HoverMouseListener implements IActionHandler {
42
33
  protected enableHover = true;
@@ -117,16 +108,3 @@ export class GlspHoverMouseListener extends HoverMouseListener implements IActio
117
108
  return bounds;
118
109
  }
119
110
  }
120
-
121
- export function getSeverity(marker: SIssueMarker): SIssueSeverity {
122
- let currentSeverity: SIssueSeverity = 'info';
123
- for (const severity of marker.issues.map(s => s.severity)) {
124
- if (severity === 'error') {
125
- return severity;
126
- }
127
- if (severity === 'warning' && currentSeverity === 'info') {
128
- currentSeverity = severity;
129
- }
130
- }
131
- return currentSeverity;
132
- }
@@ -14,19 +14,27 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
16
  import { ContainerModule } from 'inversify';
17
- import { configureCommand, configureLayout, HBoxLayouter, VBoxLayouter } from 'sprotty';
17
+ import { configureActionHandler, configureLayout, HBoxLayouter, TYPES, VBoxLayouter } from 'sprotty';
18
18
  import { FreeFormLayouter } from './freeform-layout';
19
19
  import { HBoxLayouterExt } from './hbox-layout';
20
- import { AlignElementsCommand, ResizeElementsCommand } from './layout-commands';
20
+ import {
21
+ AlignElementsAction,
22
+ AlignElementsActionHandler,
23
+ ResizeElementsAction,
24
+ ResizeElementsActionHandler
25
+ } from './layout-elements-action';
26
+ import { LayouterExt } from './layouter';
21
27
  import { VBoxLayouterExt } from './vbox-layout';
22
28
 
23
- const layoutCommandsModule = new ContainerModule((bind, _unbind, isBound, rebind) => {
24
- configureCommand({ bind, isBound }, ResizeElementsCommand);
25
- configureCommand({ bind, isBound }, AlignElementsCommand);
29
+ const layoutModule = new ContainerModule((bind, _unbind, isBound, rebind) => {
30
+ const context = { bind, isBound };
31
+ configureActionHandler(context, ResizeElementsAction.KIND, ResizeElementsActionHandler);
32
+ configureActionHandler(context, AlignElementsAction.KIND, AlignElementsActionHandler);
26
33
 
27
34
  rebind(VBoxLayouter).to(VBoxLayouterExt);
28
35
  rebind(HBoxLayouter).to(HBoxLayouterExt);
36
+ rebind(TYPES.Layouter).to(LayouterExt);
29
37
  configureLayout({ bind, isBound }, FreeFormLayouter.KIND, FreeFormLayouter);
30
38
  });
31
39
 
32
- export default layoutCommandsModule;
40
+ export default layoutModule;
@@ -49,6 +49,9 @@ export class HBoxLayouterExt extends HBoxLayouter {
49
49
 
50
50
  const fixedSize = this.getFixedContainerBounds(container, options, layouter);
51
51
 
52
+ const currentWidth = boundsData.bounds ? boundsData.bounds?.width - options.paddingLeft - options.paddingRight : 0;
53
+ const currentHeight = boundsData.bounds ? boundsData.bounds?.height - options.paddingTop - options.paddingBottom : 0;
54
+
52
55
  const maxWidth =
53
56
  options.paddingFactor *
54
57
  (options.resizeContainer
@@ -60,8 +63,11 @@ export class HBoxLayouterExt extends HBoxLayouter {
60
63
  ? Math.max(fixedSize.height - options.paddingTop - options.paddingBottom, childrenSize.height)
61
64
  : Math.max(0, fixedSize.height - options.paddingTop - options.paddingBottom));
62
65
 
66
+ const width = Math.max(currentWidth, maxWidth);
67
+ const height = Math.max(currentHeight, maxHeight);
68
+
63
69
  // Remaining size that can be grabbed by children with the hGrab option
64
- const grabWidth: number = maxWidth - childrenSize.width;
70
+ const grabWidth = width - childrenSize.width;
65
71
  // Number of children that request hGrab
66
72
  // FIXME: This approach works fine when only 1 child uses HGrab, but may cause rounding issues
67
73
  // when the grabHeight can't be equally shared by all children.
@@ -69,8 +75,8 @@ export class HBoxLayouterExt extends HBoxLayouter {
69
75
  .map(child => this.getChildLayoutOptions(child, options))
70
76
  .filter(opt => opt.hGrab).length;
71
77
 
72
- if (maxWidth > 0 && maxHeight > 0) {
73
- const offset = this.layoutChildren(container, layouter, options, maxWidth, maxHeight, grabWidth, grabbingChildren);
78
+ if (width > 0 && height > 0) {
79
+ const offset = this.layoutChildren(container, layouter, options, width, height, grabWidth, grabbingChildren);
74
80
  boundsData.bounds = this.getFinalContainerBounds(container, offset, options, childrenSize.width, childrenSize.height);
75
81
  boundsData.boundsChanged = true;
76
82
  }
@@ -155,8 +161,15 @@ export class HBoxLayouterExt extends HBoxLayouter {
155
161
  grabWidth?: number,
156
162
  grabbingChildren?: number
157
163
  ): Point {
164
+ const vAlign = childOptions.vGrab ? 'top' : childOptions.vAlign;
165
+ const dy = this.getDy(vAlign, bounds, maxHeight);
158
166
  let offset = super.layoutChild(child, boundsData, bounds, childOptions, containerOptions, currentOffset, maxWidth, maxHeight);
159
- if (childOptions.hGrab) {
167
+ boundsData.bounds = {
168
+ ...boundsData.bounds!,
169
+ x: currentOffset.x,
170
+ y: currentOffset.y + dy
171
+ };
172
+ if (childOptions.vGrab) {
160
173
  boundsData.bounds = {
161
174
  x: boundsData.bounds!.x,
162
175
  y: boundsData.bounds!.y,
@@ -197,7 +210,7 @@ export class HBoxLayouterExt extends HBoxLayouter {
197
210
  }
198
211
 
199
212
  protected override getChildLayoutOptions(child: SChildElement, containerOptions: HBoxLayoutOptionsExt): HBoxLayoutOptionsExt {
200
- return super.getChildLayoutOptions(child, containerOptions) as HBoxLayoutOptionsExt;
213
+ return super.getChildLayoutOptions(child, this.filterContainerOptions(containerOptions)) as HBoxLayoutOptionsExt;
201
214
  }
202
215
 
203
216
  protected override getLayoutOptions(element: SModelElement): HBoxLayoutOptionsExt {
@@ -250,7 +263,11 @@ export class HBoxLayouterExt extends HBoxLayouter {
250
263
  };
251
264
  }
252
265
 
253
- protected override spread(a: HBoxLayoutOptionsExt, b: HBoxLayoutOptionsExt): HBoxLayoutOptionsExt {
254
- return { ...a, ...b };
266
+ protected filterContainerOptions(containerOptions: HBoxLayoutOptionsExt): HBoxLayoutOptionsExt {
267
+ // Reset object-specific layout options to default before merging,
268
+ // to make sure they won't be inherited (grab, prefSize)
269
+ // eslint-disable-next-line no-null/no-null
270
+ const localOptions = { vGrab: false, hGrab: false, prefHeight: null, prefWidth: null };
271
+ return { ...containerOptions, ...localOptions };
255
272
  }
256
273
  }