Port the color picker popover to a Rust-defined layout (#4102)

* Break out VisualColorPickersInput.svelte

* Break out ColorComparisonInput.svelte and ColorPresetsInput.svelte

* Add backend definitions and plumbing for the 4 new widgets

* Port the ColorPicker.svelte layout and business logic to Rust

* Port more ColorComparisonInput.svelte logic to Rust

* Port more SpectrumInput.svelte logic to Rust

* Port more frontend logic to Rust

* Code review

* Code review

* Fix some CSS
This commit is contained in:
Keavon Chambers
2026-05-05 02:47:53 -07:00
committed by GitHub
parent 62203cb171
commit e59612c4ce
31 changed files with 2260 additions and 1333 deletions
@@ -8,20 +8,25 @@
import PopoverButton from "/src/components/widgets/buttons/PopoverButton.svelte";
import TextButton from "/src/components/widgets/buttons/TextButton.svelte";
import CheckboxInput from "/src/components/widgets/inputs/CheckboxInput.svelte";
import ColorComparisonInput from "/src/components/widgets/inputs/ColorComparisonInput.svelte";
import ColorInput from "/src/components/widgets/inputs/ColorInput.svelte";
import ColorPresetsInput from "/src/components/widgets/inputs/ColorPresetsInput.svelte";
import CurveInput from "/src/components/widgets/inputs/CurveInput.svelte";
import DropdownInput from "/src/components/widgets/inputs/DropdownInput.svelte";
import NumberInput from "/src/components/widgets/inputs/NumberInput.svelte";
import RadioInput from "/src/components/widgets/inputs/RadioInput.svelte";
import ReferencePointInput from "/src/components/widgets/inputs/ReferencePointInput.svelte";
import SpectrumInput from "/src/components/widgets/inputs/SpectrumInput.svelte";
import TextAreaInput from "/src/components/widgets/inputs/TextAreaInput.svelte";
import TextInput from "/src/components/widgets/inputs/TextInput.svelte";
import VisualColorPickersInput from "/src/components/widgets/inputs/VisualColorPickersInput.svelte";
import WorkingColorsInput from "/src/components/widgets/inputs/WorkingColorsInput.svelte";
import IconLabel from "/src/components/widgets/labels/IconLabel.svelte";
import ImageLabel from "/src/components/widgets/labels/ImageLabel.svelte";
import Separator from "/src/components/widgets/labels/Separator.svelte";
import ShortcutLabel from "/src/components/widgets/labels/ShortcutLabel.svelte";
import TextLabel from "/src/components/widgets/labels/TextLabel.svelte";
import type { ColorPickerStore } from "/src/stores/color-picker";
import { parseFillChoice } from "/src/utility-functions/colors";
import type { EditorWrapper, LayoutTarget, Widget, WidgetInstance } from "/wrapper/pkg/graphite_wasm_wrapper";
@@ -33,6 +38,7 @@
type UnwrappedWidget = { [K in WidgetKind]: [kind: K, props: WidgetProps<K>] }[WidgetKind];
const editor = getContext<EditorWrapper>("editor");
const colorPickerStore = getContext<ColorPickerStore>("colorPicker");
export let widgets: WidgetInstance[];
export let direction: "row" | "column";
@@ -116,6 +122,15 @@
$$events: { checked: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true) },
}),
},
ColorComparisonInput: {
component: ColorComparisonInput,
getProps: (props, index) => ({
...props,
$$events: {
swap: () => widgetValueCommitAndUpdate(index, undefined, true),
},
}),
},
ColorInput: {
component: ColorInput,
getProps: (props, index) => ({
@@ -127,6 +142,17 @@
},
}),
},
ColorPresetsInput: {
component: ColorPresetsInput,
getProps: (props, index) => ({
...props,
$$events: {
// The widget dispatches `"None"` or a bare `Color`, wrap the color in `{ Solid: ... }` so the payload matches Rust's `FillChoice` shape (which the `Preset` variant expects).
preset: (e: CustomEvent) => widgetValueCommitAndUpdate(index, { Preset: e.detail === "None" ? "None" : { Solid: e.detail } }, true),
eyedropperColorCode: (e: CustomEvent) => widgetValueCommitAndUpdate(index, { EyedropperColorCode: e.detail }, true),
},
}),
},
CurveInput: {
// TODO: CurvesInput is currently unused
component: CurveInput,
@@ -210,6 +236,28 @@
$$events: { value: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true) },
}),
},
SpectrumInput: {
component: SpectrumInput,
getProps: (props, index) => ({
...props,
$$events: {
update: (e: CustomEvent) => widgetValueUpdate(index, e.detail, false),
dragging: (e: CustomEvent<boolean>) => colorPickerStore.setDragging(e.detail),
},
}),
},
VisualColorPickersInput: {
component: VisualColorPickersInput,
getProps: (props, index) => ({
...props,
$$events: {
update: (e: CustomEvent) => widgetValueUpdate(index, e.detail, false),
startHistoryTransaction: () => widgetValueCommit(index, undefined),
commitHistoryTransaction: () => widgetValueCommit(index, undefined),
dragStateChange: (e: CustomEvent<boolean>) => colorPickerStore.setDragging(e.detail),
},
}),
},
PopoverButton: {
component: PopoverButton,
getProps: (props) => ({