[Bug] Fix DOM widgets position when canvas bounding box change (#3565)

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Chenlei Hu
2025-04-22 15:11:54 -04:00
committed by GitHub
parent b7535755f0
commit 585d52e24e
7 changed files with 88 additions and 84 deletions

View File

@@ -24,4 +24,11 @@ test.describe('DOM Widget', () => {
await expect(firstMultiline).not.toBeVisible()
await expect(lastMultiline).not.toBeVisible()
})
test('Position update when entering focus mode', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.executeCommand('Workspace.ToggleFocusMode')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('focus-mode-on.png')
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -73,6 +73,7 @@ watch(
updateWidgets()
}
)
}
},
{ immediate: true }
)
</script>

View File

@@ -19,7 +19,6 @@
<GraphCanvasMenu v-if="canvasMenuEnabled" class="pointer-events-auto" />
</template>
</LiteGraphCanvasSplitterOverlay>
<TitleEditor />
<GraphCanvasMenu v-if="!betaMenuEnabled && canvasMenuEnabled" />
<canvas
id="graph-canvas"
@@ -27,13 +26,20 @@
tabindex="1"
class="w-full h-full touch-none"
/>
<NodeSearchboxPopover />
<SelectionOverlay v-if="selectionToolboxEnabled">
<SelectionToolbox />
</SelectionOverlay>
<NodeTooltip v-if="tooltipEnabled" />
<NodeBadge />
<DomWidgets />
<NodeTooltip v-if="tooltipEnabled" />
<NodeSearchboxPopover />
<!-- Initialize components after comfyApp is ready. useAbsolutePosition requires
canvasStore.canvas to be initialized. -->
<template v-if="comfyAppReady">
<TitleEditor />
<SelectionOverlay v-if="selectionToolboxEnabled">
<SelectionToolbox />
</SelectionOverlay>
<DomWidgets />
</template>
</template>
<script setup lang="ts">
@@ -78,7 +84,9 @@ import { useSettingStore } from '@/stores/settingStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
const emit = defineEmits(['ready'])
const emit = defineEmits<{
ready: []
}>()
const canvasRef = ref<HTMLCanvasElement | null>(null)
const settingStore = useSettingStore()
const nodeDefStore = useNodeDefStore()

View File

@@ -16,7 +16,7 @@
import { LGraphGroup, LGraphNode, LiteGraph } from '@comfyorg/litegraph'
import type { LiteGraphCanvasEvent } from '@comfyorg/litegraph'
import { useEventListener } from '@vueuse/core'
import { ref, watch } from 'vue'
import { type CSSProperties, computed, ref, watch } from 'vue'
import EditableText from '@/components/common/EditableText.vue'
import { useAbsolutePosition } from '@/composables/element/useAbsolutePosition'
@@ -28,7 +28,12 @@ const settingStore = useSettingStore()
const showInput = ref(false)
const editedTitle = ref('')
const { style: inputStyle, updatePosition } = useAbsolutePosition()
const { style: inputPositionStyle, updatePosition } = useAbsolutePosition()
const inputFontStyle = ref<CSSProperties>({})
const inputStyle = computed<CSSProperties>(() => ({
...inputPositionStyle.value,
...inputFontStyle.value
}))
const titleEditorStore = useTitleEditorStore()
const canvasStore = useCanvasStore()
@@ -59,23 +64,19 @@ watch(
if (target instanceof LGraphGroup) {
const group = target
updatePosition(
{
pos: group.pos,
size: [group.size[0], group.titleHeight]
},
{ fontSize: `${group.font_size * scale}px` }
)
updatePosition({
pos: group.pos,
size: [group.size[0], group.titleHeight]
})
inputFontStyle.value = { fontSize: `${group.font_size * scale}px` }
} else if (target instanceof LGraphNode) {
const node = target
const [x, y] = node.getBounding()
updatePosition(
{
pos: [x, y],
size: [node.width, LiteGraph.NODE_TITLE_HEIGHT]
},
{ fontSize: `${12 * scale}px` }
)
updatePosition({
pos: [x, y],
size: [node.width, LiteGraph.NODE_TITLE_HEIGHT]
})
inputFontStyle.value = { fontSize: `${12 * scale}px` }
}
}
)

View File

@@ -37,13 +37,14 @@ const { widget, widgetState } = defineProps<{
}>()
const emit = defineEmits<{
(e: 'update:widgetValue', value: string | object): void
'update:widgetValue': [value: string | object]
}>()
const widgetElement = ref<HTMLElement | undefined>()
const { style: positionStyle, updatePositionWithTransform } =
useAbsolutePosition()
const { style: positionStyle, updatePosition } = useAbsolutePosition({
useTransform: true
})
const { style: clippingStyle, updateClipPath } = useDomClipping()
const style = computed<CSSProperties>(() => ({
...positionStyle.value,
@@ -94,7 +95,7 @@ const updateDomClipping = () => {
watch(
() => widgetState,
(newState) => {
updatePositionWithTransform(newState)
updatePosition(newState)
if (enableDomClipping.value) {
updateDomClipping()
}

View File

@@ -1,7 +1,7 @@
import type { Size, Vector2 } from '@comfyorg/litegraph'
import { CSSProperties, ref } from 'vue'
import { CSSProperties, computed, ref } from 'vue'
import { app } from '@/scripts/app'
import { useCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
import { useCanvasStore } from '@/stores/graphStore'
export interface PositionConfig {
@@ -13,70 +13,56 @@ export interface PositionConfig {
scale?: number
}
export function useAbsolutePosition() {
export function useAbsolutePosition(options: { useTransform?: boolean } = {}) {
const { useTransform = false } = options
const canvasStore = useCanvasStore()
const style = ref<CSSProperties>({
position: 'fixed',
left: '0px',
top: '0px',
width: '0px',
height: '0px'
const lgCanvas = canvasStore.getCanvas()
const { canvasPosToClientPos } = useCanvasPositionConversion(
lgCanvas.canvas,
lgCanvas
)
const position = ref<PositionConfig>({
pos: [0, 0],
size: [0, 0]
})
const style = computed<CSSProperties>(() => {
const { pos, size, scale = lgCanvas.ds.scale } = position.value
const [left, top] = canvasPosToClientPos(pos)
const [width, height] = size
return useTransform
? {
position: 'fixed',
transformOrigin: '0 0',
transform: `scale(${scale})`,
left: `${left}px`,
top: `${top}px`,
width: `${width}px`,
height: `${height}px`
}
: {
position: 'fixed',
left: `${left}px`,
top: `${top}px`,
width: `${width * scale}px`,
height: `${height * scale}px`
}
})
/**
* Update the position of the element on the litegraph canvas.
*
* @param config
* @param extraStyle
*/
const updatePosition = (
config: PositionConfig,
extraStyle?: CSSProperties
) => {
const { pos, size, scale = canvasStore.canvas?.ds?.scale ?? 1 } = config
const [left, top] = app.canvasPosToClientPos(pos)
const [width, height] = size
style.value = {
...style.value,
left: `${left}px`,
top: `${top}px`,
width: `${width * scale}px`,
height: `${height * scale}px`,
...extraStyle
}
}
/**
* Update the position and size of the element on the litegraph canvas,
* with CSS transform scaling applied.
*
* @param config
* @param extraStyle
*/
const updatePositionWithTransform = (
config: PositionConfig,
extraStyle?: CSSProperties
) => {
const { pos, size, scale = canvasStore.canvas?.ds?.scale ?? 1 } = config
const [left, top] = app.canvasPosToClientPos(pos)
const [width, height] = size
style.value = {
...style.value,
transformOrigin: '0 0',
transform: `scale(${scale})`,
left: `${left}px`,
top: `${top}px`,
width: `${width}px`,
height: `${height}px`,
...extraStyle
}
const updatePosition = (config: PositionConfig) => {
position.value = config
}
return {
style,
updatePosition,
updatePositionWithTransform
updatePosition
}
}