js-draw 0.24.1 → 0.25.1

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 (72) hide show
  1. package/dist/bundle.js +2 -2
  2. package/dist/bundledStyles.js +1 -1
  3. package/dist/cjs/Pointer.js +5 -3
  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/testing/getUniquePointerId.d.ts +4 -0
  10. package/dist/cjs/testing/getUniquePointerId.js +16 -0
  11. package/dist/cjs/testing/sendPenEvent.d.ts +1 -1
  12. package/dist/cjs/testing/sendPenEvent.js +4 -1
  13. package/dist/cjs/testing/sendTouchEvent.js +2 -9
  14. package/dist/cjs/toolbar/IconProvider.d.ts +1 -1
  15. package/dist/cjs/toolbar/IconProvider.js +76 -10
  16. package/dist/cjs/toolbar/localization.d.ts +2 -2
  17. package/dist/cjs/toolbar/localization.js +2 -2
  18. package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +2 -0
  19. package/dist/cjs/toolbar/widgets/BaseToolWidget.js +7 -0
  20. package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +3 -1
  21. package/dist/cjs/toolbar/widgets/PenToolWidget.js +125 -41
  22. package/dist/cjs/toolbar/widgets/SelectionToolWidget.js +4 -0
  23. package/dist/cjs/tools/BaseTool.d.ts +17 -1
  24. package/dist/cjs/tools/BaseTool.js +18 -0
  25. package/dist/cjs/tools/Pen.d.ts +5 -2
  26. package/dist/cjs/tools/Pen.js +37 -4
  27. package/dist/cjs/tools/ToolController.js +14 -2
  28. package/dist/mjs/Pointer.mjs +5 -3
  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/testing/getUniquePointerId.d.ts +4 -0
  35. package/dist/mjs/testing/getUniquePointerId.mjs +14 -0
  36. package/dist/mjs/testing/sendPenEvent.d.ts +1 -1
  37. package/dist/mjs/testing/sendPenEvent.mjs +4 -1
  38. package/dist/mjs/testing/sendTouchEvent.mjs +2 -9
  39. package/dist/mjs/toolbar/IconProvider.d.ts +1 -1
  40. package/dist/mjs/toolbar/IconProvider.mjs +76 -10
  41. package/dist/mjs/toolbar/localization.d.ts +2 -2
  42. package/dist/mjs/toolbar/localization.mjs +2 -2
  43. package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +2 -0
  44. package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +7 -0
  45. package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +3 -1
  46. package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +125 -41
  47. package/dist/mjs/toolbar/widgets/SelectionToolWidget.mjs +4 -0
  48. package/dist/mjs/tools/BaseTool.d.ts +17 -1
  49. package/dist/mjs/tools/BaseTool.mjs +18 -0
  50. package/dist/mjs/tools/Pen.d.ts +5 -2
  51. package/dist/mjs/tools/Pen.mjs +37 -4
  52. package/dist/mjs/tools/ToolController.mjs +14 -2
  53. package/package.json +2 -2
  54. package/src/Pointer.ts +5 -2
  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/testing/getUniquePointerId.ts +18 -0
  60. package/src/testing/sendPenEvent.ts +6 -1
  61. package/src/testing/sendTouchEvent.ts +2 -9
  62. package/src/toolbar/IconProvider.ts +92 -23
  63. package/src/toolbar/localization.ts +4 -4
  64. package/src/toolbar/toolbar.css +1 -0
  65. package/src/toolbar/widgets/BaseToolWidget.ts +10 -1
  66. package/src/toolbar/widgets/PenToolWidget.css +53 -0
  67. package/src/toolbar/widgets/PenToolWidget.ts +156 -44
  68. package/src/toolbar/widgets/SelectionToolWidget.ts +4 -0
  69. package/src/tools/BaseTool.ts +22 -1
  70. package/src/tools/Pen.test.ts +68 -0
  71. package/src/tools/Pen.ts +42 -4
  72. package/src/tools/ToolController.ts +17 -2
@@ -9,6 +9,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
9
9
  };
10
10
  import Pointer, { PointerDevice } from '../Pointer.mjs';
11
11
  import { InputEvtType } from '../types.mjs';
12
+ import getUniquePointerId from './getUniquePointerId.mjs';
12
13
  /**
13
14
  * Dispatch a touch event to the currently selected tool. Intended for unit tests.
14
15
  *
@@ -47,17 +48,9 @@ import { InputEvtType } from '../types.mjs';
47
48
  */
48
49
  var sendTouchEvent = function (editor, eventType, screenPos, allOtherPointers) {
49
50
  var canvasPos = editor.viewport.screenToCanvas(screenPos);
50
- var ptrId = 0;
51
- var maxPtrId = 0;
52
51
  // Get a unique ID for the main pointer
53
52
  // (try to use id=0, but don't use it if it's already in use).
54
- for (var _i = 0, _a = allOtherPointers !== null && allOtherPointers !== void 0 ? allOtherPointers : []; _i < _a.length; _i++) {
55
- var pointer = _a[_i];
56
- maxPtrId = Math.max(pointer.id, maxPtrId);
57
- if (pointer.id === ptrId) {
58
- ptrId = maxPtrId + 1;
59
- }
60
- }
53
+ var ptrId = getUniquePointerId(allOtherPointers !== null && allOtherPointers !== void 0 ? allOtherPointers : []);
61
54
  var mainPointer = Pointer.ofCanvasPoint(canvasPos, eventType !== InputEvtType.PointerUpEvt, editor.viewport, ptrId, PointerDevice.Touch);
62
55
  editor.toolController.dispatchInputEvent({
63
56
  kind: eventType,
@@ -49,7 +49,7 @@ export default class IconProvider {
49
49
  makeInsertImageIcon(): IconType;
50
50
  makeTextIcon(textStyle: TextRenderingStyle): IconType;
51
51
  makePenIcon(strokeSize: number, color: string | Color4, rounded?: boolean): IconType;
52
- makeIconFromFactory(pen: Pen, factory: ComponentBuilderFactory): IconType;
52
+ makeIconFromFactory(pen: Pen, factory: ComponentBuilderFactory, includeTransparencyGrid?: boolean): IconType;
53
53
  makePipetteIcon(color?: Color4): IconType;
54
54
  makeFormatSelectionIcon(): IconType;
55
55
  makeResizeViewportIcon(): IconType;
@@ -1,3 +1,18 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ if (typeof b !== "function" && b !== null)
10
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
+ extendStatics(d, b);
12
+ function __() { this.constructor = d; }
13
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
+ };
15
+ })();
1
16
  import Color4 from '../Color4.mjs';
2
17
  import { Vec2 } from '../math/Vec2.mjs';
3
18
  import SVGRenderer from '../rendering/renderers/SVGRenderer.mjs';
@@ -5,8 +20,13 @@ import Viewport from '../Viewport.mjs';
5
20
  var svgNamespace = 'http://www.w3.org/2000/svg';
6
21
  var iconColorFill = "\n\tstyle='fill: var(--icon-color);'\n";
7
22
  var iconColorStrokeFill = "\n\tstyle='fill: var(--icon-color); stroke: var(--icon-color);'\n";
8
- var checkerboardPatternDef = "\n\t<pattern\n\t\tid='checkerboard'\n\t\tviewBox='0,0,10,10'\n\t\twidth='20%'\n\t\theight='20%'\n\t\tpatternUnits='userSpaceOnUse'\n\t>\n\t\t<rect x=0 y=0 width=10 height=10 fill='white'/>\n\t\t<rect x=0 y=0 width=5 height=5 fill='gray'/>\n\t\t<rect x=5 y=5 width=5 height=5 fill='gray'/>\n\t</pattern>\n";
9
- var checkerboardPatternRef = 'url(#checkerboard)';
23
+ var checkerboardIdCounter = 0;
24
+ var makeCheckerboardPattern = function () {
25
+ var id = "checkerboard-".concat(checkerboardIdCounter++);
26
+ var patternDef = "\n\t\t<pattern\n\t\t\tid='".concat(id, "'\n\t\t\tviewBox='0,0,10,10'\n\t\t\twidth='20%'\n\t\t\theight='20%'\n\t\t\tpatternUnits='userSpaceOnUse'\n\t\t>\n\t\t\t<rect x=0 y=0 width=10 height=10 fill='white'/>\n\t\t\t<rect x=0 y=0 width=5 height=5 fill='gray'/>\n\t\t\t<rect x=5 y=5 width=5 height=5 fill='gray'/>\n\t\t</pattern>\n\t");
27
+ var patternRef = "url(#".concat(id, ")");
28
+ return { patternDef: patternDef, patternRef: patternRef };
29
+ };
10
30
  /**
11
31
  * Provides icons that can be used in the toolbar, etc.
12
32
  * Extend this class and override methods to customize icons.
@@ -176,13 +196,17 @@ var IconProvider = /** @class */ (function () {
176
196
  var penTipPath = "M 25,35 ".concat(10 - tipThickness / 4, ",").concat(70 - tipThickness / 2, " 20,75 25,85 60,75 70,55 45,25 Z");
177
197
  var pencilTipColor = Color4.fromHex('#f4d7d7');
178
198
  var tipColor = pencilTipColor.mix(Color4.fromString(color), tipThickness / 40 - 0.1).toHexString();
179
- var ink = "\n\t\t\t<path\n\t\t\t\tfill=\"".concat(checkerboardPatternRef, "\"\n\t\t\t\td=\"").concat(inkTipPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(checkerboardPatternRef, "\"\n\t\t\t\td=\"").concat(inkTrailPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(color, "\"\n\t\t\t\td=\"").concat(inkTipPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(color, "\"\n\t\t\t\td=\"").concat(inkTrailPath, "\"\n\t\t\t/>\n\t\t");
180
- var penTip = "\n\t\t\t<path\n\t\t\t\tfill=\"".concat(checkerboardPatternRef, "\"\n\t\t\t\td=\"").concat(penTipPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(tipColor, "\"\n\t\t\t\tstroke=\"").concat(color, "\"\n\t\t\t\td=\"").concat(penTipPath, "\"\n\t\t\t/>\n\t\t");
181
- var grip = "\n\t\t\t<path\n\t\t\t\t".concat(iconColorStrokeFill, "\n\t\t\t\td=\"").concat(gripMainPath, "\"\n\t\t\t/>\n\n\t\t\t<!-- shadows -->\n\t\t\t<path\n\t\t\t\tfill=\"rgba(150, 150, 150, 0.3)\"\n\t\t\t\td=\"").concat(gripShadow1Path, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"rgba(100, 100, 100, 0.2)\"\n\t\t\t\td=\"").concat(gripShadow2Path, "\"\n\t\t\t/>\n\n\t\t\t<!-- color bubble -->\n\t\t\t<path\n\t\t\t\tfill=\"").concat(checkerboardPatternRef, "\"\n\t\t\t\td=\"").concat(colorBubblePath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(color, "\"\n\t\t\t\td=\"").concat(colorBubblePath, "\"\n\t\t\t/>\n\t\t");
182
- icon.innerHTML = "\n\t\t<defs>\n\t\t\t".concat(checkerboardPatternDef, "\n\t\t</defs>\n\t\t<g>\n\t\t\t").concat(ink, "\n\t\t\t").concat(penTip, "\n\t\t\t").concat(grip, "\n\t\t</g>\n\t\t");
199
+ var checkerboardPattern = makeCheckerboardPattern();
200
+ var ink = "\n\t\t\t<path\n\t\t\t\tfill=\"".concat(checkerboardPattern.patternRef, "\"\n\t\t\t\td=\"").concat(inkTipPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(checkerboardPattern.patternRef, "\"\n\t\t\t\td=\"").concat(inkTrailPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(color, "\"\n\t\t\t\td=\"").concat(inkTipPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(color, "\"\n\t\t\t\td=\"").concat(inkTrailPath, "\"\n\t\t\t/>\n\t\t");
201
+ var penTip = "\n\t\t\t<path\n\t\t\t\tfill=\"".concat(checkerboardPattern.patternRef, "\"\n\t\t\t\td=\"").concat(penTipPath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(tipColor, "\"\n\t\t\t\tstroke=\"").concat(color, "\"\n\t\t\t\td=\"").concat(penTipPath, "\"\n\t\t\t/>\n\t\t");
202
+ var grip = "\n\t\t\t<path\n\t\t\t\t".concat(iconColorStrokeFill, "\n\t\t\t\td=\"").concat(gripMainPath, "\"\n\t\t\t/>\n\n\t\t\t<!-- shadows -->\n\t\t\t<path\n\t\t\t\tfill=\"rgba(150, 150, 150, 0.3)\"\n\t\t\t\td=\"").concat(gripShadow1Path, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"rgba(100, 100, 100, 0.2)\"\n\t\t\t\td=\"").concat(gripShadow2Path, "\"\n\t\t\t/>\n\n\t\t\t<!-- color bubble -->\n\t\t\t<path\n\t\t\t\tfill=\"").concat(checkerboardPattern.patternRef, "\"\n\t\t\t\td=\"").concat(colorBubblePath, "\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"").concat(color, "\"\n\t\t\t\td=\"").concat(colorBubblePath, "\"\n\t\t\t/>\n\t\t");
203
+ icon.innerHTML = "\n\t\t<defs>\n\t\t\t".concat(checkerboardPattern.patternDef, "\n\t\t</defs>\n\t\t<g>\n\t\t\t").concat(ink, "\n\t\t\t").concat(penTip, "\n\t\t\t").concat(grip, "\n\t\t</g>\n\t\t");
183
204
  return icon;
184
205
  };
185
- IconProvider.prototype.makeIconFromFactory = function (pen, factory) {
206
+ IconProvider.prototype.makeIconFromFactory = function (pen, factory,
207
+ // If true, attempts to guess the location of a transparency grid
208
+ includeTransparencyGrid) {
209
+ if (includeTransparencyGrid === void 0) { includeTransparencyGrid = false; }
186
210
  // Increase the thickness we use to generate the icon less with larger actual thicknesses.
187
211
  // We want the icon to be recognisable with a large range of thicknesses.
188
212
  var thickness = Math.sqrt(pen.getThickness()) * 3;
@@ -205,8 +229,49 @@ var IconProvider = /** @class */ (function () {
205
229
  var icon = document.createElementNS(svgNamespace, 'svg');
206
230
  icon.setAttribute('viewBox', '0 0 100 100');
207
231
  viewport.updateScreenSize(Vec2.of(100, 100));
208
- var renderer = new SVGRenderer(icon, viewport);
232
+ var renderer;
233
+ if (includeTransparencyGrid) {
234
+ var checkerboardPattern_1 = makeCheckerboardPattern();
235
+ var defs = document.createElementNS(svgNamespace, 'defs');
236
+ defs.innerHTML = checkerboardPattern_1.patternDef;
237
+ icon.appendChild(defs);
238
+ var background_1 = document.createElementNS(svgNamespace, 'g');
239
+ icon.appendChild(background_1);
240
+ renderer = new /** @class */ (function (_super) {
241
+ __extends(class_1, _super);
242
+ function class_1() {
243
+ return _super.call(this, icon, viewport) || this;
244
+ }
245
+ class_1.prototype.addPathToSVG = function () {
246
+ var addedPath = _super.prototype.addPathToSVG.call(this);
247
+ if (addedPath) {
248
+ // Add a copy of the path on the background
249
+ var copy = addedPath.cloneNode(true);
250
+ copy.style.zIndex = '-1';
251
+ // Make the
252
+ if (copy.hasAttribute('fill')
253
+ && copy.getAttribute('fill') !== 'transparent'
254
+ && copy.getAttribute('fill') !== 'none') {
255
+ copy.setAttribute('fill', checkerboardPattern_1.patternRef);
256
+ }
257
+ if (copy.hasAttribute('stroke')) {
258
+ copy.setAttribute('stroke', checkerboardPattern_1.patternRef);
259
+ }
260
+ background_1.appendChild(copy);
261
+ }
262
+ return addedPath;
263
+ };
264
+ return class_1;
265
+ }(SVGRenderer))();
266
+ }
267
+ else {
268
+ renderer = new SVGRenderer(icon, viewport);
269
+ }
209
270
  builder.preview(renderer);
271
+ // If only a single path was rendered, try to give it a checkerboard background to
272
+ // emphasize transparency. TODO: This is very fragile
273
+ var bbox = builder.getBBox();
274
+ icon.setAttribute('viewBox', "".concat(bbox.x, " ").concat(bbox.y, " ").concat(bbox.w, " ").concat(bbox.h));
210
275
  return icon;
211
276
  };
212
277
  IconProvider.prototype.makePipetteIcon = function (color) {
@@ -215,8 +280,9 @@ var IconProvider = /** @class */ (function () {
215
280
  pipette.setAttribute('d', "\n\t\t\tM 47,6\n\t\t\tC 35,5 25,15 35,30\n\t\t\tc -9.2,1.3 -15,0 -15,3\n\t\t\t\t0,2 5,5 15,7\n\t\t\tV 81\n\t\t\tL 40,90\n\t\t\th 6\n\t\t\tL 40,80\n\t\t\tV 40\n\t\t\th 15\n\t\t\tv 40\n\t\t\tl -6,10\n\t\t\th 6\n\t\t\tl 5,-9.2\n\t\t\tV 40\n\t\t\tC 70,38 75,35 75,33\n\t\t\t\t75,30 69.2,31.2 60,30\n\t\t\t\t65,15 65,5 47,6\n\t\t\tZ\n\t\t");
216
281
  pipette.style.fill = 'var(--icon-color)';
217
282
  if (color) {
283
+ var checkerboardPattern = makeCheckerboardPattern();
218
284
  var defs = document.createElementNS(svgNamespace, 'defs');
219
- defs.innerHTML = checkerboardPatternDef;
285
+ defs.innerHTML = checkerboardPattern.patternDef;
220
286
  icon.appendChild(defs);
221
287
  var fluidBackground = document.createElementNS(svgNamespace, 'path');
222
288
  var fluid = document.createElementNS(svgNamespace, 'path');
@@ -224,7 +290,7 @@ var IconProvider = /** @class */ (function () {
224
290
  fluid.setAttribute('d', fluidPathData);
225
291
  fluidBackground.setAttribute('d', fluidPathData);
226
292
  fluid.style.fill = color.toHexString();
227
- fluidBackground.style.fill = checkerboardPatternRef;
293
+ fluidBackground.style.fill = checkerboardPattern.patternRef;
228
294
  icon.appendChild(fluidBackground);
229
295
  icon.appendChild(fluid);
230
296
  }
@@ -13,8 +13,8 @@ export interface ToolbarLocalization {
13
13
  chooseFile: string;
14
14
  cancel: string;
15
15
  submit: string;
16
- freehandPen: string;
17
- pressureSensitiveFreehandPen: string;
16
+ roundedTipPen: string;
17
+ flatTipPen: string;
18
18
  selectPenType: string;
19
19
  colorLabel: string;
20
20
  pen: string;
@@ -32,8 +32,8 @@ export var defaultToolbarLocalization = {
32
32
  useGridOption: 'Grid: ',
33
33
  toggleOverflow: 'More',
34
34
  touchPanning: 'Touchscreen panning',
35
- freehandPen: 'Freehand',
36
- pressureSensitiveFreehandPen: 'Freehand (pressure sensitive)',
35
+ roundedTipPen: 'Rounded Tip',
36
+ flatTipPen: 'Flat Tip',
37
37
  arrowPen: 'Arrow',
38
38
  linePen: 'Line',
39
39
  outlinedRectanglePen: 'Outlined rectangle',
@@ -1,10 +1,12 @@
1
1
  import Editor from '../../Editor';
2
2
  import BaseTool from '../../tools/BaseTool';
3
+ import { KeyPressEvent } from '../../types';
3
4
  import { ToolbarLocalization } from '../localization';
4
5
  import BaseWidget from './BaseWidget';
5
6
  export default abstract class BaseToolWidget extends BaseWidget {
6
7
  protected targetTool: BaseTool;
7
8
  constructor(editor: Editor, targetTool: BaseTool, id: string, localizationTable?: ToolbarLocalization);
8
9
  protected handleClick(): void;
10
+ protected onKeyPress(event: KeyPressEvent): boolean;
9
11
  addTo(parent: HTMLElement): HTMLElement;
10
12
  }
@@ -52,6 +52,13 @@ var BaseToolWidget = /** @class */ (function (_super) {
52
52
  this.targetTool.setEnabled(!this.targetTool.isEnabled());
53
53
  }
54
54
  };
55
+ BaseToolWidget.prototype.onKeyPress = function (event) {
56
+ if (this.isSelected() && event.key === ' ' && this.hasDropdown) {
57
+ this.handleClick();
58
+ return true;
59
+ }
60
+ return false;
61
+ };
55
62
  BaseToolWidget.prototype.addTo = function (parent) {
56
63
  var result = _super.prototype.addTo.call(this, parent);
57
64
  this.setSelected(this.targetTool.isEnabled());
@@ -14,12 +14,14 @@ export default class PenToolWidget extends BaseToolWidget {
14
14
  private tool;
15
15
  private updateInputs;
16
16
  protected penTypes: PenTypeRecord[];
17
+ private static idCounter;
17
18
  constructor(editor: Editor, tool: Pen, localization?: ToolbarLocalization);
18
19
  protected getTitle(): string;
19
20
  private getCurrentPenTypeIdx;
20
21
  private getCurrentPenType;
22
+ private createIconForRecord;
21
23
  protected createIcon(): Element;
22
- private static idCounter;
24
+ private createPenTypeSelector;
23
25
  protected fillDropdown(dropdown: HTMLElement): boolean;
24
26
  protected onKeyPress(event: KeyPressEvent): boolean;
25
27
  serializeState(): SavedToolbuttonState;
@@ -45,12 +45,12 @@ var PenToolWidget = /** @class */ (function (_super) {
45
45
  // Default pen types
46
46
  _this.penTypes = [
47
47
  {
48
- name: _this.localizationTable.pressureSensitiveFreehandPen,
48
+ name: _this.localizationTable.flatTipPen,
49
49
  id: 'pressure-sensitive-pen',
50
50
  factory: makePressureSensitiveFreehandLineBuilder,
51
51
  },
52
52
  {
53
- name: _this.localizationTable.freehandPen,
53
+ name: _this.localizationTable.roundedTipPen,
54
54
  id: 'freehand-pen',
55
55
  factory: makeFreehandLineBuilder,
56
56
  },
@@ -117,38 +117,140 @@ var PenToolWidget = /** @class */ (function (_super) {
117
117
  }
118
118
  return null;
119
119
  };
120
- PenToolWidget.prototype.createIcon = function () {
121
- var strokeFactory = this.tool.getStrokeFactory();
122
- if (strokeFactory === makeFreehandLineBuilder || strokeFactory === makePressureSensitiveFreehandLineBuilder) {
120
+ PenToolWidget.prototype.createIconForRecord = function (record) {
121
+ var color = this.tool.getColor();
122
+ var strokeFactory = record === null || record === void 0 ? void 0 : record.factory;
123
+ if (!strokeFactory || strokeFactory === makeFreehandLineBuilder || strokeFactory === makePressureSensitiveFreehandLineBuilder) {
123
124
  // Use a square-root scale to prevent the pen's tip from overflowing.
124
125
  var scale = Math.round(Math.sqrt(this.tool.getThickness()) * 4);
125
- var color = this.tool.getColor();
126
126
  var roundedTip = strokeFactory === makeFreehandLineBuilder;
127
127
  return this.editor.icons.makePenIcon(scale, color.toHexString(), roundedTip);
128
128
  }
129
129
  else {
130
- var strokeFactory_1 = this.tool.getStrokeFactory();
131
- return this.editor.icons.makeIconFromFactory(this.tool, strokeFactory_1);
130
+ var hasTransparency = color.a < 1;
131
+ return this.editor.icons.makeIconFromFactory(this.tool, strokeFactory, hasTransparency);
132
132
  }
133
133
  };
134
+ PenToolWidget.prototype.createIcon = function () {
135
+ return this.createIconForRecord(this.getCurrentPenType());
136
+ };
137
+ // Creates a widget that allows selecting different pen types
138
+ PenToolWidget.prototype.createPenTypeSelector = function () {
139
+ var _this = this;
140
+ var outerContainer = document.createElement('div');
141
+ outerContainer.classList.add("".concat(toolbarCSSPrefix, "pen-type-selector"));
142
+ var scrollingContainer = document.createElement('div');
143
+ scrollingContainer.setAttribute('role', 'menu');
144
+ scrollingContainer.id = "".concat(toolbarCSSPrefix, "-pen-type-selector-id-").concat(PenToolWidget.idCounter++);
145
+ scrollingContainer.onwheel = function (event) {
146
+ var hasScroll = scrollingContainer.clientWidth !== scrollingContainer.scrollWidth
147
+ && event.deltaX !== 0;
148
+ var eventScrollsPastLeft = scrollingContainer.scrollLeft + event.deltaX <= 0;
149
+ var scrollRight = scrollingContainer.scrollLeft + scrollingContainer.clientWidth;
150
+ var eventScrollsPastRight = scrollRight + event.deltaX > scrollingContainer.scrollWidth;
151
+ // Stop the editor from receiving the event if it will scroll the pen type selector
152
+ // instead.
153
+ if (hasScroll && !eventScrollsPastLeft && !eventScrollsPastRight) {
154
+ event.stopPropagation();
155
+ }
156
+ };
157
+ var label = document.createElement('label');
158
+ label.innerText = this.localizationTable.selectPenType;
159
+ label.htmlFor = scrollingContainer.id;
160
+ outerContainer.appendChild(label);
161
+ // All buttons in a radiogroup need the same name attribute.
162
+ var radiogroupName = "".concat(toolbarCSSPrefix, "-pen-type-selector-").concat(PenToolWidget.idCounter++);
163
+ var createTypeSelectorButton = function (record) {
164
+ var buttonContainer = document.createElement('div');
165
+ buttonContainer.classList.add('pen-type-button');
166
+ var button = document.createElement('input');
167
+ button.type = 'radio';
168
+ button.name = radiogroupName;
169
+ button.id = "".concat(toolbarCSSPrefix, "-pen-type-button-").concat(PenToolWidget.idCounter++);
170
+ var labelContainer = document.createElement('label');
171
+ var rebuildLabel = function () {
172
+ var labelText = document.createElement('span');
173
+ var icon = _this.createIconForRecord(record);
174
+ icon.classList.add('icon');
175
+ // The title of the record
176
+ labelText.innerText = record.name;
177
+ labelContainer.htmlFor = button.id;
178
+ labelContainer.replaceChildren(icon, labelText);
179
+ };
180
+ rebuildLabel();
181
+ var updateButtonCSS = function () {
182
+ if (button.checked) {
183
+ buttonContainer.classList.add('checked');
184
+ }
185
+ else {
186
+ buttonContainer.classList.remove('checked');
187
+ }
188
+ };
189
+ button.oninput = function () {
190
+ // Setting the stroke factory fires an event that causes the value
191
+ // of this button to be set.
192
+ if (button.checked) {
193
+ _this.tool.setStrokeFactory(record.factory);
194
+ }
195
+ updateButtonCSS();
196
+ };
197
+ buttonContainer.replaceChildren(button, labelContainer);
198
+ scrollingContainer.appendChild(buttonContainer);
199
+ // Set whether the button is checked, assuming the stroke factory associated
200
+ // with the button was set elsewhere.
201
+ var setChecked = function (checked) {
202
+ button.checked = checked;
203
+ updateButtonCSS();
204
+ if (checked) {
205
+ button.scrollIntoView();
206
+ }
207
+ };
208
+ setChecked(false);
209
+ // Updates the factory's icon based on the current style of the tool.
210
+ var updateIcon = function () {
211
+ rebuildLabel();
212
+ };
213
+ return { setChecked: setChecked, updateIcon: updateIcon };
214
+ };
215
+ var buttons = [];
216
+ for (var _i = 0, _a = this.penTypes; _i < _a.length; _i++) {
217
+ var penType = _a[_i];
218
+ buttons.push(createTypeSelectorButton(penType));
219
+ }
220
+ // invariant: buttons.length = this.penTypes.length
221
+ outerContainer.appendChild(scrollingContainer);
222
+ return {
223
+ setValue: function (penTypeIndex) {
224
+ // Select the value specified
225
+ if (penTypeIndex < 0 || penTypeIndex >= _this.penTypes.length) {
226
+ console.error('Invalid pen type index', penTypeIndex);
227
+ return;
228
+ }
229
+ for (var i = 0; i < buttons.length; i++) {
230
+ buttons[i].setChecked(i === penTypeIndex);
231
+ }
232
+ },
233
+ updateIcons: function () {
234
+ buttons.forEach(function (button) { return button.updateIcon(); });
235
+ },
236
+ addTo: function (parent) {
237
+ parent.appendChild(outerContainer);
238
+ },
239
+ };
240
+ };
134
241
  PenToolWidget.prototype.fillDropdown = function (dropdown) {
135
242
  var _this = this;
136
243
  var container = document.createElement('div');
137
244
  container.classList.add("".concat(toolbarCSSPrefix, "spacedList"));
138
245
  var thicknessRow = document.createElement('div');
139
- var objectTypeRow = document.createElement('div');
140
246
  // Thickness: Value of the input is squared to allow for finer control/larger values.
141
247
  var thicknessLabel = document.createElement('label');
142
248
  var thicknessInput = document.createElement('input');
143
- var objectSelectLabel = document.createElement('label');
144
- var objectTypeSelect = document.createElement('select');
249
+ var penTypeSelect = this.createPenTypeSelector();
145
250
  // Give inputs IDs so we can label them with a <label for=...>Label text</label>
146
251
  thicknessInput.id = "".concat(toolbarCSSPrefix, "penThicknessInput").concat(PenToolWidget.idCounter++);
147
- objectTypeSelect.id = "".concat(toolbarCSSPrefix, "penBuilderSelect").concat(PenToolWidget.idCounter++);
148
252
  thicknessLabel.innerText = this.localizationTable.thicknessLabel;
149
253
  thicknessLabel.setAttribute('for', thicknessInput.id);
150
- objectSelectLabel.innerText = this.localizationTable.selectPenType;
151
- objectSelectLabel.setAttribute('for', objectTypeSelect.id);
152
254
  // Use a logarithmic scale for thicknessInput (finer control over thinner strokewidths.)
153
255
  var inverseThicknessInputFn = function (t) { return Math.log10(t); };
154
256
  var thicknessInputFn = function (t) { return Math.pow(10, t); };
@@ -161,16 +263,6 @@ var PenToolWidget = /** @class */ (function (_super) {
161
263
  };
162
264
  thicknessRow.appendChild(thicknessLabel);
163
265
  thicknessRow.appendChild(thicknessInput);
164
- objectTypeSelect.oninput = function () {
165
- var penTypeIdx = parseInt(objectTypeSelect.value);
166
- if (penTypeIdx < 0 || penTypeIdx >= _this.penTypes.length) {
167
- console.error('Invalid pen type index', penTypeIdx);
168
- return;
169
- }
170
- _this.tool.setStrokeFactory(_this.penTypes[penTypeIdx].factory);
171
- };
172
- objectTypeRow.appendChild(objectSelectLabel);
173
- objectTypeRow.appendChild(objectTypeSelect);
174
266
  var colorRow = document.createElement('div');
175
267
  var colorLabel = document.createElement('label');
176
268
  var _a = makeColorInput(this.editor, function (color) {
@@ -184,26 +276,13 @@ var PenToolWidget = /** @class */ (function (_super) {
184
276
  this.updateInputs = function () {
185
277
  setColorInputValue(_this.tool.getColor());
186
278
  thicknessInput.value = inverseThicknessInputFn(_this.tool.getThickness()).toString();
187
- // Update the list of stroke factories
188
- objectTypeSelect.replaceChildren();
189
- for (var i = 0; i < _this.penTypes.length; i++) {
190
- var penType = _this.penTypes[i];
191
- var option = document.createElement('option');
192
- option.value = i.toString();
193
- option.innerText = penType.name;
194
- objectTypeSelect.appendChild(option);
195
- }
279
+ penTypeSelect.updateIcons();
196
280
  // Update the selected stroke factory.
197
- var strokeFactoryIdx = _this.getCurrentPenTypeIdx();
198
- if (strokeFactoryIdx === -1) {
199
- objectTypeSelect.value = '';
200
- }
201
- else {
202
- objectTypeSelect.value = strokeFactoryIdx.toString();
203
- }
281
+ penTypeSelect.setValue(_this.getCurrentPenTypeIdx());
204
282
  };
205
283
  this.updateInputs();
206
- container.replaceChildren(colorRow, thicknessRow, objectTypeRow);
284
+ container.replaceChildren(colorRow, thicknessRow);
285
+ penTypeSelect.addTo(container);
207
286
  dropdown.replaceChildren(container);
208
287
  return true;
209
288
  };
@@ -221,6 +300,10 @@ var PenToolWidget = /** @class */ (function (_super) {
221
300
  }
222
301
  }
223
302
  }
303
+ // Run any default actions registered by the parent class.
304
+ if (_super.prototype.onKeyPress.call(this, event)) {
305
+ return true;
306
+ }
224
307
  return false;
225
308
  };
226
309
  PenToolWidget.prototype.serializeState = function () {
@@ -256,6 +339,7 @@ var PenToolWidget = /** @class */ (function (_super) {
256
339
  }
257
340
  }
258
341
  };
342
+ // A counter variable that ensures different HTML elements are given unique names/ids.
259
343
  PenToolWidget.idCounter = 0;
260
344
  return PenToolWidget;
261
345
  }(BaseToolWidget));
@@ -198,6 +198,10 @@ var SelectionToolWidget = /** @class */ (function (_super) {
198
198
  this.resizeImageToSelection();
199
199
  return true;
200
200
  }
201
+ // If we didn't handle the event, allow the superclass to handle it.
202
+ if (_super.prototype.onKeyPress.call(this, event)) {
203
+ return true;
204
+ }
201
205
  return false;
202
206
  };
203
207
  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;
@@ -6,8 +6,18 @@ var BaseTool = /** @class */ (function () {
6
6
  this.enabled = true;
7
7
  this.group = null;
8
8
  }
9
+ /**
10
+ * Returns true iff the tool handled the event and thus should receive additional
11
+ * events.
12
+ */
9
13
  BaseTool.prototype.onPointerDown = function (_event) { return false; };
10
14
  BaseTool.prototype.onPointerMove = function (_event) { };
15
+ /**
16
+ * Returns true iff there are additional pointers down and the tool should
17
+ * remain active to handle the additional events.
18
+ *
19
+ * For most purposes, this should return `false` or nothing.
20
+ */
11
21
  BaseTool.prototype.onPointerUp = function (_event) { };
12
22
  BaseTool.prototype.onGestureCancel = function () { };
13
23
  BaseTool.prototype.onWheel = function (_event) {
@@ -25,6 +35,14 @@ var BaseTool = /** @class */ (function () {
25
35
  BaseTool.prototype.onKeyUp = function (_event) {
26
36
  return false;
27
37
  };
38
+ /**
39
+ * Return true if, while this tool is active, `_event` can be delivered to
40
+ * another tool that is higher priority than this.
41
+ * @internal May be renamed
42
+ */
43
+ BaseTool.prototype.eventCanBeDeliveredToNonActiveTool = function (_event) {
44
+ return true;
45
+ };
28
46
  BaseTool.prototype.setEnabled = function (enabled) {
29
47
  var _a;
30
48
  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;