Graph canvas menu (#1023)

* add graph canvas menu

* Move to corner

* Remove action bar reset zoom button

* nit

* Add setting

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
This commit is contained in:
pythongosssss
2024-10-01 05:06:43 +09:00
committed by GitHub
parent 224c0080ee
commit f75f774ddb
9 changed files with 145 additions and 13 deletions

View File

@@ -9,8 +9,8 @@
>
<slot name="side-bar-panel"></slot>
</SplitterPanel>
<SplitterPanel class="graph-canvas-panel" :size="100">
<div></div>
<SplitterPanel class="graph-canvas-panel relative" :size="100">
<slot name="graph-canvas-panel"></slot>
</SplitterPanel>
<SplitterPanel
class="side-bar-panel"

View File

@@ -61,12 +61,6 @@
commandStore.getCommandFunction('Comfy.RefreshNodeDefinitions')()
"
/>
<Button
v-tooltip.bottom="$t('menu.resetView')"
icon="pi pi-expand"
severity="secondary"
@click="() => commandStore.getCommandFunction('Comfy.ResetView')()"
/>
</ButtonGroup>
</div>
</Panel>

View File

@@ -4,6 +4,9 @@
<template #side-bar-panel>
<SideToolbar />
</template>
<template #graph-canvas-panel>
<GraphCanvasMenu v-if="canvasMenuEnabled" />
</template>
</LiteGraphCanvasSplitterOverlay>
<TitleEditor />
<canvas ref="canvasRef" id="graph-canvas" tabindex="1" />
@@ -44,6 +47,7 @@ import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
import { useCanvasStore } from '@/stores/graphStore'
import { ComfyModelDef } from '@/stores/modelStore'
import { useModelToNodeStore } from '@/stores/modelToNodeStore'
import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue'
const emit = defineEmits(['ready'])
const canvasRef = ref<HTMLCanvasElement | null>(null)
@@ -55,6 +59,9 @@ const modelToNodeStore = useModelToNodeStore()
const betaMenuEnabled = computed(
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
)
const canvasMenuEnabled = computed(() =>
settingStore.get('Comfy.Graph.CanvasMenu')
)
watchEffect(() => {
const canvasInfoEnabled = settingStore.get('Comfy.Graph.CanvasInfo')

View File

@@ -0,0 +1,81 @@
<template>
<ButtonGroup
class="p-buttongroup-vertical absolute bottom-[10px] right-[10px] z-[1000] pointer-events-auto"
>
<Button
severity="secondary"
icon="pi pi-plus"
v-tooltip.left="t('graphCanvasMenu.zoomIn')"
@mousedown="repeat('Comfy.Canvas.ZoomIn')"
@mouseup="stopRepeat"
/>
<Button
severity="secondary"
icon="pi pi-minus"
v-tooltip.left="t('graphCanvasMenu.zoomOut')"
@mousedown="repeat('Comfy.Canvas.ZoomOut')"
@mouseup="stopRepeat"
/>
<Button
severity="secondary"
icon="pi pi-expand"
v-tooltip.left="t('graphCanvasMenu.resetView')"
@click="() => commandStore.getCommandFunction('Comfy.Canvas.ResetView')()"
/>
<Button
severity="secondary"
v-tooltip.left="
t('graphCanvasMenu.' + (canvasStore.readOnly ? 'unlock' : 'lock'))
"
@click="
() => commandStore.getCommandFunction('Comfy.Canvas.ToggleLock')()
"
>
<template #icon>
<i-material-symbols:lock-outline v-if="canvasStore.readOnly" />
<i-material-symbols:lock-open-outline v-else />
</template>
</Button>
</ButtonGroup>
</template>
<script setup lang="ts">
import ButtonGroup from 'primevue/buttongroup'
import Button from 'primevue/button'
import { useCommandStore } from '@/stores/commandStore'
import { useCanvasStore } from '@/stores/graphStore'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const commandStore = useCommandStore()
const canvasStore = useCanvasStore()
let interval: number | null = null
const repeat = (command: string) => {
if (interval) return
const cmd = commandStore.getCommandFunction(command)
cmd()
interval = window.setInterval(cmd, 100)
}
const stopRepeat = () => {
if (interval) {
clearInterval(interval)
interval = null
}
}
</script>
<style scoped>
.p-buttongroup-vertical {
display: flex;
flex-direction: column;
border-radius: var(--p-button-border-radius);
overflow: hidden;
border: 1px solid var(--p-panel-border-color);
}
.p-buttongroup-vertical .p-button {
margin: 0;
border-radius: 0;
}
</style>

View File

@@ -94,6 +94,13 @@ const messages = {
upscale: '2 Pass Upscale',
flux_schnell: 'Flux Schnell'
}
},
graphCanvasMenu: {
zoomIn: 'Zoom In',
zoomOut: 'Zoom Out',
resetView: 'Reset View',
lock: 'Lock Graph',
unlock: 'Unlock Graph'
}
},
zh: {

View File

@@ -354,7 +354,8 @@ LGraphNode.prototype.addDOMWidget = function (
width: `${widgetWidth - margin * 2}px`,
height: `${(widget.computedHeight ?? 50) - margin * 2}px`,
position: 'absolute',
zIndex: app.graph.nodes.indexOf(node)
zIndex: app.graph.nodes.indexOf(node),
pointerEvents: app.canvas.read_only ? 'none' : 'auto'
})
if (enableDomClipping) {

View File

@@ -124,7 +124,7 @@ export const useCommandStore = defineStore('command', () => {
}
},
{
id: 'Comfy.ResetView',
id: 'Comfy.Canvas.ResetView',
icon: 'pi pi-expand',
label: 'Reset View',
function: () => {
@@ -180,6 +180,32 @@ export const useCommandStore = defineStore('command', () => {
icon: 'pi pi-folder-open',
label: 'Browse Templates',
function: showTemplateWorkflowsDialog
},
{
id: 'Comfy.Canvas.ZoomIn',
icon: 'pi pi-plus',
label: 'Zoom In',
function: () => {
app.canvas.ds.changeScale(app.canvas.ds.scale + 0.1)
app.canvas.setDirty(true, true)
}
},
{
id: 'Comfy.Canvas.ZoomOut',
icon: 'pi pi-minus',
label: 'Zoom Out',
function: () => {
app.canvas.ds.changeScale(app.canvas.ds.scale - 0.1)
app.canvas.setDirty(true, true)
}
},
{
id: 'Comfy.Canvas.ToggleLock',
icon: 'pi pi-lock',
label: 'Toggle Lock',
function: () => {
app.canvas['read_only'] = !app.canvas['read_only']
}
}
]

View File

@@ -12,7 +12,6 @@ export const CORE_SETTINGS: SettingParams[] = [
type: 'boolean',
defaultValue: true
},
{
id: 'Comfy.NodeSearchBoxImpl',
category: ['Comfy', 'Node Search Box', 'Implementation'],
@@ -381,5 +380,11 @@ export const CORE_SETTINGS: SettingParams[] = [
type: 'hidden',
options: ['Sidebar', 'Topbar'],
defaultValue: 'Sidebar'
},
{
id: 'Comfy.Graph.CanvasMenu',
name: 'Show graph canvas menu',
type: 'boolean',
defaultValue: true
}
]

View File

@@ -1,6 +1,6 @@
import { LGraphNode, LGraphGroup, LGraphCanvas } from '@comfyorg/litegraph'
import { defineStore } from 'pinia'
import { shallowRef } from 'vue'
import { ref, shallowRef } from 'vue'
export const useTitleEditorStore = defineStore('titleEditor', () => {
const titleEditorTarget = shallowRef<LGraphNode | LGraphGroup | null>(null)
@@ -12,8 +12,19 @@ export const useTitleEditorStore = defineStore('titleEditor', () => {
export const useCanvasStore = defineStore('canvas', () => {
const canvas = shallowRef<LGraphCanvas | null>(null)
const readOnly = ref(false)
document.addEventListener(
'litegraph:canvas',
(e: CustomEvent<{ subType: string; readOnly: boolean }>) => {
if (e.detail?.subType === 'read-only') {
readOnly.value = e.detail.readOnly
}
}
)
return {
canvas
canvas,
readOnly
}
})