@lvl99dotcom/paintcanvas 0.5.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/brushes/configRanges/index.d.ts +17 -0
- package/dist/lib/brushes/configRanges/index.js +31 -1
- package/dist/lib/brushes/params/index.d.ts +2 -1
- package/dist/lib/brushes/params/index.js +7 -4
- package/dist/lib/brushes/types/index.d.ts +8 -0
- package/dist/lib/classes/Layer/index.d.ts +10 -0
- package/dist/lib/classes/Layer/index.js +15 -1
- package/dist/lib/classes/LayerAutosave/index.d.ts +9 -15
- package/dist/lib/classes/LayerAutosave/index.js +30 -43
- package/dist/lib/classes/LayerStack/index.d.ts +15 -0
- package/dist/lib/classes/LayerStack/index.js +26 -2
- package/dist/lib/classes/PaintCanvas/index.d.ts +36 -5
- package/dist/lib/classes/PaintCanvas/index.js +45 -14
- package/dist/lib/classes/PngEncoder/index.d.ts +41 -0
- package/dist/lib/classes/PngEncoder/index.js +104 -0
- package/dist/lib/classes/ProfileGraphs/index.d.ts +87 -0
- package/dist/lib/classes/ProfileGraphs/index.js +148 -0
- package/dist/lib/classes/SettingsAutosave/index.d.ts +1 -1
- package/dist/lib/classes/SettingsAutosave/index.js +2 -1
- package/dist/lib/classes/UiSettings/index.d.ts +24 -2
- package/dist/lib/classes/UiSettings/index.js +32 -7
- package/dist/lib/messages/schema.d.ts +720 -0
- package/dist/lib/presets/schema.d.ts +360 -0
- package/dist/lib/presets/schema.js +8 -1
- package/dist/lib/profiles/index.d.ts +61 -0
- package/dist/lib/profiles/index.js +131 -0
- package/dist/lib/profiles/schema.d.ts +73 -0
- package/dist/lib/profiles/schema.js +53 -0
- package/dist/lib/rendering/sample/index.d.ts +11 -0
- package/dist/lib/rendering/sample/index.js +36 -5
- package/dist/lib/settings/index.d.ts +2 -1
- package/dist/lib/settings/index.js +4 -1
- package/dist/lib/settings/schema.d.ts +53 -1
- package/dist/lib/settings/schema.js +9 -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 +13061 -3675
- package/dist/lib/version/index.d.ts +25 -0
- package/dist/lib/version/index.js +35 -0
- package/dist/locales/en-GB.js +35 -0
- package/dist/locales/en-US.js +35 -0
- package/dist/locales/fr.js +35 -0
- package/dist/locales/ja.js +35 -0
- package/dist/locales/ko.js +35 -0
- package/dist/locales/zh-Hans.js +35 -0
- package/dist/locales/zh-Hant.js +35 -0
- package/dist/package.js +1 -1
- package/dist/ui/ask/index.d.ts +48 -0
- package/dist/ui/ask/index.js +67 -0
- package/dist/ui/base/index.d.ts +8 -1
- package/dist/ui/base/index.js +19 -4
- package/dist/ui/elements/PaintCanvas/index.d.ts +13 -3
- package/dist/ui/elements/PaintCanvas/index.js +25 -6
- package/dist/ui/elements/PaintCanvas/style.js +1 -1
- package/dist/ui/elements/PcAppSettings/index.d.ts +15 -7
- package/dist/ui/elements/PcAppSettings/index.js +53 -62
- package/dist/ui/elements/PcBrushConfig/index.d.ts +26 -14
- package/dist/ui/elements/PcBrushConfig/index.js +214 -145
- package/dist/ui/elements/PcBrushConfig/style.js +1 -1
- package/dist/ui/elements/PcBrushTest/index.d.ts +1 -0
- package/dist/ui/elements/PcBrushTest/index.js +15 -9
- package/dist/ui/elements/PcBrushTest/style.js +1 -1
- package/dist/ui/elements/PcButton/index.d.ts +12 -3
- package/dist/ui/elements/PcButton/index.js +18 -10
- 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 +15 -1
- package/dist/ui/elements/PcCanvas/index.js +42 -19
- package/dist/ui/elements/PcCanvas/style.js +1 -1
- package/dist/ui/elements/PcClipboardTools/index.d.ts +11 -7
- package/dist/ui/elements/PcClipboardTools/index.js +30 -29
- package/dist/ui/elements/PcDialog/index.d.ts +46 -10
- package/dist/ui/elements/PcDialog/index.js +102 -51
- package/dist/ui/elements/PcDialogWindow/index.d.ts +141 -0
- package/dist/ui/elements/PcDialogWindow/index.js +385 -0
- 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/PcPanelBrushes/index.d.ts +3 -2
- package/dist/ui/elements/PcPanelBrushes/index.js +7 -5
- package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
- package/dist/ui/elements/PcPanelColours/index.js +2 -2
- package/dist/ui/elements/PcPanelColours/style.js +1 -1
- package/dist/ui/elements/PcPanelLayers/index.js +1 -1
- package/dist/ui/elements/PcPanelLayers/style.js +1 -1
- package/dist/ui/elements/PcProfileEditor/index.d.ts +103 -0
- package/dist/ui/elements/PcProfileEditor/index.js +259 -0
- package/dist/ui/elements/PcProfileGraph/index.d.ts +84 -0
- package/dist/ui/elements/PcProfileGraph/index.js +317 -0
- package/dist/ui/elements/PcShortcutSettings/index.js +9 -13
- package/dist/ui/elements/PcSliderRange/index.d.ts +147 -0
- package/dist/ui/elements/PcSliderRange/index.js +308 -0
- package/dist/ui/elements/PcStats/index.d.ts +19 -4
- package/dist/ui/elements/PcStats/index.js +29 -13
- 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 +14 -14
- package/dist/ui/elements/PcVersion/index.d.ts +12 -0
- package/dist/ui/elements/PcVersion/index.js +43 -0
- package/dist/ui/icons/images/icon-profile-graph@2x.js +3 -0
- package/dist/ui/icons/index.d.ts +1 -1
- package/dist/ui/icons/index.js +2 -0
- package/dist/ui/index.d.ts +9 -2
- package/dist/ui/index.js +9 -2
- package/dist/ui/layout/index.d.ts +2 -2
- package/dist/ui/layout/index.js +2 -2
- package/dist/ui/styles/index.d.ts +21 -1
- package/dist/ui/styles/index.js +73 -14
- package/locales/en-GB.json +35 -0
- package/locales/en-US.json +35 -0
- package/locales/fr.json +35 -0
- package/locales/ja.json +35 -0
- package/locales/ko.json +35 -0
- package/locales/zh-Hans.json +35 -0
- package/locales/zh-Hant.json +35 -0
- package/package.json +25 -23
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { nothing } from "lit";
|
|
2
|
+
import { type ConfirmFn, type PromptFn } from "../../ask";
|
|
2
3
|
import { PcElement } from "../../base";
|
|
3
4
|
import "../PcBrushTest";
|
|
5
|
+
import "../PcToggleCheckbox";
|
|
6
|
+
import "../PcDialogWindow";
|
|
7
|
+
import "../PcSliderRange";
|
|
8
|
+
import "../PcProfileEditor";
|
|
4
9
|
import type PaintCanvas from "../../../lib/classes/PaintCanvas";
|
|
5
10
|
import { SmoothingType, type BrushConfig } from "../../../lib/brushes/types";
|
|
11
|
+
import { type VaryingParam } from "../../../lib/brushes/configRanges";
|
|
6
12
|
import { type ShapeImage } from "../../../lib/brushes/shapeImage";
|
|
7
|
-
|
|
8
|
-
export type ConfirmFn = (message: string) => boolean;
|
|
9
|
-
/** Asks the user for text; replaceable for tests */
|
|
10
|
-
export type PromptFn = (message: string, value: string) => string | null;
|
|
13
|
+
export type { ConfirmFn, PromptFn } from "../../ask";
|
|
11
14
|
/** The smoothing choices, in the order shown: value, translation key of the name, and icon name */
|
|
12
15
|
export declare const SMOOTHING_CHOICES: readonly [{
|
|
13
16
|
readonly value: SmoothingType.Linear;
|
|
@@ -26,12 +29,13 @@ export declare const SMOOTHING_CHOICES: readonly [{
|
|
|
26
29
|
* The brush (or eraser) configuration window: a testing area, the shape (pressing it picks a built-in shape from the
|
|
27
30
|
* device's own list, which says "Custom" for an uploaded or pasted one; or upload or paste one), smoothing, width, height,
|
|
28
31
|
* opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
|
|
29
|
-
* pressure, tilt and randomness; when any is on, the setting has a min and max
|
|
32
|
+
* pressure, tilt and randomness; when any is on, the setting has a min and max, set with a `<pc-slider-range>`.
|
|
30
33
|
*
|
|
31
34
|
* Changes stay in the window until "Save" (fires `pc-save` with the settings, to apply to the current tool) or "Add to
|
|
32
35
|
* presets" (saves them as one of the user's presets, asking before replacing one with the same name, and selects it).
|
|
33
|
-
* "Delete preset…" shows when editing one of the user's presets.
|
|
34
|
-
*
|
|
36
|
+
* "Delete preset…" shows when editing one of the user's presets. It's a shaded `<pc-dialog-window>`, closed with its ⨉
|
|
37
|
+
* button or Escape (not a press outside it, which would lose the changes). Fires `pc-close` when closed, and `pc-toast`
|
|
38
|
+
* with a message after adding or deleting a preset, or when a shape can't be used.
|
|
35
39
|
*
|
|
36
40
|
* @example
|
|
37
41
|
* html`<pc-brush-config .app=${app} kind="brush" .config=${app.brush.preset} @pc-save=${apply}></pc-brush-config>`
|
|
@@ -56,6 +60,9 @@ export declare class PcBrushConfig extends PcElement {
|
|
|
56
60
|
busy: {
|
|
57
61
|
state: boolean;
|
|
58
62
|
};
|
|
63
|
+
editingProfile: {
|
|
64
|
+
state: boolean;
|
|
65
|
+
};
|
|
59
66
|
translations: {
|
|
60
67
|
attribute: boolean;
|
|
61
68
|
};
|
|
@@ -79,7 +86,11 @@ export declare class PcBrushConfig extends PcElement {
|
|
|
79
86
|
draft?: BrushConfig;
|
|
80
87
|
/** Whether a preset is being saved or deleted */
|
|
81
88
|
busy: boolean;
|
|
89
|
+
/** The setting whose profile graph is being edited, if any */
|
|
90
|
+
editingProfile?: VaryingParam;
|
|
91
|
+
/** Asks the user to confirm, in a dialog (replaceable for tests) */
|
|
82
92
|
confirm: ConfirmFn;
|
|
93
|
+
/** Asks the user for text, in a dialog (replaceable for tests) */
|
|
83
94
|
prompt: PromptFn;
|
|
84
95
|
/** Turns files into brush shapes (replaceable for tests) */
|
|
85
96
|
readShape: (file: File) => Promise<ShapeImage>;
|
|
@@ -87,10 +98,7 @@ export declare class PcBrushConfig extends PcElement {
|
|
|
87
98
|
* Reads a brush shape from the clipboard, or else from image data copied on the canvas (replaceable for tests)
|
|
88
99
|
*/
|
|
89
100
|
readClipboard: () => Promise<ShapeImage>;
|
|
90
|
-
private readonly onKeyDown;
|
|
91
101
|
constructor();
|
|
92
|
-
connectedCallback(): void;
|
|
93
|
-
disconnectedCallback(): void;
|
|
94
102
|
protected willUpdate(changed: Map<PropertyKey, unknown>): void;
|
|
95
103
|
/** Fire an event that crosses shadow DOM boundaries */
|
|
96
104
|
private emit;
|
|
@@ -121,10 +129,10 @@ export declare class PcBrushConfig extends PcElement {
|
|
|
121
129
|
* Delete the preset being edited, after confirming.
|
|
122
130
|
*/
|
|
123
131
|
deletePreset(): Promise<void>;
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
private
|
|
132
|
+
/**
|
|
133
|
+
* Render a slider for one value (a single `<pc-slider-range>`), the same as the ranges of settings that vary.
|
|
134
|
+
*/
|
|
135
|
+
private single;
|
|
128
136
|
/** Render a setting that can vary: its value (or min and max), and its pressure, tilt and randomise icon toggles */
|
|
129
137
|
private renderParam;
|
|
130
138
|
/**
|
|
@@ -134,4 +142,8 @@ export declare class PcBrushConfig extends PcElement {
|
|
|
134
142
|
/** Render the shape and smoothing row */
|
|
135
143
|
private renderShape;
|
|
136
144
|
render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
145
|
+
/** The profile graph editor for the setting being edited, while open */
|
|
146
|
+
private renderProfileEditor;
|
|
147
|
+
/** Profile graphs, when there's no canvas to keep them */
|
|
148
|
+
private readonly profiles;
|
|
137
149
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import style_default from "./style.js";
|
|
2
2
|
import { ICON_CONTROLS_CSS, PILLS_CSS, WINDOWS_CSS } from "../../styles/index.js";
|
|
3
3
|
import { iconLabel } from "../../icons/index.js";
|
|
4
|
+
import { askConfirm, askText, deletePresetQuestion } from "../../ask/index.js";
|
|
4
5
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
5
6
|
import "../PcBrushTest/index.js";
|
|
7
|
+
import "../PcToggleCheckbox/index.js";
|
|
8
|
+
import "../PcDialogWindow/index.js";
|
|
9
|
+
import "../PcSliderRange/index.js";
|
|
10
|
+
import "../PcProfileEditor/index.js";
|
|
11
|
+
import ProfileGraphs from "../../../lib/classes/ProfileGraphs/index.js";
|
|
12
|
+
import { isLinear } from "../../../lib/profiles/index.js";
|
|
6
13
|
import { SmoothingType } from "../../../lib/brushes/types/index.js";
|
|
7
|
-
import { PARAM_LIMITS, VARIATION_SOURCES, readRange,
|
|
14
|
+
import { PARAM_LIMITS, VARIATION_SOURCES, configKey, readRange, selectSource, writeRange } from "../../../lib/brushes/configRanges/index.js";
|
|
8
15
|
import { SHAPE_ERRORS, ShapeImageError, readClipboardShape, readShapeImage } from "../../../lib/brushes/shapeImage/index.js";
|
|
9
16
|
import { BUILT_IN_SHAPES, builtInShapeId } from "../../../lib/brushes/shapes/index.js";
|
|
10
17
|
import "../../../lib/brushes/StrokeSampler/index.js";
|
|
@@ -36,6 +43,15 @@ var SOURCE_ICONS = {
|
|
|
36
43
|
tilt: "tilt",
|
|
37
44
|
randomise: "randomise"
|
|
38
45
|
};
|
|
46
|
+
/** Translation keys of the varying settings' names */
|
|
47
|
+
var PARAM_LABELS = {
|
|
48
|
+
width: "paintCanvas.common.width",
|
|
49
|
+
height: "paintCanvas.common.height",
|
|
50
|
+
alpha: "paintCanvas.common.opacity",
|
|
51
|
+
rotation: "paintCanvas.brushConfig.rotation",
|
|
52
|
+
offsetX: "paintCanvas.brushConfig.offsetX",
|
|
53
|
+
offsetY: "paintCanvas.brushConfig.offsetY"
|
|
54
|
+
};
|
|
39
55
|
/** Translation keys of the variation sources' names */
|
|
40
56
|
var SOURCE_LABELS = {
|
|
41
57
|
pressure: "paintCanvas.toolSettings.pressure",
|
|
@@ -53,21 +69,17 @@ var SHAPE_ERROR_KEYS = {
|
|
|
53
69
|
};
|
|
54
70
|
/** Largest stamp spacing in pixels */
|
|
55
71
|
var MAX_SPACING = 200;
|
|
56
|
-
/** Read a number input, or undefined if it isn't a number */
|
|
57
|
-
var numberValue = (event) => {
|
|
58
|
-
const value = event.target.valueAsNumber;
|
|
59
|
-
return Number.isFinite(value) ? value : void 0;
|
|
60
|
-
};
|
|
61
72
|
/**
|
|
62
73
|
* The brush (or eraser) configuration window: a testing area, the shape (pressing it picks a built-in shape from the
|
|
63
74
|
* device's own list, which says "Custom" for an uploaded or pasted one; or upload or paste one), smoothing, width, height,
|
|
64
75
|
* opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
|
|
65
|
-
* pressure, tilt and randomness; when any is on, the setting has a min and max
|
|
76
|
+
* pressure, tilt and randomness; when any is on, the setting has a min and max, set with a `<pc-slider-range>`.
|
|
66
77
|
*
|
|
67
78
|
* Changes stay in the window until "Save" (fires `pc-save` with the settings, to apply to the current tool) or "Add to
|
|
68
79
|
* presets" (saves them as one of the user's presets, asking before replacing one with the same name, and selects it).
|
|
69
|
-
* "Delete preset…" shows when editing one of the user's presets.
|
|
70
|
-
*
|
|
80
|
+
* "Delete preset…" shows when editing one of the user's presets. It's a shaded `<pc-dialog-window>`, closed with its ⨉
|
|
81
|
+
* button or Escape (not a press outside it, which would lose the changes). Fires `pc-close` when closed, and `pc-toast`
|
|
82
|
+
* with a message after adding or deleting a preset, or when a shape can't be used.
|
|
71
83
|
*
|
|
72
84
|
* @example
|
|
73
85
|
* html`<pc-brush-config .app=${app} kind="brush" .config=${app.brush.preset} @pc-save=${apply}></pc-brush-config>`
|
|
@@ -80,7 +92,8 @@ var PcBrushConfig = class extends PcElement {
|
|
|
80
92
|
config: { attribute: false },
|
|
81
93
|
editingId: { type: String },
|
|
82
94
|
draft: { state: true },
|
|
83
|
-
busy: { state: true }
|
|
95
|
+
busy: { state: true },
|
|
96
|
+
editingProfile: { state: true }
|
|
84
97
|
};
|
|
85
98
|
static styles = [
|
|
86
99
|
PcElement.styles,
|
|
@@ -89,30 +102,21 @@ var PcBrushConfig = class extends PcElement {
|
|
|
89
102
|
unsafeCSS(ICON_CONTROLS_CSS),
|
|
90
103
|
unsafeCSS(PILLS_CSS)
|
|
91
104
|
];
|
|
92
|
-
confirm
|
|
93
|
-
|
|
105
|
+
/** Asks the user to confirm, in a dialog (replaceable for tests) */
|
|
106
|
+
confirm = (options) => askConfirm(this.renderRoot, options);
|
|
107
|
+
/** Asks the user for text, in a dialog (replaceable for tests) */
|
|
108
|
+
prompt = (options) => askText(this.renderRoot, options);
|
|
94
109
|
/** Turns files into brush shapes (replaceable for tests) */
|
|
95
110
|
readShape = readShapeImage;
|
|
96
111
|
/**
|
|
97
112
|
* Reads a brush shape from the clipboard, or else from image data copied on the canvas (replaceable for tests)
|
|
98
113
|
*/
|
|
99
114
|
readClipboard = () => readClipboardShape(navigator.clipboard, {}, async () => this.app?.copiedPng());
|
|
100
|
-
onKeyDown = (event) => {
|
|
101
|
-
if (event.key === "Escape") this.close();
|
|
102
|
-
};
|
|
103
115
|
constructor() {
|
|
104
116
|
super();
|
|
105
117
|
this.kind = "brush";
|
|
106
118
|
this.busy = false;
|
|
107
119
|
}
|
|
108
|
-
connectedCallback() {
|
|
109
|
-
super.connectedCallback();
|
|
110
|
-
window.addEventListener("keydown", this.onKeyDown);
|
|
111
|
-
}
|
|
112
|
-
disconnectedCallback() {
|
|
113
|
-
super.disconnectedCallback();
|
|
114
|
-
window.removeEventListener("keydown", this.onKeyDown);
|
|
115
|
-
}
|
|
116
120
|
willUpdate(changed) {
|
|
117
121
|
super.willUpdate(changed);
|
|
118
122
|
if (changed.has("config") && this.config) this.draft = structuredClone(this.config);
|
|
@@ -190,10 +194,22 @@ var PcBrushConfig = class extends PcElement {
|
|
|
190
194
|
const app = this.app;
|
|
191
195
|
const draft = this.draft;
|
|
192
196
|
if (!app || !draft) return;
|
|
193
|
-
const name = this.prompt(
|
|
197
|
+
const name = (await this.prompt({
|
|
198
|
+
heading: this.t("paintCanvas.brushConfig.addToPresets"),
|
|
199
|
+
label: this.t("paintCanvas.brushConfig.presetName"),
|
|
200
|
+
value: draft.name ?? "",
|
|
201
|
+
confirm: this.t("paintCanvas.common.save"),
|
|
202
|
+
cancel: this.t("paintCanvas.common.cancel")
|
|
203
|
+
}))?.trim();
|
|
194
204
|
if (!name) return;
|
|
195
205
|
const existing = app.customPresets.findOwn(name, app.authorHandle, app.names.unknownAuthor);
|
|
196
|
-
if (existing && !this.confirm(
|
|
206
|
+
if (existing && !await this.confirm({
|
|
207
|
+
heading: this.t("paintCanvas.presets.replaceHeading"),
|
|
208
|
+
message: this.t("paintCanvas.presets.replaceConfirm", { name }),
|
|
209
|
+
confirm: this.t("paintCanvas.presets.replace"),
|
|
210
|
+
cancel: this.t("paintCanvas.common.cancel"),
|
|
211
|
+
cancelling: true
|
|
212
|
+
})) return;
|
|
197
213
|
const preset = {
|
|
198
214
|
...app.newPreset(draft, name),
|
|
199
215
|
...existing ? { id: existing.id } : {}
|
|
@@ -224,7 +240,7 @@ var PcBrushConfig = class extends PcElement {
|
|
|
224
240
|
const id = this.editingId;
|
|
225
241
|
if (!app || !id) return;
|
|
226
242
|
const name = app.customPresets.get(id)?.name ?? "this preset";
|
|
227
|
-
if (!this.confirm(this.
|
|
243
|
+
if (!await this.confirm(deletePresetQuestion(this.translations, name))) return;
|
|
228
244
|
this.busy = true;
|
|
229
245
|
try {
|
|
230
246
|
await app.customPresets.remove(id);
|
|
@@ -242,32 +258,22 @@ var PcBrushConfig = class extends PcElement {
|
|
|
242
258
|
this.busy = false;
|
|
243
259
|
}
|
|
244
260
|
}
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
?disabled=${disabled || this.busy}
|
|
253
|
-
.value=${String(Math.round(value))}
|
|
254
|
-
@change=${(e) => {
|
|
255
|
-
const next = numberValue(e);
|
|
256
|
-
if (next !== void 0) onChange(next);
|
|
257
|
-
}}
|
|
258
|
-
/>`;
|
|
259
|
-
}
|
|
260
|
-
/** Render a range slider */
|
|
261
|
-
slider(className, value, limits, onChange, disabled = false) {
|
|
262
|
-
return html`<input
|
|
263
|
-
type="range"
|
|
261
|
+
/**
|
|
262
|
+
* Render a slider for one value (a single `<pc-slider-range>`), the same as the ranges of settings that vary.
|
|
263
|
+
*/
|
|
264
|
+
single(className, label, value, limits, onChange, disabled = false) {
|
|
265
|
+
const changed = (event) => onChange(event.detail.value);
|
|
266
|
+
return html`<pc-slider-range
|
|
267
|
+
single
|
|
264
268
|
class=${className}
|
|
269
|
+
label=${label}
|
|
265
270
|
min=${limits.min}
|
|
266
271
|
max=${limits.max}
|
|
272
|
+
.value=${live(Math.round(value))}
|
|
267
273
|
?disabled=${disabled || this.busy}
|
|
268
|
-
|
|
269
|
-
@
|
|
270
|
-
|
|
274
|
+
@pc-input=${changed}
|
|
275
|
+
@pc-change=${changed}
|
|
276
|
+
></pc-slider-range>`;
|
|
271
277
|
}
|
|
272
278
|
/** Render a setting that can vary: its value (or min and max), and its pressure, tilt and randomise icon toggles */
|
|
273
279
|
renderParam(draft, param, label, disabled = false) {
|
|
@@ -275,31 +281,61 @@ var PcBrushConfig = class extends PcElement {
|
|
|
275
281
|
const limits = PARAM_LIMITS[param];
|
|
276
282
|
const isOffset = param === "offsetX" || param === "offsetY";
|
|
277
283
|
const write = (values) => this.changeParam(param, writeRange(draft, param, values));
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
const writeEnds = (event) => write({
|
|
285
|
+
min: event.detail.low,
|
|
286
|
+
max: event.detail.high
|
|
287
|
+
});
|
|
288
|
+
const values = range.varies ? html`<pc-slider-range
|
|
289
|
+
class=${`${param} range`}
|
|
290
|
+
label=${label}
|
|
291
|
+
min=${limits.min}
|
|
292
|
+
max=${limits.max}
|
|
293
|
+
.low=${live(Math.round(range.min))}
|
|
294
|
+
.high=${live(Math.round(range.max))}
|
|
295
|
+
?disabled=${disabled || this.busy}
|
|
296
|
+
@pc-input=${writeEnds}
|
|
297
|
+
@pc-change=${writeEnds}
|
|
298
|
+
></pc-slider-range>` : this.single(param, label, range.value, limits, (value) => write({ value }), disabled || isOffset);
|
|
299
|
+
const active = VARIATION_SOURCES.find((source) => range.sources[source]);
|
|
300
|
+
const select = (source) => this.changeParam(param, selectSource(draft, param, source));
|
|
283
301
|
return html`<div class="row" data-param=${param}>
|
|
284
302
|
<span class="label">${label}</span>
|
|
285
303
|
${values}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
304
|
+
<fieldset class="sources" ?disabled=${disabled || this.busy}>
|
|
305
|
+
<legend class="visually-hidden">
|
|
306
|
+
${this.t("paintCanvas.brushConfig.varyWith", { param: label })}
|
|
307
|
+
</legend>
|
|
308
|
+
${VARIATION_SOURCES.map((source) => html`<label
|
|
309
|
+
class="icon-toggle"
|
|
310
|
+
title=${this.t(SOURCE_LABELS[source])}
|
|
311
|
+
>
|
|
312
|
+
<input
|
|
313
|
+
type="radio"
|
|
314
|
+
name=${`${param}-source`}
|
|
315
|
+
class=${`${param} ${source}`}
|
|
316
|
+
aria-label=${this.t("paintCanvas.brushConfig.variation", {
|
|
294
317
|
param: label,
|
|
295
318
|
source: this.t(SOURCE_LABELS[source])
|
|
296
319
|
})}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
320
|
+
.checked=${live(active === source)}
|
|
321
|
+
@change=${() => select(source)}
|
|
322
|
+
@click=${() => {
|
|
323
|
+
if (active === source) select(void 0);
|
|
324
|
+
}}
|
|
325
|
+
/>
|
|
326
|
+
${iconLabel(this.iconUrl(SOURCE_ICONS[source]), this.t(SOURCE_LABELS[source]))}
|
|
327
|
+
</label>`)}
|
|
328
|
+
</fieldset>
|
|
329
|
+
<button
|
|
330
|
+
type="button"
|
|
331
|
+
class=${`icon-button profile ${param} ${draft[configKey(param)]?.profile ? "configured" : ""}`}
|
|
332
|
+
title=${this.t("paintCanvas.profileEditor.button", { param: label })}
|
|
333
|
+
aria-haspopup="dialog"
|
|
334
|
+
?disabled=${!active || disabled || this.busy}
|
|
335
|
+
@click=${() => this.editingProfile = param}
|
|
336
|
+
>
|
|
337
|
+
${iconLabel(this.iconUrl("profileGraph"), this.t("paintCanvas.profileEditor.button", { param: label }))}
|
|
338
|
+
</button>
|
|
303
339
|
</div>`;
|
|
304
340
|
}
|
|
305
341
|
/**
|
|
@@ -406,13 +442,17 @@ var PcBrushConfig = class extends PcElement {
|
|
|
406
442
|
min: 0,
|
|
407
443
|
max: MAX_SPACING
|
|
408
444
|
};
|
|
409
|
-
return html`<
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
445
|
+
return html`<pc-dialog-window
|
|
446
|
+
open
|
|
447
|
+
shaded
|
|
448
|
+
dismissible="false"
|
|
449
|
+
label=${this.t(this.kind === "eraser" ? "paintCanvas.brushConfig.eraserTitle" : "paintCanvas.brushConfig.brushTitle")}
|
|
450
|
+
@pc-dismiss=${(event) => {
|
|
451
|
+
event.preventDefault();
|
|
452
|
+
this.close();
|
|
453
|
+
}}
|
|
454
|
+
>
|
|
455
|
+
<label slot="header-start" class="name-field">
|
|
416
456
|
${this.t("paintCanvas.brushConfig.name")}
|
|
417
457
|
<input
|
|
418
458
|
class="name"
|
|
@@ -421,86 +461,115 @@ var PcBrushConfig = class extends PcElement {
|
|
|
421
461
|
@input=${(e) => this.change({ name: e.target.value })}
|
|
422
462
|
/>
|
|
423
463
|
</label>
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
${this.
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
464
|
+
<div slot="body" class="settings">
|
|
465
|
+
<pc-brush-test
|
|
466
|
+
.config=${draft}
|
|
467
|
+
.tips=${this.app?.tips}
|
|
468
|
+
></pc-brush-test>
|
|
469
|
+
${this.renderShape(draft)}
|
|
470
|
+
${this.renderParam(draft, "width", this.t(PARAM_LABELS.width))}
|
|
471
|
+
<div class="row">
|
|
472
|
+
<span class="label"></span>
|
|
473
|
+
<label
|
|
474
|
+
class="icon-toggle"
|
|
475
|
+
title=${this.t("paintCanvas.toolSettings.constrainProportions")}
|
|
476
|
+
>
|
|
477
|
+
<input
|
|
478
|
+
type="checkbox"
|
|
479
|
+
class="constrain"
|
|
480
|
+
.checked=${constrained}
|
|
481
|
+
?disabled=${this.busy}
|
|
482
|
+
@change=${(e) => this.change({ constrainProportions: e.target.checked })}
|
|
483
|
+
/>
|
|
484
|
+
${iconLabel(this.iconUrl(constrained ? "linked" : "unlinked"), this.t("paintCanvas.toolSettings.constrainProportions"))}
|
|
485
|
+
</label>
|
|
486
|
+
</div>
|
|
487
|
+
${this.renderParam(draft, "height", this.t(PARAM_LABELS.height), constrained)}
|
|
488
|
+
${this.renderParam(draft, "alpha", this.t(PARAM_LABELS.alpha))}
|
|
489
|
+
${this.renderParam(draft, "rotation", this.t(PARAM_LABELS.rotation))}
|
|
490
|
+
${this.renderParam(draft, "offsetX", this.t(PARAM_LABELS.offsetX))}
|
|
491
|
+
${this.renderParam(draft, "offsetY", this.t(PARAM_LABELS.offsetY))}
|
|
492
|
+
<div class="row">
|
|
493
|
+
<span class="label"
|
|
494
|
+
>${this.t("paintCanvas.brushConfig.sampleRate")}</span
|
|
495
|
+
>
|
|
496
|
+
${this.single("sample-rate", this.t("paintCanvas.brushConfig.sampleRate"), draft.sampleRate ?? 5, sampleLimits, (sampleRate) => this.change({ sampleRate }))}
|
|
497
|
+
</div>
|
|
498
|
+
<div class="row">
|
|
499
|
+
<span class="label"
|
|
500
|
+
>${this.t("paintCanvas.brushConfig.spacing")}</span
|
|
501
|
+
>
|
|
502
|
+
${this.single("spacing", this.t("paintCanvas.brushConfig.spacing"), draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
|
|
503
|
+
</div>
|
|
504
|
+
<pc-toggle-checkbox
|
|
505
|
+
class="snap"
|
|
506
|
+
.checked=${draft.snapToPixelGrid ?? false}
|
|
445
507
|
?disabled=${this.busy}
|
|
446
|
-
@change=${(e) => this.change({
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
${this.renderParam(draft, "height", this.t("paintCanvas.common.height"), constrained)}
|
|
452
|
-
${this.renderParam(draft, "alpha", this.t("paintCanvas.common.opacity"))}
|
|
453
|
-
${this.renderParam(draft, "rotation", this.t("paintCanvas.brushConfig.rotation"))}
|
|
454
|
-
${this.renderParam(draft, "offsetX", this.t("paintCanvas.brushConfig.offsetX"))}
|
|
455
|
-
${this.renderParam(draft, "offsetY", this.t("paintCanvas.brushConfig.offsetY"))}
|
|
456
|
-
<div class="row">
|
|
457
|
-
<span class="label"
|
|
458
|
-
>${this.t("paintCanvas.brushConfig.sampleRate")}</span
|
|
459
|
-
>
|
|
460
|
-
${this.number("sample-rate", draft.sampleRate ?? 5, sampleLimits, (sampleRate) => this.change({ sampleRate }))}
|
|
461
|
-
${this.slider("sample-rate", draft.sampleRate ?? 5, sampleLimits, (sampleRate) => this.change({ sampleRate }))}
|
|
462
|
-
<span class="label">${this.t("paintCanvas.brushConfig.spacing")}</span>
|
|
463
|
-
${this.number("spacing", draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
|
|
464
|
-
${this.slider("spacing", draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
|
|
465
|
-
</div>
|
|
466
|
-
<label>
|
|
467
|
-
<input
|
|
468
|
-
type="checkbox"
|
|
469
|
-
class="snap"
|
|
470
|
-
.checked=${draft.snapToPixelGrid ?? false}
|
|
471
|
-
?disabled=${this.busy}
|
|
472
|
-
@change=${(e) => this.change({ snapToPixelGrid: e.target.checked })}
|
|
473
|
-
/>
|
|
474
|
-
${this.t("paintCanvas.brushConfig.snapToPixelGrid")}
|
|
475
|
-
</label>
|
|
476
|
-
<div class="row">
|
|
508
|
+
@change=${(e) => this.change({ snapToPixelGrid: e.detail.checked })}
|
|
509
|
+
>
|
|
510
|
+
${this.t("paintCanvas.brushConfig.snapToPixelGrid")}
|
|
511
|
+
</pc-toggle-checkbox>
|
|
512
|
+
</div>
|
|
477
513
|
${this.editingId ? html`<button
|
|
514
|
+
slot="footer-start"
|
|
478
515
|
type="button"
|
|
479
|
-
class="delete"
|
|
516
|
+
class="cancel delete"
|
|
480
517
|
?disabled=${this.busy}
|
|
481
518
|
@click=${() => void this.deletePreset()}
|
|
482
519
|
>
|
|
483
520
|
${this.t("paintCanvas.brushConfig.deletePreset")}
|
|
484
521
|
</button>` : nothing}
|
|
485
|
-
<
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
522
|
+
<div slot="footer-end" class="actions">
|
|
523
|
+
<button
|
|
524
|
+
type="button"
|
|
525
|
+
class="add affirmative"
|
|
526
|
+
?disabled=${this.busy}
|
|
527
|
+
@click=${() => void this.addToPresets()}
|
|
528
|
+
>
|
|
529
|
+
${this.t("paintCanvas.brushConfig.addToPresets")}
|
|
530
|
+
</button>
|
|
531
|
+
<button
|
|
532
|
+
type="button"
|
|
533
|
+
class="save affirmative"
|
|
534
|
+
?disabled=${this.busy}
|
|
535
|
+
@click=${() => this.save()}
|
|
536
|
+
>
|
|
537
|
+
${this.t("paintCanvas.common.save")}
|
|
538
|
+
</button>
|
|
539
|
+
</div>
|
|
540
|
+
</pc-dialog-window>
|
|
541
|
+
${this.renderProfileEditor(draft)}`;
|
|
542
|
+
}
|
|
543
|
+
/** The profile graph editor for the setting being edited, while open */
|
|
544
|
+
renderProfileEditor(draft) {
|
|
545
|
+
const param = this.editingProfile;
|
|
546
|
+
if (!param) return nothing;
|
|
547
|
+
const key = configKey(param);
|
|
548
|
+
const variation = draft[key];
|
|
549
|
+
return html`<pc-profile-editor
|
|
550
|
+
.profiles=${this.app?.profiles ?? this.profiles}
|
|
551
|
+
.points=${variation.profile}
|
|
552
|
+
profile-id=${variation.profileId ?? (variation.profile ? nothing : "linear")}
|
|
553
|
+
label=${this.t(PARAM_LABELS[param])}
|
|
554
|
+
@pc-save=${(event) => {
|
|
555
|
+
event.stopPropagation();
|
|
556
|
+
const { points, profileId } = event.detail;
|
|
557
|
+
const linear = isLinear(points);
|
|
558
|
+
this.changeParam(param, { [key]: {
|
|
559
|
+
...variation,
|
|
560
|
+
profile: linear ? void 0 : points,
|
|
561
|
+
profileId: linear ? void 0 : profileId
|
|
562
|
+
} });
|
|
563
|
+
this.editingProfile = void 0;
|
|
564
|
+
}}
|
|
565
|
+
@pc-close=${(event) => {
|
|
566
|
+
event.stopPropagation();
|
|
567
|
+
this.editingProfile = void 0;
|
|
568
|
+
}}
|
|
569
|
+
></pc-profile-editor>`;
|
|
503
570
|
}
|
|
571
|
+
/** Profile graphs, when there's no canvas to keep them */
|
|
572
|
+
profiles = new ProfileGraphs();
|
|
504
573
|
};
|
|
505
574
|
defineElement("pc-brush-config", PcBrushConfig);
|
|
506
575
|
export { PcBrushConfig, SMOOTHING_CHOICES };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// src/ui/elements/PcBrushConfig/style.css?inline
|
|
2
|
-
var style_default = ":host {\n
|
|
2
|
+
var style_default = ":host {\n display: contents;\n}\npc-dialog-window {\n --pc-dialog-width: 560px;\n}\n.settings {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.name-field {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.actions {\n display: flex;\n gap: 6px;\n}\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n.row > .label {\n width: 6em;\n}\ninput[type=\"number\"] {\n width: 4.5em;\n}\ninput[type=\"range\"] {\n flex: 1;\n min-width: 60px;\n}\npc-slider-range {\n flex: 1;\n min-width: 200px;\n}\n.shape-preview {\n display: inline-block;\n width: 40px;\n height: 40px;\n background: var(--pc-text);\n border-radius: 50%;\n}\n.shape-preview.image {\n border-radius: 0;\n mask-repeat: no-repeat;\n mask-size: contain;\n mask-position: center;\n}\n/* The shape (preview, Upload and Paste) and the smoothing icons on one row */\n.shape-row {\n flex-wrap: nowrap;\n}\n/* The shape, with an invisible list over it: pressing the shape opens the device's own list of shapes */\n.shape-picker {\n position: relative;\n display: inline-flex;\n flex: none;\n border-radius: var(--pc-radius-small);\n}\n.shape-picker:hover {\n background: var(--pc-surface-hover);\n}\n.shape-picker:focus-within {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.shape-select {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n}\nfieldset.smoothing {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 0 auto;\n padding: 0;\n border: 0;\n}\nfieldset.smoothing legend {\n float: left;\n margin-right: 4px;\n padding: 0;\n}\n/* Pressure, tilt and randomise: one at a time, side by side */\nfieldset.sources {\n display: flex;\n gap: 4px;\n margin: 0;\n padding: 0;\n border: 0;\n}\n/* A setting with a profile graph: its button is purple */\nbutton.icon-button.profile.configured {\n border-color: var(--pc-accent);\n background: var(--pc-accent);\n}\n";
|
|
3
3
|
export { style_default as default };
|
|
@@ -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,5 +1,7 @@
|
|
|
1
1
|
import style_default from "./style.js";
|
|
2
|
+
import { PILLS_CSS } from "../../styles/index.js";
|
|
2
3
|
import { PcElement, defineElement } from "../../base/index.js";
|
|
4
|
+
import "../PcToggleCheckbox/index.js";
|
|
3
5
|
import StrokeEngine from "../../../lib/brushes/StrokeEngine/index.js";
|
|
4
6
|
import Layer, { createOffscreenCanvas } from "../../../lib/classes/Layer/index.js";
|
|
5
7
|
import { html, unsafeCSS } from "lit";
|
|
@@ -38,7 +40,11 @@ var PcBrushTest = class extends PcElement {
|
|
|
38
40
|
attribute: "show-points"
|
|
39
41
|
}
|
|
40
42
|
};
|
|
41
|
-
static styles = [
|
|
43
|
+
static styles = [
|
|
44
|
+
PcElement.styles,
|
|
45
|
+
unsafeCSS(PILLS_CSS),
|
|
46
|
+
unsafeCSS(style_default)
|
|
47
|
+
];
|
|
42
48
|
constructor() {
|
|
43
49
|
super();
|
|
44
50
|
this.showPoints = false;
|
|
@@ -185,17 +191,17 @@ var PcBrushTest = class extends PcElement {
|
|
|
185
191
|
@pointercancel=${this.onPointerUp}
|
|
186
192
|
></canvas>
|
|
187
193
|
<div class="actions">
|
|
188
|
-
<button type="button" class="clear" @click=${() => this.clear()}>
|
|
194
|
+
<button type="button" class="pill clear" @click=${() => this.clear()}>
|
|
189
195
|
${this.t("paintCanvas.common.clear")}
|
|
190
196
|
</button>
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
<pc-toggle-checkbox
|
|
198
|
+
class="show-points"
|
|
199
|
+
size="s"
|
|
200
|
+
.checked=${this.showPoints}
|
|
201
|
+
@change=${(e) => this.setShowPoints(e.detail.checked)}
|
|
202
|
+
>
|
|
197
203
|
${this.t("paintCanvas.brushConfig.showStrokePoints")}
|
|
198
|
-
</
|
|
204
|
+
</pc-toggle-checkbox>
|
|
199
205
|
</div>
|
|
200
206
|
`;
|
|
201
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 };
|