diff --git a/browser_tests/tests/interaction.spec.ts b/browser_tests/tests/interaction.spec.ts index de46bca2e..c3754ed3b 100644 --- a/browser_tests/tests/interaction.spec.ts +++ b/browser_tests/tests/interaction.spec.ts @@ -1012,6 +1012,8 @@ test.describe('Canvas Navigation', () => { test('Shift + mouse wheel should pan canvas horizontally', async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.Canvas.MouseWheelScroll', 'panning') + await comfyPage.page.click('canvas') await comfyPage.nextFrame() diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-center-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-center-chromium-linux.png index 57b6438ae..a9d0efb74 100644 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-center-chromium-linux.png and b/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-center-chromium-linux.png differ diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-left-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-left-chromium-linux.png index a9d0efb74..57a92edc5 100644 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-left-chromium-linux.png and b/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-left-chromium-linux.png differ diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-right-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-right-chromium-linux.png index 57b6438ae..e607294e3 100644 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-right-chromium-linux.png and b/browser_tests/tests/interaction.spec.ts-snapshots/standard-shift-wheel-pan-right-chromium-linux.png differ diff --git a/src/lib/litegraph/src/LGraphCanvas.ts b/src/lib/litegraph/src/LGraphCanvas.ts index fade868c2..e351ce722 100644 --- a/src/lib/litegraph/src/LGraphCanvas.ts +++ b/src/lib/litegraph/src/LGraphCanvas.ts @@ -2348,7 +2348,7 @@ export class LGraphCanvas if ( ctrlOrMeta && !e.altKey && - LiteGraph.canvasNavigationMode === 'legacy' + LiteGraph.leftMouseClickBehavior === 'panning' ) { this.#setupNodeSelectionDrag(e, pointer, node) @@ -2616,8 +2616,8 @@ export class LGraphCanvas !pointer.onDrag && this.allow_dragcanvas ) { - // allow dragging canvas if canvas is not in standard, or read-only (pan mode in standard) - if (LiteGraph.canvasNavigationMode !== 'standard' || this.read_only) { + // allow dragging canvas based on leftMouseClickBehavior or read-only mode + if (LiteGraph.leftMouseClickBehavior === 'panning' || this.read_only) { pointer.onClick = () => this.processSelect(null, e) pointer.finally = () => (this.dragging_canvas = false) this.dragging_canvas = true @@ -3629,8 +3629,8 @@ export class LGraphCanvas e.ctrlKey || (e.metaKey && navigator.platform.includes('Mac')) const isZoomModifier = isCtrlOrMacMeta && !e.altKey && !e.shiftKey - if (isZoomModifier || LiteGraph.canvasNavigationMode === 'legacy') { - // Legacy mode or standard mode with ctrl - use wheel for zoom + if (isZoomModifier || LiteGraph.mouseWheelScroll === 'zoom') { + // Zoom mode or modifier key pressed - use wheel for zoom if (isTrackpad) { // Trackpad gesture - use smooth scaling scale *= 1 + e.deltaY * (1 - this.zoom_speed) * 0.18 @@ -3645,7 +3645,6 @@ export class LGraphCanvas this.ds.changeScale(scale, [e.clientX, e.clientY]) } } else { - // Standard mode without ctrl - use wheel / gestures to pan // Trackpads and mice work on significantly different scales const factor = isTrackpad ? 0.18 : 0.008_333 diff --git a/src/lib/litegraph/src/LiteGraphGlobal.ts b/src/lib/litegraph/src/LiteGraphGlobal.ts index 85911e5b9..5d762799a 100644 --- a/src/lib/litegraph/src/LiteGraphGlobal.ts +++ b/src/lib/litegraph/src/LiteGraphGlobal.ts @@ -304,9 +304,14 @@ export class LiteGraphGlobal { /** * "standard": change the dragging on left mouse button click to select, enable middle-click or spacebar+left-click dragging * "legacy": Enable dragging on left-click (original behavior) + * "custom": Use leftMouseClickBehavior and mouseWheelScroll settings * @default "legacy" */ - canvasNavigationMode: 'standard' | 'legacy' = 'legacy' + canvasNavigationMode: 'standard' | 'legacy' | 'custom' = 'legacy' + + leftMouseClickBehavior: 'panning' | 'select' = 'panning' + + mouseWheelScroll: 'panning' | 'zoom' = 'panning' /** * If `true`, widget labels and values will both be truncated (proportionally to size), diff --git a/src/lib/litegraph/test/__snapshots__/ConfigureGraph.test.ts.snap b/src/lib/litegraph/test/__snapshots__/ConfigureGraph.test.ts.snap index 80e344d5b..a90c8d3de 100644 --- a/src/lib/litegraph/test/__snapshots__/ConfigureGraph.test.ts.snap +++ b/src/lib/litegraph/test/__snapshots__/ConfigureGraph.test.ts.snap @@ -1,333 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`LGraph configure() > LGraph matches previous snapshot (normal configure() usage) > configuredBasicGraph 1`] = ` -LGraph { - "_groups": [ - LGraphGroup { - "_bounding": Float32Array [ - 20, - 20, - 1, - 3, - ], - "_children": Set {}, - "_nodes": [], - "_pos": Float32Array [ - 20, - 20, - ], - "_size": Float32Array [ - 1, - 3, - ], - "color": "#6029aa", - "flags": {}, - "font": undefined, - "font_size": 14, - "graph": [Circular], - "id": 123, - "isPointInside": [Function], - "selected": undefined, - "setDirtyCanvas": [Function], - "title": "A group to test with", - }, - ], - "_input_nodes": undefined, - "_last_trigger_time": undefined, - "_links": Map {}, - "_nodes": [ - LGraphNode { - "_collapsed_width": undefined, - "_level": undefined, - "_pos": Float32Array [ - 10, - 10, - ], - "_posSize": Float32Array [ - 10, - 10, - 140, - 60, - ], - "_relative_id": undefined, - "_shape": undefined, - "_size": Float32Array [ - 140, - 60, - ], - "action_call": undefined, - "action_triggered": undefined, - "badgePosition": "top-left", - "badges": [], - "bgcolor": undefined, - "block_delete": undefined, - "boxcolor": undefined, - "clip_area": undefined, - "clonable": undefined, - "color": undefined, - "console": undefined, - "exec_version": undefined, - "execute_triggered": undefined, - "flags": {}, - "freeWidgetSpace": undefined, - "gotFocusAt": undefined, - "graph": [Circular], - "has_errors": undefined, - "id": 1, - "ignore_remove": undefined, - "inputs": [], - "last_serialization": undefined, - "locked": undefined, - "lostFocusAt": undefined, - "mode": 0, - "mouseOver": undefined, - "order": 0, - "outputs": [], - "progress": undefined, - "properties": {}, - "properties_info": [], - "redraw_on_mouse": undefined, - "removable": undefined, - "resizable": undefined, - "selected": undefined, - "serialize_widgets": undefined, - "showAdvanced": undefined, - "strokeStyles": { - "error": [Function], - "selected": [Function], - }, - "title": "LGraphNode", - "title_buttons": [], - "type": "mustBeSet", - "widgets": undefined, - "widgets_start_y": undefined, - "widgets_up": undefined, - }, - ], - "_nodes_by_id": { - "1": LGraphNode { - "_collapsed_width": undefined, - "_level": undefined, - "_pos": Float32Array [ - 10, - 10, - ], - "_posSize": Float32Array [ - 10, - 10, - 140, - 60, - ], - "_relative_id": undefined, - "_shape": undefined, - "_size": Float32Array [ - 140, - 60, - ], - "action_call": undefined, - "action_triggered": undefined, - "badgePosition": "top-left", - "badges": [], - "bgcolor": undefined, - "block_delete": undefined, - "boxcolor": undefined, - "clip_area": undefined, - "clonable": undefined, - "color": undefined, - "console": undefined, - "exec_version": undefined, - "execute_triggered": undefined, - "flags": {}, - "freeWidgetSpace": undefined, - "gotFocusAt": undefined, - "graph": [Circular], - "has_errors": undefined, - "id": 1, - "ignore_remove": undefined, - "inputs": [], - "last_serialization": undefined, - "locked": undefined, - "lostFocusAt": undefined, - "mode": 0, - "mouseOver": undefined, - "order": 0, - "outputs": [], - "progress": undefined, - "properties": {}, - "properties_info": [], - "redraw_on_mouse": undefined, - "removable": undefined, - "resizable": undefined, - "selected": undefined, - "serialize_widgets": undefined, - "showAdvanced": undefined, - "strokeStyles": { - "error": [Function], - "selected": [Function], - }, - "title": "LGraphNode", - "title_buttons": [], - "type": "mustBeSet", - "widgets": undefined, - "widgets_start_y": undefined, - "widgets_up": undefined, - }, - }, - "_nodes_executable": [], - "_nodes_in_order": [ - LGraphNode { - "_collapsed_width": undefined, - "_level": undefined, - "_pos": Float32Array [ - 10, - 10, - ], - "_posSize": Float32Array [ - 10, - 10, - 140, - 60, - ], - "_relative_id": undefined, - "_shape": undefined, - "_size": Float32Array [ - 140, - 60, - ], - "action_call": undefined, - "action_triggered": undefined, - "badgePosition": "top-left", - "badges": [], - "bgcolor": undefined, - "block_delete": undefined, - "boxcolor": undefined, - "clip_area": undefined, - "clonable": undefined, - "color": undefined, - "console": undefined, - "exec_version": undefined, - "execute_triggered": undefined, - "flags": {}, - "freeWidgetSpace": undefined, - "gotFocusAt": undefined, - "graph": [Circular], - "has_errors": undefined, - "id": 1, - "ignore_remove": undefined, - "inputs": [], - "last_serialization": undefined, - "locked": undefined, - "lostFocusAt": undefined, - "mode": 0, - "mouseOver": undefined, - "order": 0, - "outputs": [], - "progress": undefined, - "properties": {}, - "properties_info": [], - "redraw_on_mouse": undefined, - "removable": undefined, - "resizable": undefined, - "selected": undefined, - "serialize_widgets": undefined, - "showAdvanced": undefined, - "strokeStyles": { - "error": [Function], - "selected": [Function], - }, - "title": "LGraphNode", - "title_buttons": [], - "type": "mustBeSet", - "widgets": undefined, - "widgets_start_y": undefined, - "widgets_up": undefined, - }, - ], - "_subgraphs": Map {}, - "_version": 3, - "catch_errors": true, - "config": {}, - "elapsed_time": 0.01, - "errors_in_execution": undefined, - "events": CustomEventTarget {}, - "execution_time": undefined, - "execution_timer_id": undefined, - "extra": {}, - "filter": undefined, - "fixedtime": 0, - "fixedtime_lapse": 0.01, - "globaltime": 0, - "id": "ca9da7d8-fddd-4707-ad32-67be9be13140", - "iteration": 0, - "last_update_time": 0, - "links": Map {}, - "list_of_graphcanvas": null, - "nodes_actioning": [], - "nodes_executedAction": [], - "nodes_executing": [], - "revision": 0, - "runningtime": 0, - "starttime": 0, - "state": { - "lastGroupId": 123, - "lastLinkId": 0, - "lastNodeId": 1, - "lastRerouteId": 0, - }, - "status": 1, - "vars": {}, - "version": 1, -} -`; - -exports[`LGraph configure() > LGraph matches previous snapshot (normal configure() usage) > configuredMinGraph 1`] = ` -LGraph { - "_groups": [], - "_input_nodes": undefined, - "_last_trigger_time": undefined, - "_links": Map {}, - "_nodes": [], - "_nodes_by_id": {}, - "_nodes_executable": [], - "_nodes_in_order": [], - "_subgraphs": Map {}, - "_version": 0, - "catch_errors": true, - "config": {}, - "elapsed_time": 0.01, - "errors_in_execution": undefined, - "events": CustomEventTarget {}, - "execution_time": undefined, - "execution_timer_id": undefined, - "extra": {}, - "filter": undefined, - "fixedtime": 0, - "fixedtime_lapse": 0.01, - "globaltime": 0, - "id": "d175890f-716a-4ece-ba33-1d17a513b7be", - "iteration": 0, - "last_update_time": 0, - "links": Map {}, - "list_of_graphcanvas": null, - "nodes_actioning": [], - "nodes_executedAction": [], - "nodes_executing": [], - "revision": 0, - "runningtime": 0, - "starttime": 0, - "state": { - "lastGroupId": 0, - "lastLinkId": 0, - "lastNodeId": 0, - "lastRerouteId": 0, - }, - "status": 1, - "vars": {}, - "version": 1, -} -`; -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - exports[`LGraph configure() > LGraph matches previous snapshot (normal configure() usage) > configuredBasicGraph 1`] = ` LGraph { "_groups": [ diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 7c7cec305..09335e18a 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -30,7 +30,7 @@ "tooltip": "Image URL for the canvas background. You can right-click an image in the outputs panel and select \"Set as Background\" to use it, or upload your own image using the upload button." }, "Comfy_Canvas_NavigationMode": { - "name": "Canvas Navigation Mode", + "name": "Navigation Mode", "options": { "Standard (New)": "Standard (New)", "Drag Navigation": "Drag Navigation" diff --git a/src/platform/settings/composables/useLitegraphSettings.ts b/src/platform/settings/composables/useLitegraphSettings.ts index 347e0289e..468c7d339 100644 --- a/src/platform/settings/composables/useLitegraphSettings.ts +++ b/src/platform/settings/composables/useLitegraphSettings.ts @@ -131,11 +131,26 @@ export const useLitegraphSettings = () => { const navigationMode = settingStore.get('Comfy.Canvas.NavigationMode') as | 'standard' | 'legacy' + | 'custom' LiteGraph.canvasNavigationMode = navigationMode LiteGraph.macTrackpadGestures = navigationMode === 'standard' }) + watchEffect(() => { + const leftMouseBehavior = settingStore.get( + 'Comfy.Canvas.LeftMouseClickBehavior' + ) as 'panning' | 'select' + LiteGraph.leftMouseClickBehavior = leftMouseBehavior + }) + + watchEffect(() => { + const mouseWheelScroll = settingStore.get( + 'Comfy.Canvas.MouseWheelScroll' + ) as 'panning' | 'zoom' + LiteGraph.mouseWheelScroll = mouseWheelScroll + }) + watchEffect(() => { LiteGraph.saveViewportWithGraph = settingStore.get( 'Comfy.EnableWorkflowViewRestore' diff --git a/src/platform/settings/constants/coreSettings.ts b/src/platform/settings/constants/coreSettings.ts index cdb2fe709..127229044 100644 --- a/src/platform/settings/constants/coreSettings.ts +++ b/src/platform/settings/constants/coreSettings.ts @@ -1,4 +1,5 @@ import { LinkMarkerShape, LiteGraph } from '@/lib/litegraph/src/litegraph' +import { useSettingStore } from '@/platform/settings/settingStore' import type { SettingParams } from '@/platform/settings/types' import type { ColorPalettes } from '@/schemas/colorPaletteSchema' import type { Keybinding } from '@/schemas/keyBindingSchema' @@ -138,6 +139,95 @@ export const CORE_SETTINGS: SettingParams[] = [ type: 'boolean', defaultValue: false }, + { + id: 'Comfy.Canvas.NavigationMode', + category: ['LiteGraph', 'Canvas Navigation', 'NavigationMode'], + name: 'Navigation Mode', + defaultValue: 'legacy', + type: 'combo', + sortOrder: 100, + options: [ + { value: 'standard', text: 'Standard (New)' }, + { value: 'legacy', text: 'Drag Navigation' }, + { value: 'custom', text: 'Custom' } + ], + versionAdded: '1.25.0', + defaultsByInstallVersion: { + '1.25.0': 'legacy' + }, + onChange: async (newValue: string) => { + const settingStore = useSettingStore() + + if (newValue === 'standard') { + // Update related settings to match standard mode - select + panning + await settingStore.set('Comfy.Canvas.LeftMouseClickBehavior', 'select') + await settingStore.set('Comfy.Canvas.MouseWheelScroll', 'panning') + } else if (newValue === 'legacy') { + // Update related settings to match legacy mode - panning + zoom + await settingStore.set('Comfy.Canvas.LeftMouseClickBehavior', 'panning') + await settingStore.set('Comfy.Canvas.MouseWheelScroll', 'zoom') + } + } + }, + { + id: 'Comfy.Canvas.LeftMouseClickBehavior', + category: ['LiteGraph', 'Canvas Navigation', 'LeftMouseClickBehavior'], + name: 'Left Mouse Click Behavior', + defaultValue: 'panning', + type: 'radio', + sortOrder: 50, + options: [ + { value: 'panning', text: 'Panning' }, + { value: 'select', text: 'Select' } + ], + versionAdded: '1.27.4', + onChange: async (newValue: string) => { + const settingStore = useSettingStore() + + const navigationMode = settingStore.get('Comfy.Canvas.NavigationMode') + + if (navigationMode !== 'custom') { + if ( + (newValue === 'select' && navigationMode === 'standard') || + (newValue === 'panning' && navigationMode === 'legacy') + ) { + return + } + + // only set to custom if it doesn't match the preset modes + await settingStore.set('Comfy.Canvas.NavigationMode', 'custom') + } + } + }, + { + id: 'Comfy.Canvas.MouseWheelScroll', + category: ['LiteGraph', 'Canvas Navigation', 'MouseWheelScroll'], + name: 'Mouse Wheel Scroll', + defaultValue: 'zoom', + type: 'radio', + options: [ + { value: 'panning', text: 'Panning' }, + { value: 'zoom', text: 'Zoom in/out' } + ], + versionAdded: '1.27.4', + onChange: async (newValue: string) => { + const settingStore = useSettingStore() + + const navigationMode = settingStore.get('Comfy.Canvas.NavigationMode') + + if (navigationMode !== 'custom') { + if ( + (newValue === 'panning' && navigationMode === 'standard') || + (newValue === 'zoom' && navigationMode === 'legacy') + ) { + return + } + + // only set to custom if it doesn't match the preset modes + await settingStore.set('Comfy.Canvas.NavigationMode', 'custom') + } + } + }, { id: 'Comfy.Graph.CanvasInfo', category: ['LiteGraph', 'Canvas', 'CanvasInfo'], @@ -813,21 +903,6 @@ export const CORE_SETTINGS: SettingParams[] = [ defaultValue: 8, versionAdded: '1.26.7' }, - { - id: 'Comfy.Canvas.NavigationMode', - category: ['LiteGraph', 'Canvas', 'CanvasNavigationMode'], - name: 'Canvas Navigation Mode', - defaultValue: 'legacy', - type: 'combo', - options: [ - { value: 'standard', text: 'Standard (New)' }, - { value: 'legacy', text: 'Drag Navigation' } - ], - versionAdded: '1.25.0', - defaultsByInstallVersion: { - '1.25.0': 'legacy' - } - }, { id: 'Comfy.Canvas.SelectionToolbox', category: ['LiteGraph', 'Canvas', 'SelectionToolbox'], diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index d761133ff..007b3e58d 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -469,6 +469,8 @@ const zSettings = z.object({ 'Comfy.Minimap.RenderBypassState': z.boolean(), 'Comfy.Minimap.RenderErrorState': z.boolean(), 'Comfy.Canvas.NavigationMode': z.string(), + 'Comfy.Canvas.LeftMouseClickBehavior': z.string(), + 'Comfy.Canvas.MouseWheelScroll': z.string(), 'Comfy.VueNodes.Enabled': z.boolean(), 'Comfy.Assets.UseAssetAPI': z.boolean(), 'Comfy-Desktop.AutoUpdate': z.boolean(), diff --git a/tests-ui/tests/litegraph/core/__snapshots__/litegraph.test.ts.snap b/tests-ui/tests/litegraph/core/__snapshots__/litegraph.test.ts.snap index d45626827..5302edcbc 100644 --- a/tests-ui/tests/litegraph/core/__snapshots__/litegraph.test.ts.snap +++ b/tests-ui/tests/litegraph/core/__snapshots__/litegraph.test.ts.snap @@ -155,9 +155,11 @@ LiteGraphGlobal { "do_add_triggers_slots": false, "highlight_selected_group": true, "isInsideRectangle": [Function], + "leftMouseClickBehavior": "panning", "macGesturesRequireMac": true, "macTrackpadGestures": false, "middle_click_slot_add_default_node": false, + "mouseWheelScroll": "panning", "node_box_coloured_by_mode": false, "node_box_coloured_when_on": false, "node_images_path": "",