diff --git a/frontend-svelte/assets/LICENSE.md b/frontend-svelte/assets/LICENSE.md new file mode 100644 index 00000000..476bb8c6 --- /dev/null +++ b/frontend-svelte/assets/LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2021-2023 Keavon Chambers + +The design assets in this directory (including SVG code for icons and logos) are NOT licensed under the Apache 2.0 license terms applied to other Graphite source code files. This directory and its entire contents are excluded from the Apache 2.0 source code license, and copyrights are held by the author for the creative works contained as files herein. + +Parties interested in using Graphite source code in a capacity that deploys the Graphite Editor reference frontend are advised to substitute all assets and "Graphite" branding or otherwise arrange written permission from the rightsholder. The recommended use case for adopting Graphite open source code is to develop one's own unique frontend user interface implementation that integrates Graphite's backend technology. + +The author and rightsholder, Keavon Chambers, may be reached through the email address listed at https://graphite.rs/contact/ or https://keavon.com. diff --git a/frontend-svelte/assets/icon-16px-two-tone/mouse-hint-lmb-double.svg b/frontend-svelte/assets/icon-16px-two-tone/mouse-hint-lmb-double.svg new file mode 100644 index 00000000..c25da603 --- /dev/null +++ b/frontend-svelte/assets/icon-16px-two-tone/mouse-hint-lmb-double.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend-svelte/assets/icon-16px-two-tone/mouse-hint-rmb-double.svg b/frontend-svelte/assets/icon-16px-two-tone/mouse-hint-rmb-double.svg new file mode 100644 index 00000000..2a3cebeb --- /dev/null +++ b/frontend-svelte/assets/icon-16px-two-tone/mouse-hint-rmb-double.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend-svelte/src/components/floating-menus/ColorPicker.svelte b/frontend-svelte/src/components/floating-menus/ColorPicker.svelte index b45b8326..d1ae9c8c 100644 --- a/frontend-svelte/src/components/floating-menus/ColorPicker.svelte +++ b/frontend-svelte/src/components/floating-menus/ColorPicker.svelte @@ -320,7 +320,10 @@ {/if} setColorRGB(channel, detail)} + on:value={({ detail }) => { + strength = detail; + setColorRGB(channel, detail); + }} min={0} max={255} minWidth={56} @@ -341,7 +344,10 @@ {/if} setColorHSV(channel, detail)} + on:value={({ detail }) => { + strength = detail; + setColorHSV(channel, detail); + }} min={0} max={channel === "h" ? 360 : 100} unit={channel === "h" ? "°" : "%"} @@ -358,7 +364,10 @@ setColorAlphaPercent(detail)} + on:value={({ detail }) => { + if (detail !== undefined) alpha = detail / 100; + setColorAlphaPercent(detail); + }} min={0} max={100} rangeMin={0} diff --git a/frontend-svelte/src/components/panels/NodeGraph.svelte b/frontend-svelte/src/components/panels/NodeGraph.svelte index 53444085..e3a7f72b 100644 --- a/frontend-svelte/src/components/panels/NodeGraph.svelte +++ b/frontend-svelte/src/components/panels/NodeGraph.svelte @@ -59,7 +59,7 @@ return sparse; } - function buildNodeCategories(nodeTypes: FrontendNodeType[], searchTerm: string) { + function buildNodeCategories(nodeTypes: FrontendNodeType[], searchTerm: string): [string, FrontendNodeType[]][] { const categories = new Map(); nodeTypes.forEach((node) => { if (searchTerm.length > 0 && !node.name.toLowerCase().includes(searchTerm.toLowerCase()) && !node.category.toLowerCase().includes(searchTerm.toLowerCase())) { @@ -488,10 +488,10 @@ {#if nodeListLocation} (searchTerm = detail)} bind:this={nodeSearchInput} /> - {#each nodeCategories as nodeCategory (nodeCategory[0])} + {#each nodeCategories as nodeCategory} {nodeCategory[0]} - {#each nodeCategory[1] as nodeType (String(nodeType))} + {#each nodeCategory[1] as nodeType} createNode(nodeType.name)} /> {/each} diff --git a/frontend-svelte/src/components/widgets/WidgetRow.svelte b/frontend-svelte/src/components/widgets/WidgetRow.svelte index cf1cfdcf..a6bec62f 100644 --- a/frontend-svelte/src/components/widgets/WidgetRow.svelte +++ b/frontend-svelte/src/components/widgets/WidgetRow.svelte @@ -121,7 +121,7 @@ {#if numberInput} debouncer(() => updateLayout(index, detail))} + on:value={({ detail }) => debouncer((value) => updateLayout(index, value)).updateValue(detail)} incrementCallbackIncrease={() => updateLayout(index, "Increment")} incrementCallbackDecrease={() => updateLayout(index, "Decrement")} sharpRightCorners={nextIsSuffix} diff --git a/frontend-svelte/src/components/widgets/inputs/FieldInput.svelte b/frontend-svelte/src/components/widgets/inputs/FieldInput.svelte index fdf1869d..968632d8 100644 --- a/frontend-svelte/src/components/widgets/inputs/FieldInput.svelte +++ b/frontend-svelte/src/components/widgets/inputs/FieldInput.svelte @@ -31,8 +31,8 @@ let inputOrTextarea: HTMLInputElement | HTMLTextAreaElement; let id = `${Math.random()}`.substring(2); let macKeyboardLayout = platformIsMac(); - let inputValue = value; + $: inputValue = value; $: dispatch("value", inputValue); // Select (highlight) all the text. For technical reasons, it is necessary to pass the current text. diff --git a/frontend-svelte/src/components/widgets/inputs/NumberInput.svelte b/frontend-svelte/src/components/widgets/inputs/NumberInput.svelte index 36ca5908..5fc906aa 100644 --- a/frontend-svelte/src/components/widgets/inputs/NumberInput.svelte +++ b/frontend-svelte/src/components/widgets/inputs/NumberInput.svelte @@ -52,7 +52,7 @@ export let incrementCallbackDecrease: (() => void) | undefined = undefined; let self: FieldInput; - let text = displayText(value); + let text = displayText(value, displayDecimalPlaces, unit); let editing = false; // Stays in sync with a binding to the actual input range slider element. let rangeSliderValue = value !== undefined ? value : 0; @@ -84,10 +84,10 @@ if (typeof min === "number") sanitized = Math.max(sanitized, min); if (typeof max === "number") sanitized = Math.min(sanitized, max); - text = displayText(sanitized); + text = displayText(sanitized, displayDecimalPlaces, unit); } - function sliderInput() { + function onSliderInput() { // Keep only 4 digits after the decimal point const ROUNDING_EXPONENT = 4; const ROUNDING_MAGNITUDE = 10 ** ROUNDING_EXPONENT; @@ -113,10 +113,10 @@ // If we're in a dragging state, we want to use the new slider value rangeSliderValueAsRendered = roundedValue; - updateValue(roundedValue); + updateValue(roundedValue, min, max, displayDecimalPlaces, unit); } - function sliderPointerDown() { + function onSliderPointerDown() { // We want to render the fake slider thumb at the old position, which is still the number held by `value` rangeSliderValueAsRendered = value || 0; @@ -124,7 +124,7 @@ // invocation will transition the state machine to `mousedown`. That's why we don't do it here. } - function sliderPointerUp() { + function onSliderPointerUp() { // User clicked but didn't drag, so we focus the text input element if (rangeSliderClickDragState === "mousedown") { const inputElement = self.element().querySelector("[data-input-element]") as HTMLInputElement | undefined; @@ -160,7 +160,7 @@ const parsed = parseFloat(text); const newValue = Number.isNaN(parsed) ? undefined : parsed; - updateValue(newValue); + updateValue(newValue, min, max, displayDecimalPlaces, unit); editing = false; @@ -168,7 +168,7 @@ } function onCancelTextChange() { - updateValue(undefined); + updateValue(undefined, min, max, displayDecimalPlaces, unit); editing = false; @@ -181,11 +181,11 @@ const actions: Record void> = { Add: () => { const directionAddend = direction === "Increase" ? step : -step; - updateValue(value !== undefined ? value + directionAddend : undefined); + updateValue(value !== undefined ? value + directionAddend : undefined, min, max, displayDecimalPlaces, unit); }, Multiply: () => { const directionMultiplier = direction === "Increase" ? step : 1 / step; - updateValue(value !== undefined ? value * directionMultiplier : undefined); + updateValue(value !== undefined ? value * directionMultiplier : undefined, min, max, displayDecimalPlaces, unit); }, Callback: () => { if (direction === "Increase") incrementCallbackIncrease?.(); @@ -197,20 +197,20 @@ action(); } - function updateValue(newValue: number | undefined) { + function updateValue(newValue: number | undefined, min: number | undefined, max: number | undefined, displayDecimalPlaces: number, unit: string) { + // Check if the new value is valid, otherwise we use the old value (rounded if it's an integer) const nowValid = value !== undefined && isInteger ? Math.round(value) : value; let cleaned = newValue !== undefined ? newValue : nowValid; if (typeof min === "number" && !Number.isNaN(min) && cleaned !== undefined) cleaned = Math.max(cleaned, min); if (typeof max === "number" && !Number.isNaN(max) && cleaned !== undefined) cleaned = Math.min(cleaned, max); - // Required as the call to update:value can, not change the value - text = displayText(value); + text = displayText(cleaned, displayDecimalPlaces, unit); if (newValue !== undefined) dispatch("value", cleaned); } - function displayText(value: number | undefined): string { + function displayText(value: number | undefined, displayDecimalPlaces: number, unit: string): string { if (value === undefined) return "-"; // Find the amount of digits on the left side of the decimal @@ -261,9 +261,9 @@ max={rangeMax} step={sliderStepValue} {disabled} - on:input={sliderInput} - on:pointerdown={sliderPointerDown} - on:pointerup={sliderPointerUp} + on:input={onSliderInput} + on:pointerdown={onSliderPointerDown} + on:pointerup={onSliderPointerUp} tabindex="-1" /> {/if} diff --git a/frontend/assets/LICENSE.md b/frontend/assets/LICENSE.md new file mode 100644 index 00000000..476bb8c6 --- /dev/null +++ b/frontend/assets/LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2021-2023 Keavon Chambers + +The design assets in this directory (including SVG code for icons and logos) are NOT licensed under the Apache 2.0 license terms applied to other Graphite source code files. This directory and its entire contents are excluded from the Apache 2.0 source code license, and copyrights are held by the author for the creative works contained as files herein. + +Parties interested in using Graphite source code in a capacity that deploys the Graphite Editor reference frontend are advised to substitute all assets and "Graphite" branding or otherwise arrange written permission from the rightsholder. The recommended use case for adopting Graphite open source code is to develop one's own unique frontend user interface implementation that integrates Graphite's backend technology. + +The author and rightsholder, Keavon Chambers, may be reached through the email address listed at https://graphite.rs/contact/ or https://keavon.com.