Fix Ctrl+Space not closing the graph after opening the node creation menu
This commit is contained in:
parent
dc0ae74cab
commit
c1b15fcfdf
|
|
@ -566,7 +566,7 @@
|
||||||
background: var(--color-e-nearwhite);
|
background: var(--color-e-nearwhite);
|
||||||
color: var(--color-2-mildblack);
|
color: var(--color-2-mildblack);
|
||||||
|
|
||||||
svg {
|
> .icon-label {
|
||||||
fill: var(--color-2-mildblack);
|
fill: var(--color-2-mildblack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@
|
||||||
await refreshWires();
|
await refreshWires();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveWire(wire: FrontendNodeWire): { nodeOutput: SVGSVGElement | undefined; nodeInput: SVGSVGElement | undefined } {
|
function resolveWire(wire: FrontendNodeWire): { nodeOutput: SVGSVGElement; nodeInput: SVGSVGElement } | undefined {
|
||||||
// TODO: Avoid the linear search
|
// TODO: Avoid the linear search
|
||||||
const wireStartNodeIdIndex = Array.from($nodeGraph.nodes.keys()).findIndex((nodeId) => nodeId === (wire.wireStart as Node).nodeId);
|
const wireStartNodeIdIndex = Array.from($nodeGraph.nodes.keys()).findIndex((nodeId) => nodeId === (wire.wireStart as Node).nodeId);
|
||||||
let nodeOutputConnectors = outputs[wireStartNodeIdIndex + 1];
|
let nodeOutputConnectors = outputs[wireStartNodeIdIndex + 1];
|
||||||
|
|
@ -146,6 +146,7 @@
|
||||||
}
|
}
|
||||||
const indexOutput = Number(wire.wireStart.index);
|
const indexOutput = Number(wire.wireStart.index);
|
||||||
const nodeOutput = nodeOutputConnectors?.[indexOutput] as SVGSVGElement | undefined;
|
const nodeOutput = nodeOutputConnectors?.[indexOutput] as SVGSVGElement | undefined;
|
||||||
|
if (nodeOutput === undefined) return undefined;
|
||||||
|
|
||||||
// TODO: Avoid the linear search
|
// TODO: Avoid the linear search
|
||||||
const wireEndNodeIdIndex = Array.from($nodeGraph.nodes.keys()).findIndex((nodeId) => nodeId === (wire.wireEnd as Node).nodeId);
|
const wireEndNodeIdIndex = Array.from($nodeGraph.nodes.keys()).findIndex((nodeId) => nodeId === (wire.wireEnd as Node).nodeId);
|
||||||
|
|
@ -155,6 +156,7 @@
|
||||||
}
|
}
|
||||||
const indexInput = Number(wire.wireEnd.index);
|
const indexInput = Number(wire.wireEnd.index);
|
||||||
const nodeInput = nodeInputConnectors?.[indexInput] as SVGSVGElement | undefined;
|
const nodeInput = nodeInputConnectors?.[indexInput] as SVGSVGElement | undefined;
|
||||||
|
if (nodeInput === undefined) return undefined;
|
||||||
|
|
||||||
return { nodeOutput, nodeInput };
|
return { nodeOutput, nodeInput };
|
||||||
}
|
}
|
||||||
|
|
@ -177,8 +179,9 @@
|
||||||
|
|
||||||
nodeWirePaths = $nodeGraph.wires.flatMap((wire) => {
|
nodeWirePaths = $nodeGraph.wires.flatMap((wire) => {
|
||||||
// TODO: This call contains linear searches, which combined with the loop we're in, causes O(n^2) complexity as the graph grows
|
// TODO: This call contains linear searches, which combined with the loop we're in, causes O(n^2) complexity as the graph grows
|
||||||
const { nodeOutput, nodeInput } = resolveWire(wire);
|
const resolvedWires = resolveWire(wire);
|
||||||
if (!nodeOutput || !nodeInput) return [];
|
if (!resolvedWires) return [];
|
||||||
|
const { nodeOutput, nodeInput } = resolvedWires;
|
||||||
|
|
||||||
const wireStartNode = wire.wireStart.nodeId !== undefined ? $nodeGraph.nodes.get(wire.wireStart.nodeId) : undefined;
|
const wireStartNode = wire.wireStart.nodeId !== undefined ? $nodeGraph.nodes.get(wire.wireStart.nodeId) : undefined;
|
||||||
const wireStart = wireStartNode?.isLayer || false;
|
const wireStart = wireStartNode?.isLayer || false;
|
||||||
|
|
|
||||||
|
|
@ -217,5 +217,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// paddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpaddingpadding
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
if (["KeyC", "KeyI", "KeyJ"].includes(key) && accelKey && e.shiftKey) return false;
|
if (["KeyC", "KeyI", "KeyJ"].includes(key) && accelKey && e.shiftKey) return false;
|
||||||
|
|
||||||
// Don't redirect tab or enter if not in canvas (to allow navigating elements)
|
// Don't redirect tab or enter if not in canvas (to allow navigating elements)
|
||||||
|
potentiallyRestoreCanvasFocus(e);
|
||||||
if (!canvasFocused && !targetIsTextField(e.target || undefined) && ["Tab", "Enter", "NumpadEnter", "Space", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowUp"].includes(key)) return false;
|
if (!canvasFocused && !targetIsTextField(e.target || undefined) && ["Tab", "Enter", "NumpadEnter", "Space", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowUp"].includes(key)) return false;
|
||||||
|
|
||||||
// Don't redirect if a MenuList is open
|
// Don't redirect if a MenuList is open
|
||||||
|
|
@ -145,6 +146,8 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
|
|
||||||
// While any pointer button is already down, additional button down events are not reported, but they are sent as `pointermove` events and these are handled in the backend
|
// While any pointer button is already down, additional button down events are not reported, but they are sent as `pointermove` events and these are handled in the backend
|
||||||
function onPointerMove(e: PointerEvent) {
|
function onPointerMove(e: PointerEvent) {
|
||||||
|
potentiallyRestoreCanvasFocus(e);
|
||||||
|
|
||||||
if (!e.buttons) viewportPointerInteractionOngoing = false;
|
if (!e.buttons) viewportPointerInteractionOngoing = false;
|
||||||
|
|
||||||
// Don't redirect pointer movement to the backend if there's no ongoing interaction and it's over a floating menu, or the graph overlay, on top of the canvas
|
// Don't redirect pointer movement to the backend if there's no ongoing interaction and it's over a floating menu, or the graph overlay, on top of the canvas
|
||||||
|
|
@ -155,25 +158,15 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
const inGraphOverlay = get(document).graphViewOverlayOpen;
|
const inGraphOverlay = get(document).graphViewOverlayOpen;
|
||||||
if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return;
|
if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return;
|
||||||
|
|
||||||
const { target } = e;
|
|
||||||
const newInCanvasArea = (target instanceof Element && target.closest("[data-viewport], [data-graph]")) instanceof Element && !targetIsTextField(window.document.activeElement || undefined);
|
|
||||||
if (newInCanvasArea && !canvasFocused) {
|
|
||||||
canvasFocused = true;
|
|
||||||
app?.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.handle.onMouseMove(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.handle.onMouseMove(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseDown(e: MouseEvent) {
|
|
||||||
// Block middle mouse button auto-scroll mode (the circlar gizmo that appears and allows quick scrolling by moving the cursor above or below it)
|
|
||||||
if (e.button === BUTTON_MIDDLE) e.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onPointerDown(e: PointerEvent) {
|
function onPointerDown(e: PointerEvent) {
|
||||||
|
potentiallyRestoreCanvasFocus(e);
|
||||||
|
|
||||||
const { target } = e;
|
const { target } = e;
|
||||||
const isTargetingCanvas = target instanceof Element && (target.closest("[data-viewport]") || target.closest("[data-node-graph]"));
|
const isTargetingCanvas = target instanceof Element && target.closest("[data-viewport], [data-node-graph]");
|
||||||
const inDialog = target instanceof Element && target.closest("[data-dialog] [data-floating-menu-content]");
|
const inDialog = target instanceof Element && target.closest("[data-dialog] [data-floating-menu-content]");
|
||||||
const inContextMenu = target instanceof Element && target.closest("[data-context-menu]");
|
const inContextMenu = target instanceof Element && target.closest("[data-context-menu]");
|
||||||
const inTextInput = target === textToolInteractiveInputElement;
|
const inTextInput = target === textToolInteractiveInputElement;
|
||||||
|
|
@ -185,18 +178,23 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inTextInput && !inContextMenu) {
|
if (!inTextInput && !inContextMenu) {
|
||||||
const isLeftOrRightClick = e.button === BUTTON_RIGHT || e.button === BUTTON_LEFT;
|
if (textToolInteractiveInputElement) {
|
||||||
if (textToolInteractiveInputElement) editor.handle.onChangeText(textInputCleanup(textToolInteractiveInputElement.innerText), isLeftOrRightClick);
|
const isLeftOrRightClick = e.button === BUTTON_RIGHT || e.button === BUTTON_LEFT;
|
||||||
else viewportPointerInteractionOngoing = isTargetingCanvas instanceof Element;
|
editor.handle.onChangeText(textInputCleanup(textToolInteractiveInputElement.innerText), isLeftOrRightClick);
|
||||||
|
} else {
|
||||||
|
viewportPointerInteractionOngoing = isTargetingCanvas instanceof Element;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewportPointerInteractionOngoing) {
|
if (viewportPointerInteractionOngoing && isTargetingCanvas instanceof Element) {
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.handle.onMouseDown(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.handle.onMouseDown(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPointerUp(e: PointerEvent) {
|
function onPointerUp(e: PointerEvent) {
|
||||||
|
potentiallyRestoreCanvasFocus(e);
|
||||||
|
|
||||||
// Don't let the browser navigate back or forward when using the buttons on some mice
|
// Don't let the browser navigate back or forward when using the buttons on some mice
|
||||||
// TODO: This works in Chrome but not in Firefox
|
// TODO: This works in Chrome but not in Firefox
|
||||||
// TODO: Possible workaround: use the browser's history API to block navigation:
|
// TODO: Possible workaround: use the browser's history API to block navigation:
|
||||||
|
|
@ -211,9 +209,16 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
editor.handle.onMouseUp(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.handle.onMouseUp(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mouse events
|
||||||
|
|
||||||
function onPotentialDoubleClick(e: MouseEvent) {
|
function onPotentialDoubleClick(e: MouseEvent) {
|
||||||
if (textToolInteractiveInputElement || inPointerLock) return;
|
if (textToolInteractiveInputElement || inPointerLock) return;
|
||||||
|
|
||||||
|
// Allow only events within the viewport or node graph boundaries
|
||||||
|
const { target } = e;
|
||||||
|
const isTargetingCanvas = target instanceof Element && target.closest("[data-viewport], [data-node-graph]");
|
||||||
|
if (!(isTargetingCanvas instanceof Element)) return;
|
||||||
|
|
||||||
// Allow only repeated increments of double-clicks (not 1, 3, 5, etc.)
|
// Allow only repeated increments of double-clicks (not 1, 3, 5, etc.)
|
||||||
if (e.detail % 2 == 1) return;
|
if (e.detail % 2 == 1) return;
|
||||||
|
|
||||||
|
|
@ -229,15 +234,26 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
editor.handle.onDoubleClick(e.clientX, e.clientY, buttons, modifiers);
|
editor.handle.onDoubleClick(e.clientX, e.clientY, buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onMouseDown(e: MouseEvent) {
|
||||||
|
// Block middle mouse button auto-scroll mode (the circlar gizmo that appears and allows quick scrolling by moving the cursor above or below it)
|
||||||
|
if (e.button === BUTTON_MIDDLE) e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onContextMenu(e: MouseEvent) {
|
||||||
|
if (!targetIsTextField(e.target || undefined) && e.target !== textToolInteractiveInputElement) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onPointerLockChange() {
|
function onPointerLockChange() {
|
||||||
inPointerLock = Boolean(window.document.pointerLockElement);
|
inPointerLock = Boolean(window.document.pointerLockElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse events
|
// Wheel events
|
||||||
|
|
||||||
function onWheelScroll(e: WheelEvent) {
|
function onWheelScroll(e: WheelEvent) {
|
||||||
const { target } = e;
|
const { target } = e;
|
||||||
const isTargetingCanvas = target instanceof Element && (target.closest("[data-viewport]") || target.closest("[data-node-graph]"));
|
const isTargetingCanvas = target instanceof Element && target.closest("[data-viewport], [data-node-graph]");
|
||||||
|
|
||||||
// Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
|
// Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
|
||||||
// There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
|
// There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
|
||||||
|
|
@ -254,12 +270,6 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContextMenu(e: MouseEvent) {
|
|
||||||
if (!targetIsTextField(e.target || undefined) && e.target !== textToolInteractiveInputElement) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receives a custom event dispatched when the user begins interactively editing with the text tool.
|
// Receives a custom event dispatched when the user begins interactively editing with the text tool.
|
||||||
// We keep a copy of the text input element to check against when it's active for text entry.
|
// We keep a copy of the text input element to check against when it's active for text entry.
|
||||||
function onModifyInputField(e: CustomEvent) {
|
function onModifyInputField(e: CustomEvent) {
|
||||||
|
|
@ -420,6 +430,17 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
function potentiallyRestoreCanvasFocus(e: Event) {
|
||||||
|
const { target } = e;
|
||||||
|
const newInCanvasArea = (target instanceof Element && target.closest("[data-viewport], [data-graph]")) instanceof Element && !targetIsTextField(window.document.activeElement || undefined);
|
||||||
|
if (!canvasFocused && newInCanvasArea) {
|
||||||
|
canvasFocused = true;
|
||||||
|
app?.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
|
|
||||||
// Bind the event listeners
|
// Bind the event listeners
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue