@eclipse-glsp/client 0.9.0-next.da0ffb79 → 0.9.0-next.e0116720

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 (32) hide show
  1. package/LICENSE +642 -277
  2. package/lib/base/types.d.ts +1 -0
  3. package/lib/base/types.d.ts.map +1 -1
  4. package/lib/base/types.js +2 -1
  5. package/lib/base/types.js.map +1 -1
  6. package/lib/features/layout/di.config.d.ts +0 -4
  7. package/lib/features/layout/di.config.d.ts.map +1 -1
  8. package/lib/features/layout/di.config.js +1 -37
  9. package/lib/features/layout/di.config.js.map +1 -1
  10. package/lib/features/tools/marquee-behavior.d.ts +42 -0
  11. package/lib/features/tools/marquee-behavior.d.ts.map +1 -0
  12. package/lib/features/tools/marquee-behavior.js +128 -0
  13. package/lib/features/tools/marquee-behavior.js.map +1 -0
  14. package/lib/features/tools/marquee-behavior.spec.d.ts +17 -0
  15. package/lib/features/tools/marquee-behavior.spec.d.ts.map +1 -0
  16. package/lib/features/tools/marquee-behavior.spec.js +165 -0
  17. package/lib/features/tools/marquee-behavior.spec.js.map +1 -0
  18. package/lib/features/tools/marquee-mouse-tool.d.ts +9 -14
  19. package/lib/features/tools/marquee-mouse-tool.d.ts.map +1 -1
  20. package/lib/features/tools/marquee-mouse-tool.js +30 -119
  21. package/lib/features/tools/marquee-mouse-tool.js.map +1 -1
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +1 -0
  25. package/lib/index.js.map +1 -1
  26. package/package.json +3 -3
  27. package/src/base/types.ts +2 -1
  28. package/src/features/layout/di.config.ts +3 -25
  29. package/src/features/tools/marquee-behavior.spec.ts +175 -0
  30. package/src/features/tools/marquee-behavior.ts +158 -0
  31. package/src/features/tools/marquee-mouse-tool.ts +46 -144
  32. package/src/index.ts +1 -0
@@ -0,0 +1,158 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2021 EclipseSource 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 { Bounds, Point } from '@eclipse-glsp/protocol';
18
+ import { PointToPointLine } from 'sprotty';
19
+ import { DrawMarqueeAction } from '../tool-feedback/marquee-tool-feedback';
20
+
21
+ export interface IMarqueeBehavior {
22
+ readonly entireElement: boolean;
23
+ readonly entireEdge: boolean;
24
+ }
25
+
26
+ export class MarqueeUtil {
27
+ protected startPoint: Point;
28
+ protected currentPoint: Point;
29
+ protected marqueeBehavior: IMarqueeBehavior;
30
+
31
+ constructor(marqueeBehavior?: IMarqueeBehavior) {
32
+ if (marqueeBehavior) {
33
+ this.marqueeBehavior = marqueeBehavior;
34
+ } else {
35
+ this.marqueeBehavior = { entireElement: false, entireEdge: false };
36
+ }
37
+ }
38
+
39
+ updateStartPoint(position: Point): void {
40
+ this.startPoint = position;
41
+ }
42
+
43
+ updateCurrentPoint(position: Point): void {
44
+ this.currentPoint = position;
45
+ }
46
+
47
+ drawMarqueeAction(): DrawMarqueeAction {
48
+ return new DrawMarqueeAction(this.startPoint, this.currentPoint);
49
+ }
50
+
51
+ isEdgePathMarked(path: string | null): boolean {
52
+ if (!path) {
53
+ return false;
54
+ }
55
+ const points = path
56
+ .split(/M|L/)
57
+ .filter(p => p)
58
+ .map(p => {
59
+ const coord = p.split(',');
60
+ return { x: parseInt(coord[0], 10), y: parseInt(coord[1], 10) };
61
+ });
62
+ return this.isEdgeMarked(points);
63
+ }
64
+
65
+ isEdgeMarked(points: Point[]): boolean {
66
+ return this.marqueeBehavior.entireEdge ? this.isEntireEdgeMarked(points) : this.isPartOfEdgeMarked(points);
67
+ }
68
+
69
+ isNodeMarked(elementBounds: Bounds): boolean {
70
+ const horizontallyIn =
71
+ this.startPoint.x < this.currentPoint.x
72
+ ? this.isElementBetweenXAxis(elementBounds, this.startPoint.x, this.currentPoint.x)
73
+ : this.isElementBetweenXAxis(elementBounds, this.currentPoint.x, this.startPoint.x);
74
+ const verticallyIn =
75
+ this.startPoint.y < this.currentPoint.y
76
+ ? this.isElementBetweenYAxis(elementBounds, this.startPoint.y, this.currentPoint.y)
77
+ : this.isElementBetweenYAxis(elementBounds, this.currentPoint.y, this.startPoint.y);
78
+ return horizontallyIn && verticallyIn;
79
+ }
80
+
81
+ private isEntireEdgeMarked(points: Point[]): boolean {
82
+ for (let i = 0; i < points.length; i++) {
83
+ if (!this.pointInRect(points[i])) {
84
+ return false;
85
+ }
86
+ }
87
+ return true;
88
+ }
89
+
90
+ private isPartOfEdgeMarked(points: Point[]): boolean {
91
+ for (let i = 0; i < points.length - 1; i++) {
92
+ if (this.isLineMarked(points[i], points[i + 1])) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+
99
+ private isLineMarked(point1: Point, point2: Point): boolean {
100
+ const line = new PointToPointLine(point1, point2);
101
+ return (
102
+ this.pointInRect(point1) ||
103
+ this.pointInRect(point2) ||
104
+ this.lineIntersect(line, this.startPoint, { x: this.startPoint.x, y: this.currentPoint.y }) ||
105
+ this.lineIntersect(line, this.startPoint, { x: this.currentPoint.x, y: this.startPoint.y }) ||
106
+ this.lineIntersect(line, { x: this.currentPoint.x, y: this.startPoint.y }, this.currentPoint) ||
107
+ this.lineIntersect(line, { x: this.startPoint.x, y: this.currentPoint.y }, this.currentPoint)
108
+ );
109
+ }
110
+
111
+ private lineIntersect(line: PointToPointLine, p1: Point, p2: Point): boolean {
112
+ return line.intersection(new PointToPointLine(p1, p2)) !== undefined;
113
+ }
114
+
115
+ private pointInRect(point: Point): boolean {
116
+ const boolX =
117
+ this.startPoint.x <= this.currentPoint.x
118
+ ? this.isBetween(point.x, this.startPoint.x, this.currentPoint.x)
119
+ : this.isBetween(point.x, this.currentPoint.x, this.startPoint.x);
120
+ const boolY =
121
+ this.startPoint.y <= this.currentPoint.y
122
+ ? this.isBetween(point.y, this.startPoint.y, this.currentPoint.y)
123
+ : this.isBetween(point.y, this.currentPoint.y, this.startPoint.y);
124
+ return boolX && boolY;
125
+ }
126
+
127
+ private isElementBetweenXAxis(elementBounds: Bounds, marqueeLeft: number, marqueeRight: number): boolean {
128
+ const leftEdge = this.isBetween(elementBounds.x, marqueeLeft, marqueeRight);
129
+ const rightEdge = this.isBetween(elementBounds.x + elementBounds.width, marqueeLeft, marqueeRight);
130
+ if (this.marqueeBehavior.entireElement) {
131
+ return leftEdge && rightEdge;
132
+ }
133
+ return (
134
+ leftEdge ||
135
+ rightEdge ||
136
+ this.isBetween(marqueeLeft, elementBounds.x, elementBounds.x + elementBounds.width) ||
137
+ this.isBetween(marqueeRight, elementBounds.x, elementBounds.x + elementBounds.width)
138
+ );
139
+ }
140
+
141
+ private isElementBetweenYAxis(elementBounds: Bounds, marqueeTop: number, marqueeBottom: number): boolean {
142
+ const topEdge = this.isBetween(elementBounds.y, marqueeTop, marqueeBottom);
143
+ const bottomEdge = this.isBetween(elementBounds.y + elementBounds.height, marqueeTop, marqueeBottom);
144
+ if (this.marqueeBehavior.entireElement) {
145
+ return topEdge && bottomEdge;
146
+ }
147
+ return (
148
+ topEdge ||
149
+ bottomEdge ||
150
+ this.isBetween(marqueeTop, elementBounds.y, elementBounds.y + elementBounds.height) ||
151
+ this.isBetween(marqueeBottom, elementBounds.y, elementBounds.y + elementBounds.height)
152
+ );
153
+ }
154
+
155
+ private isBetween(x: number, lower: number, upper: number): boolean {
156
+ return lower <= x && x <= upper;
157
+ }
158
+ }
@@ -13,21 +13,35 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { Action, Point, SelectAction } from '@eclipse-glsp/protocol';
17
- import { inject, injectable } from 'inversify';
18
- import { BoundsAware, EnableDefaultToolsAction, isSelectable, isSelected, KeyListener, SEdge, SModelElement, SNode, TYPES } from 'sprotty';
16
+ import { Action, SelectAction } from '@eclipse-glsp/protocol';
17
+ import { inject, injectable, optional } from 'inversify';
18
+ import {
19
+ BoundsAware,
20
+ EnableDefaultToolsAction,
21
+ isSelectable,
22
+ isSelected,
23
+ KeyListener,
24
+ SEdge,
25
+ SModelElement,
26
+ SModelRoot,
27
+ SNode,
28
+ TYPES
29
+ } from 'sprotty';
19
30
  import { DOMHelper } from 'sprotty/lib/base/views/dom-helper';
31
+ import { GLSP_TYPES } from '../../base/types';
20
32
  import { DragAwareMouseListener } from '../../base/drag-aware-mouse-listener';
21
33
  import { getAbsolutePosition, toAbsoluteBounds } from '../../utils/viewpoint-util';
22
34
  import { CursorCSS, cursorFeedbackAction } from '../tool-feedback/css-feedback';
23
- import { DrawMarqueeAction, RemoveMarqueeAction } from '../tool-feedback/marquee-tool-feedback';
24
- import { BaseGLSPTool } from '../tools/base-glsp-tool';
35
+ import { RemoveMarqueeAction } from '../tool-feedback/marquee-tool-feedback';
36
+ import { BaseGLSPTool } from './base-glsp-tool';
37
+ import { IMarqueeBehavior, MarqueeUtil } from './marquee-behavior';
25
38
 
26
39
  @injectable()
27
40
  export class MarqueeMouseTool extends BaseGLSPTool {
28
41
  static ID = 'glsp.marquee-mouse-tool';
29
42
 
30
43
  @inject(TYPES.DOMHelper) protected domHelper: DOMHelper;
44
+ @inject(GLSP_TYPES.IMarqueeBehavior) @optional() protected marqueeBehavior: IMarqueeBehavior;
31
45
 
32
46
  protected marqueeMouseListener: MarqueeMouseListener;
33
47
  protected shiftKeyListener: ShiftKeyListener = new ShiftKeyListener();
@@ -37,7 +51,7 @@ export class MarqueeMouseTool extends BaseGLSPTool {
37
51
  }
38
52
 
39
53
  enable(): void {
40
- this.marqueeMouseListener = new MarqueeMouseListener(this.domHelper);
54
+ this.marqueeMouseListener = new MarqueeMouseListener(this.domHelper, this.editorContext.modelRoot, this.marqueeBehavior);
41
55
  this.mouseTool.register(this.marqueeMouseListener);
42
56
  this.keyTool.register(this.shiftKeyListener);
43
57
  this.dispatchFeedback([cursorFeedbackAction(CursorCSS.MARQUEE)]);
@@ -50,26 +64,38 @@ export class MarqueeMouseTool extends BaseGLSPTool {
50
64
  }
51
65
  }
52
66
 
53
- @injectable()
54
67
  export class MarqueeMouseListener extends DragAwareMouseListener {
55
- protected startPoint: Point;
56
-
57
- protected currentPoint: Point;
58
-
59
68
  protected domHelper: DOMHelper;
60
-
69
+ protected marqueeUtil: MarqueeUtil;
70
+ protected nodes: (SModelElement & BoundsAware)[];
71
+ protected edges: SVGGElement[];
61
72
  protected previouslySelected: string[];
62
-
63
73
  protected isActive = false;
64
74
 
65
- constructor(domHelper: DOMHelper) {
75
+ constructor(domHelper: DOMHelper, root: SModelRoot, marqueeBehavior: IMarqueeBehavior | undefined) {
66
76
  super();
67
77
  this.domHelper = domHelper;
78
+ this.marqueeUtil = new MarqueeUtil(marqueeBehavior);
79
+ this.nodes = Array.from(
80
+ root.index
81
+ .all()
82
+ .map(e => e as SModelElement & BoundsAware)
83
+ .filter(e => isSelectable(e))
84
+ .filter(e => e instanceof SNode)
85
+ );
86
+ const sEdges = Array.from(
87
+ root.index
88
+ .all()
89
+ .filter(e => e instanceof SEdge)
90
+ .filter(e => isSelectable(e))
91
+ .map(e => e.id)
92
+ );
93
+ this.edges = Array.from(document.querySelectorAll('g')).filter(e => sEdges.includes(this.domHelper.findSModelIdByDOMElement(e)));
68
94
  }
69
95
 
70
96
  mouseDown(target: SModelElement, event: MouseEvent): Action[] {
71
97
  this.isActive = true;
72
- this.startPoint = { x: getAbsolutePosition(target, event).x, y: getAbsolutePosition(target, event).y };
98
+ this.marqueeUtil.updateStartPoint(getAbsolutePosition(target, event));
73
99
  if (event.ctrlKey) {
74
100
  this.previouslySelected = Array.from(
75
101
  target.root.index
@@ -83,23 +109,15 @@ export class MarqueeMouseListener extends DragAwareMouseListener {
83
109
  }
84
110
 
85
111
  mouseMove(target: SModelElement, event: MouseEvent): Action[] {
86
- this.currentPoint = { x: getAbsolutePosition(target, event).x, y: getAbsolutePosition(target, event).y };
112
+ this.marqueeUtil.updateCurrentPoint(getAbsolutePosition(target, event));
87
113
  if (this.isActive) {
88
- const nodeIdsSelected = Array.from(
89
- target.root.index
90
- .all()
91
- .map(e => e as SModelElement & BoundsAware)
92
- .filter(e => isSelectable(e))
93
- .filter(e => e instanceof SNode)
94
- .filter(e => this.isNodeMarked(e))
95
- .map(e => e.id)
96
- );
97
- const edgeIdsSelected = this.getMarkedEdges(target.root);
114
+ const nodeIdsSelected = this.nodes.filter(e => this.marqueeUtil.isNodeMarked(toAbsoluteBounds(e))).map(e => e.id);
115
+ const edgeIdsSelected = this.edges.filter(e => this.isEdgeMarked(e)).map(e => this.domHelper.findSModelIdByDOMElement(e));
98
116
  const selected = nodeIdsSelected.concat(edgeIdsSelected);
99
117
  return [
100
118
  new SelectAction([], Array.from(target.root.index.all().map(e => e.id))),
101
119
  new SelectAction(selected.concat(this.previouslySelected), []),
102
- new DrawMarqueeAction(this.startPoint, { x: getAbsolutePosition(target, event).x, y: getAbsolutePosition(target, event).y })
120
+ this.marqueeUtil.drawMarqueeAction()
103
121
  ];
104
122
  }
105
123
  return [];
@@ -113,131 +131,15 @@ export class MarqueeMouseListener extends DragAwareMouseListener {
113
131
  return [new RemoveMarqueeAction(), new EnableDefaultToolsAction()];
114
132
  }
115
133
 
116
- getMarkedEdges(root: SModelElement): string[] {
117
- const elements = Array.from(document.querySelectorAll('g'));
118
- const edges = Array.from(
119
- root.index
120
- .all()
121
- .filter(e => e instanceof SEdge)
122
- .filter(e => isSelectable(e))
123
- .map(e => e.id)
124
- );
125
- return elements
126
- .filter(e => edges.includes(this.domHelper.findSModelIdByDOMElement(e)))
127
- .filter(e => this.isEdgeMarked(e))
128
- .map(e => this.domHelper.findSModelIdByDOMElement(e));
129
- }
130
-
131
134
  isEdgeMarked(element: SVGElement): boolean {
132
135
  if (!element.getAttribute('transform')) {
133
136
  if (element.children[0]) {
134
137
  const path = element.children[0].getAttribute('d');
135
- if (path) {
136
- const points = path.split(/M|L/);
137
- for (let i = 0; i < points.length - 1; i++) {
138
- const coord1 = points[i].split(',');
139
- const coord2 = points[i + 1].split(',');
140
- const point1 = { x: parseInt(coord1[0], 10), y: parseInt(coord1[1], 10) };
141
- const point2 = { x: parseInt(coord2[0], 10), y: parseInt(coord2[1], 10) };
142
- if (this.isLineMarked(point1, point2)) {
143
- return true;
144
- }
145
- }
146
- }
138
+ return this.marqueeUtil.isEdgePathMarked(path);
147
139
  }
148
140
  }
149
141
  return false;
150
142
  }
151
-
152
- isLineMarked(point1: Point, point2: Point): boolean {
153
- if (this.pointInRect(point1) || this.pointInRect(point2)) {
154
- return true;
155
- }
156
- if (this.linesIntersect(point1, point2, this.startPoint, { x: this.startPoint.x, y: this.currentPoint.y })) {
157
- return true;
158
- }
159
- if (this.linesIntersect(point1, point2, this.startPoint, { x: this.currentPoint.x, y: this.startPoint.y })) {
160
- return true;
161
- }
162
- if (this.linesIntersect(point1, point2, { x: this.currentPoint.x, y: this.startPoint.y }, this.currentPoint)) {
163
- return true;
164
- }
165
- if (this.linesIntersect(point1, point2, { x: this.startPoint.x, y: this.currentPoint.y }, this.currentPoint)) {
166
- return true;
167
- }
168
- return false;
169
- }
170
-
171
- linesIntersect(p1: Point, p2: Point, p3: Point, p4: Point): boolean {
172
- const tCount = (p1.x - p3.x) * (p3.y - p4.y) - (p1.y - p3.y) * (p3.x - p4.x);
173
- const tDenom = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
174
- const t = tCount / tDenom;
175
- const uCount = (p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x);
176
- const uDenom = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
177
- const u = uCount / uDenom;
178
- if (t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0) {
179
- return true;
180
- }
181
- return false;
182
- }
183
-
184
- pointInRect(point: Point): boolean {
185
- const boolX =
186
- this.startPoint.x <= this.currentPoint.x
187
- ? this.isBetween(point.x, this.startPoint.x, this.currentPoint.x)
188
- : this.isBetween(point.x, this.currentPoint.x, this.startPoint.x);
189
- const boolY =
190
- this.startPoint.y <= this.currentPoint.y
191
- ? this.isBetween(point.y, this.startPoint.y, this.currentPoint.y)
192
- : this.isBetween(point.y, this.currentPoint.y, this.startPoint.y);
193
- return boolX && boolY;
194
- }
195
-
196
- isNodeMarked(element: SModelElement & BoundsAware): boolean {
197
- const horizontallyIn =
198
- this.startPoint.x < this.currentPoint.x
199
- ? this.isElementBetweenXAxis(element, this.startPoint.x, this.currentPoint.x)
200
- : this.isElementBetweenXAxis(element, this.currentPoint.x, this.startPoint.x);
201
- const verticallyIn =
202
- this.startPoint.y < this.currentPoint.y
203
- ? this.isElementBetweenYAxis(element, this.startPoint.y, this.currentPoint.y)
204
- : this.isElementBetweenYAxis(element, this.currentPoint.y, this.startPoint.y);
205
- if (horizontallyIn && verticallyIn) {
206
- return true;
207
- }
208
- return false;
209
- }
210
-
211
- isElementBetweenXAxis(element: SModelElement & BoundsAware, marqueeLeft: number, marqueeRight: number): boolean {
212
- if (
213
- this.isBetween(marqueeLeft, toAbsoluteBounds(element).x, toAbsoluteBounds(element).x + toAbsoluteBounds(element).width) ||
214
- this.isBetween(marqueeRight, toAbsoluteBounds(element).x, toAbsoluteBounds(element).x + toAbsoluteBounds(element).width)
215
- ) {
216
- return true;
217
- }
218
- const leftEdge = this.isBetween(toAbsoluteBounds(element).x, marqueeLeft, marqueeRight);
219
- const rightEdge = this.isBetween(toAbsoluteBounds(element).x + toAbsoluteBounds(element).width, marqueeLeft, marqueeRight);
220
- return leftEdge || rightEdge;
221
- }
222
-
223
- isElementBetweenYAxis(element: SModelElement & BoundsAware, marqueeTop: number, marqueeBottom: number): boolean {
224
- if (
225
- this.isBetween(marqueeTop, toAbsoluteBounds(element).y, toAbsoluteBounds(element).y + toAbsoluteBounds(element).height) ||
226
- this.isBetween(marqueeBottom, toAbsoluteBounds(element).y, toAbsoluteBounds(element).y + toAbsoluteBounds(element).height)
227
- ) {
228
- return true;
229
- }
230
- const topEdge = this.isBetween(toAbsoluteBounds(element).y, marqueeTop, marqueeBottom);
231
- const bottomEdge = this.isBetween(toAbsoluteBounds(element).y + toAbsoluteBounds(element).height, marqueeTop, marqueeBottom);
232
- return topEdge || bottomEdge;
233
- }
234
-
235
- isBetween(x: number, lower: number, upper: number): boolean {
236
- if (lower <= x && x <= upper) {
237
- return true;
238
- }
239
- return false;
240
- }
241
143
  }
242
144
 
243
145
  @injectable()
package/src/index.ts CHANGED
@@ -88,6 +88,7 @@ export * from './features/tools/delete-tool';
88
88
  export * from './features/tools/edge-creation-tool';
89
89
  export * from './features/tools/edge-edit-tool';
90
90
  export * from './features/tools/node-creation-tool';
91
+ export * from './features/tools/marquee-behavior';
91
92
  export * from './features/validation/issue-marker';
92
93
  export * from './features/validation/marker-navigator';
93
94
  export * from './features/validation/validate';