@lvl99dotcom/paintcanvas 0.3.1 → 0.5.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 (212) hide show
  1. package/README.md +30 -15
  2. package/dist/lib/brushes/StrokeEngine/index.d.ts +2 -2
  3. package/dist/lib/brushes/StrokeEngine/index.js +26 -28
  4. package/dist/lib/brushes/presets/round/index.d.ts +1 -1
  5. package/dist/lib/brushes/presets/round/index.js +12 -7
  6. package/dist/lib/brushes/presets/square/index.d.ts +1 -1
  7. package/dist/lib/brushes/presets/square/index.js +7 -3
  8. package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
  9. package/dist/lib/brushes/shapeImage/index.js +15 -9
  10. package/dist/lib/brushes/shapes/index.d.ts +18 -0
  11. package/dist/lib/brushes/shapes/index.js +86 -0
  12. package/dist/lib/classes/BrushSettings/index.d.ts +53 -2
  13. package/dist/lib/classes/BrushSettings/index.js +85 -14
  14. package/dist/lib/classes/KeyboardShortcuts/index.d.ts +47 -7
  15. package/dist/lib/classes/KeyboardShortcuts/index.js +145 -20
  16. package/dist/lib/classes/PaintCanvas/index.d.ts +123 -27
  17. package/dist/lib/classes/PaintCanvas/index.js +208 -41
  18. package/dist/lib/classes/PaintCanvas/tool.d.ts +15 -0
  19. package/dist/lib/classes/PaintCanvas/tool.js +18 -0
  20. package/dist/lib/classes/Palette/index.d.ts +58 -0
  21. package/dist/lib/classes/Palette/index.js +92 -0
  22. package/dist/lib/classes/SettingsAutosave/index.d.ts +53 -0
  23. package/dist/lib/classes/SettingsAutosave/index.js +98 -0
  24. package/dist/lib/classes/ShortcutSettings/index.d.ts +63 -0
  25. package/dist/lib/classes/ShortcutSettings/index.js +110 -0
  26. package/dist/lib/classes/UiSettings/index.d.ts +68 -0
  27. package/dist/lib/classes/UiSettings/index.js +82 -0
  28. package/dist/lib/palette/index.d.ts +49 -0
  29. package/dist/lib/palette/index.js +82 -0
  30. package/dist/lib/palette/schema.d.ts +45 -0
  31. package/dist/lib/palette/schema.js +41 -0
  32. package/dist/lib/rendering/cursors/index.d.ts +2 -0
  33. package/dist/lib/rendering/cursors/index.js +3 -1
  34. package/dist/lib/rendering/sample/index.js +3 -2
  35. package/dist/lib/settings/index.d.ts +63 -0
  36. package/dist/lib/settings/index.js +93 -0
  37. package/dist/lib/settings/schema.d.ts +168 -0
  38. package/dist/lib/settings/schema.js +116 -0
  39. package/dist/lib/storage/index.d.ts +36 -5
  40. package/dist/lib/storage/index.js +87 -29
  41. package/dist/lib/utils/colour/index.d.ts +72 -0
  42. package/dist/lib/utils/colour/index.js +154 -0
  43. package/dist/lib/utils/shortcuts/index.d.ts +174 -11
  44. package/dist/lib/utils/shortcuts/index.js +391 -145
  45. package/dist/lib/validation/validators.generated.d.ts +5 -0
  46. package/dist/lib/validation/validators.generated.js +1040 -1
  47. package/dist/locales/en-GB.js +96 -3
  48. package/dist/locales/en-US.js +96 -3
  49. package/dist/locales/fr.js +96 -3
  50. package/dist/locales/ja.js +96 -3
  51. package/dist/locales/ko.js +96 -3
  52. package/dist/locales/zh-Hans.js +96 -3
  53. package/dist/locales/zh-Hant.js +96 -3
  54. package/dist/package.js +1 -1
  55. package/dist/testing/recordingCanvas.js +2 -1
  56. package/dist/ui/base/index.d.ts +2 -2
  57. package/dist/ui/dismiss/index.d.ts +21 -0
  58. package/dist/ui/dismiss/index.js +27 -0
  59. package/dist/ui/drag/index.d.ts +51 -0
  60. package/dist/ui/drag/index.js +81 -0
  61. package/dist/ui/elements/PaintCanvas/index.d.ts +57 -50
  62. package/dist/ui/elements/PaintCanvas/index.js +141 -174
  63. package/dist/ui/elements/PaintCanvas/style.js +1 -1
  64. package/dist/ui/elements/PcAppSettings/index.d.ts +59 -0
  65. package/dist/ui/elements/PcAppSettings/index.js +222 -0
  66. package/dist/ui/elements/PcBrushConfig/index.d.ts +9 -2
  67. package/dist/ui/elements/PcBrushConfig/index.js +47 -7
  68. package/dist/ui/elements/PcBrushConfig/style.js +1 -1
  69. package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
  70. package/dist/ui/elements/PcBrushSize/index.js +66 -0
  71. package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
  72. package/dist/ui/elements/PcBrushTest/index.js +73 -5
  73. package/dist/ui/elements/PcBrushTest/style.js +1 -1
  74. package/dist/ui/elements/PcCanvas/index.d.ts +22 -2
  75. package/dist/ui/elements/PcCanvas/index.js +60 -15
  76. package/dist/ui/elements/PcCanvasMap/style.js +1 -1
  77. package/dist/ui/elements/PcClipboardTools/index.d.ts +48 -0
  78. package/dist/ui/elements/PcClipboardTools/index.js +118 -0
  79. package/dist/ui/elements/PcColourPair/index.d.ts +43 -0
  80. package/dist/ui/elements/PcColourPair/index.js +125 -0
  81. package/dist/ui/elements/PcContextMenu/index.d.ts +71 -34
  82. package/dist/ui/elements/PcContextMenu/index.js +128 -97
  83. package/dist/ui/elements/PcInputSettings/index.d.ts +13 -0
  84. package/dist/ui/elements/PcInputSettings/index.js +16 -1
  85. package/dist/ui/elements/PcLayerItem/index.d.ts +13 -34
  86. package/dist/ui/elements/PcLayerItem/index.js +39 -91
  87. package/dist/ui/elements/PcLayerItem/style.js +1 -1
  88. package/dist/ui/elements/PcMenu/index.d.ts +138 -0
  89. package/dist/ui/elements/PcMenu/index.js +296 -0
  90. package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
  91. package/dist/ui/elements/PcNumberSlider/index.js +247 -0
  92. package/dist/ui/elements/PcPane/index.d.ts +120 -0
  93. package/dist/ui/elements/PcPane/index.js +394 -0
  94. package/dist/ui/elements/PcPanel/index.d.ts +26 -8
  95. package/dist/ui/elements/PcPanel/index.js +76 -8
  96. package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.d.ts +22 -21
  97. package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.js +24 -57
  98. package/dist/ui/elements/PcPanelBrushes/style.js +3 -0
  99. package/dist/ui/elements/PcPanelColours/index.d.ts +171 -0
  100. package/dist/ui/elements/PcPanelColours/index.js +650 -0
  101. package/dist/ui/elements/PcPanelColours/style.js +3 -0
  102. package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.d.ts +17 -4
  103. package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.js +17 -10
  104. package/dist/ui/elements/PcPanelLayers/style.js +3 -0
  105. package/dist/ui/elements/PcPopover/index.d.ts +39 -1
  106. package/dist/ui/elements/PcPopover/index.js +72 -4
  107. package/dist/ui/elements/PcShortcutSettings/index.d.ts +69 -0
  108. package/dist/ui/elements/PcShortcutSettings/index.js +273 -0
  109. package/dist/ui/elements/PcThemePicker/index.d.ts +17 -15
  110. package/dist/ui/elements/PcThemePicker/index.js +22 -21
  111. package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
  112. package/dist/ui/elements/PcToolSettings/index.js +384 -319
  113. package/dist/ui/elements/PcToolSettings/style.js +1 -1
  114. package/dist/ui/elements/PcToolbar/index.d.ts +62 -12
  115. package/dist/ui/elements/PcToolbar/index.js +119 -57
  116. package/dist/ui/elements/PcToolbar/style.js +1 -1
  117. package/dist/ui/icons/images/icon-brush@2x.js +3 -0
  118. package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
  119. package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
  120. package/dist/ui/icons/images/icon-cog@2x.js +3 -0
  121. package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
  122. package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
  123. package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
  124. package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
  125. package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
  126. package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
  127. package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
  128. package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
  129. package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
  130. package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
  131. package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
  132. package/dist/ui/icons/images/icon-layers@2x.js +3 -0
  133. package/dist/ui/icons/images/icon-linked@2x.js +3 -0
  134. package/dist/ui/icons/images/icon-locked@2x.js +3 -0
  135. package/dist/ui/icons/images/icon-moon@2x.js +3 -0
  136. package/dist/ui/icons/images/icon-pan@2x.js +3 -0
  137. package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
  138. package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
  139. package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
  140. package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
  141. package/dist/ui/icons/images/icon-redo@2x.js +3 -0
  142. package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
  143. package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
  144. package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
  145. package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
  146. package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
  147. package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
  148. package/dist/ui/icons/images/icon-star@2x.js +3 -0
  149. package/dist/ui/icons/images/icon-sun@2x.js +3 -0
  150. package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
  151. package/dist/ui/icons/images/icon-undo@2x.js +3 -0
  152. package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
  153. package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
  154. package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
  155. package/dist/ui/icons/images/icon-visible@2x.js +3 -0
  156. package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
  157. package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
  158. package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
  159. package/dist/ui/icons/index.d.ts +1 -1
  160. package/dist/ui/icons/index.js +84 -72
  161. package/dist/ui/index.d.ts +13 -2
  162. package/dist/ui/index.js +16 -4
  163. package/dist/ui/layout/index.d.ts +9 -1
  164. package/dist/ui/layout/index.js +11 -2
  165. package/dist/ui/tokens/index.d.ts +5 -4
  166. package/dist/ui/tokens/index.js +15 -8
  167. package/locales/en-GB.json +96 -3
  168. package/locales/en-US.json +96 -3
  169. package/locales/fr.json +96 -3
  170. package/locales/ja.json +96 -3
  171. package/locales/ko.json +96 -3
  172. package/locales/zh-Hans.json +96 -3
  173. package/locales/zh-Hant.json +96 -3
  174. package/package.json +11 -4
  175. package/dist/ui/elements/PcBrushMenu/style.js +0 -3
  176. package/dist/ui/elements/PcLayersMenu/style.js +0 -3
  177. package/dist/ui/icons/images/brush.js +0 -3
  178. package/dist/ui/icons/images/circle-mask.js +0 -3
  179. package/dist/ui/icons/images/clipboard.js +0 -3
  180. package/dist/ui/icons/images/cursor.js +0 -3
  181. package/dist/ui/icons/images/eraser.js +0 -3
  182. package/dist/ui/icons/images/eyedropper.js +0 -3
  183. package/dist/ui/icons/images/flip-horizontal.js +0 -3
  184. package/dist/ui/icons/images/flip-vertical.js +0 -3
  185. package/dist/ui/icons/images/focus-pen.js +0 -3
  186. package/dist/ui/icons/images/freehand-mask.js +0 -3
  187. package/dist/ui/icons/images/hidden.js +0 -3
  188. package/dist/ui/icons/images/layers.js +0 -3
  189. package/dist/ui/icons/images/linked.js +0 -3
  190. package/dist/ui/icons/images/locked.js +0 -3
  191. package/dist/ui/icons/images/moon.js +0 -3
  192. package/dist/ui/icons/images/pan.js +0 -3
  193. package/dist/ui/icons/images/polygon-mask.js +0 -3
  194. package/dist/ui/icons/images/pressure.js +0 -3
  195. package/dist/ui/icons/images/randomise.js +0 -3
  196. package/dist/ui/icons/images/rectangle-mask.js +0 -3
  197. package/dist/ui/icons/images/redo.js +0 -3
  198. package/dist/ui/icons/images/rotate-90-left.js +0 -3
  199. package/dist/ui/icons/images/rotate-90-right.js +0 -3
  200. package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
  201. package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
  202. package/dist/ui/icons/images/smoothing-linear.js +0 -3
  203. package/dist/ui/icons/images/star.js +0 -3
  204. package/dist/ui/icons/images/sun.js +0 -3
  205. package/dist/ui/icons/images/tilt.js +0 -3
  206. package/dist/ui/icons/images/undo.js +0 -3
  207. package/dist/ui/icons/images/unlinked.js +0 -3
  208. package/dist/ui/icons/images/unlocked.js +0 -3
  209. package/dist/ui/icons/images/visible.js +0 -3
  210. package/dist/ui/icons/images/zoom-in.js +0 -3
  211. package/dist/ui/icons/images/zoom-out.js +0 -3
  212. package/dist/ui/icons/images/zoom.js +0 -3
@@ -10,9 +10,20 @@ var TEST_AREA_WIDTH = 800;
10
10
  var TEST_AREA_HEIGHT = 320;
11
11
  /** The colour test strokes are drawn in, whatever the current colour */
12
12
  var TEST_COLOUR = "#000000";
13
+ /** The colour of the circles showing a stroke's sampled points, while stroke points are shown */
14
+ var POINT_COLOUR = "#e8175d";
15
+ /** The colour of the dots showing where a stroke placed its stamps, while stroke points are shown */
16
+ var STAMP_COLOUR = "#1e88e5";
17
+ /** How long a finished stroke's points stay shown (milliseconds) */
18
+ var STROKE_POINTS_MS = 3e3;
19
+ /** Radius of the circles showing a stroke's sampled points (test area pixels) */
20
+ var POINT_RADIUS = 8;
13
21
  /**
14
22
  * A small area to try brush settings before saving them: strokes are drawn in black, at the settings' own size, on a
15
23
  * white background at double resolution (800 × 320 pixels shown in the window's width). "Clear" (a red pill) empties it.
24
+ * "Show stroke points" draws the stroke's sampled points (pink circles) and where it placed stamps (blue dots) over it,
25
+ * like the canvas's debug overlay, to see how sampling and smoothing work. They go 3 seconds after the stroke ends, or
26
+ * on "Clear".
16
27
  *
17
28
  * @example
18
29
  * html`<pc-brush-test .config=${config} .tips=${app.tips}></pc-brush-test>`
@@ -21,9 +32,17 @@ var PcBrushTest = class extends PcElement {
21
32
  static properties = {
22
33
  ...PcElement.properties,
23
34
  config: { attribute: false },
24
- tips: { attribute: false }
35
+ tips: { attribute: false },
36
+ showPoints: {
37
+ type: Boolean,
38
+ attribute: "show-points"
39
+ }
25
40
  };
26
41
  static styles = [PcElement.styles, unsafeCSS(style_default)];
42
+ constructor() {
43
+ super();
44
+ this.showPoints = false;
45
+ }
27
46
  /** Creates the layer and stroke canvases (replaceable for tests) */
28
47
  createCanvas = createOffscreenCanvas;
29
48
  layer;
@@ -31,8 +50,11 @@ var PcBrushTest = class extends PcElement {
31
50
  /** The pointer drawing, if any */
32
51
  pointerId;
33
52
  onEngineChange = () => this.paint();
53
+ /** Clears the finished stroke's points once they've been shown long enough */
54
+ pointsTimer;
34
55
  disconnectedCallback() {
35
56
  super.disconnectedCallback();
57
+ clearTimeout(this.pointsTimer);
36
58
  this.engine?.removeEventListener("change", this.onEngineChange);
37
59
  }
38
60
  /** The visible canvas */
@@ -45,6 +67,7 @@ var PcBrushTest = class extends PcElement {
45
67
  if (!this.engine) {
46
68
  this.engine = new StrokeEngine(800, 320, this.createCanvas);
47
69
  this.engine.addEventListener("change", this.onEngineChange);
70
+ this.engine.setDebug(this.showPoints);
48
71
  }
49
72
  return {
50
73
  layer: this.layer,
@@ -72,6 +95,7 @@ var PcBrushTest = class extends PcElement {
72
95
  const config = this.config;
73
96
  if (!config || !this.tips || event.button !== 0) return;
74
97
  const { layer, engine } = this.drawing;
98
+ clearTimeout(this.pointsTimer);
75
99
  this.pointerId = event.pointerId;
76
100
  event.currentTarget.setPointerCapture?.(event.pointerId);
77
101
  engine.begin({
@@ -98,12 +122,29 @@ var PcBrushTest = class extends PcElement {
98
122
  this.pointerId = void 0;
99
123
  this.engine?.end(this.point(event), event.timeStamp);
100
124
  this.paint();
125
+ clearTimeout(this.pointsTimer);
126
+ this.pointsTimer = setTimeout(() => this.clearPoints(), STROKE_POINTS_MS);
127
+ }
128
+ /** Stop showing the last stroke's points and stamps (still recording the next stroke's while shown) */
129
+ clearPoints() {
130
+ clearTimeout(this.pointsTimer);
131
+ this.engine?.setDebug(this.showPoints);
132
+ }
133
+ /**
134
+ * Show or hide the last stroke's sampled points and stamps. They're recorded from the next stroke.
135
+ */
136
+ setShowPoints(shown) {
137
+ const changed = shown !== this.showPoints;
138
+ this.showPoints = shown;
139
+ if (changed) this.engine?.setDebug(shown);
140
+ this.paint();
101
141
  }
102
142
  /**
103
143
  * Empty the testing area.
104
144
  */
105
145
  clear() {
106
146
  this.engine?.cancel();
147
+ this.clearPoints();
107
148
  this.layer?.context.clearRect(0, 0, 800, 320);
108
149
  this.paint();
109
150
  }
@@ -114,6 +155,23 @@ var PcBrushTest = class extends PcElement {
114
155
  ctx.clearRect(0, 0, 800, 320);
115
156
  const image = this.engine?.preview?.image ?? this.layer?.canvas;
116
157
  if (image) ctx.drawImage(image, 0, 0);
158
+ if (this.showPoints && this.engine) this.paintPoints(ctx, this.engine);
159
+ }
160
+ /** Draw the last stroke's sampled points (circles) and stamp positions (dots) */
161
+ paintPoints(ctx, engine) {
162
+ const { points, stamps } = engine.debugTrace;
163
+ ctx.save();
164
+ ctx.fillStyle = STAMP_COLOUR;
165
+ for (const { x, y } of stamps) ctx.fillRect(x - 2, y - 2, 4, 4);
166
+ ctx.strokeStyle = POINT_COLOUR;
167
+ ctx.lineWidth = 2;
168
+ ctx.beginPath();
169
+ for (const { x, y } of points) {
170
+ ctx.moveTo(x + POINT_RADIUS, y);
171
+ ctx.arc(x, y, POINT_RADIUS, 0, Math.PI * 2);
172
+ }
173
+ ctx.stroke();
174
+ ctx.restore();
117
175
  }
118
176
  render() {
119
177
  return html`
@@ -126,11 +184,21 @@ var PcBrushTest = class extends PcElement {
126
184
  @pointerup=${this.onPointerUp}
127
185
  @pointercancel=${this.onPointerUp}
128
186
  ></canvas>
129
- <button type="button" class="clear" @click=${() => this.clear()}>
130
- ${this.t("paintCanvas.common.clear")}
131
- </button>
187
+ <div class="actions">
188
+ <button type="button" class="clear" @click=${() => this.clear()}>
189
+ ${this.t("paintCanvas.common.clear")}
190
+ </button>
191
+ <label class="show-points">
192
+ <input
193
+ type="checkbox"
194
+ .checked=${this.showPoints}
195
+ @change=${(e) => this.setShowPoints(e.target.checked)}
196
+ />
197
+ ${this.t("paintCanvas.brushConfig.showStrokePoints")}
198
+ </label>
199
+ </div>
132
200
  `;
133
201
  }
134
202
  };
135
203
  defineElement("pc-brush-test", PcBrushTest);
136
- export { PcBrushTest, TEST_AREA_HEIGHT, TEST_AREA_WIDTH, TEST_COLOUR };
204
+ export { POINT_COLOUR, PcBrushTest, STAMP_COLOUR, STROKE_POINTS_MS, TEST_AREA_HEIGHT, TEST_AREA_WIDTH, TEST_COLOUR };
@@ -1,3 +1,3 @@
1
1
  // src/ui/elements/PcBrushTest/style.css?inline
2
- var style_default = ":host {\n display: block;\n}\ncanvas {\n display: block;\n width: 100%;\n background: #fff;\n border: 1px solid var(--pc-border);\n touch-action: none;\n}\n/* \"Clear\": a red pill */\nbutton.clear {\n margin-top: 6px;\n padding: 4px 16px;\n border: 0;\n border-radius: 999px;\n background: var(--pc-red);\n color: #ffffff;\n font: inherit;\n font-weight: bold;\n cursor: pointer;\n}\nbutton.clear:hover {\n filter: brightness(1.1);\n}\nbutton.clear:focus-visible {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n outline-offset: 2px;\n}\n";
2
+ var style_default = ":host {\n display: block;\n}\ncanvas {\n display: block;\n width: 100%;\n background: #fff;\n border: 1px solid var(--pc-border);\n touch-action: none;\n}\n/* \"Clear\", then \"Show stroke points\" */\n.actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 12px;\n margin-top: 6px;\n}\n.show-points {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n cursor: pointer;\n}\n/* \"Clear\": a red pill */\nbutton.clear {\n padding: 4px 16px;\n border: 0;\n border-radius: 999px;\n background: var(--pc-red);\n color: #ffffff;\n font: inherit;\n font-weight: bold;\n cursor: pointer;\n}\nbutton.clear:hover {\n filter: brightness(1.1);\n}\nbutton.clear:focus-visible {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n outline-offset: 2px;\n}\n";
3
3
  export { style_default as default };
@@ -123,6 +123,8 @@ export declare class PcCanvas extends PcElement {
123
123
  private drag;
124
124
  /** Pointer drawing the current stroke, and the layer it draws on */
125
125
  private stroke;
126
+ /** A press that closed a panel or menu, held until it moves far enough to count (see `onPointerDown`) */
127
+ private heldPress;
126
128
  /** Pointer position over the stage (for the brush cursor), or null when outside */
127
129
  private hover;
128
130
  /** Name of the key held to zoom with the scroll wheel (replaceable for tests) */
@@ -256,15 +258,33 @@ export declare class PcCanvas extends PcElement {
256
258
  * Get a pointer's position relative to the stage.
257
259
  */
258
260
  private pointerPoint;
261
+ /**
262
+ * Send a pointer's events to the canvas until it's lifted, even outside it.
263
+ */
264
+ private capturePointer;
265
+ /**
266
+ * Handle a press, unless it closed a panel or menu (see `isDismissingPress`): that press is held, and only starts
267
+ * drawing (or whatever the tool does) once it moves `DRAG_THRESHOLD` pixels, so tapping the canvas to close a panel
268
+ * leaves no mark.
269
+ */
270
+ private onPointerDown;
271
+ /**
272
+ * Start a held press (see `onPointerDown`) once it moves far enough. Returns whether the pointer is still held.
273
+ */
274
+ private releaseHeldPress;
259
275
  /**
260
276
  * Start panning, or zoom, depending on the tool and button. In focus pen mode, touches only pan, pinch and make
261
277
  * gestures, and are ignored while a pen is down or just lifted (see `focusPenTouch`).
262
278
  */
263
- private onPointerDown;
279
+ private pointerDown;
264
280
  /**
265
281
  * Continue a stroke (using every in-between event the browser recorded), pan while dragging, and move the brush cursor.
266
282
  */
267
283
  private onPointerMove;
284
+ /**
285
+ * Forget a held press lifted (or cancelled) before it moved far enough. Returns whether it was one.
286
+ */
287
+ private dropHeldPress;
268
288
  /**
269
289
  * Finish a stroke (writing it into the layer) or stop panning.
270
290
  */
@@ -301,7 +321,7 @@ export declare class PcCanvas extends PcElement {
301
321
  */
302
322
  private beginSample;
303
323
  /**
304
- * Set the current colour from the sample: the pixel under the pointer, or the average of every pixel in the area.
324
+ * Set the selected colour (foreground or background) from the sample: the pixel under the pointer, or the average of every pixel in the area.
305
325
  * Nothing changes if it's outside the canvas.
306
326
  */
307
327
  private finishSample;
@@ -1,9 +1,12 @@
1
+ import { Tool } from "../../../lib/classes/PaintCanvas/tool.js";
1
2
  import { unionRect } from "../../../lib/brushes/StrokeEngine/index.js";
2
3
  import style_default from "./style.js";
3
4
  import { NO_TEXT_SELECTION_CSS } from "../../styles/index.js";
4
5
  import { PcElement, defineElement } from "../../base/index.js";
5
6
  import ChangeController from "../../ChangeController/index.js";
6
- import { Tool } from "../../../lib/classes/PaintCanvas/index.js";
7
+ import { isDismissingPress } from "../../dismiss/index.js";
8
+ import "../../drag/index.js";
9
+ import "../../../lib/classes/PaintCanvas/index.js";
7
10
  import { PASTEBOARD_COLOUR, drawScene } from "../../../lib/rendering/compositing/index.js";
8
11
  import { drawBrushCursor } from "../../../lib/rendering/cursor/index.js";
9
12
  import { drawLassoPreview, drawMaskOutline } from "../../../lib/rendering/maskOutline/index.js";
@@ -171,6 +174,8 @@ var PcCanvas = class extends PcElement {
171
174
  drag = null;
172
175
  /** Pointer drawing the current stroke, and the layer it draws on */
173
176
  stroke = null;
177
+ /** A press that closed a panel or menu, held until it moves far enough to count (see `onPointerDown`) */
178
+ heldPress = null;
174
179
  /** Pointer position over the stage (for the brush cursor), or null when outside */
175
180
  hover = null;
176
181
  /** Name of the key held to zoom with the scroll wheel (replaceable for tests) */
@@ -424,7 +429,7 @@ var PcCanvas = class extends PcElement {
424
429
  point: app.viewport.screenToCanvas(screen),
425
430
  constrain: app.constrainProportions
426
431
  };
427
- event.currentTarget.setPointerCapture(event.pointerId);
432
+ this.capturePointer(event.pointerId);
428
433
  }
429
434
  /**
430
435
  * Update the transform box while dragging.
@@ -498,7 +503,7 @@ var PcCanvas = class extends PcElement {
498
503
  const point = this.pointerPoint(event);
499
504
  const first = app.lasso.nodes[0];
500
505
  const nearFirstNode = first !== void 0 && isNear(app.viewport.canvasToScreen(first), point, 8);
501
- if (kind !== "polygon") event.currentTarget.setPointerCapture(event.pointerId);
506
+ if (kind !== "polygon") this.capturePointer(event.pointerId);
502
507
  app.applyLasso(app.lasso.down(kind, app.viewport.screenToCanvas(point), event, nearFirstNode));
503
508
  }
504
509
  /**
@@ -562,16 +567,17 @@ var PcCanvas = class extends PcElement {
562
567
  pointerId: event.pointerId,
563
568
  layerId: layer.id
564
569
  };
565
- event.currentTarget.setPointerCapture(event.pointerId);
570
+ this.capturePointer(event.pointerId);
571
+ const erasing = app.activeTool === Tool.Eraser;
566
572
  app.strokes.begin({
567
573
  layer,
568
- mode: app.activeTool === Tool.Eraser ? "erase" : "paint",
574
+ mode: erasing ? "erase" : "paint",
569
575
  preset: settings.preset,
570
576
  size: {
571
577
  width: settings.width,
572
578
  height: settings.height
573
579
  },
574
- colour: app.colour,
580
+ colour: app.colours[erasing ? "background" : "foreground"],
575
581
  tip: app.tips.get(settings.preset),
576
582
  isPen: event.pointerType === "pen",
577
583
  mask: app.selection.mask
@@ -592,12 +598,42 @@ var PcCanvas = class extends PcElement {
592
598
  };
593
599
  }
594
600
  /**
601
+ * Send a pointer's events to the canvas until it's lifted, even outside it.
602
+ */
603
+ capturePointer(pointerId) {
604
+ this.renderRoot.querySelector("canvas.scene")?.setPointerCapture?.(pointerId);
605
+ }
606
+ /**
607
+ * Handle a press, unless it closed a panel or menu (see `isDismissingPress`): that press is held, and only starts
608
+ * drawing (or whatever the tool does) once it moves `DRAG_THRESHOLD` pixels, so tapping the canvas to close a panel
609
+ * leaves no mark.
610
+ */
611
+ onPointerDown(event) {
612
+ if (!this.app) return;
613
+ if (!isDismissingPress(event)) {
614
+ this.pointerDown(event);
615
+ return;
616
+ }
617
+ this.heldPress = event;
618
+ this.capturePointer(event.pointerId);
619
+ }
620
+ /**
621
+ * Start a held press (see `onPointerDown`) once it moves far enough. Returns whether the pointer is still held.
622
+ */
623
+ releaseHeldPress(event) {
624
+ const held = this.heldPress;
625
+ if (held?.pointerId !== event.pointerId) return false;
626
+ if (Math.hypot(event.clientX - held.clientX, event.clientY - held.clientY) < 4) return true;
627
+ this.heldPress = null;
628
+ this.pointerDown(held);
629
+ return false;
630
+ }
631
+ /**
595
632
  * Start panning, or zoom, depending on the tool and button. In focus pen mode, touches only pan, pinch and make
596
633
  * gestures, and are ignored while a pen is down or just lifted (see `focusPenTouch`).
597
634
  */
598
- onPointerDown(event) {
635
+ pointerDown(event) {
599
636
  const app = this.app;
600
- if (!app) return;
601
637
  if (event.pointerType === "pen") this.penDown(event);
602
638
  if (event.pointerType === "touch") {
603
639
  const ignored = this.focusPenTouch(event);
@@ -630,7 +666,7 @@ var PcCanvas = class extends PcElement {
630
666
  pointerId: event.pointerId,
631
667
  last: point
632
668
  };
633
- event.currentTarget.setPointerCapture(event.pointerId);
669
+ this.capturePointer(event.pointerId);
634
670
  this.updateCursor();
635
671
  } else if (app.activeTool === Tool.Zoom && event.button === 0) {
636
672
  if (event.altKey) app.viewport.zoomOut(point);
@@ -643,6 +679,7 @@ var PcCanvas = class extends PcElement {
643
679
  onPointerMove(event) {
644
680
  const app = this.app;
645
681
  if (!app || this.ignoredTouches.has(event.pointerId)) return;
682
+ if (this.releaseHeldPress(event)) return;
646
683
  const point = this.pointerPoint(event);
647
684
  if (this.touches.has(event.pointerId)) {
648
685
  this.touches.set(event.pointerId, point);
@@ -678,10 +715,18 @@ var PcCanvas = class extends PcElement {
678
715
  this.requestOverlay();
679
716
  }
680
717
  /**
718
+ * Forget a held press lifted (or cancelled) before it moved far enough. Returns whether it was one.
719
+ */
720
+ dropHeldPress(event) {
721
+ if (this.heldPress?.pointerId !== event.pointerId) return false;
722
+ this.heldPress = null;
723
+ return true;
724
+ }
725
+ /**
681
726
  * Finish a stroke (writing it into the layer) or stop panning.
682
727
  */
683
728
  onPointerUp(event) {
684
- if (this.ignoredTouches.delete(event.pointerId)) return;
729
+ if (this.ignoredTouches.delete(event.pointerId) || this.dropHeldPress(event)) return;
685
730
  this.penUp(event);
686
731
  this.touchUp(event);
687
732
  if (this.sampling?.pointerId === event.pointerId) this.finishSample();
@@ -711,7 +756,7 @@ var PcCanvas = class extends PcElement {
711
756
  * Abandon a stroke or pan when the browser cancels the pointer (e.g. a system gesture).
712
757
  */
713
758
  onPointerCancel(event) {
714
- if (this.ignoredTouches.delete(event.pointerId)) return;
759
+ if (this.ignoredTouches.delete(event.pointerId) || this.dropHeldPress(event)) return;
715
760
  this.navigationTouches.delete(event.pointerId);
716
761
  this.penUp(event);
717
762
  this.touchUp(event);
@@ -789,7 +834,7 @@ var PcCanvas = class extends PcElement {
789
834
  this.drag = null;
790
835
  const [a, b] = [...this.touches.values()];
791
836
  this.pinch = touchPair(a, b);
792
- event.currentTarget.setPointerCapture(event.pointerId);
837
+ this.capturePointer(event.pointerId);
793
838
  }
794
839
  return true;
795
840
  }
@@ -807,12 +852,12 @@ var PcCanvas = class extends PcElement {
807
852
  point,
808
853
  screen
809
854
  };
810
- event.currentTarget.setPointerCapture(event.pointerId);
855
+ this.capturePointer(event.pointerId);
811
856
  this.updateCursor();
812
857
  this.drawOverlay();
813
858
  }
814
859
  /**
815
- * Set the current colour from the sample: the pixel under the pointer, or the average of every pixel in the area.
860
+ * Set the selected colour (foreground or background) from the sample: the pixel under the pointer, or the average of every pixel in the area.
816
861
  * Nothing changes if it's outside the canvas.
817
862
  */
818
863
  finishSample() {
@@ -820,7 +865,7 @@ var PcCanvas = class extends PcElement {
820
865
  const { start, point } = this.sampling;
821
866
  const colour = app.sampleMode === "area" ? app.sampleAreaColour(start, point) : app.sampleColour(point);
822
867
  this.endSample();
823
- if (colour) app.setColour(colour);
868
+ if (colour) app.setColour(colour, app.selectedColour);
824
869
  }
825
870
  /** Stop sampling and close the sampling window */
826
871
  endSample() {
@@ -1,3 +1,3 @@
1
1
  // src/ui/elements/PcCanvasMap/style.css?inline
2
- var style_default = ":host {\n display: block;\n position: relative;\n touch-action: none;\n /* A solid grey border, drawn outside so the map and its frame keep their size and position */\n outline: 1px solid var(--pc-border);\n}\ncanvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n/* The part of the canvas the user can see */\n.frame {\n position: absolute;\n outline: 1px solid var(--pc-text);\n pointer-events: none;\n}\n";
2
+ var style_default = ":host {\n display: block;\n position: relative;\n touch-action: none;\n /* A solid grey border, drawn outside so the map and its frame keep their size and position */\n outline: 1px solid var(--pc-map-border);\n}\ncanvas {\n display: block;\n width: 100%;\n height: 100%;\n}\n/* The part of the canvas the user can see */\n.frame {\n position: absolute;\n outline: 1px solid var(--pc-map-frame);\n pointer-events: none;\n}\n";
3
3
  export { style_default as default };
@@ -0,0 +1,48 @@
1
+ import { nothing } from "lit";
2
+ import { PcElement } from "../../base";
3
+ import PaintCanvas, { Tool } from "../../../lib/classes/PaintCanvas";
4
+ import "../PcContextMenu";
5
+ /** The tools that cut, copy and paste are shown for: the brush, eraser, lassos and Move tool */
6
+ export declare const CLIPBOARD_TOOLS: readonly Tool[];
7
+ /**
8
+ * Cut, copy and paste buttons for the canvas's masked image data, shown for the brush, eraser, lassos and Move tool
9
+ * (see `CLIPBOARD_TOOLS`). Cut and copy work while there's a mask and no transform is pending; paste while there's
10
+ * copied image data. While `narrow`, they're one ⋮ menu (`pc-context-menu`) instead.
11
+ *
12
+ * `<paint-canvas>` puts it at the end of its top pane, after the tool settings.
13
+ *
14
+ * @example
15
+ * html`<pc-clipboard-tools .app=${app} ?narrow=${narrow}></pc-clipboard-tools>`
16
+ */
17
+ export declare class PcClipboardTools extends PcElement {
18
+ static properties: {
19
+ app: {
20
+ attribute: boolean;
21
+ };
22
+ narrow: {
23
+ type: BooleanConstructor;
24
+ reflect: boolean;
25
+ };
26
+ translations: {
27
+ attribute: boolean;
28
+ };
29
+ icons: {
30
+ attribute: boolean;
31
+ };
32
+ theme: {
33
+ type: StringConstructor;
34
+ reflect: boolean;
35
+ };
36
+ };
37
+ static styles: import("lit").CSSResultGroup[];
38
+ app?: PaintCanvas;
39
+ /** Whether the buttons are one ⋮ menu (e.g. while the layout is narrow) */
40
+ narrow: boolean;
41
+ private changes;
42
+ constructor();
43
+ /** Cut, copy and paste, each with whether it can be done now */
44
+ private actions;
45
+ /** Hide for tools without clipboard actions (e.g. zoom) */
46
+ protected willUpdate(): void;
47
+ render(): import("lit").TemplateResult<1> | typeof nothing | import("lit").TemplateResult<1>[];
48
+ }
@@ -0,0 +1,118 @@
1
+ import { Tool } from "../../../lib/classes/PaintCanvas/tool.js";
2
+ import { PcElement, defineElement } from "../../base/index.js";
3
+ import ChangeController from "../../ChangeController/index.js";
4
+ import { PILLS_CSS } from "../../styles/index.js";
5
+ import "../../../lib/classes/PaintCanvas/index.js";
6
+ import "../PcContextMenu/index.js";
7
+ import { css, html, nothing, unsafeCSS } from "lit";
8
+ // src/ui/elements/PcClipboardTools/index.ts
9
+ /** The tools that cut, copy and paste are shown for: the brush, eraser, lassos and Move tool */
10
+ var CLIPBOARD_TOOLS = [
11
+ Tool.Brush,
12
+ Tool.Eraser,
13
+ Tool.RectangleLasso,
14
+ Tool.CircleLasso,
15
+ Tool.PolygonLasso,
16
+ Tool.FreeformLasso,
17
+ Tool.Move
18
+ ];
19
+ /**
20
+ * Cut, copy and paste buttons for the canvas's masked image data, shown for the brush, eraser, lassos and Move tool
21
+ * (see `CLIPBOARD_TOOLS`). Cut and copy work while there's a mask and no transform is pending; paste while there's
22
+ * copied image data. While `narrow`, they're one ⋮ menu (`pc-context-menu`) instead.
23
+ *
24
+ * `<paint-canvas>` puts it at the end of its top pane, after the tool settings.
25
+ *
26
+ * @example
27
+ * html`<pc-clipboard-tools .app=${app} ?narrow=${narrow}></pc-clipboard-tools>`
28
+ */
29
+ var PcClipboardTools = class extends PcElement {
30
+ static properties = {
31
+ ...PcElement.properties,
32
+ app: { attribute: false },
33
+ narrow: {
34
+ type: Boolean,
35
+ reflect: true
36
+ }
37
+ };
38
+ static styles = [
39
+ PcElement.styles,
40
+ unsafeCSS(PILLS_CSS),
41
+ css`
42
+ :host {
43
+ display: flex;
44
+ flex: none;
45
+ align-items: center;
46
+ gap: 8px;
47
+ padding: 4px 8px;
48
+ background: var(--pc-bar);
49
+ border-bottom: 1px solid var(--pc-surface-active);
50
+ }
51
+ :host([hidden]) {
52
+ display: none;
53
+ }
54
+ `
55
+ ];
56
+ changes = new ChangeController(this, () => this.app ? [
57
+ this.app,
58
+ this.app.selection,
59
+ this.app.transform
60
+ ] : []);
61
+ constructor() {
62
+ super();
63
+ this.narrow = false;
64
+ }
65
+ /** Cut, copy and paste, each with whether it can be done now */
66
+ actions(app) {
67
+ const noCopy = !app.selection.active || app.transform.pending;
68
+ return [
69
+ {
70
+ name: "cut",
71
+ label: this.t("paintCanvas.common.cut"),
72
+ disabled: noCopy,
73
+ run: () => app.cut()
74
+ },
75
+ {
76
+ name: "copy",
77
+ label: this.t("paintCanvas.common.copy"),
78
+ disabled: noCopy,
79
+ run: () => app.copy()
80
+ },
81
+ {
82
+ name: "paste",
83
+ label: this.t("paintCanvas.common.paste"),
84
+ disabled: !app.hasClipboard,
85
+ run: () => app.paste()
86
+ }
87
+ ];
88
+ }
89
+ /** Hide for tools without clipboard actions (e.g. zoom) */
90
+ willUpdate() {
91
+ this.hidden = !this.app || !CLIPBOARD_TOOLS.includes(this.app.tool);
92
+ }
93
+ render() {
94
+ const app = this.app;
95
+ if (!app || this.hidden) return nothing;
96
+ const actions = this.actions(app);
97
+ if (this.narrow) return html`<pc-context-menu
98
+ class="more"
99
+ label=${this.t("paintCanvas.toolSettings.moreActions")}
100
+ .items=${actions.map(({ name, label, disabled }) => ({
101
+ value: name,
102
+ label,
103
+ disabled
104
+ }))}
105
+ @pc-select=${(event) => actions.find((action) => action.name === event.detail.value)?.run()}
106
+ ></pc-context-menu>`;
107
+ return actions.map((action) => html`<button
108
+ type="button"
109
+ class="pill ${action.name}"
110
+ ?disabled=${action.disabled}
111
+ @click=${action.run}
112
+ >
113
+ ${action.label}
114
+ </button>`);
115
+ }
116
+ };
117
+ defineElement("pc-clipboard-tools", PcClipboardTools);
118
+ export { CLIPBOARD_TOOLS, PcClipboardTools };
@@ -0,0 +1,43 @@
1
+ import { PcElement } from "../../base";
2
+ import type { default as PaintCanvas } from "../../../lib/classes/PaintCanvas";
3
+ /** Event fired when the foreground or background colour is pressed (detail: `{ slot }`) */
4
+ export declare const COLOUR_CLICK_EVENT = "pc-colour-click";
5
+ /**
6
+ * The foreground and background colours as two overlapping circles in a square (the foreground at the top left), the
7
+ * one selected for configuration outlined and on top. Pressing either fires `pc-colour-click` with its `slot`; the
8
+ * implementer decides what it does (e.g. select it and open a colour palette). `openPicker()` opens the browser's colour
9
+ * picker for the selected colour, which sets it. Sized by `--pc-colour-pair-size` (50px by default).
10
+ *
11
+ * @example
12
+ * html`<pc-colour-pair .app=${app} @pc-colour-click=${(e) => app.selectColour(e.detail.slot)}></pc-colour-pair>`
13
+ */
14
+ export declare class PcColourPair extends PcElement {
15
+ static properties: {
16
+ app: {
17
+ attribute: boolean;
18
+ };
19
+ translations: {
20
+ attribute: boolean;
21
+ };
22
+ icons: {
23
+ attribute: boolean;
24
+ };
25
+ theme: {
26
+ type: StringConstructor;
27
+ reflect: boolean;
28
+ };
29
+ };
30
+ static styles: import("lit").CSSResultGroup[];
31
+ app?: PaintCanvas;
32
+ private changes;
33
+ /**
34
+ * Open the browser's colour picker for the selected colour (call it while handling a press, as browsers only open
35
+ * pickers for one).
36
+ */
37
+ openPicker(): void;
38
+ /** Tell listeners a colour was pressed */
39
+ private onClick;
40
+ /** Set the selected colour from the browser's colour picker */
41
+ private onPick;
42
+ render(): import("lit").TemplateResult<1>;
43
+ }