mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 21:37:59 +00:00
parent
9c10d18308
commit
bfbabbc4dc
17 changed files with 62 additions and 69 deletions
|
@ -62,19 +62,17 @@
|
|||
let draggingPickerTrack: HTMLDivElement | undefined = undefined;
|
||||
let strayCloses = true;
|
||||
|
||||
$: rgbChannels = Object.entries(newColor.toRgb255() || { r: undefined, g: undefined, b: undefined }) as [keyof RGB, number | undefined][];
|
||||
$: hsvChannels = Object.entries(!isNone ? { h: hue * 360, s: saturation * 100, v: value * 100 } : { h: undefined, s: undefined, v: undefined }) as [keyof HSV, number | undefined][];
|
||||
$: opaqueHueColor = new Color({ h: hue, s: 1, v: 1, a: 1 });
|
||||
$: newColor = isNone ? new Color("none") : new Color({ h: hue, s: saturation, v: value, a: alpha });
|
||||
$: initialColor = updateInitialColor(initialHue, initialSaturation, initialValue, initialAlpha, initialIsNone, open);
|
||||
|
||||
$: watchOpen(open);
|
||||
$: watchColor(color);
|
||||
|
||||
// Taking `_open` is necessary to make Svelte order the reactive processing queue so this works as required, see:
|
||||
// https://stackoverflow.com/questions/63934543/svelte-reactivity-not-triggering-when-variable-changed-in-a-function
|
||||
function updateInitialColor(h: number, s: number, v: number, a: number, initialIsNone: boolean, _open: boolean) {
|
||||
if (initialIsNone) return new Color("none");
|
||||
$: initialColor = generateColor(initialHue, initialSaturation, initialValue, initialAlpha, initialIsNone);
|
||||
$: newColor = generateColor(hue, saturation, value, alpha, isNone);
|
||||
$: rgbChannels = Object.entries(newColor.toRgb255() || { r: undefined, g: undefined, b: undefined }) as [keyof RGB, number | undefined][];
|
||||
$: hsvChannels = Object.entries(!isNone ? { h: hue * 360, s: saturation * 100, v: value * 100 } : { h: undefined, s: undefined, v: undefined }) as [keyof HSV, number | undefined][];
|
||||
$: opaqueHueColor = new Color({ h: hue, s: 1, v: 1, a: 1 });
|
||||
|
||||
function generateColor(h: number, s: number, v: number, a: number, none: boolean, ..._: any[]) {
|
||||
if (none) return new Color("none");
|
||||
return new Color({ h, s, v, a });
|
||||
}
|
||||
|
||||
|
@ -96,7 +94,7 @@
|
|||
// - ...reset the hue to 0° if the color's value is black, where all hues are equivalent
|
||||
if (!(hsva.h === 0 && hue === 1) && hsva.s > 0 && hsva.v > 0) hue = hsva.h;
|
||||
// Update the saturation, but only if it is necessary so we don't:
|
||||
// - ...reset the saturation to the left is the color's value is black along the bottom edge, where all saturations are equivalent
|
||||
// - ...reset the saturation to the left if the color's value is black along the bottom edge, where all saturations are equivalent
|
||||
if (hsva.v !== 0) saturation = hsva.s;
|
||||
// Update the value
|
||||
value = hsva.v;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
// Called only when `open` is changed from outside this component
|
||||
$: watchOpen(open);
|
||||
$: watchRemeasureWidth(entries, drawIcon);
|
||||
|
||||
$: virtualScrollingTotalHeight = entries.length === 0 ? 0 : entries[0].length * virtualScrollingEntryHeight;
|
||||
$: virtualScrollingStartIndex = Math.floor(virtualScrollingEntriesStart / virtualScrollingEntryHeight) || 0;
|
||||
$: virtualScrollingEndIndex = entries.length === 0 ? 0 : Math.min(entries[0].length, virtualScrollingStartIndex + 1 + 400 / virtualScrollingEntryHeight);
|
||||
|
@ -62,7 +63,10 @@
|
|||
dispatch("activeEntry", menuListEntry);
|
||||
|
||||
// Close the containing menu
|
||||
if (menuListEntry.ref) menuListEntry.ref.open = false;
|
||||
if (menuListEntry.ref) {
|
||||
menuListEntry.ref.open = false;
|
||||
entries = entries;
|
||||
}
|
||||
dispatch("open", false);
|
||||
open = false;
|
||||
}
|
||||
|
@ -70,15 +74,19 @@
|
|||
function onEntryPointerEnter(menuListEntry: MenuListEntry): void {
|
||||
if (!menuListEntry.children?.length) return;
|
||||
|
||||
if (menuListEntry.ref) menuListEntry.ref.open = true;
|
||||
else dispatch("open", true);
|
||||
if (menuListEntry.ref) {
|
||||
menuListEntry.ref.open = true;
|
||||
entries = entries;
|
||||
} else dispatch("open", true);
|
||||
}
|
||||
|
||||
function onEntryPointerLeave(menuListEntry: MenuListEntry): void {
|
||||
if (!menuListEntry.children?.length) return;
|
||||
|
||||
if (menuListEntry.ref) menuListEntry.ref.open = false;
|
||||
else dispatch("open", false);
|
||||
if (menuListEntry.ref) {
|
||||
menuListEntry.ref.open = false;
|
||||
entries = entries;
|
||||
} else dispatch("open", false);
|
||||
}
|
||||
|
||||
function isEntryOpen(menuListEntry: MenuListEntry): boolean {
|
||||
|
@ -96,12 +104,15 @@
|
|||
const flatEntries = entries.flat().filter((entry) => !entry.disabled);
|
||||
const openChild = flatEntries.findIndex((entry) => entry.children?.length && entry.ref?.open);
|
||||
|
||||
const openSubmenu = (highlighted: MenuListEntry): void => {
|
||||
if (highlighted.ref && highlighted.children?.length) {
|
||||
highlighted.ref.open = true;
|
||||
const openSubmenu = (highlightedEntry: MenuListEntry): void => {
|
||||
if (highlightedEntry.ref && highlightedEntry.children?.length) {
|
||||
highlightedEntry.ref.open = true;
|
||||
// The reason we bother taking `highlightdEntry` as an argument is because, when this function is called, it can ensure `highlightedEntry` is not undefined.
|
||||
// But here we still have to set `highlighted` to itself so Svelte knows to reactively update it after we set its `.ref.open` property.
|
||||
highlighted = highlighted;
|
||||
|
||||
// Highlight first item
|
||||
highlighted.ref.setHighlighted(highlighted.children[0][0]);
|
||||
highlightedEntry.ref.setHighlighted(highlightedEntry.children[0][0]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
let floatingMenuBounds = new DOMRect();
|
||||
let floatingMenuContentBounds = new DOMRect();
|
||||
|
||||
$: watchOpenChange(open);
|
||||
|
||||
$: minWidthStyleValue = measuringOngoing ? "0" : `${Math.max(minWidth, minWidthParentWidth)}px`;
|
||||
$: displayTail = open && type === "Popover";
|
||||
$: displayContainer = open || measuringOngoing;
|
||||
|
@ -60,8 +62,6 @@
|
|||
.flatMap((styleAndValue) => (styleAndValue[1] !== undefined ? [`${styleAndValue[0]}: ${styleAndValue[1]};`] : []))
|
||||
.join(" ");
|
||||
|
||||
$: watchOpenChange(open);
|
||||
|
||||
// Called only when `open` is changed from outside this component
|
||||
async function watchOpenChange(isOpen: boolean) {
|
||||
// Switching from closed to open
|
||||
|
|
|
@ -100,8 +100,9 @@
|
|||
async function onEditLayerName(listing: LayerListingInfo) {
|
||||
if (listing.editingName) return;
|
||||
|
||||
listing.editingName = true;
|
||||
draggable = false;
|
||||
listing.editingName = true;
|
||||
layers = layers;
|
||||
|
||||
await tick();
|
||||
|
||||
|
@ -117,6 +118,7 @@
|
|||
|
||||
const name = (e.target as HTMLInputElement | undefined)?.value;
|
||||
listing.editingName = false;
|
||||
layers = layers;
|
||||
if (name) editor.instance.setLayerName(listing.entry.path, name);
|
||||
}
|
||||
|
||||
|
@ -124,6 +126,7 @@
|
|||
draggable = true;
|
||||
|
||||
listing.editingName = false;
|
||||
layers = layers;
|
||||
|
||||
await tick();
|
||||
window.getSelection()?.removeAllRanges();
|
||||
|
@ -211,7 +214,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
markerHeight -= (treeOffset || 0);
|
||||
markerHeight -= treeOffset || 0;
|
||||
|
||||
return {
|
||||
select,
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
let searchTerm = "";
|
||||
let nodeListLocation: { x: number; y: number } | undefined = undefined;
|
||||
|
||||
$: watchNodes($nodeGraph.nodes);
|
||||
|
||||
$: gridSpacing = calculateGridSpacing(transform.scale);
|
||||
$: dotRadius = 1 + Math.floor(transform.scale - 0.5 + 0.001) / 2;
|
||||
$: nodeGraphBarLayout = $nodeGraph.nodeGraphBarLayout;
|
||||
|
@ -47,8 +49,6 @@
|
|||
$: linkPathInProgress = createLinkPathInProgress(linkInProgressFromConnector, linkInProgressToConnector);
|
||||
$: linkPaths = createLinkPaths(linkPathInProgress, nodeLinkPaths);
|
||||
|
||||
$: watchNodes($nodeGraph.nodes);
|
||||
|
||||
function calculateGridSpacing(scale: number): number {
|
||||
const dense = scale * GRID_SIZE;
|
||||
let sparse = dense;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</script>
|
||||
|
||||
<LayoutRow class="parameter-expose-button">
|
||||
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="0" />
|
||||
<button class:exposed style:--data-type-color={`var(--color-data-${dataType})`} on:click={action} title={tooltip} tabindex="-1" />
|
||||
</LayoutRow>
|
||||
|
||||
<style lang="scss" global>
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
let open = false;
|
||||
let minWidth = 0;
|
||||
|
||||
$: selectedIndex, watchSelectedIndex();
|
||||
$: watchSelectedIndex(selectedIndex);
|
||||
$: watchActiveEntry(activeEntry);
|
||||
|
||||
// Called only when `selectedIndex` is changed from outside this component
|
||||
function watchSelectedIndex() {
|
||||
function watchSelectedIndex(_?: number) {
|
||||
activeEntrySkipWatcher = true;
|
||||
activeEntry = makeActiveEntry();
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
let macKeyboardLayout = platformIsMac();
|
||||
|
||||
$: inputValue = value;
|
||||
|
||||
$: dispatch("value", inputValue);
|
||||
|
||||
// Select (highlight) all the text. For technical reasons, it is necessary to pass the current text.
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
let activeEntry: MenuListEntry | undefined = undefined;
|
||||
let minWidth = isStyle ? 0 : 300;
|
||||
|
||||
$: fontFamily, fontStyle, watchFont();
|
||||
$: watchFont(fontFamily, fontStyle);
|
||||
|
||||
async function watchFont(): Promise<void> {
|
||||
async function watchFont(..._: string[]): Promise<void> {
|
||||
// We set this function's result to a local variable to avoid reading from `entries` which causes Svelte to trigger an update that results in an infinite loop
|
||||
const newEntries = await getEntries();
|
||||
entries = newEntries;
|
||||
|
|
|
@ -95,7 +95,10 @@
|
|||
{#if entry.children && entry.children.length > 0}
|
||||
<MenuList
|
||||
on:open={({ detail }) => {
|
||||
if (entry.ref) entry.ref.open = detail;
|
||||
if (entry.ref) {
|
||||
entry.ref.open = detail;
|
||||
entries = entries;
|
||||
}
|
||||
}}
|
||||
open={entry.ref?.open || false}
|
||||
entries={entry.children || []}
|
||||
|
|
|
@ -64,9 +64,10 @@
|
|||
// "dragging": the user is dragging the slider left/right.
|
||||
let rangeSliderClickDragState: "default" | "mousedown" | "dragging" = "default";
|
||||
|
||||
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
|
||||
$: watchValue(value);
|
||||
|
||||
$: sliderStepValue = isInteger ? (step === undefined ? 1 : step) : "any";
|
||||
|
||||
// Called only when `value` is changed from outside this component
|
||||
function watchValue(value: number | undefined) {
|
||||
// Don't update if the slider is currently being dragged (we don't want the backend fighting with the user's drag)
|
||||
|
@ -130,7 +131,7 @@
|
|||
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;
|
||||
const inputElement = self?.element();
|
||||
if (!inputElement) return;
|
||||
|
||||
// Set the slider position back to the original position to undo the user moving it
|
||||
|
|
|
@ -38,29 +38,11 @@
|
|||
<LayoutCol class="swatch-pair">
|
||||
<LayoutRow class="primary swatch">
|
||||
<button on:click={clickPrimarySwatch} style:--swatch-color={primary.toRgbaCSS()} data-floating-menu-spawner="no-hover-transfer" tabindex="0" />
|
||||
<ColorPicker
|
||||
open={primaryOpen}
|
||||
on:open={({ detail }) => (primaryOpen = detail)}
|
||||
color={primary}
|
||||
on:color={({ detail }) => {
|
||||
primary = detail;
|
||||
primaryColorChanged(detail);
|
||||
}}
|
||||
direction="Right"
|
||||
/>
|
||||
<ColorPicker open={primaryOpen} on:open={({ detail }) => (primaryOpen = detail)} color={primary} on:color={({ detail }) => primaryColorChanged(detail)} direction="Right" />
|
||||
</LayoutRow>
|
||||
<LayoutRow class="secondary swatch">
|
||||
<button on:click={clickSecondarySwatch} style:--swatch-color={secondary.toRgbaCSS()} data-floating-menu-spawner="no-hover-transfer" tabindex="0" />
|
||||
<ColorPicker
|
||||
open={secondaryOpen}
|
||||
on:open={({ detail }) => (secondaryOpen = detail)}
|
||||
color={secondary}
|
||||
on:color={({ detail }) => {
|
||||
secondary = detail;
|
||||
secondaryColorChanged(detail);
|
||||
}}
|
||||
direction="Right"
|
||||
/>
|
||||
<ColorPicker open={secondaryOpen} on:open={({ detail }) => (secondaryOpen = detail)} color={secondary} on:color={({ detail }) => secondaryColorChanged(detail)} direction="Right" />
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
if (self) dispatch("commitText", self.getValue());
|
||||
|
||||
// Required if value is not changed by the parent component upon update:value event
|
||||
self?.setInputElementValue(value);
|
||||
self?.setInputElementValue(self.getValue());
|
||||
}
|
||||
|
||||
function onCancelTextChange() {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
if (self) dispatch("commitText", self.getValue());
|
||||
|
||||
// Required if value is not changed by the parent component upon update:value event
|
||||
self?.setInputElementValue(value);
|
||||
self?.setInputElementValue(self.getValue());
|
||||
}
|
||||
|
||||
function onCancelTextChange() {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
export let requiresLock = false;
|
||||
|
||||
$: keyboardLockInfoMessage = watchKeyboardLockInfoMessage(fullscreen.keyboardLockApiSupported);
|
||||
|
||||
$: displayKeyboardLockNotice = requiresLock && !$fullscreen.keyboardLocked;
|
||||
|
||||
function watchKeyboardLockInfoMessage(keyboardLockApiSupported: boolean): string {
|
||||
|
|
|
@ -8,20 +8,19 @@
|
|||
|
||||
const fullscreen = getContext<FullscreenState>("fullscreen");
|
||||
|
||||
$: windowFullscreen = $fullscreen.windowFullscreen;
|
||||
$: requestFullscreenHotkeys = fullscreen.keyboardLockApiSupported && !$fullscreen.keyboardLocked;
|
||||
|
||||
async function handleClick() {
|
||||
if (windowFullscreen) fullscreen.exitFullscreen();
|
||||
if ($fullscreen.windowFullscreen) fullscreen.exitFullscreen();
|
||||
else fullscreen.enterFullscreen();
|
||||
}
|
||||
</script>
|
||||
|
||||
<LayoutRow class="window-buttons-web" on:click={() => handleClick()} tooltip={(windowFullscreen ? "Exit" : "Enter") + " Fullscreen (F11)"}>
|
||||
<LayoutRow class="window-buttons-web" on:click={() => handleClick()} tooltip={($fullscreen.windowFullscreen ? "Exit" : "Enter") + " Fullscreen (F11)"}>
|
||||
{#if requestFullscreenHotkeys}
|
||||
<TextLabel italic={true}>Go fullscreen to access all hotkeys</TextLabel>
|
||||
{/if}
|
||||
<IconLabel icon={windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
|
||||
<IconLabel icon={$fullscreen.windowFullscreen ? "FullscreenExit" : "FullscreenEnter"} />
|
||||
</LayoutRow>
|
||||
|
||||
<style lang="scss" global>
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
let panelSizes = PANEL_SIZES;
|
||||
let documentPanel: Panel | undefined;
|
||||
|
||||
$: activeDocumentIndex = $portfolio.activeDocumentIndex;
|
||||
$: nodeGraphVisible = $workspace.nodeGraphVisible;
|
||||
$: documentPanel?.scrollTabIntoView($portfolio.activeDocumentIndex);
|
||||
|
||||
$: documentTabLabels = $portfolio.documents.map((doc: FrontendDocumentDetails) => {
|
||||
const name = doc.displayName;
|
||||
|
||||
|
@ -34,12 +34,6 @@
|
|||
const tooltip = `Document ID ${doc.id}`;
|
||||
return { name, tooltip };
|
||||
});
|
||||
$: {
|
||||
scrollIntoView(activeDocumentIndex);
|
||||
}
|
||||
function scrollIntoView(activeDocumentIndex: number) {
|
||||
documentPanel?.scrollTabIntoView(activeDocumentIndex);
|
||||
}
|
||||
|
||||
const editor = getContext<Editor>("editor");
|
||||
const workspace = getContext<WorkspaceState>("workspace");
|
||||
|
@ -117,7 +111,7 @@
|
|||
bind:this={documentPanel}
|
||||
/>
|
||||
</LayoutRow>
|
||||
{#if nodeGraphVisible}
|
||||
{#if $workspace.nodeGraphVisible}
|
||||
<LayoutRow class="workspace-grid-resize-gutter" data-gutter-vertical on:pointerdown={resizePanel} />
|
||||
<LayoutRow class="workspace-grid-subdivision" styles={{ "flex-grow": panelSizes["graph"] }} data-subdivision-name="graph">
|
||||
<Panel panelType="NodeGraph" tabLabels={[{ name: "Node Graph" }]} tabActiveIndex={0} />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue