Add a button to selection toolbox to open mask editor (#3603)

Co-authored-by: bymyself <cbyrne@comfy.org>
This commit is contained in:
Yiximail
2025-05-20 16:23:39 +08:00
committed by GitHub
parent fec4c4e928
commit f2c4e567e4
10 changed files with 124 additions and 45 deletions

View File

@@ -10,6 +10,7 @@
<ColorPickerButton /> <ColorPickerButton />
<BypassButton /> <BypassButton />
<PinButton /> <PinButton />
<MaskEditorButton />
<DeleteButton /> <DeleteButton />
<RefreshButton /> <RefreshButton />
<ExtensionCommandButton <ExtensionCommandButton
@@ -24,18 +25,18 @@
import Panel from 'primevue/panel' import Panel from 'primevue/panel'
import { computed } from 'vue' import { computed } from 'vue'
import BypassButton from '@/components/graph/selectionToolbox/BypassButton.vue'
import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue' import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue'
import DeleteButton from '@/components/graph/selectionToolbox/DeleteButton.vue'
import ExecuteButton from '@/components/graph/selectionToolbox/ExecuteButton.vue' import ExecuteButton from '@/components/graph/selectionToolbox/ExecuteButton.vue'
import ExtensionCommandButton from '@/components/graph/selectionToolbox/ExtensionCommandButton.vue'
import MaskEditorButton from '@/components/graph/selectionToolbox/MaskEditorButton.vue'
import PinButton from '@/components/graph/selectionToolbox/PinButton.vue'
import RefreshButton from '@/components/graph/selectionToolbox/RefreshButton.vue'
import { useExtensionService } from '@/services/extensionService' import { useExtensionService } from '@/services/extensionService'
import { type ComfyCommandImpl, useCommandStore } from '@/stores/commandStore' import { type ComfyCommandImpl, useCommandStore } from '@/stores/commandStore'
import { useCanvasStore } from '@/stores/graphStore' import { useCanvasStore } from '@/stores/graphStore'
import BypassButton from './selectionToolbox/BypassButton.vue'
import DeleteButton from './selectionToolbox/DeleteButton.vue'
import ExtensionCommandButton from './selectionToolbox/ExtensionCommandButton.vue'
import PinButton from './selectionToolbox/PinButton.vue'
import RefreshButton from './selectionToolbox/RefreshButton.vue'
const commandStore = useCommandStore() const commandStore = useCommandStore()
const canvasStore = useCanvasStore() const canvasStore = useCanvasStore()
const extensionService = useExtensionService() const extensionService = useExtensionService()

View File

@@ -0,0 +1,35 @@
<template>
<Button
v-show="isSingleImageNode"
v-tooltip.top="{
value: t('commands.Comfy_MaskEditor_OpenMaskEditor.label'),
showDelay: 1000
}"
severity="secondary"
text
icon="pi pi-pencil"
@click="openMaskEditor"
/>
</template>
<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'
import { t } from '@/i18n'
import { useCommandStore } from '@/stores/commandStore'
import { useCanvasStore } from '@/stores/graphStore'
import { isImageNode, isLGraphNode } from '@/utils/litegraphUtil'
const commandStore = useCommandStore()
const canvasStore = useCanvasStore()
const isSingleImageNode = computed(() => {
const nodes = canvasStore.selectedItems.filter(isLGraphNode)
return nodes.length === 1 && nodes.some(isImageNode)
})
const openMaskEditor = () => {
void commandStore.execute('Comfy.MaskEditor.OpenMaskEditor')
}
</script>

View File

@@ -4912,6 +4912,45 @@ class KeyboardManager {
} }
} }
// Function to open the mask editor
function openMaskEditor(): void {
const useNewEditor = app.extensionManager.setting.get(
'Comfy.MaskEditor.UseNewEditor'
)
if (useNewEditor) {
const dlg = MaskEditorDialog.getInstance() as any
if (dlg?.isOpened && !dlg.isOpened()) {
dlg.show()
}
} else {
const dlg = MaskEditorDialogOld.getInstance() as any
if (dlg?.isOpened && !dlg.isOpened()) {
dlg.show()
}
}
}
// Check if the dialog is already opened
function isOpened(): boolean {
const useNewEditor = app.extensionManager.setting.get(
'Comfy.MaskEditor.UseNewEditor'
)
if (useNewEditor) {
return MaskEditorDialog.instance?.isOpened?.() ?? false
} else {
return (MaskEditorDialogOld.instance as any)?.isOpened?.() ?? false
}
}
// Ensure boolean return type for context predicate
const context_predicate = (): boolean => {
return !!(
ComfyApp.clipspace &&
ComfyApp.clipspace.imgs &&
ComfyApp.clipspace.imgs.length > 0
)
}
app.registerExtension({ app.registerExtension({
name: 'Comfy.MaskEditor', name: 'Comfy.MaskEditor',
settings: [ settings: [
@@ -4951,50 +4990,33 @@ app.registerExtension({
experimental: true experimental: true
} }
], ],
init(app) { commands: [
// Create function before assignment {
function openMaskEditor(): void { id: 'Comfy.MaskEditor.OpenMaskEditor',
const useNewEditor = app.extensionManager.setting.get( icon: 'pi pi-pencil',
'Comfy.MaskEditor.UseNewEditor' label: 'Open Mask Editor for Selected Node',
) function: () => {
if (useNewEditor) { const selectedNodes = app.canvas.selected_nodes
const dlg = MaskEditorDialog.getInstance() as any if (!selectedNodes || Object.keys(selectedNodes).length !== 1) return
if (dlg?.isOpened && !dlg.isOpened()) {
dlg.show() const selectedNode = selectedNodes[Object.keys(selectedNodes)[0]]
} if (
} else { !selectedNode.imgs?.length &&
const dlg = MaskEditorDialogOld.getInstance() as any selectedNode.previewMediaType !== 'image'
if (dlg?.isOpened && !dlg.isOpened()) { )
dlg.show() return
}
ComfyApp.copyToClipspace(selectedNode)
// @ts-expect-error clipspace_return_node is an extension property added at runtime
ComfyApp.clipspace_return_node = selectedNode
openMaskEditor()
} }
} }
],
// Check if the dialog is already opened init() {
function isOpened(): boolean {
const useNewEditor = app.extensionManager.setting.get(
'Comfy.MaskEditor.UseNewEditor'
)
if (useNewEditor) {
return MaskEditorDialog.instance?.isOpened?.() ?? false
} else {
return (MaskEditorDialogOld.instance as any)?.isOpened?.() ?? false
}
}
// Assign the created function
ComfyApp.open_maskeditor = openMaskEditor ComfyApp.open_maskeditor = openMaskEditor
ComfyApp.maskeditor_is_opended = isOpened ComfyApp.maskeditor_is_opended = isOpened
// Ensure boolean return type
const context_predicate = (): boolean => {
return !!(
ComfyApp.clipspace &&
ComfyApp.clipspace.imgs &&
ComfyApp.clipspace.imgs.length > 0
)
}
ClipspaceDialog.registerButton( ClipspaceDialog.registerButton(
'MaskEditor', 'MaskEditor',
context_predicate, context_predicate,

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelectedNodes_Pin": { "Comfy_Canvas_ToggleSelectedNodes_Pin": {
"label": "Pin/Unpin Selected Nodes" "label": "Pin/Unpin Selected Nodes"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "Open Mask Editor for Selected Node"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "Zoom In" "label": "Zoom In"
}, },

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelected_Pin": { "Comfy_Canvas_ToggleSelected_Pin": {
"label": "Anclar/Desanclar elementos seleccionados" "label": "Anclar/Desanclar elementos seleccionados"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "Abrir editor de máscara para el nodo seleccionado"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "Acercar" "label": "Acercar"
}, },

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelected_Pin": { "Comfy_Canvas_ToggleSelected_Pin": {
"label": "Épingler/Désépingler les éléments sélectionnés" "label": "Épingler/Désépingler les éléments sélectionnés"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "Ouvrir l'éditeur de masque pour le nœud sélectionné"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "Zoom avant" "label": "Zoom avant"
}, },

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelected_Pin": { "Comfy_Canvas_ToggleSelected_Pin": {
"label": "選択したアイテムのピン留め/ピン留め解除" "label": "選択したアイテムのピン留め/ピン留め解除"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "選択したノードのマスクエディタを開く"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "ズームイン" "label": "ズームイン"
}, },

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelected_Pin": { "Comfy_Canvas_ToggleSelected_Pin": {
"label": "선택한 항목 고정/고정 해제" "label": "선택한 항목 고정/고정 해제"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "선택한 노드 마스크 편집기 열기"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "확대" "label": "확대"
}, },

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelected_Pin": { "Comfy_Canvas_ToggleSelected_Pin": {
"label": "Закрепить/Открепить выбранных нод" "label": "Закрепить/Открепить выбранных нод"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "Открыть редактор масок для выбранной ноды"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "Увеличить" "label": "Увеличить"
}, },

View File

@@ -71,6 +71,9 @@
"Comfy_Canvas_ToggleSelected_Pin": { "Comfy_Canvas_ToggleSelected_Pin": {
"label": "固定/取消固定选中项" "label": "固定/取消固定选中项"
}, },
"Comfy_MaskEditor_OpenMaskEditor": {
"label": "打开选中节点的遮罩编辑器"
},
"Comfy_Canvas_ZoomIn": { "Comfy_Canvas_ZoomIn": {
"label": "放大" "label": "放大"
}, },