mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
fix: make document context reactive
This commit is contained in:
parent
f1e0af6595
commit
692b17ffd3
5 changed files with 48 additions and 128 deletions
|
@ -10,7 +10,7 @@
|
|||
import { createPanicManager } from "@graphite/io-managers/panic";
|
||||
import { createPersistenceManager } from "@graphite/io-managers/persistence";
|
||||
import { createDialogState } from "@graphite/state-providers/dialog";
|
||||
import { createDocumentState } from "@graphite/state-providers/document";
|
||||
import { createDocumentState } from "@graphite/state-providers/document.svelte";
|
||||
import { createFontsState } from "@graphite/state-providers/fonts";
|
||||
import { createFullscreenState } from "@graphite/state-providers/fullscreen";
|
||||
import { createNodeGraphState } from "@graphite/state-providers/node-graph";
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
UpdateMouseCursor,
|
||||
isWidgetSpanRow,
|
||||
} from "@graphite/messages.svelte";
|
||||
import type { DocumentState } from "@graphite/state-providers/document";
|
||||
import type { DocumentState } from "@graphite/state-providers/document.svelte";
|
||||
import { textInputCleanup } from "@graphite/utility-functions/keyboard-entry";
|
||||
import { extractPixelData, rasterizeSVGCanvas } from "@graphite/utility-functions/rasterization";
|
||||
import { updateBoundsOfViewports } from "@graphite/utility-functions/viewports";
|
||||
|
@ -124,7 +124,7 @@
|
|||
totalToolRowsFor2Columns,
|
||||
totalToolRowsFor3Columns,
|
||||
};
|
||||
})($document.toolShelfLayout.layout[0]));
|
||||
})(document.toolShelfLayout.layout[0]));
|
||||
|
||||
function dropFile(e: DragEvent) {
|
||||
const { dataTransfer } = e;
|
||||
|
@ -462,14 +462,14 @@
|
|||
</script>
|
||||
|
||||
<LayoutCol class="document" ondragover={(e) => e.preventDefault()} ondrop={dropFile}>
|
||||
<LayoutRow class="control-bar" classes={{ "for-graph": $document.graphViewOverlayOpen }} scrollableX={true}>
|
||||
{#if !$document.graphViewOverlayOpen}
|
||||
<WidgetLayout layout={$document.documentModeLayout} />
|
||||
<WidgetLayout layout={$document.toolOptionsLayout} />
|
||||
<LayoutRow class="control-bar" classes={{ "for-graph": document.graphViewOverlayOpen }} scrollableX={true}>
|
||||
{#if !document.graphViewOverlayOpen}
|
||||
<WidgetLayout layout={document.documentModeLayout} />
|
||||
<WidgetLayout layout={document.toolOptionsLayout} />
|
||||
<LayoutRow class="spacer" />
|
||||
<WidgetLayout layout={$document.documentBarLayout} />
|
||||
<WidgetLayout layout={document.documentBarLayout} />
|
||||
{:else}
|
||||
<WidgetLayout layout={$document.nodeGraphControlBarLayout} />
|
||||
<WidgetLayout layout={document.nodeGraphControlBarLayout} />
|
||||
{/if}
|
||||
</LayoutRow>
|
||||
<LayoutRow
|
||||
|
@ -482,15 +482,15 @@
|
|||
}}
|
||||
>
|
||||
<LayoutCol class="tool-shelf">
|
||||
{#if !$document.graphViewOverlayOpen}
|
||||
{#if !document.graphViewOverlayOpen}
|
||||
<LayoutCol class="tools" scrollableY={true}>
|
||||
<WidgetLayout layout={$document.toolShelfLayout} />
|
||||
<WidgetLayout layout={document.toolShelfLayout} />
|
||||
</LayoutCol>
|
||||
{:else}
|
||||
<LayoutRow class="spacer" />
|
||||
{/if}
|
||||
<LayoutCol class="tool-shelf-bottom-widgets">
|
||||
<WidgetLayout class={"working-colors-input-area"} layout={$document.workingColorsLayout} />
|
||||
<WidgetLayout class={"working-colors-input-area"} layout={document.workingColorsLayout} />
|
||||
</LayoutCol>
|
||||
</LayoutCol>
|
||||
<LayoutCol class="viewport-container">
|
||||
|
@ -536,7 +536,7 @@
|
|||
>
|
||||
</canvas>
|
||||
</div>
|
||||
<div class="graph-view" class:open={$document.graphViewOverlayOpen} style:--fade-artwork={`${$document.fadeArtwork}%`} data-graph>
|
||||
<div class="graph-view" class:open={document.graphViewOverlayOpen} style:--fade-artwork={`${document.fadeArtwork}%`} data-graph>
|
||||
<Graph />
|
||||
</div>
|
||||
</LayoutCol>
|
||||
|
|
|
@ -3,7 +3,8 @@ import { get } from "svelte/store";
|
|||
import { type Editor } from "@graphite/editor";
|
||||
import { TriggerPaste } from "@graphite/messages.svelte";
|
||||
import { type DialogState } from "@graphite/state-providers/dialog";
|
||||
import { type DocumentState } from "@graphite/state-providers/document";
|
||||
import { type DocumentState } from "@graphite/state-providers/document.svelte";
|
||||
import { documentContextState } from "@graphite/state-providers/document.svelte";
|
||||
import { type FullscreenState } from "@graphite/state-providers/fullscreen";
|
||||
import { type PortfolioState } from "@graphite/state-providers/portfolio";
|
||||
import { makeKeyboardModifiersBitfield, textInputCleanup, getLocalizedScanCode } from "@graphite/utility-functions/keyboard-entry";
|
||||
|
@ -155,7 +156,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
|||
// TODO: This would allow it to properly decide to act on removing hover focus from something that was hovered in the canvas before moving over the GUI.
|
||||
// TODO: Further explanation: https://github.com/GraphiteEditor/Graphite/pull/623#discussion_r866436197
|
||||
const inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]");
|
||||
const inGraphOverlay = get(document).graphViewOverlayOpen;
|
||||
const inGraphOverlay = documentContextState.graphViewOverlayOpen;
|
||||
if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return;
|
||||
|
||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||
|
|
|
@ -1495,62 +1495,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
|
|||
});
|
||||
}
|
||||
|
||||
// export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: WidgetDiffUpdate) {
|
||||
// // Use Reflect.set to ensure proxy reactivity
|
||||
// Reflect.set(layout, "layoutTarget", updates.layoutTarget);
|
||||
|
||||
// updates.diff.forEach((update) => {
|
||||
// // Find the object where the diff applies to
|
||||
// let diffObject = update.widgetPath.reduce((targetLayout, index) => {
|
||||
// // Use Reflect.get to ensure we're going through the proxy
|
||||
// if (Reflect.has(targetLayout, "columnWidgets")) {
|
||||
// return Reflect.get(Reflect.get(targetLayout, "columnWidgets"), index);
|
||||
// }
|
||||
// if (Reflect.has(targetLayout, "rowWidgets")) {
|
||||
// return Reflect.get(Reflect.get(targetLayout, "rowWidgets"), index);
|
||||
// }
|
||||
// if (Reflect.has(targetLayout, "tableWidgets")) {
|
||||
// return Reflect.get(Reflect.get(targetLayout, "tableWidgets"), index);
|
||||
// }
|
||||
// if (Reflect.has(targetLayout, "layout")) {
|
||||
// return Reflect.get(Reflect.get(targetLayout, "layout"), index);
|
||||
// }
|
||||
// if (targetLayout instanceof Widget) {
|
||||
// if (targetLayout.props.kind === "PopoverButton" && targetLayout.props instanceof PopoverButton && targetLayout.props.popoverLayout) {
|
||||
// return Reflect.get(targetLayout.props.popoverLayout, index);
|
||||
// }
|
||||
// console.error("Tried to index widget");
|
||||
// return targetLayout;
|
||||
// }
|
||||
// if (Reflect.has(targetLayout, "action")) {
|
||||
// const children = Reflect.get(targetLayout, "children");
|
||||
// return children ? Reflect.get(children, index) : undefined;
|
||||
// }
|
||||
// return Reflect.get(targetLayout, index);
|
||||
// }, layout.layout as UIItem);
|
||||
|
||||
// // Clear array length using Reflect to trigger reactivity
|
||||
// if (Reflect.has(diffObject, "length")) {
|
||||
// Reflect.set(diffObject, "length", 0);
|
||||
// }
|
||||
|
||||
// // Remove all keys using Reflect.deleteProperty to ensure proxy notifications
|
||||
// Reflect.ownKeys(diffObject).forEach((key) => {
|
||||
// if (key !== "length") {
|
||||
// // Don't delete length property on arrays
|
||||
// Reflect.deleteProperty(diffObject, key);
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Assign new properties using Reflect.set
|
||||
// if (update.newValue && typeof update.newValue === "object") {
|
||||
// Object.entries(update.newValue).forEach(([key, value]) => {
|
||||
// Reflect.set(diffObject, key, value);
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
export type LayoutGroup = WidgetSpanRow | WidgetSpanColumn | WidgetTable | WidgetSection;
|
||||
|
||||
export type WidgetSpanColumn = { columnWidgets: Widget[] };
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { tick } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
import { type Editor } from "@graphite/editor";
|
||||
|
||||
|
@ -17,83 +16,59 @@ import {
|
|||
UpdateGraphFadeArtwork,
|
||||
} from "@graphite/messages.svelte";
|
||||
|
||||
export const documentContextState = $state({
|
||||
// Layouts - these are already $state objects from defaultWidgetLayout()
|
||||
documentModeLayout: defaultWidgetLayout(),
|
||||
toolOptionsLayout: defaultWidgetLayout(),
|
||||
documentBarLayout: defaultWidgetLayout(),
|
||||
toolShelfLayout: defaultWidgetLayout(),
|
||||
workingColorsLayout: defaultWidgetLayout(),
|
||||
nodeGraphControlBarLayout: defaultWidgetLayout(),
|
||||
// Graph view overlay
|
||||
graphViewOverlayOpen: false,
|
||||
fadeArtwork: 100,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
export function createDocumentState(editor: Editor) {
|
||||
const state = writable({
|
||||
// Layouts
|
||||
documentModeLayout: defaultWidgetLayout(),
|
||||
toolOptionsLayout: defaultWidgetLayout(),
|
||||
documentBarLayout: defaultWidgetLayout(),
|
||||
toolShelfLayout: defaultWidgetLayout(),
|
||||
workingColorsLayout: defaultWidgetLayout(),
|
||||
nodeGraphControlBarLayout: defaultWidgetLayout(),
|
||||
// Graph view overlay
|
||||
graphViewOverlayOpen: false,
|
||||
fadeArtwork: 100,
|
||||
});
|
||||
const { subscribe, update } = state;
|
||||
|
||||
// Update layouts
|
||||
// Set up subscriptions (these run once when the function is called)
|
||||
editor.subscriptions.subscribeJsMessage(UpdateGraphFadeArtwork, (updateGraphFadeArtwork) => {
|
||||
update((state) => {
|
||||
state.fadeArtwork = updateGraphFadeArtwork.percentage;
|
||||
return state;
|
||||
});
|
||||
documentContextState.fadeArtwork = updateGraphFadeArtwork.percentage;
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => {
|
||||
await tick();
|
||||
|
||||
update((state) => {
|
||||
patchWidgetLayout(state.documentModeLayout, updateDocumentModeLayout);
|
||||
return state;
|
||||
});
|
||||
patchWidgetLayout(documentContextState.documentModeLayout, updateDocumentModeLayout);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateToolOptionsLayout, async (updateToolOptionsLayout) => {
|
||||
await tick();
|
||||
|
||||
update((state) => {
|
||||
patchWidgetLayout(state.toolOptionsLayout, updateToolOptionsLayout);
|
||||
return state;
|
||||
});
|
||||
patchWidgetLayout(documentContextState.toolOptionsLayout, updateToolOptionsLayout);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateDocumentBarLayout, async (updateDocumentBarLayout) => {
|
||||
await tick();
|
||||
|
||||
update((state) => {
|
||||
patchWidgetLayout(state.documentBarLayout, updateDocumentBarLayout);
|
||||
return state;
|
||||
});
|
||||
patchWidgetLayout(documentContextState.documentBarLayout, updateDocumentBarLayout);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateToolShelfLayout, async (updateToolShelfLayout) => {
|
||||
await tick();
|
||||
|
||||
update((state) => {
|
||||
patchWidgetLayout(state.toolShelfLayout, updateToolShelfLayout);
|
||||
return state;
|
||||
});
|
||||
patchWidgetLayout(documentContextState.toolShelfLayout, updateToolShelfLayout);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateWorkingColorsLayout, async (updateWorkingColorsLayout) => {
|
||||
await tick();
|
||||
|
||||
update((state) => {
|
||||
patchWidgetLayout(state.workingColorsLayout, updateWorkingColorsLayout);
|
||||
return state;
|
||||
});
|
||||
patchWidgetLayout(documentContextState.workingColorsLayout, updateWorkingColorsLayout);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphControlBarLayout, (updateNodeGraphControlBarLayout) => {
|
||||
update((state) => {
|
||||
patchWidgetLayout(state.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
|
||||
return state;
|
||||
});
|
||||
patchWidgetLayout(documentContextState.nodeGraphControlBarLayout, updateNodeGraphControlBarLayout);
|
||||
});
|
||||
|
||||
// Show or hide the graph view overlay
|
||||
editor.subscriptions.subscribeJsMessage(UpdateGraphViewOverlay, (updateGraphViewOverlay) => {
|
||||
update((state) => {
|
||||
state.graphViewOverlayOpen = updateGraphViewOverlay.open;
|
||||
return state;
|
||||
});
|
||||
documentContextState.graphViewOverlayOpen = updateGraphViewOverlay.open;
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(TriggerDelayedZoomCanvasToFitAll, () => {
|
||||
// TODO: This is horribly hacky
|
||||
[0, 1, 10, 50, 100, 200, 300, 400, 500].forEach((delay) => {
|
||||
|
@ -101,8 +76,8 @@ export function createDocumentState(editor: Editor) {
|
|||
});
|
||||
});
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
};
|
||||
// Return the reactive state object directly
|
||||
return documentContextState;
|
||||
}
|
||||
|
||||
export type DocumentState = ReturnType<typeof createDocumentState>;
|
Loading…
Add table
Add a link
Reference in a new issue