js-draw 0.24.0 → 0.25.0

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 (75) hide show
  1. package/README.md +217 -0
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundledStyles.js +1 -1
  4. package/dist/cjs/localizations/de.js +1 -1
  5. package/dist/cjs/localizations/es.js +1 -1
  6. package/dist/cjs/rendering/renderers/CanvasRenderer.js +2 -0
  7. package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +1 -1
  8. package/dist/cjs/rendering/renderers/SVGRenderer.js +4 -2
  9. package/dist/cjs/shortcuts/KeyBinding.js +1 -1
  10. package/dist/cjs/testing/getUniquePointerId.d.ts +4 -0
  11. package/dist/cjs/testing/getUniquePointerId.js +16 -0
  12. package/dist/cjs/testing/sendPenEvent.d.ts +1 -1
  13. package/dist/cjs/testing/sendPenEvent.js +4 -1
  14. package/dist/cjs/testing/sendTouchEvent.js +2 -9
  15. package/dist/cjs/toolbar/IconProvider.d.ts +1 -1
  16. package/dist/cjs/toolbar/IconProvider.js +76 -10
  17. package/dist/cjs/toolbar/localization.d.ts +2 -2
  18. package/dist/cjs/toolbar/localization.js +2 -2
  19. package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +2 -0
  20. package/dist/cjs/toolbar/widgets/BaseToolWidget.js +7 -0
  21. package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +3 -1
  22. package/dist/cjs/toolbar/widgets/PenToolWidget.js +125 -41
  23. package/dist/cjs/toolbar/widgets/SelectionToolWidget.js +4 -0
  24. package/dist/cjs/tools/BaseTool.d.ts +17 -1
  25. package/dist/cjs/tools/BaseTool.js +18 -0
  26. package/dist/cjs/tools/Pen.d.ts +5 -2
  27. package/dist/cjs/tools/Pen.js +37 -4
  28. package/dist/cjs/tools/ToolController.js +14 -2
  29. package/dist/mjs/localizations/de.mjs +1 -1
  30. package/dist/mjs/localizations/es.mjs +1 -1
  31. package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +2 -0
  32. package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +1 -1
  33. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +4 -2
  34. package/dist/mjs/shortcuts/KeyBinding.mjs +1 -1
  35. package/dist/mjs/testing/getUniquePointerId.d.ts +4 -0
  36. package/dist/mjs/testing/getUniquePointerId.mjs +14 -0
  37. package/dist/mjs/testing/sendPenEvent.d.ts +1 -1
  38. package/dist/mjs/testing/sendPenEvent.mjs +4 -1
  39. package/dist/mjs/testing/sendTouchEvent.mjs +2 -9
  40. package/dist/mjs/toolbar/IconProvider.d.ts +1 -1
  41. package/dist/mjs/toolbar/IconProvider.mjs +76 -10
  42. package/dist/mjs/toolbar/localization.d.ts +2 -2
  43. package/dist/mjs/toolbar/localization.mjs +2 -2
  44. package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +2 -0
  45. package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +7 -0
  46. package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +3 -1
  47. package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +125 -41
  48. package/dist/mjs/toolbar/widgets/SelectionToolWidget.mjs +4 -0
  49. package/dist/mjs/tools/BaseTool.d.ts +17 -1
  50. package/dist/mjs/tools/BaseTool.mjs +18 -0
  51. package/dist/mjs/tools/Pen.d.ts +5 -2
  52. package/dist/mjs/tools/Pen.mjs +37 -4
  53. package/dist/mjs/tools/ToolController.mjs +14 -2
  54. package/package.json +4 -3
  55. package/src/localizations/de.ts +2 -2
  56. package/src/localizations/es.ts +1 -1
  57. package/src/rendering/renderers/CanvasRenderer.ts +2 -0
  58. package/src/rendering/renderers/SVGRenderer.ts +6 -3
  59. package/src/shortcuts/KeyBinding.ts +1 -1
  60. package/src/testing/getUniquePointerId.ts +18 -0
  61. package/src/testing/sendPenEvent.ts +6 -1
  62. package/src/testing/sendTouchEvent.ts +2 -9
  63. package/src/toolbar/IconProvider.ts +92 -23
  64. package/src/toolbar/localization.ts +4 -4
  65. package/src/toolbar/toolbar.css +1 -0
  66. package/src/toolbar/widgets/BaseToolWidget.ts +10 -1
  67. package/src/toolbar/widgets/PenToolWidget.css +53 -0
  68. package/src/toolbar/widgets/PenToolWidget.ts +156 -44
  69. package/src/toolbar/widgets/SelectionToolWidget.ts +4 -0
  70. package/src/tools/BaseTool.ts +22 -1
  71. package/src/tools/PanZoom.test.ts +39 -0
  72. package/src/tools/Pen.test.ts +68 -0
  73. package/src/tools/Pen.ts +42 -4
  74. package/src/tools/ToolController.ts +17 -2
  75. package/tools/copyREADME.ts +62 -0
@@ -47,12 +47,12 @@ var PenToolWidget = /** @class */ (function (_super) {
47
47
  // Default pen types
48
48
  _this.penTypes = [
49
49
  {
50
- name: _this.localizationTable.pressureSensitiveFreehandPen,
50
+ name: _this.localizationTable.flatTipPen,
51
51
  id: 'pressure-sensitive-pen',
52
52
  factory: PressureSensitiveFreehandLineBuilder_1.makePressureSensitiveFreehandLineBuilder,
53
53
  },
54
54
  {
55
- name: _this.localizationTable.freehandPen,
55
+ name: _this.localizationTable.roundedTipPen,
56
56
  id: 'freehand-pen',
57
57
  factory: FreehandLineBuilder_1.makeFreehandLineBuilder,
58
58
  },
@@ -119,38 +119,140 @@ var PenToolWidget = /** @class */ (function (_super) {
119
119
  }
120
120
  return null;
121
121
  };
122
- PenToolWidget.prototype.createIcon = function () {
123
- var strokeFactory = this.tool.getStrokeFactory();
124
- if (strokeFactory === FreehandLineBuilder_1.makeFreehandLineBuilder || strokeFactory === PressureSensitiveFreehandLineBuilder_1.makePressureSensitiveFreehandLineBuilder) {
122
+ PenToolWidget.prototype.createIconForRecord = function (record) {
123
+ var color = this.tool.getColor();
124
+ var strokeFactory = record === null || record === void 0 ? void 0 : record.factory;
125
+ if (!strokeFactory || strokeFactory === FreehandLineBuilder_1.makeFreehandLineBuilder || strokeFactory === PressureSensitiveFreehandLineBuilder_1.makePressureSensitiveFreehandLineBuilder) {
125
126
  // Use a square-root scale to prevent the pen's tip from overflowing.
126
127
  var scale = Math.round(Math.sqrt(this.tool.getThickness()) * 4);
127
- var color = this.tool.getColor();
128
128
  var roundedTip = strokeFactory === FreehandLineBuilder_1.makeFreehandLineBuilder;
129
129
  return this.editor.icons.makePenIcon(scale, color.toHexString(), roundedTip);
130
130
  }
131
131
  else {
132
- var strokeFactory_1 = this.tool.getStrokeFactory();
133
- return this.editor.icons.makeIconFromFactory(this.tool, strokeFactory_1);
132
+ var hasTransparency = color.a < 1;
133
+ return this.editor.icons.makeIconFromFactory(this.tool, strokeFactory, hasTransparency);
134
134
  }
135
135
  };
136
+ PenToolWidget.prototype.createIcon = function () {
137
+ return this.createIconForRecord(this.getCurrentPenType());
138
+ };
139
+ // Creates a widget that allows selecting different pen types
140
+ PenToolWidget.prototype.createPenTypeSelector = function () {
141
+ var _this = this;
142
+ var outerContainer = document.createElement('div');
143
+ outerContainer.classList.add("".concat(HTMLToolbar_1.toolbarCSSPrefix, "pen-type-selector"));
144
+ var scrollingContainer = document.createElement('div');
145
+ scrollingContainer.setAttribute('role', 'menu');
146
+ scrollingContainer.id = "".concat(HTMLToolbar_1.toolbarCSSPrefix, "-pen-type-selector-id-").concat(PenToolWidget.idCounter++);
147
+ scrollingContainer.onwheel = function (event) {
148
+ var hasScroll = scrollingContainer.clientWidth !== scrollingContainer.scrollWidth
149
+ && event.deltaX !== 0;
150
+ var eventScrollsPastLeft = scrollingContainer.scrollLeft + event.deltaX <= 0;
151
+ var scrollRight = scrollingContainer.scrollLeft + scrollingContainer.clientWidth;
152
+ var eventScrollsPastRight = scrollRight + event.deltaX > scrollingContainer.scrollWidth;
153
+ // Stop the editor from receiving the event if it will scroll the pen type selector
154
+ // instead.
155
+ if (hasScroll && !eventScrollsPastLeft && !eventScrollsPastRight) {
156
+ event.stopPropagation();
157
+ }
158
+ };
159
+ var label = document.createElement('label');
160
+ label.innerText = this.localizationTable.selectPenType;
161
+ label.htmlFor = scrollingContainer.id;
162
+ outerContainer.appendChild(label);
163
+ // All buttons in a radiogroup need the same name attribute.
164
+ var radiogroupName = "".concat(HTMLToolbar_1.toolbarCSSPrefix, "-pen-type-selector-").concat(PenToolWidget.idCounter++);
165
+ var createTypeSelectorButton = function (record) {
166
+ var buttonContainer = document.createElement('div');
167
+ buttonContainer.classList.add('pen-type-button');
168
+ var button = document.createElement('input');
169
+ button.type = 'radio';
170
+ button.name = radiogroupName;
171
+ button.id = "".concat(HTMLToolbar_1.toolbarCSSPrefix, "-pen-type-button-").concat(PenToolWidget.idCounter++);
172
+ var labelContainer = document.createElement('label');
173
+ var rebuildLabel = function () {
174
+ var labelText = document.createElement('span');
175
+ var icon = _this.createIconForRecord(record);
176
+ icon.classList.add('icon');
177
+ // The title of the record
178
+ labelText.innerText = record.name;
179
+ labelContainer.htmlFor = button.id;
180
+ labelContainer.replaceChildren(icon, labelText);
181
+ };
182
+ rebuildLabel();
183
+ var updateButtonCSS = function () {
184
+ if (button.checked) {
185
+ buttonContainer.classList.add('checked');
186
+ }
187
+ else {
188
+ buttonContainer.classList.remove('checked');
189
+ }
190
+ };
191
+ button.oninput = function () {
192
+ // Setting the stroke factory fires an event that causes the value
193
+ // of this button to be set.
194
+ if (button.checked) {
195
+ _this.tool.setStrokeFactory(record.factory);
196
+ }
197
+ updateButtonCSS();
198
+ };
199
+ buttonContainer.replaceChildren(button, labelContainer);
200
+ scrollingContainer.appendChild(buttonContainer);
201
+ // Set whether the button is checked, assuming the stroke factory associated
202
+ // with the button was set elsewhere.
203
+ var setChecked = function (checked) {
204
+ button.checked = checked;
205
+ updateButtonCSS();
206
+ if (checked) {
207
+ button.scrollIntoView();
208
+ }
209
+ };
210
+ setChecked(false);
211
+ // Updates the factory's icon based on the current style of the tool.
212
+ var updateIcon = function () {
213
+ rebuildLabel();
214
+ };
215
+ return { setChecked: setChecked, updateIcon: updateIcon };
216
+ };
217
+ var buttons = [];
218
+ for (var _i = 0, _a = this.penTypes; _i < _a.length; _i++) {
219
+ var penType = _a[_i];
220
+ buttons.push(createTypeSelectorButton(penType));
221
+ }
222
+ // invariant: buttons.length = this.penTypes.length
223
+ outerContainer.appendChild(scrollingContainer);
224
+ return {
225
+ setValue: function (penTypeIndex) {
226
+ // Select the value specified
227
+ if (penTypeIndex < 0 || penTypeIndex >= _this.penTypes.length) {
228
+ console.error('Invalid pen type index', penTypeIndex);
229
+ return;
230
+ }
231
+ for (var i = 0; i < buttons.length; i++) {
232
+ buttons[i].setChecked(i === penTypeIndex);
233
+ }
234
+ },
235
+ updateIcons: function () {
236
+ buttons.forEach(function (button) { return button.updateIcon(); });
237
+ },
238
+ addTo: function (parent) {
239
+ parent.appendChild(outerContainer);
240
+ },
241
+ };
242
+ };
136
243
  PenToolWidget.prototype.fillDropdown = function (dropdown) {
137
244
  var _this = this;
138
245
  var container = document.createElement('div');
139
246
  container.classList.add("".concat(HTMLToolbar_1.toolbarCSSPrefix, "spacedList"));
140
247
  var thicknessRow = document.createElement('div');
141
- var objectTypeRow = document.createElement('div');
142
248
  // Thickness: Value of the input is squared to allow for finer control/larger values.
143
249
  var thicknessLabel = document.createElement('label');
144
250
  var thicknessInput = document.createElement('input');
145
- var objectSelectLabel = document.createElement('label');
146
- var objectTypeSelect = document.createElement('select');
251
+ var penTypeSelect = this.createPenTypeSelector();
147
252
  // Give inputs IDs so we can label them with a <label for=...>Label text</label>
148
253
  thicknessInput.id = "".concat(HTMLToolbar_1.toolbarCSSPrefix, "penThicknessInput").concat(PenToolWidget.idCounter++);
149
- objectTypeSelect.id = "".concat(HTMLToolbar_1.toolbarCSSPrefix, "penBuilderSelect").concat(PenToolWidget.idCounter++);
150
254
  thicknessLabel.innerText = this.localizationTable.thicknessLabel;
151
255
  thicknessLabel.setAttribute('for', thicknessInput.id);
152
- objectSelectLabel.innerText = this.localizationTable.selectPenType;
153
- objectSelectLabel.setAttribute('for', objectTypeSelect.id);
154
256
  // Use a logarithmic scale for thicknessInput (finer control over thinner strokewidths.)
155
257
  var inverseThicknessInputFn = function (t) { return Math.log10(t); };
156
258
  var thicknessInputFn = function (t) { return Math.pow(10, t); };
@@ -163,16 +265,6 @@ var PenToolWidget = /** @class */ (function (_super) {
163
265
  };
164
266
  thicknessRow.appendChild(thicknessLabel);
165
267
  thicknessRow.appendChild(thicknessInput);
166
- objectTypeSelect.oninput = function () {
167
- var penTypeIdx = parseInt(objectTypeSelect.value);
168
- if (penTypeIdx < 0 || penTypeIdx >= _this.penTypes.length) {
169
- console.error('Invalid pen type index', penTypeIdx);
170
- return;
171
- }
172
- _this.tool.setStrokeFactory(_this.penTypes[penTypeIdx].factory);
173
- };
174
- objectTypeRow.appendChild(objectSelectLabel);
175
- objectTypeRow.appendChild(objectTypeSelect);
176
268
  var colorRow = document.createElement('div');
177
269
  var colorLabel = document.createElement('label');
178
270
  var _a = (0, makeColorInput_1.default)(this.editor, function (color) {
@@ -186,26 +278,13 @@ var PenToolWidget = /** @class */ (function (_super) {
186
278
  this.updateInputs = function () {
187
279
  setColorInputValue(_this.tool.getColor());
188
280
  thicknessInput.value = inverseThicknessInputFn(_this.tool.getThickness()).toString();
189
- // Update the list of stroke factories
190
- objectTypeSelect.replaceChildren();
191
- for (var i = 0; i < _this.penTypes.length; i++) {
192
- var penType = _this.penTypes[i];
193
- var option = document.createElement('option');
194
- option.value = i.toString();
195
- option.innerText = penType.name;
196
- objectTypeSelect.appendChild(option);
197
- }
281
+ penTypeSelect.updateIcons();
198
282
  // Update the selected stroke factory.
199
- var strokeFactoryIdx = _this.getCurrentPenTypeIdx();
200
- if (strokeFactoryIdx === -1) {
201
- objectTypeSelect.value = '';
202
- }
203
- else {
204
- objectTypeSelect.value = strokeFactoryIdx.toString();
205
- }
283
+ penTypeSelect.setValue(_this.getCurrentPenTypeIdx());
206
284
  };
207
285
  this.updateInputs();
208
- container.replaceChildren(colorRow, thicknessRow, objectTypeRow);
286
+ container.replaceChildren(colorRow, thicknessRow);
287
+ penTypeSelect.addTo(container);
209
288
  dropdown.replaceChildren(container);
210
289
  return true;
211
290
  };
@@ -223,6 +302,10 @@ var PenToolWidget = /** @class */ (function (_super) {
223
302
  }
224
303
  }
225
304
  }
305
+ // Run any default actions registered by the parent class.
306
+ if (_super.prototype.onKeyPress.call(this, event)) {
307
+ return true;
308
+ }
226
309
  return false;
227
310
  };
228
311
  PenToolWidget.prototype.serializeState = function () {
@@ -258,6 +341,7 @@ var PenToolWidget = /** @class */ (function (_super) {
258
341
  }
259
342
  }
260
343
  };
344
+ // A counter variable that ensures different HTML elements are given unique names/ids.
261
345
  PenToolWidget.idCounter = 0;
262
346
  return PenToolWidget;
263
347
  }(BaseToolWidget_1.default));
@@ -200,6 +200,10 @@ var SelectionToolWidget = /** @class */ (function (_super) {
200
200
  this.resizeImageToSelection();
201
201
  return true;
202
202
  }
203
+ // If we didn't handle the event, allow the superclass to handle it.
204
+ if (_super.prototype.onKeyPress.call(this, event)) {
205
+ return true;
206
+ }
203
207
  return false;
204
208
  };
205
209
  SelectionToolWidget.prototype.getTitle = function () {
@@ -5,9 +5,19 @@ export default abstract class BaseTool implements PointerEvtListener {
5
5
  readonly description: string;
6
6
  private enabled;
7
7
  private group;
8
+ /**
9
+ * Returns true iff the tool handled the event and thus should receive additional
10
+ * events.
11
+ */
8
12
  onPointerDown(_event: PointerEvt): boolean;
9
13
  onPointerMove(_event: PointerEvt): void;
10
- onPointerUp(_event: PointerEvt): void;
14
+ /**
15
+ * Returns true iff there are additional pointers down and the tool should
16
+ * remain active to handle the additional events.
17
+ *
18
+ * For most purposes, this should return `false` or nothing.
19
+ */
20
+ onPointerUp(_event: PointerEvt): boolean | void;
11
21
  onGestureCancel(): void;
12
22
  protected constructor(notifier: EditorNotifier, description: string);
13
23
  onWheel(_event: WheelEvt): boolean;
@@ -15,6 +25,12 @@ export default abstract class BaseTool implements PointerEvtListener {
15
25
  onPaste(_event: PasteEvent): boolean;
16
26
  onKeyPress(_event: KeyPressEvent): boolean;
17
27
  onKeyUp(_event: KeyUpEvent): boolean;
28
+ /**
29
+ * Return true if, while this tool is active, `_event` can be delivered to
30
+ * another tool that is higher priority than this.
31
+ * @internal May be renamed
32
+ */
33
+ eventCanBeDeliveredToNonActiveTool(_event: PointerEvt): boolean;
18
34
  setEnabled(enabled: boolean): void;
19
35
  isEnabled(): boolean;
20
36
  setToolGroup(group: ToolEnabledGroup): void;
@@ -8,8 +8,18 @@ var BaseTool = /** @class */ (function () {
8
8
  this.enabled = true;
9
9
  this.group = null;
10
10
  }
11
+ /**
12
+ * Returns true iff the tool handled the event and thus should receive additional
13
+ * events.
14
+ */
11
15
  BaseTool.prototype.onPointerDown = function (_event) { return false; };
12
16
  BaseTool.prototype.onPointerMove = function (_event) { };
17
+ /**
18
+ * Returns true iff there are additional pointers down and the tool should
19
+ * remain active to handle the additional events.
20
+ *
21
+ * For most purposes, this should return `false` or nothing.
22
+ */
13
23
  BaseTool.prototype.onPointerUp = function (_event) { };
14
24
  BaseTool.prototype.onGestureCancel = function () { };
15
25
  BaseTool.prototype.onWheel = function (_event) {
@@ -27,6 +37,14 @@ var BaseTool = /** @class */ (function () {
27
37
  BaseTool.prototype.onKeyUp = function (_event) {
28
38
  return false;
29
39
  };
40
+ /**
41
+ * Return true if, while this tool is active, `_event` can be delivered to
42
+ * another tool that is higher priority than this.
43
+ * @internal May be renamed
44
+ */
45
+ BaseTool.prototype.eventCanBeDeliveredToNonActiveTool = function (_event) {
46
+ return true;
47
+ };
30
48
  BaseTool.prototype.setEnabled = function (enabled) {
31
49
  var _a;
32
50
  this.enabled = enabled;
@@ -15,6 +15,7 @@ export default class Pen extends BaseTool {
15
15
  protected builder: ComponentBuilder | null;
16
16
  private lastPoint;
17
17
  private startPoint;
18
+ private currentDeviceType;
18
19
  private snapToGridEnabled;
19
20
  private angleLockEnabled;
20
21
  constructor(editor: Editor, description: string, style: PenStyle, builderFactory?: ComponentBuilderFactory);
@@ -23,9 +24,11 @@ export default class Pen extends BaseTool {
23
24
  protected toStrokePoint(pointer: Pointer): StrokeDataPoint;
24
25
  protected previewStroke(): void;
25
26
  protected addPointToStroke(point: StrokeDataPoint): void;
26
- onPointerDown({ current, allPointers }: PointerEvt): boolean;
27
+ onPointerDown(event: PointerEvt): boolean;
28
+ private eventCanCancelStroke;
29
+ eventCanBeDeliveredToNonActiveTool(event: PointerEvt): boolean;
27
30
  onPointerMove({ current }: PointerEvt): void;
28
- onPointerUp({ current }: PointerEvt): void;
31
+ onPointerUp({ current }: PointerEvt): boolean;
29
32
  onGestureCancel(): void;
30
33
  private finalizeStroke;
31
34
  private noteUpdated;
@@ -44,6 +44,7 @@ var Pen = /** @class */ (function (_super) {
44
44
  _this.builder = null;
45
45
  _this.lastPoint = null;
46
46
  _this.startPoint = null;
47
+ _this.currentDeviceType = null;
47
48
  _this.snapToGridEnabled = false;
48
49
  _this.angleLockEnabled = false;
49
50
  return _this;
@@ -100,8 +101,8 @@ var Pen = /** @class */ (function (_super) {
100
101
  this.lastPoint = point;
101
102
  this.previewStroke();
102
103
  };
103
- Pen.prototype.onPointerDown = function (_a) {
104
- var current = _a.current, allPointers = _a.allPointers;
104
+ Pen.prototype.onPointerDown = function (event) {
105
+ var current = event.current, allPointers = event.allPointers;
105
106
  var isEraser = current.device === Pointer_1.PointerDevice.Eraser;
106
107
  var anyDeviceIsStylus = false;
107
108
  for (var _i = 0, allPointers_1 = allPointers; _i < allPointers_1.length; _i++) {
@@ -111,24 +112,54 @@ var Pen = /** @class */ (function (_super) {
111
112
  break;
112
113
  }
113
114
  }
115
+ // Avoid canceling an existing stroke
116
+ if (this.builder && !this.eventCanCancelStroke(event)) {
117
+ return true;
118
+ }
114
119
  if ((allPointers.length === 1 && !isEraser) || anyDeviceIsStylus) {
115
120
  this.startPoint = this.toStrokePoint(current);
116
121
  this.builder = this.builderFactory(this.startPoint, this.editor.viewport);
122
+ this.currentDeviceType = current.device;
117
123
  return true;
118
124
  }
119
125
  return false;
120
126
  };
127
+ Pen.prototype.eventCanCancelStroke = function (event) {
128
+ var _a, _b;
129
+ // If there has been a delay since the last input event,
130
+ // it's always okay to cancel
131
+ var lastInputTime = (_b = (_a = this.lastPoint) === null || _a === void 0 ? void 0 : _a.time) !== null && _b !== void 0 ? _b : 0;
132
+ if (event.current.timeStamp - lastInputTime > 1000) {
133
+ return true;
134
+ }
135
+ var isPenStroke = this.currentDeviceType === Pointer_1.PointerDevice.Pen;
136
+ var isTouchEvent = event.current.device === Pointer_1.PointerDevice.Touch;
137
+ // Don't allow pen strokes to be cancelled by touch events.
138
+ if (isPenStroke && isTouchEvent) {
139
+ return false;
140
+ }
141
+ return true;
142
+ };
143
+ Pen.prototype.eventCanBeDeliveredToNonActiveTool = function (event) {
144
+ return this.eventCanCancelStroke(event);
145
+ };
121
146
  Pen.prototype.onPointerMove = function (_a) {
122
147
  var current = _a.current;
123
148
  if (!this.builder)
124
149
  return;
150
+ if (current.device !== this.currentDeviceType)
151
+ return;
125
152
  this.addPointToStroke(this.toStrokePoint(current));
126
153
  };
127
154
  Pen.prototype.onPointerUp = function (_a) {
128
155
  var _b, _c;
129
156
  var current = _a.current;
130
- if (!this.builder) {
131
- return;
157
+ if (!this.builder)
158
+ return false;
159
+ if (current.device !== this.currentDeviceType) {
160
+ // this.builder still exists, so we're handling events from another
161
+ // device type.
162
+ return true;
132
163
  }
133
164
  // onPointerUp events can have zero pressure. Use the last pressure instead.
134
165
  var currentPoint = this.toStrokePoint(current);
@@ -137,8 +168,10 @@ var Pen = /** @class */ (function (_super) {
137
168
  if (current.isPrimary) {
138
169
  this.finalizeStroke();
139
170
  }
171
+ return false;
140
172
  };
141
173
  Pen.prototype.onGestureCancel = function () {
174
+ this.builder = null;
142
175
  this.editor.clearWetInk();
143
176
  };
144
177
  Pen.prototype.finalizeStroke = function () {
@@ -108,8 +108,15 @@ var ToolController = /** @class */ (function () {
108
108
  var _a, _b;
109
109
  var handled = false;
110
110
  if (event.kind === types_1.InputEvtType.PointerDownEvt) {
111
+ var canOnlySendToActiveTool = false;
112
+ if (this.activeTool && !this.activeTool.eventCanBeDeliveredToNonActiveTool(event)) {
113
+ canOnlySendToActiveTool = true;
114
+ }
111
115
  for (var _i = 0, _c = this.tools; _i < _c.length; _i++) {
112
116
  var tool = _c[_i];
117
+ if (canOnlySendToActiveTool && tool !== this.activeTool) {
118
+ continue;
119
+ }
113
120
  if (tool.isEnabled() && tool.onPointerDown(event)) {
114
121
  if (this.activeTool !== tool) {
115
122
  (_a = this.activeTool) === null || _a === void 0 ? void 0 : _a.onGestureCancel();
@@ -121,8 +128,13 @@ var ToolController = /** @class */ (function () {
121
128
  }
122
129
  }
123
130
  else if (event.kind === types_1.InputEvtType.PointerUpEvt) {
124
- (_b = this.activeTool) === null || _b === void 0 ? void 0 : _b.onPointerUp(event);
125
- this.activeTool = null;
131
+ var upResult = (_b = this.activeTool) === null || _b === void 0 ? void 0 : _b.onPointerUp(event);
132
+ var continueHandlingEvents = upResult && event.allPointers.length > 1;
133
+ // Should the active tool continue handling events (without an additional pointer down?)
134
+ if (!continueHandlingEvents) {
135
+ // No -- Remove the current tool
136
+ this.activeTool = null;
137
+ }
126
138
  handled = true;
127
139
  }
128
140
  else if (event.kind === types_1.InputEvtType.PointerMoveEvt) {
@@ -11,5 +11,5 @@ var __assign = (this && this.__assign) || function () {
11
11
  };
12
12
  import { defaultEditorLocalization } from '../localization.mjs';
13
13
  // German localization
14
- var localization = __assign(__assign({}, defaultEditorLocalization), { pen: 'Stift', eraser: 'Radierer', select: 'Auswahl', handTool: 'Verschieben', zoom: 'Vergrößerung', image: 'Bild', inputAltText: 'Alt-Text: ', chooseFile: 'Wähle Datei: ', submit: 'Absenden', cancel: 'Abbrechen', resetView: 'Ansicht zurücksetzen', thicknessLabel: 'Dicke: ', colorLabel: 'Farbe: ', fontLabel: 'Schriftart: ', textSize: 'Größe: ', resizeImageToSelection: 'Bildgröße an Auswahl anpassen', deleteSelection: 'Auswahl löschen', duplicateSelection: 'Auswahl duplizieren', undo: 'Rückgängig', redo: 'Wiederholen', pickColorFromScreen: 'Farbe von Bildschirm auswählen', clickToPickColorAnnouncement: 'Klicke auf den Bildschirm, um eine Farbe auszuwählen', selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.', touchPanning: 'Ansicht mit Touchscreen verschieben', anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben', selectPenType: 'Objekt-Typ: ', freehandPen: 'Freihand', pressureSensitiveFreehandPen: 'Stift (druckempfindlich)', arrowPen: 'Pfeil', linePen: 'Linie', outlinedRectanglePen: 'Umrissenes Rechteck', filledRectanglePen: 'Ausgefülltes Rechteck', lockRotation: 'Sperre Rotation', paste: 'Einfügen', dropdownShown: function (toolName) { return "Dropdown-Men\u00FC f\u00FCr ".concat(toolName, " angezeigt"); }, dropdownHidden: function (toolName) { return "Dropdown-Men\u00FC f\u00FCr ".concat(toolName, " versteckt"); }, zoomLevel: function (zoomPercent) { return "Verg\u00F6\u00DFerung: ".concat(zoomPercent, "%"); }, colorChangedAnnouncement: function (color) { return "Farbe zu ".concat(color, " ge\u00E4ndert"); }, imageSize: function (size, units) { return "Bild-Gr\u00F6\u00DFe: ".concat(size, " ").concat(units); }, imageLoadError: function (message) { return "Fehler beim Laden des Bildes: ".concat(message); }, errorImageHasZeroSize: 'Fehler: Bild hat Größe Null', penTool: function (penNumber) { return "Stift ".concat(penNumber); }, selectionTool: 'Auswahl', eraserTool: 'Radiergummi', touchPanTool: 'Ansicht mit Touchscreen verschieben', twoFingerPanZoomTool: 'Ansicht verschieben und vergrößern', undoRedoTool: 'Rückgängig/Wiederholen', rightClickDragPanTool: 'Rechtsklick-Ziehen', pipetteTool: 'Farbe von Bildschirm auswählen', keyboardPanZoom: 'Tastaturkürzel zum Verschieben/Vergrößern der Ansicht', textTool: 'Text', enterTextToInsert: 'Einzufügender Text', changeTool: 'Wechsle Werkzeug', pasteHandler: 'Copy-Paste-Handler', findLabel: 'Finde', toNextMatch: 'Nächstes', closeFindDialog: 'Schließen', findDialogShown: 'Finde-Dialog angezeigt', findDialogHidden: 'Finde-Dialog versteckt', focusedFoundText: function (matchIdx, totalMatches) { return "Sieh Treffer ".concat(matchIdx, " von ").concat(totalMatches, " an"); }, toolEnabledAnnouncement: function (toolName) { return "".concat(toolName, " aktiviert"); }, toolDisabledAnnouncement: function (toolName) { return "".concat(toolName, " deaktiviert"); }, updatedViewport: 'Transformierte Ansicht', transformedElements: function (elemCount) { return "".concat(elemCount, " Element").concat(1 === elemCount ? '' : 'e', " transformiert"); }, resizeOutputCommand: function (newSize) { return "Bildgr\u00F6\u00DFe auf ".concat(newSize.w, "x").concat(newSize.h, " ge\u00E4ndert"); }, addElementAction: function (componentDescription) { return "".concat(componentDescription, " hinzugef\u00FCgt"); }, eraseAction: function (elemDescription, countErased) { return "".concat(countErased, " ").concat(elemDescription, " gel\u00F6scht"); }, duplicateAction: function (elemDescription, countErased) { return "".concat(countErased, " ").concat(elemDescription, " dupliziert"); }, inverseOf: function (actionDescription) { return "".concat(actionDescription, " umgekehrt"); }, elements: 'Elemente', erasedNoElements: 'Nichts entfernt', duplicatedNoElements: 'Nichts dupliziert', rotatedBy: function (degrees) { return "".concat(Math.abs(degrees), " Grad ").concat(degrees < 0 ? 'im Uhrzeigersinn' : 'gegen den Uhrzeigersinn', " gedreht"); }, movedLeft: 'Nacht links bewegt', movedUp: 'Nacht oben bewegt', movedDown: 'Nacht unten bewegt', movedRight: 'Nacht rechts bewegt', zoomedOut: 'Ansicht verkleinert', zoomedIn: 'Ansicht vergrößert', selectedElements: function (count) { return "".concat(count, " Element").concat(1 === count ? '' : 'e', " ausgew\u00E4hlt"); }, stroke: 'Strich', svgObject: 'SVG-Objekt', text: function (text) { return "Text-Objekt: ".concat(text); }, pathNodeCount: function (count) { return "Es gibt ".concat(count, " sichtbare Pfad-Objekte."); }, textNodeCount: function (count) { return "Es gibt ".concat(count, " sichtbare Text-Knotenpunkte."); }, textNode: function (content) { return "Text: ".concat(content); }, imageNodeCount: function (nodeCount) { return "Es gibt ".concat(nodeCount, " sichtbare Bild-Knoten."); }, imageNode: function (label) { return "Bild: ".concat(label); }, unlabeledImageNode: 'Bild ohne Label', rerenderAsText: 'Als Text darstellen', accessibilityInputInstructions: 'Drücke ‚t‘, um den Inhalt des Ansichtsfensters als Text zu lesen. Verwende die Pfeiltasten, um die Ansicht zu verschieben, und klicke und ziehe, um Striche zu zeichnen. Drücke ‚w‘ zum Vergrößern und ‚s‘ zum Verkleinern der Ansicht.', loading: function (percentage) { return "Laden ".concat(percentage, "%..."); }, doneLoading: 'Laden fertig', imageEditor: 'Bild-Editor', undoAnnouncement: function (commandDescription) { return "".concat(commandDescription, " r\u00FCckg\u00E4ngig gemacht"); }, redoAnnouncement: function (commandDescription) { return "".concat(commandDescription, " wiederholt"); }, reformatSelection: 'Formatiere Auswahl', documentProperties: 'Seite', backgroundColor: 'Hintergrundfarbe: ', imageWidthOption: 'Breite: ', imageHeightOption: 'Höhe: ', useGridOption: 'Gitter: ', toggleOverflow: 'Mehr', selectAllTool: 'Alle auswählen', soundExplorer: 'Klangbasierte Bilderkundung', disableAccessibilityExploreTool: 'Deaktiviere klangbasierte Erkundung', enableAccessibilityExploreTool: 'Aktiviere klangbasierte Erkundung', copied: function (count, description) { return "".concat(count, " ").concat(description, " kopiert"); }, pasted: function (count, description) { return "".concat(count, " ").concat(description, " eingef\u00FCgt"); }, unionOf: function (actionDescription, actionCount) { return "Vereinigung: ".concat(actionCount, " ").concat(actionDescription); }, emptyBackground: 'Leerer Hintergrund', filledBackgroundWithColor: function (color) { return "Gef\u00FCllter Hintergrund (".concat(color, ")"); }, restyledElement: function (elementDescription) { return "".concat(elementDescription, " umgestaltet"); } });
14
+ var localization = __assign(__assign({}, defaultEditorLocalization), { pen: 'Stift', eraser: 'Radierer', select: 'Auswahl', handTool: 'Verschieben', zoom: 'Vergrößerung', image: 'Bild', inputAltText: 'Alt-Text: ', chooseFile: 'Wähle Datei: ', submit: 'Absenden', cancel: 'Abbrechen', resetView: 'Ansicht zurücksetzen', thicknessLabel: 'Dicke: ', colorLabel: 'Farbe: ', fontLabel: 'Schriftart: ', textSize: 'Größe: ', resizeImageToSelection: 'Bildgröße an Auswahl anpassen', deleteSelection: 'Auswahl löschen', duplicateSelection: 'Auswahl duplizieren', undo: 'Rückgängig', redo: 'Wiederholen', pickColorFromScreen: 'Farbe von Bildschirm auswählen', clickToPickColorAnnouncement: 'Klicke auf den Bildschirm, um eine Farbe auszuwählen', selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.', touchPanning: 'Ansicht mit Touchscreen verschieben', anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben', selectPenType: 'Objekt-Typ: ', roundedTipPen: 'Freihand', flatTipPen: 'Stift (druckempfindlich)', arrowPen: 'Pfeil', linePen: 'Linie', outlinedRectanglePen: 'Umrissenes Rechteck', filledRectanglePen: 'Ausgefülltes Rechteck', lockRotation: 'Sperre Rotation', paste: 'Einfügen', dropdownShown: function (toolName) { return "Dropdown-Men\u00FC f\u00FCr ".concat(toolName, " angezeigt"); }, dropdownHidden: function (toolName) { return "Dropdown-Men\u00FC f\u00FCr ".concat(toolName, " versteckt"); }, zoomLevel: function (zoomPercent) { return "Verg\u00F6\u00DFerung: ".concat(zoomPercent, "%"); }, colorChangedAnnouncement: function (color) { return "Farbe zu ".concat(color, " ge\u00E4ndert"); }, imageSize: function (size, units) { return "Bild-Gr\u00F6\u00DFe: ".concat(size, " ").concat(units); }, imageLoadError: function (message) { return "Fehler beim Laden des Bildes: ".concat(message); }, errorImageHasZeroSize: 'Fehler: Bild hat Größe Null', penTool: function (penNumber) { return "Stift ".concat(penNumber); }, selectionTool: 'Auswahl', eraserTool: 'Radiergummi', touchPanTool: 'Ansicht mit Touchscreen verschieben', twoFingerPanZoomTool: 'Ansicht verschieben und vergrößern', undoRedoTool: 'Rückgängig/Wiederholen', rightClickDragPanTool: 'Rechtsklick-Ziehen', pipetteTool: 'Farbe von Bildschirm auswählen', keyboardPanZoom: 'Tastaturkürzel zum Verschieben/Vergrößern der Ansicht', textTool: 'Text', enterTextToInsert: 'Einzufügender Text', changeTool: 'Wechsle Werkzeug', pasteHandler: 'Copy-Paste-Handler', findLabel: 'Finde', toNextMatch: 'Nächstes', closeFindDialog: 'Schließen', findDialogShown: 'Finde-Dialog angezeigt', findDialogHidden: 'Finde-Dialog versteckt', focusedFoundText: function (matchIdx, totalMatches) { return "Sieh Treffer ".concat(matchIdx, " von ").concat(totalMatches, " an"); }, toolEnabledAnnouncement: function (toolName) { return "".concat(toolName, " aktiviert"); }, toolDisabledAnnouncement: function (toolName) { return "".concat(toolName, " deaktiviert"); }, updatedViewport: 'Transformierte Ansicht', transformedElements: function (elemCount) { return "".concat(elemCount, " Element").concat(1 === elemCount ? '' : 'e', " transformiert"); }, resizeOutputCommand: function (newSize) { return "Bildgr\u00F6\u00DFe auf ".concat(newSize.w, "x").concat(newSize.h, " ge\u00E4ndert"); }, addElementAction: function (componentDescription) { return "".concat(componentDescription, " hinzugef\u00FCgt"); }, eraseAction: function (elemDescription, countErased) { return "".concat(countErased, " ").concat(elemDescription, " gel\u00F6scht"); }, duplicateAction: function (elemDescription, countErased) { return "".concat(countErased, " ").concat(elemDescription, " dupliziert"); }, inverseOf: function (actionDescription) { return "".concat(actionDescription, " umgekehrt"); }, elements: 'Elemente', erasedNoElements: 'Nichts entfernt', duplicatedNoElements: 'Nichts dupliziert', rotatedBy: function (degrees) { return "".concat(Math.abs(degrees), " Grad ").concat(degrees < 0 ? 'im Uhrzeigersinn' : 'gegen den Uhrzeigersinn', " gedreht"); }, movedLeft: 'Nacht links bewegt', movedUp: 'Nacht oben bewegt', movedDown: 'Nacht unten bewegt', movedRight: 'Nacht rechts bewegt', zoomedOut: 'Ansicht verkleinert', zoomedIn: 'Ansicht vergrößert', selectedElements: function (count) { return "".concat(count, " Element").concat(1 === count ? '' : 'e', " ausgew\u00E4hlt"); }, stroke: 'Strich', svgObject: 'SVG-Objekt', text: function (text) { return "Text-Objekt: ".concat(text); }, pathNodeCount: function (count) { return "Es gibt ".concat(count, " sichtbare Pfad-Objekte."); }, textNodeCount: function (count) { return "Es gibt ".concat(count, " sichtbare Text-Knotenpunkte."); }, textNode: function (content) { return "Text: ".concat(content); }, imageNodeCount: function (nodeCount) { return "Es gibt ".concat(nodeCount, " sichtbare Bild-Knoten."); }, imageNode: function (label) { return "Bild: ".concat(label); }, unlabeledImageNode: 'Bild ohne Label', rerenderAsText: 'Als Text darstellen', accessibilityInputInstructions: 'Drücke ‚t‘, um den Inhalt des Ansichtsfensters als Text zu lesen. Verwende die Pfeiltasten, um die Ansicht zu verschieben, und klicke und ziehe, um Striche zu zeichnen. Drücke ‚w‘ zum Vergrößern und ‚s‘ zum Verkleinern der Ansicht.', loading: function (percentage) { return "Laden ".concat(percentage, "%..."); }, doneLoading: 'Laden fertig', imageEditor: 'Bild-Editor', undoAnnouncement: function (commandDescription) { return "".concat(commandDescription, " r\u00FCckg\u00E4ngig gemacht"); }, redoAnnouncement: function (commandDescription) { return "".concat(commandDescription, " wiederholt"); }, reformatSelection: 'Formatiere Auswahl', documentProperties: 'Seite', backgroundColor: 'Hintergrundfarbe: ', imageWidthOption: 'Breite: ', imageHeightOption: 'Höhe: ', useGridOption: 'Gitter: ', toggleOverflow: 'Mehr', selectAllTool: 'Alle auswählen', soundExplorer: 'Klangbasierte Bilderkundung', disableAccessibilityExploreTool: 'Deaktiviere klangbasierte Erkundung', enableAccessibilityExploreTool: 'Aktiviere klangbasierte Erkundung', copied: function (count, description) { return "".concat(count, " ").concat(description, " kopiert"); }, pasted: function (count, description) { return "".concat(count, " ").concat(description, " eingef\u00FCgt"); }, unionOf: function (actionDescription, actionCount) { return "Vereinigung: ".concat(actionCount, " ").concat(actionDescription); }, emptyBackground: 'Leerer Hintergrund', filledBackgroundWithColor: function (color) { return "Gef\u00FCllter Hintergrund (".concat(color, ")"); }, restyledElement: function (elementDescription) { return "".concat(elementDescription, " umgestaltet"); } });
15
15
  export default localization;
@@ -17,7 +17,7 @@ var localization = __assign(__assign({}, defaultEditorLocalization), {
17
17
  loading: function (percentage) { return "Cargando: ".concat(percentage, "%..."); }, imageEditor: 'Editor de dibujos', undoAnnouncement: function (commandDescription) { return "".concat(commandDescription, " fue deshecho"); }, redoAnnouncement: function (commandDescription) { return "".concat(commandDescription, " fue rehecho"); }, undo: 'Deshace', redo: 'Rehace',
18
18
  // Strings for the toolbar
19
19
  // (see src/toolbar/localization.ts)
20
- pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma', selectPenType: 'Forma de dibuja:', handTool: 'Mover', zoom: 'Zoom', resetView: 'Reiniciar vista', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFromScreen: 'Selecciona un color de la pantalla', clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color', dropdownShown: function (toolName) {
20
+ pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', roundedTipPen: 'Lapiz Redondeado', selectPenType: 'Forma de dibuja:', handTool: 'Mover', zoom: 'Zoom', resetView: 'Reiniciar vista', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFromScreen: 'Selecciona un color de la pantalla', clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color', dropdownShown: function (toolName) {
21
21
  return "Men\u00FA por ".concat(toolName, " es visible");
22
22
  }, dropdownHidden: function (toolName) {
23
23
  return "Men\u00FA por ".concat(toolName, " fue ocultado");
@@ -103,6 +103,7 @@ var CanvasRenderer = /** @class */ (function (_super) {
103
103
  this.ctx.moveTo(startPoint.x, startPoint.y);
104
104
  };
105
105
  CanvasRenderer.prototype.endPath = function (style) {
106
+ this.ctx.save();
106
107
  this.ctx.fillStyle = style.fill.toHexString();
107
108
  this.ctx.fill();
108
109
  if (style.stroke) {
@@ -113,6 +114,7 @@ var CanvasRenderer = /** @class */ (function (_super) {
113
114
  this.ctx.stroke();
114
115
  }
115
116
  this.ctx.closePath();
117
+ this.ctx.restore();
116
118
  };
117
119
  CanvasRenderer.prototype.lineTo = function (point) {
118
120
  point = this.canvasToScreen(point);
@@ -30,7 +30,7 @@ export default class SVGRenderer extends AbstractRenderer {
30
30
  setRootSVGAttribute(name: string, value: string | null): void;
31
31
  displaySize(): Vec2;
32
32
  clear(): void;
33
- private addPathToSVG;
33
+ protected addPathToSVG(): SVGPathElement;
34
34
  drawPath(pathSpec: RenderablePathSpec): void;
35
35
  private transformFrom;
36
36
  private textContainer;
@@ -110,11 +110,12 @@ var SVGRenderer = /** @class */ (function (_super) {
110
110
  this.overwrittenAttrs = {};
111
111
  }
112
112
  };
113
- // Push [this.fullPath] to the SVG
113
+ // Push `this.fullPath` to the SVG. Returns the path added to the SVG, if any.
114
+ // @internal
114
115
  SVGRenderer.prototype.addPathToSVG = function () {
115
116
  var _a;
116
117
  if (!this.lastPathStyle || this.lastPathString.length === 0) {
117
- return;
118
+ return null;
118
119
  }
119
120
  var pathElem = document.createElementNS(svgNameSpace, 'path');
120
121
  pathElem.setAttribute('d', this.lastPathString.join(' '));
@@ -131,6 +132,7 @@ var SVGRenderer = /** @class */ (function (_super) {
131
132
  }
132
133
  this.elem.appendChild(pathElem);
133
134
  (_a = this.objectElems) === null || _a === void 0 ? void 0 : _a.push(pathElem);
135
+ return pathElem;
134
136
  };
135
137
  SVGRenderer.prototype.drawPath = function (pathSpec) {
136
138
  var style = pathSpec.style;
@@ -16,7 +16,7 @@ var KeyBinding = /** @class */ (function () {
16
16
  var isUpperCaseKey = ((_b = keyEvent.key) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === keyEvent.key
17
17
  && ((_c = keyEvent.key) === null || _c === void 0 ? void 0 : _c.toLowerCase()) !== keyEvent.key
18
18
  && ((_d = keyEvent.key) === null || _d === void 0 ? void 0 : _d.length) === 1;
19
- var isLowercaseKey = ((_e = keyEvent.key) === null || _e === void 0 ? void 0 : _e.toLowerCase()) !== keyEvent.key
19
+ var isLowercaseKey = ((_e = keyEvent.key) === null || _e === void 0 ? void 0 : _e.toLowerCase()) === keyEvent.key
20
20
  && !isUpperCaseKey
21
21
  && ((_f = keyEvent.key) === null || _f === void 0 ? void 0 : _f.length) === 1;
22
22
  var ctrlKey = ((_g = keyEvent.ctrlKey) !== null && _g !== void 0 ? _g : false) || lowercaseKey === 'control';
@@ -0,0 +1,4 @@
1
+ import Pointer from '../Pointer';
2
+ /** Returns the smallest ID not used by the pointers in the given list. */
3
+ declare const getUniquePointerId: (pointers: Pointer[]) => number;
4
+ export default getUniquePointerId;
@@ -0,0 +1,14 @@
1
+ /** Returns the smallest ID not used by the pointers in the given list. */
2
+ var getUniquePointerId = function (pointers) {
3
+ var ptrId = 0;
4
+ var pointerIds = pointers.map(function (ptr) { return ptr.id; });
5
+ pointerIds.sort();
6
+ for (var _i = 0, pointerIds_1 = pointerIds; _i < pointerIds_1.length; _i++) {
7
+ var pointerId = pointerIds_1[_i];
8
+ if (ptrId === pointerId) {
9
+ ptrId = pointerId + 1;
10
+ }
11
+ }
12
+ return ptrId;
13
+ };
14
+ export default getUniquePointerId;
@@ -8,5 +8,5 @@ import { InputEvtType } from '../types';
8
8
  *
9
9
  * @see {@link sendTouchEvent}
10
10
  */
11
- declare const sendPenEvent: (editor: Editor, eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]) => void;
11
+ declare const sendPenEvent: (editor: Editor, eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]) => Pointer;
12
12
  export default sendPenEvent;
@@ -1,5 +1,6 @@
1
1
  import Pointer from '../Pointer.mjs';
2
2
  import { InputEvtType } from '../types.mjs';
3
+ import getUniquePointerId from './getUniquePointerId.mjs';
3
4
  /**
4
5
  * Dispatch a pen event to the currently selected tool.
5
6
  * Intended for unit tests.
@@ -7,7 +8,8 @@ import { InputEvtType } from '../types.mjs';
7
8
  * @see {@link sendTouchEvent}
8
9
  */
9
10
  var sendPenEvent = function (editor, eventType, point, allPointers) {
10
- var mainPointer = Pointer.ofCanvasPoint(point, eventType !== InputEvtType.PointerUpEvt, editor.viewport);
11
+ var id = getUniquePointerId(allPointers !== null && allPointers !== void 0 ? allPointers : []);
12
+ var mainPointer = Pointer.ofCanvasPoint(point, eventType !== InputEvtType.PointerUpEvt, editor.viewport, id);
11
13
  editor.toolController.dispatchInputEvent({
12
14
  kind: eventType,
13
15
  allPointers: allPointers !== null && allPointers !== void 0 ? allPointers : [
@@ -15,5 +17,6 @@ var sendPenEvent = function (editor, eventType, point, allPointers) {
15
17
  ],
16
18
  current: mainPointer,
17
19
  });
20
+ return mainPointer;
18
21
  };
19
22
  export default sendPenEvent;