@lvl99dotcom/paintcanvas 0.6.0 → 0.7.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.
- package/README.md +2 -2
- package/dist/lib/classes/PaintCanvas/index.d.ts +13 -5
- package/dist/lib/classes/PaintCanvas/index.js +10 -6
- package/dist/lib/classes/UiSettings/index.d.ts +24 -2
- package/dist/lib/classes/UiSettings/index.js +32 -7
- package/dist/lib/settings/schema.d.ts +4 -1
- package/dist/lib/settings/schema.js +3 -2
- package/dist/lib/storage/index.d.ts +26 -0
- package/dist/lib/storage/index.js +38 -1
- package/dist/lib/validation/validators.generated.js +20 -2
- package/dist/lib/version/index.d.ts +25 -0
- package/dist/lib/version/index.js +35 -0
- package/dist/locales/en-GB.js +5 -0
- package/dist/locales/en-US.js +5 -0
- package/dist/locales/fr.js +5 -0
- package/dist/locales/ja.js +5 -0
- package/dist/locales/ko.js +5 -0
- package/dist/locales/zh-Hans.js +5 -0
- package/dist/locales/zh-Hant.js +5 -0
- package/dist/package.js +1 -1
- package/dist/ui/elements/PaintCanvas/index.d.ts +13 -3
- package/dist/ui/elements/PaintCanvas/index.js +22 -5
- package/dist/ui/elements/PcAppSettings/index.d.ts +10 -2
- package/dist/ui/elements/PcAppSettings/index.js +42 -7
- package/dist/ui/elements/PcBrushConfig/index.d.ts +1 -0
- package/dist/ui/elements/PcBrushConfig/index.js +8 -9
- package/dist/ui/elements/PcBrushTest/index.d.ts +1 -0
- package/dist/ui/elements/PcBrushTest/index.js +8 -7
- package/dist/ui/elements/PcBrushTest/style.js +1 -1
- package/dist/ui/elements/PcButtonMenu/index.d.ts +88 -0
- package/dist/ui/elements/PcButtonMenu/index.js +125 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +9 -0
- package/dist/ui/elements/PcCanvas/index.js +33 -17
- package/dist/ui/elements/PcClipboardTools/index.d.ts +7 -4
- package/dist/ui/elements/PcClipboardTools/index.js +20 -26
- package/dist/ui/elements/PcInputSettings/index.d.ts +2 -1
- package/dist/ui/elements/PcInputSettings/index.js +9 -15
- package/dist/ui/elements/PcLayerItem/index.d.ts +21 -6
- package/dist/ui/elements/PcLayerItem/index.js +53 -26
- package/dist/ui/elements/PcLayerItem/style.js +1 -1
- package/dist/ui/elements/PcSliderRange/index.js +3 -2
- package/dist/ui/elements/PcToggleCheckbox/index.d.ts +13 -2
- package/dist/ui/elements/PcToggleCheckbox/index.js +27 -8
- package/dist/ui/elements/PcToolSettings/index.d.ts +1 -0
- package/dist/ui/elements/PcToolSettings/index.js +8 -8
- package/dist/ui/elements/PcVersion/index.d.ts +12 -0
- package/dist/ui/elements/PcVersion/index.js +43 -0
- package/dist/ui/index.d.ts +3 -1
- package/dist/ui/index.js +4 -2
- package/dist/ui/layout/index.d.ts +2 -2
- package/dist/ui/layout/index.js +2 -2
- package/locales/en-GB.json +5 -0
- package/locales/en-US.json +5 -0
- package/locales/fr.json +5 -0
- package/locales/ja.json +5 -0
- package/locales/ko.json +5 -0
- package/locales/zh-Hans.json +5 -0
- package/locales/zh-Hant.json +5 -0
- package/package.json +2 -2
|
@@ -23,7 +23,8 @@ import "../PcDialog";
|
|
|
23
23
|
* - `left`: `<pc-toolbar>` (a pane) by default; toolbars in the sides are minimised (their tools one button that opens a
|
|
24
24
|
* menu of them) while the layout is narrow
|
|
25
25
|
* - `top`: a horizontal `<pc-pane>` with `<pc-tool-settings>` (for tools that have settings) and, at its end,
|
|
26
|
-
* `<pc-clipboard-tools>` (cut, copy and paste; a ⋮ menu while the layout is narrow
|
|
26
|
+
* `<pc-clipboard-tools>` (cut, copy and paste; a ⋮ menu while the layout is narrow or the window is narrower than
|
|
27
|
+
* 900px) by default
|
|
27
28
|
* - `right`: a `<pc-pane>` with `<pc-panel-layers>` by default
|
|
28
29
|
* - `center`: `<pc-canvas>` by default
|
|
29
30
|
* - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
|
|
@@ -36,7 +37,7 @@ import "../PcDialog";
|
|
|
36
37
|
* does by default), the toolbar has no layers menu button.
|
|
37
38
|
*
|
|
38
39
|
* Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
|
|
39
|
-
*
|
|
40
|
+
* floating@600"`), which the layout gives the side's `<pc-pane>`s (see `PcPane`): `docked` beside the canvas; `rail`, a
|
|
40
41
|
* column of buttons (one per panel) that open each panel over the canvas; `floating`, the pane over the canvas as a
|
|
41
42
|
* box that collapses to a button; or `overlay`, a rail over the canvas without a background (the canvas fills the
|
|
42
43
|
* width behind it). Give side content in a `<pc-pane>` to have it follow the side's mode.
|
|
@@ -50,7 +51,9 @@ import "../PcDialog";
|
|
|
50
51
|
* that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
|
|
51
52
|
*
|
|
52
53
|
* With a `settings-store` name, the user's app settings (input settings, keyboard shortcuts, colour palette, colours,
|
|
53
|
-
* theme and
|
|
54
|
+
* theme, language and rendering) are kept in the browser under that name (see `SettingsAutosave`). Fires `pc-toast` when
|
|
55
|
+
* they can't be. The rendering chosen there is also kept in `localStorage` (see `saveRenderingChoice`), so the canvas the
|
|
56
|
+
* layout makes draws where the user last chose (see `PaintCanvasOptions.rendering`).
|
|
54
57
|
*
|
|
55
58
|
* @example
|
|
56
59
|
* html`<paint-canvas style="height: 600px"></paint-canvas>`
|
|
@@ -154,6 +157,8 @@ export declare class PaintCanvasElement extends PcElement {
|
|
|
154
157
|
private watchedAutosave?;
|
|
155
158
|
/** Keeps the app settings under the `settings-store` name, if any */
|
|
156
159
|
private ownSettingsAutosave?;
|
|
160
|
+
/** Stops keeping the rendering chosen in the canvas's UI settings under the `settings-store` name, if kept */
|
|
161
|
+
private stopKeepingRendering?;
|
|
157
162
|
/** Whether drawing was blocked when shortcuts were last watched */
|
|
158
163
|
private blockedShortcuts;
|
|
159
164
|
/** Whether each side's slot has a layers menu (the toolbar's isn't needed then) */
|
|
@@ -204,6 +209,11 @@ export declare class PaintCanvasElement extends PcElement {
|
|
|
204
209
|
* saves any change waiting to be saved).
|
|
205
210
|
*/
|
|
206
211
|
private useSettingsStore;
|
|
212
|
+
/**
|
|
213
|
+
* Keep the rendering chosen in the canvas's UI settings (e.g. when they're restored, imported or changed) under a
|
|
214
|
+
* name, for the next time the layout makes a canvas. Returns what stops it.
|
|
215
|
+
*/
|
|
216
|
+
private keepRendering;
|
|
207
217
|
/** Show errors from the canvas's current autosave */
|
|
208
218
|
private watchAutosaveErrors;
|
|
209
219
|
/** Handle keyboard shortcuts for the current canvas, if asked to (not while blocked) */
|
|
@@ -5,7 +5,7 @@ import ChangeController from "../../ChangeController/index.js";
|
|
|
5
5
|
import PaintCanvas, { Panel } from "../../../lib/classes/PaintCanvas/index.js";
|
|
6
6
|
import KeyboardShortcuts, { SHORTCUT_EVENT } from "../../../lib/classes/KeyboardShortcuts/index.js";
|
|
7
7
|
import { isTouchDevice } from "../../../lib/classes/InputSettings/index.js";
|
|
8
|
-
import IndexedDbLayerStore, { IndexedDbSettingsStore } from "../../../lib/storage/index.js";
|
|
8
|
+
import IndexedDbLayerStore, { IndexedDbSettingsStore, loadRenderingChoice, saveRenderingChoice } from "../../../lib/storage/index.js";
|
|
9
9
|
import SettingsAutosave, { SETTINGS_AUTOSAVE_ERROR_EVENT } from "../../../lib/classes/SettingsAutosave/index.js";
|
|
10
10
|
import { AUTOSAVE_ERROR_EVENT, AutosaveState } from "../../../lib/classes/LayerAutosave/index.js";
|
|
11
11
|
import { isOwnAuthor } from "../../../lib/brushes/authors/index.js";
|
|
@@ -45,7 +45,8 @@ var TAKE_OVER_CHOICE = "take-over";
|
|
|
45
45
|
* - `left`: `<pc-toolbar>` (a pane) by default; toolbars in the sides are minimised (their tools one button that opens a
|
|
46
46
|
* menu of them) while the layout is narrow
|
|
47
47
|
* - `top`: a horizontal `<pc-pane>` with `<pc-tool-settings>` (for tools that have settings) and, at its end,
|
|
48
|
-
* `<pc-clipboard-tools>` (cut, copy and paste; a ⋮ menu while the layout is narrow
|
|
48
|
+
* `<pc-clipboard-tools>` (cut, copy and paste; a ⋮ menu while the layout is narrow or the window is narrower than
|
|
49
|
+
* 900px) by default
|
|
49
50
|
* - `right`: a `<pc-pane>` with `<pc-panel-layers>` by default
|
|
50
51
|
* - `center`: `<pc-canvas>` by default
|
|
51
52
|
* - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
|
|
@@ -58,7 +59,7 @@ var TAKE_OVER_CHOICE = "take-over";
|
|
|
58
59
|
* does by default), the toolbar has no layers menu button.
|
|
59
60
|
*
|
|
60
61
|
* Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
|
|
61
|
-
*
|
|
62
|
+
* floating@600"`), which the layout gives the side's `<pc-pane>`s (see `PcPane`): `docked` beside the canvas; `rail`, a
|
|
62
63
|
* column of buttons (one per panel) that open each panel over the canvas; `floating`, the pane over the canvas as a
|
|
63
64
|
* box that collapses to a button; or `overlay`, a rail over the canvas without a background (the canvas fills the
|
|
64
65
|
* width behind it). Give side content in a `<pc-pane>` to have it follow the side's mode.
|
|
@@ -72,7 +73,9 @@ var TAKE_OVER_CHOICE = "take-over";
|
|
|
72
73
|
* that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
|
|
73
74
|
*
|
|
74
75
|
* With a `settings-store` name, the user's app settings (input settings, keyboard shortcuts, colour palette, colours,
|
|
75
|
-
* theme and
|
|
76
|
+
* theme, language and rendering) are kept in the browser under that name (see `SettingsAutosave`). Fires `pc-toast` when
|
|
77
|
+
* they can't be. The rendering chosen there is also kept in `localStorage` (see `saveRenderingChoice`), so the canvas the
|
|
78
|
+
* layout makes draws where the user last chose (see `PaintCanvasOptions.rendering`).
|
|
76
79
|
*
|
|
77
80
|
* @example
|
|
78
81
|
* html`<paint-canvas style="height: 600px"></paint-canvas>`
|
|
@@ -127,6 +130,8 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
127
130
|
watchedAutosave;
|
|
128
131
|
/** Keeps the app settings under the `settings-store` name, if any */
|
|
129
132
|
ownSettingsAutosave;
|
|
133
|
+
/** Stops keeping the rendering chosen in the canvas's UI settings under the `settings-store` name, if kept */
|
|
134
|
+
stopKeepingRendering;
|
|
130
135
|
/** Whether drawing was blocked when shortcuts were last watched */
|
|
131
136
|
blockedShortcuts = false;
|
|
132
137
|
/** Whether each side's slot has a layers menu (the toolbar's isn't needed then) */
|
|
@@ -194,7 +199,7 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
194
199
|
return sideModeFor(parseSideModes(side === "left" ? this.leftModes : this.rightModes), this.width);
|
|
195
200
|
}
|
|
196
201
|
connectedCallback() {
|
|
197
|
-
this.app ??= new PaintCanvas();
|
|
202
|
+
this.app ??= new PaintCanvas({ rendering: this.settingsStore ? loadRenderingChoice(this.settingsStore) : void 0 });
|
|
198
203
|
super.connectedCallback();
|
|
199
204
|
this.resizeObserver?.observe(this);
|
|
200
205
|
this.addEventListener("pointerdown", this.onPointerDown, true);
|
|
@@ -284,10 +289,22 @@ var PaintCanvasElement = class extends PcElement {
|
|
|
284
289
|
*/
|
|
285
290
|
useSettingsStore() {
|
|
286
291
|
this.ownSettingsAutosave?.stop();
|
|
292
|
+
this.stopKeepingRendering?.();
|
|
287
293
|
const name = this.settingsStore || void 0;
|
|
294
|
+
this.stopKeepingRendering = name === void 0 ? void 0 : this.keepRendering(name);
|
|
288
295
|
this.ownSettingsAutosave = name === void 0 ? void 0 : new SettingsAutosave(this.app, new IndexedDbSettingsStore(name));
|
|
289
296
|
this.ownSettingsAutosave?.addEventListener(SETTINGS_AUTOSAVE_ERROR_EVENT, this.onSettingsAutosaveError);
|
|
290
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Keep the rendering chosen in the canvas's UI settings (e.g. when they're restored, imported or changed) under a
|
|
300
|
+
* name, for the next time the layout makes a canvas. Returns what stops it.
|
|
301
|
+
*/
|
|
302
|
+
keepRendering(name) {
|
|
303
|
+
const ui = this.app.ui;
|
|
304
|
+
const keep = () => saveRenderingChoice(name, ui.data.rendering);
|
|
305
|
+
ui.addEventListener("change", keep);
|
|
306
|
+
return () => ui.removeEventListener("change", keep);
|
|
307
|
+
}
|
|
291
308
|
/** Show errors from the canvas's current autosave */
|
|
292
309
|
watchAutosaveErrors() {
|
|
293
310
|
const autosave = this.app?.autosave;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { nothing } from "lit";
|
|
2
2
|
import { PcElement } from "../../base";
|
|
3
3
|
import "../PcShortcutSettings";
|
|
4
|
+
import "../PcToggleCheckbox";
|
|
4
5
|
import "../PcDialogWindow";
|
|
5
6
|
import type PaintCanvas from "../../../lib/classes/PaintCanvas";
|
|
6
7
|
import { type SaveMethod } from "../../../lib/rendering/screenshot";
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* The user's app settings: a "Draw with the GPU" switch (the user's `ui.data.rendering`, used from the next start, so it
|
|
10
|
+
* says when the page needs reloading), and buttons: "Keyboard shortcuts", which opens a window to change them (see
|
|
9
11
|
* `pc-shortcut-settings`), and "Import settings" and "Export settings", which read and write a settings JSON file (input
|
|
10
|
-
* settings, keyboard shortcuts, colour palette, colours, profile graphs, theme and
|
|
12
|
+
* settings, keyboard shortcuts, colour palette, colours, profile graphs, theme, language and rendering). The window is a shaded
|
|
11
13
|
* `<pc-dialog-window>` over the whole page; it closes with its ⨉ button, Escape or a press outside it, and focus goes
|
|
12
14
|
* back to the button. Fires `pc-toast` after importing, or when importing or exporting fails.
|
|
13
15
|
*
|
|
@@ -40,6 +42,7 @@ export declare class PcAppSettings extends PcElement {
|
|
|
40
42
|
shortcutsOpen: boolean;
|
|
41
43
|
/** Saves exported files (replaceable for tests) */
|
|
42
44
|
saveFile: (blob: Blob, name: string) => Promise<SaveMethod | undefined>;
|
|
45
|
+
private changes;
|
|
43
46
|
constructor();
|
|
44
47
|
/** Close the keyboard shortcuts window */
|
|
45
48
|
closeShortcuts(): void;
|
|
@@ -55,5 +58,10 @@ export declare class PcAppSettings extends PcElement {
|
|
|
55
58
|
private toastError;
|
|
56
59
|
/** The keyboard shortcuts window, with its contents only while open */
|
|
57
60
|
private renderShortcuts;
|
|
61
|
+
/**
|
|
62
|
+
* The switch that draws with the GPU (or the CPU) from the next start, saying when the page needs reloading to use
|
|
63
|
+
* the choice (see `PaintCanvas.rendering`).
|
|
64
|
+
*/
|
|
65
|
+
private renderRendering;
|
|
58
66
|
render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
59
67
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { SETTINGS_FILE_FORMAT } from "../../../lib/settings/schema.js";
|
|
2
2
|
import { ICON_CONTROLS_CSS, PILLS_CSS, WINDOWS_CSS } from "../../styles/index.js";
|
|
3
3
|
import { PcElement, defineElement, emit } from "../../base/index.js";
|
|
4
|
+
import ChangeController from "../../ChangeController/index.js";
|
|
4
5
|
import "../PcShortcutSettings/index.js";
|
|
6
|
+
import "../PcToggleCheckbox/index.js";
|
|
5
7
|
import "../PcDialogWindow/index.js";
|
|
6
8
|
import { SETTINGS_FILE_ERRORS, SettingsFileError, applySettings, collectSettings, exportSettingsFile, parseSettingsFile, settingsFileName } from "../../../lib/settings/index.js";
|
|
7
9
|
import { JSON_FILE_TYPE, saveFile } from "../../../lib/rendering/screenshot/index.js";
|
|
@@ -13,9 +15,10 @@ var SETTINGS_FILE_ERROR_KEYS = {
|
|
|
13
15
|
[SETTINGS_FILE_ERRORS.read]: "paintCanvas.settings.fileReadFailed"
|
|
14
16
|
};
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
18
|
+
* The user's app settings: a "Draw with the GPU" switch (the user's `ui.data.rendering`, used from the next start, so it
|
|
19
|
+
* says when the page needs reloading), and buttons: "Keyboard shortcuts", which opens a window to change them (see
|
|
17
20
|
* `pc-shortcut-settings`), and "Import settings" and "Export settings", which read and write a settings JSON file (input
|
|
18
|
-
* settings, keyboard shortcuts, colour palette, colours, profile graphs, theme and
|
|
21
|
+
* settings, keyboard shortcuts, colour palette, colours, profile graphs, theme, language and rendering). The window is a shaded
|
|
19
22
|
* `<pc-dialog-window>` over the whole page; it closes with its ⨉ button, Escape or a press outside it, and focus goes
|
|
20
23
|
* back to the button. Fires `pc-toast` after importing, or when importing or exporting fails.
|
|
21
24
|
*
|
|
@@ -44,11 +47,23 @@ var PcAppSettings = class extends PcElement {
|
|
|
44
47
|
font-family: var(--pc-font);
|
|
45
48
|
font-size: var(--pc-font-size);
|
|
46
49
|
}
|
|
50
|
+
/* One full-width button per row */
|
|
47
51
|
.actions {
|
|
48
52
|
display: flex;
|
|
49
|
-
flex-
|
|
53
|
+
flex-direction: column;
|
|
50
54
|
gap: var(--pc-space-2);
|
|
51
55
|
}
|
|
56
|
+
.actions button {
|
|
57
|
+
width: 100%;
|
|
58
|
+
}
|
|
59
|
+
.hint {
|
|
60
|
+
display: block;
|
|
61
|
+
color: var(--pc-text-muted);
|
|
62
|
+
font-size: 0.85em;
|
|
63
|
+
}
|
|
64
|
+
.pending {
|
|
65
|
+
color: var(--pc-text);
|
|
66
|
+
}
|
|
52
67
|
pc-dialog-window {
|
|
53
68
|
--pc-dialog-width: 480px;
|
|
54
69
|
}
|
|
@@ -60,6 +75,7 @@ var PcAppSettings = class extends PcElement {
|
|
|
60
75
|
];
|
|
61
76
|
/** Saves exported files (replaceable for tests) */
|
|
62
77
|
saveFile = (blob, name) => saveFile(blob, name, void 0, JSON_FILE_TYPE);
|
|
78
|
+
changes = new ChangeController(this, () => [this.app?.ui]);
|
|
63
79
|
constructor() {
|
|
64
80
|
super();
|
|
65
81
|
this.shortcutsOpen = false;
|
|
@@ -127,14 +143,33 @@ var PcAppSettings = class extends PcElement {
|
|
|
127
143
|
.settings=${app.shortcuts}
|
|
128
144
|
></pc-shortcut-settings>` : nothing}
|
|
129
145
|
</pc-dialog-window>`;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The switch that draws with the GPU (or the CPU) from the next start, saying when the page needs reloading to use
|
|
149
|
+
* the choice (see `PaintCanvas.rendering`).
|
|
150
|
+
*/
|
|
151
|
+
renderRendering(app) {
|
|
152
|
+
const chosen = app.ui.data.rendering;
|
|
153
|
+
const pending = chosen !== app.rendering;
|
|
154
|
+
return html`<pc-toggle-checkbox
|
|
155
|
+
class="gpu"
|
|
156
|
+
.checked=${chosen === "gpu"}
|
|
157
|
+
@change=${(e) => app.ui.update({ rendering: e.detail.checked ? "gpu" : "cpu" })}
|
|
158
|
+
>
|
|
159
|
+
${this.t("paintCanvas.settings.gpu")}
|
|
160
|
+
<span class="hint ${pending ? "pending" : ""}" aria-live="polite">
|
|
161
|
+
${this.t(pending ? "paintCanvas.settings.reloadToApply" : "paintCanvas.settings.gpuHint")}
|
|
162
|
+
</span>
|
|
163
|
+
</pc-toggle-checkbox>`;
|
|
130
164
|
}
|
|
131
165
|
render() {
|
|
132
166
|
const app = this.app;
|
|
133
167
|
if (!app) return nothing;
|
|
134
|
-
return html
|
|
168
|
+
return html`${this.renderRendering(app)}
|
|
169
|
+
<div class="actions">
|
|
135
170
|
<button
|
|
136
171
|
type="button"
|
|
137
|
-
class="pill
|
|
172
|
+
class="pill shortcuts"
|
|
138
173
|
aria-haspopup="dialog"
|
|
139
174
|
@click=${() => this.shortcutsOpen = true}
|
|
140
175
|
>
|
|
@@ -142,14 +177,14 @@ var PcAppSettings = class extends PcElement {
|
|
|
142
177
|
</button>
|
|
143
178
|
<button
|
|
144
179
|
type="button"
|
|
145
|
-
class="pill
|
|
180
|
+
class="pill import"
|
|
146
181
|
@click=${() => this.renderRoot.querySelector(".import-file")?.click()}
|
|
147
182
|
>
|
|
148
183
|
${this.t("paintCanvas.settings.import")}
|
|
149
184
|
</button>
|
|
150
185
|
<button
|
|
151
186
|
type="button"
|
|
152
|
-
class="pill
|
|
187
|
+
class="pill export"
|
|
153
188
|
@click=${() => void this.exportFile()}
|
|
154
189
|
>
|
|
155
190
|
${this.t("paintCanvas.settings.export")}
|
|
@@ -2,6 +2,7 @@ import { nothing } from "lit";
|
|
|
2
2
|
import { type ConfirmFn, type PromptFn } from "../../ask";
|
|
3
3
|
import { PcElement } from "../../base";
|
|
4
4
|
import "../PcBrushTest";
|
|
5
|
+
import "../PcToggleCheckbox";
|
|
5
6
|
import "../PcDialogWindow";
|
|
6
7
|
import "../PcSliderRange";
|
|
7
8
|
import "../PcProfileEditor";
|
|
@@ -4,6 +4,7 @@ import { iconLabel } from "../../icons/index.js";
|
|
|
4
4
|
import { askConfirm, askText, deletePresetQuestion } from "../../ask/index.js";
|
|
5
5
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
6
6
|
import "../PcBrushTest/index.js";
|
|
7
|
+
import "../PcToggleCheckbox/index.js";
|
|
7
8
|
import "../PcDialogWindow/index.js";
|
|
8
9
|
import "../PcSliderRange/index.js";
|
|
9
10
|
import "../PcProfileEditor/index.js";
|
|
@@ -500,16 +501,14 @@ var PcBrushConfig = class extends PcElement {
|
|
|
500
501
|
>
|
|
501
502
|
${this.single("spacing", this.t("paintCanvas.brushConfig.spacing"), draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
|
|
502
503
|
</div>
|
|
503
|
-
<
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
@change=${(e) => this.change({ snapToPixelGrid: e.target.checked })}
|
|
510
|
-
/>
|
|
504
|
+
<pc-toggle-checkbox
|
|
505
|
+
class="snap"
|
|
506
|
+
.checked=${draft.snapToPixelGrid ?? false}
|
|
507
|
+
?disabled=${this.busy}
|
|
508
|
+
@change=${(e) => this.change({ snapToPixelGrid: e.detail.checked })}
|
|
509
|
+
>
|
|
511
510
|
${this.t("paintCanvas.brushConfig.snapToPixelGrid")}
|
|
512
|
-
</
|
|
511
|
+
</pc-toggle-checkbox>
|
|
513
512
|
</div>
|
|
514
513
|
${this.editingId ? html`<button
|
|
515
514
|
slot="footer-start"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PcElement } from "../../base";
|
|
2
|
+
import "../PcToggleCheckbox";
|
|
2
3
|
import type { BrushConfig } from "../../../lib/brushes/types";
|
|
3
4
|
import type BrushTipCache from "../../../lib/brushes/BrushTipCache";
|
|
4
5
|
import { type CanvasFactory } from "../../../lib/classes/Layer";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import style_default from "./style.js";
|
|
2
2
|
import { PILLS_CSS } from "../../styles/index.js";
|
|
3
3
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
4
|
+
import "../PcToggleCheckbox/index.js";
|
|
4
5
|
import StrokeEngine from "../../../lib/brushes/StrokeEngine/index.js";
|
|
5
6
|
import Layer, { createOffscreenCanvas } from "../../../lib/classes/Layer/index.js";
|
|
6
7
|
import { html, unsafeCSS } from "lit";
|
|
@@ -193,14 +194,14 @@ var PcBrushTest = class extends PcElement {
|
|
|
193
194
|
<button type="button" class="pill clear" @click=${() => this.clear()}>
|
|
194
195
|
${this.t("paintCanvas.common.clear")}
|
|
195
196
|
</button>
|
|
196
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
<pc-toggle-checkbox
|
|
198
|
+
class="show-points"
|
|
199
|
+
size="s"
|
|
200
|
+
.checked=${this.showPoints}
|
|
201
|
+
@change=${(e) => this.setShowPoints(e.detail.checked)}
|
|
202
|
+
>
|
|
202
203
|
${this.t("paintCanvas.brushConfig.showStrokePoints")}
|
|
203
|
-
</
|
|
204
|
+
</pc-toggle-checkbox>
|
|
204
205
|
</div>
|
|
205
206
|
`;
|
|
206
207
|
}
|
|
@@ -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\", 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
|
|
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}\n/* \"Clear\": a red pill */\nbutton.pill.clear {\n background: var(--pc-red);\n color: #ffffff;\n}\n";
|
|
3
3
|
export { style_default as default };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { PcElement } from "../../base";
|
|
2
|
+
import { type PillSize } from "../../styles";
|
|
3
|
+
import type { MenuItem } from "../PcMenu";
|
|
4
|
+
import "../PcContextMenu";
|
|
5
|
+
/** An action in a button menu: what `pc-select` gives, its label and whether it can be done now */
|
|
6
|
+
export type ButtonMenuItem = Pick<MenuItem, "value" | "label" | "disabled">;
|
|
7
|
+
/**
|
|
8
|
+
* Whether the window is narrower than a width (CSS pixels). False without a width or `matchMedia`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* isNarrowerThan(900); // true in a 600px-wide window
|
|
12
|
+
*/
|
|
13
|
+
export declare const isNarrowerThan: (width: number | undefined, matchMedia?: ((query: string) => {
|
|
14
|
+
matches: boolean;
|
|
15
|
+
}) | undefined) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* A row of pill buttons for actions, which can become one ⋮ menu (`pc-context-menu`): while `menu` is set, or while the
|
|
18
|
+
* window is narrower than `menu-below` (CSS pixels; only when set). Fires `pc-select` (detail: `{ value }`) when an
|
|
19
|
+
* action is chosen either way.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* html`<pc-button-menu label="More actions" menu-below="900" size="pill-s"
|
|
23
|
+
* .items=${[{ value: "cut", label: "Cut" }, { value: "paste", label: "Paste", disabled: true }]}
|
|
24
|
+
* @pc-select=${(e) => run(e.detail.value)}></pc-button-menu>`
|
|
25
|
+
*/
|
|
26
|
+
export declare class PcButtonMenu extends PcElement {
|
|
27
|
+
static properties: {
|
|
28
|
+
items: {
|
|
29
|
+
attribute: boolean;
|
|
30
|
+
};
|
|
31
|
+
label: {
|
|
32
|
+
type: StringConstructor;
|
|
33
|
+
};
|
|
34
|
+
size: {
|
|
35
|
+
type: StringConstructor;
|
|
36
|
+
};
|
|
37
|
+
menu: {
|
|
38
|
+
type: BooleanConstructor;
|
|
39
|
+
reflect: boolean;
|
|
40
|
+
};
|
|
41
|
+
menuBelow: {
|
|
42
|
+
type: NumberConstructor;
|
|
43
|
+
attribute: string;
|
|
44
|
+
};
|
|
45
|
+
narrowWindow: {
|
|
46
|
+
state: boolean;
|
|
47
|
+
};
|
|
48
|
+
translations: {
|
|
49
|
+
attribute: boolean;
|
|
50
|
+
};
|
|
51
|
+
icons: {
|
|
52
|
+
attribute: boolean;
|
|
53
|
+
};
|
|
54
|
+
theme: {
|
|
55
|
+
type: StringConstructor;
|
|
56
|
+
reflect: boolean;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
static styles: import("lit").CSSResultGroup[];
|
|
60
|
+
/** The actions, in order */
|
|
61
|
+
items: ButtonMenuItem[];
|
|
62
|
+
/** What the ⋮ button is called (e.g. "More actions") */
|
|
63
|
+
label: string;
|
|
64
|
+
/** The buttons' pill size (`pill` by default) */
|
|
65
|
+
size: PillSize;
|
|
66
|
+
/** Whether the actions are always one ⋮ menu */
|
|
67
|
+
menu: boolean;
|
|
68
|
+
/** Window width (CSS pixels) below which the actions are one ⋮ menu; none by default */
|
|
69
|
+
menuBelow?: number;
|
|
70
|
+
/** Whether the window is narrower than `menuBelow` */
|
|
71
|
+
narrowWindow: boolean;
|
|
72
|
+
/** Follows the window's width while `menuBelow` is set */
|
|
73
|
+
private query?;
|
|
74
|
+
private readonly onQueryChange;
|
|
75
|
+
constructor();
|
|
76
|
+
/** Whether the actions show as one ⋮ menu now */
|
|
77
|
+
get showsMenu(): boolean;
|
|
78
|
+
connectedCallback(): void;
|
|
79
|
+
disconnectedCallback(): void;
|
|
80
|
+
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
81
|
+
/** Follow whether the window is narrower than `menuBelow` (when set and the browser can tell) */
|
|
82
|
+
private watchWidth;
|
|
83
|
+
/** Stop following the window's width */
|
|
84
|
+
private stopWatchingWidth;
|
|
85
|
+
/** Tell listeners an action was chosen */
|
|
86
|
+
private select;
|
|
87
|
+
render(): import("lit").TemplateResult<1> | import("lit").TemplateResult<1>[];
|
|
88
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { PcElement, defineElement, emit } from "../../base/index.js";
|
|
2
|
+
import { PILLS_CSS } from "../../styles/index.js";
|
|
3
|
+
import "../PcContextMenu/index.js";
|
|
4
|
+
import { css, html, unsafeCSS } from "lit";
|
|
5
|
+
// src/ui/elements/PcButtonMenu/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* Whether the window is narrower than a width (CSS pixels). False without a width or `matchMedia`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* isNarrowerThan(900); // true in a 600px-wide window
|
|
11
|
+
*/
|
|
12
|
+
var isNarrowerThan = (width, matchMedia = typeof window.matchMedia === "function" ? (query) => window.matchMedia(query) : void 0) => width !== void 0 && width > 0 && matchMedia !== void 0 && matchMedia(`(width < ${width}px)`).matches;
|
|
13
|
+
/**
|
|
14
|
+
* A row of pill buttons for actions, which can become one ⋮ menu (`pc-context-menu`): while `menu` is set, or while the
|
|
15
|
+
* window is narrower than `menu-below` (CSS pixels; only when set). Fires `pc-select` (detail: `{ value }`) when an
|
|
16
|
+
* action is chosen either way.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* html`<pc-button-menu label="More actions" menu-below="900" size="pill-s"
|
|
20
|
+
* .items=${[{ value: "cut", label: "Cut" }, { value: "paste", label: "Paste", disabled: true }]}
|
|
21
|
+
* @pc-select=${(e) => run(e.detail.value)}></pc-button-menu>`
|
|
22
|
+
*/
|
|
23
|
+
var PcButtonMenu = class extends PcElement {
|
|
24
|
+
static properties = {
|
|
25
|
+
...PcElement.properties,
|
|
26
|
+
items: { attribute: false },
|
|
27
|
+
label: { type: String },
|
|
28
|
+
size: { type: String },
|
|
29
|
+
menu: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
reflect: true
|
|
32
|
+
},
|
|
33
|
+
menuBelow: {
|
|
34
|
+
type: Number,
|
|
35
|
+
attribute: "menu-below"
|
|
36
|
+
},
|
|
37
|
+
narrowWindow: { state: true }
|
|
38
|
+
};
|
|
39
|
+
static styles = [
|
|
40
|
+
PcElement.styles,
|
|
41
|
+
unsafeCSS(PILLS_CSS),
|
|
42
|
+
css`
|
|
43
|
+
:host {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: var(--pc-space-2);
|
|
47
|
+
}
|
|
48
|
+
:host([hidden]) {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
`
|
|
52
|
+
];
|
|
53
|
+
/** Follows the window's width while `menuBelow` is set */
|
|
54
|
+
query;
|
|
55
|
+
onQueryChange = (event) => {
|
|
56
|
+
this.narrowWindow = event.matches;
|
|
57
|
+
};
|
|
58
|
+
constructor() {
|
|
59
|
+
super();
|
|
60
|
+
this.items = [];
|
|
61
|
+
this.label = "";
|
|
62
|
+
this.size = "pill";
|
|
63
|
+
this.menu = false;
|
|
64
|
+
this.narrowWindow = false;
|
|
65
|
+
}
|
|
66
|
+
/** Whether the actions show as one ⋮ menu now */
|
|
67
|
+
get showsMenu() {
|
|
68
|
+
return this.menu || this.narrowWindow;
|
|
69
|
+
}
|
|
70
|
+
connectedCallback() {
|
|
71
|
+
super.connectedCallback();
|
|
72
|
+
this.watchWidth();
|
|
73
|
+
}
|
|
74
|
+
disconnectedCallback() {
|
|
75
|
+
super.disconnectedCallback();
|
|
76
|
+
this.stopWatchingWidth();
|
|
77
|
+
}
|
|
78
|
+
willUpdate(changed) {
|
|
79
|
+
super.willUpdate(changed);
|
|
80
|
+
if (changed.has("menuBelow") && this.isConnected) this.watchWidth();
|
|
81
|
+
}
|
|
82
|
+
/** Follow whether the window is narrower than `menuBelow` (when set and the browser can tell) */
|
|
83
|
+
watchWidth() {
|
|
84
|
+
this.stopWatchingWidth();
|
|
85
|
+
this.narrowWindow = isNarrowerThan(this.menuBelow);
|
|
86
|
+
if (!this.menuBelow || typeof window.matchMedia !== "function") return;
|
|
87
|
+
this.query = window.matchMedia(`(width < ${this.menuBelow}px)`);
|
|
88
|
+
this.query.addEventListener?.("change", this.onQueryChange);
|
|
89
|
+
}
|
|
90
|
+
/** Stop following the window's width */
|
|
91
|
+
stopWatchingWidth() {
|
|
92
|
+
this.query?.removeEventListener?.("change", this.onQueryChange);
|
|
93
|
+
this.query = void 0;
|
|
94
|
+
}
|
|
95
|
+
/** Tell listeners an action was chosen */
|
|
96
|
+
select(value) {
|
|
97
|
+
emit(this, "pc-select", { value });
|
|
98
|
+
}
|
|
99
|
+
render() {
|
|
100
|
+
if (this.showsMenu) return html`<pc-context-menu
|
|
101
|
+
class="more"
|
|
102
|
+
label=${this.label}
|
|
103
|
+
.items=${this.items.map(({ value, label, disabled }) => ({
|
|
104
|
+
value,
|
|
105
|
+
label,
|
|
106
|
+
disabled
|
|
107
|
+
}))}
|
|
108
|
+
@pc-select=${(event) => {
|
|
109
|
+
event.stopPropagation();
|
|
110
|
+
this.select(event.detail.value);
|
|
111
|
+
}}
|
|
112
|
+
></pc-context-menu>`;
|
|
113
|
+
return this.items.map(({ value, label, disabled }) => html`<button
|
|
114
|
+
type="button"
|
|
115
|
+
class="${this.size} ${value}"
|
|
116
|
+
data-value=${value}
|
|
117
|
+
?disabled=${disabled}
|
|
118
|
+
@click=${() => this.select(value)}
|
|
119
|
+
>
|
|
120
|
+
${label}
|
|
121
|
+
</button>`);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
defineElement("pc-button-menu", PcButtonMenu);
|
|
125
|
+
export { PcButtonMenu, isNarrowerThan };
|
|
@@ -121,6 +121,8 @@ export declare class PcCanvas extends PcElement {
|
|
|
121
121
|
private antsOffset;
|
|
122
122
|
/** Viewport that has been fitted once (so resizing doesn't refit) */
|
|
123
123
|
private fittedViewport?;
|
|
124
|
+
/** The screen canvas whose 2D context has been made (see `prepareScene`) */
|
|
125
|
+
private preparedCanvas?;
|
|
124
126
|
private frame;
|
|
125
127
|
/** Animation frame drawing only the brush cursor, if requested */
|
|
126
128
|
private overlayFrame;
|
|
@@ -168,6 +170,13 @@ export declare class PcCanvas extends PcElement {
|
|
|
168
170
|
connectedCallback(): void;
|
|
169
171
|
disconnectedCallback(): void;
|
|
170
172
|
firstUpdated(): void;
|
|
173
|
+
/**
|
|
174
|
+
* Make a new screen canvas's 2D context where the app's canvases draw (see `PaintCanvas.rendering`). Drawn from layers
|
|
175
|
+
* kept in regular memory (`cpu`), keeping the screen canvas there too means each frame copies only the area that
|
|
176
|
+
* changed, instead of sending whole layers to the GPU. The first context decides that; later `getContext("2d")` calls
|
|
177
|
+
* get this one, so the screen canvas is made again when the app's rendering differs (see `render`).
|
|
178
|
+
*/
|
|
179
|
+
private prepareScene;
|
|
171
180
|
/**
|
|
172
181
|
* Note what changed: the stroke in progress (and the area it changed), or anything else.
|
|
173
182
|
*/
|