Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docking action bar on top menu bar #1119

Merged
merged 7 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions browser_tests/appMenu.spec.ts → browser_tests/actionbar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,15 @@ test.describe('Actionbar', () => {
).toBe(END)
expect(promptNumber, 'queued prompt count should be 2').toBe(2)
})

test('Can dock actionbar into top menu', async ({ comfyPage }) => {
await comfyPage.page.dragAndDrop(
'.actionbar .drag-handle',
'.comfyui-menu',
{
targetPosition: { x: 0, y: 0 }
}
)
expect(await comfyPage.actionbar.isDocked()).toBe(true)
})
})
5 changes: 5 additions & 0 deletions browser_tests/helpers/actionbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export class ComfyActionbar {
this.root = page.locator('.actionbar')
this.queueButton = new ComfyQueueButton(this)
}

async isDocked() {
const className = await this.root.getAttribute('class')
return className?.includes('is-docked') ?? false
}
}

class ComfyQueueButton {
Expand Down
73 changes: 63 additions & 10 deletions src/components/actionbar/ComfyActionbar.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<template>
<Panel
v-show="visible"
class="actionbar w-fit"
:style="style"
:class="{ 'is-dragging': isDragging }"
:class="{ 'is-dragging': isDragging, 'is-docked': isDocked }"
>
<div class="actionbar-content flex items-center" ref="panelRef">
<span class="drag-handle cursor-move mr-2 p-0!" ref="dragHandleRef">
Expand All @@ -24,42 +23,56 @@
</template>

<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { computed, inject, nextTick, onMounted, Ref, ref, watch } from 'vue'
import Panel from 'primevue/panel'
import Divider from 'primevue/divider'
import Button from 'primevue/button'
import ButtonGroup from 'primevue/buttongroup'
import ComfyQueueButton from './ComfyQueueButton.vue'
import { useSettingStore } from '@/stores/settingStore'
import { useCommandStore } from '@/stores/commandStore'
import { useDraggable, useEventListener, useLocalStorage } from '@vueuse/core'
import { debounce, clamp } from 'lodash'
import {
useDraggable,
useElementBounding,
useEventBus,
useEventListener,
useLocalStorage,
watchDebounced
} from '@vueuse/core'
import { clamp } from 'lodash'

const settingsStore = useSettingStore()
const commandStore = useCommandStore()

const visible = computed(
() => settingsStore.get('Comfy.UseNewMenu') === 'Floating'
() => settingsStore.get('Comfy.UseNewMenu') !== 'Disabled'
)

const panelRef = ref<HTMLElement | null>(null)
const dragHandleRef = ref<HTMLElement | null>(null)
const isDocked = useLocalStorage('Comfy.MenuPosition.Docked', false)
const storedPosition = useLocalStorage('Comfy.MenuPosition.Floating', {
x: 0,
y: 0
})
const { x, y, style, isDragging } = useDraggable(panelRef, {
const {
x,
y,
style: style,
isDragging
} = useDraggable(panelRef, {
initialValue: { x: 0, y: 0 },
handle: dragHandleRef,
containerElement: document.body
})

// Update storedPosition when x or y changes
watch(
watchDebounced(
[x, y],
debounce(([newX, newY]) => {
([newX, newY]) => {
storedPosition.value = { x: newX, y: newY }
}, 300)
},
{ debounce: 300 }
)

// Set initial position to bottom center
Expand Down Expand Up @@ -109,6 +122,41 @@ const adjustMenuPosition = () => {
}

useEventListener(window, 'resize', adjustMenuPosition)

const topMenuRef = inject<Ref<HTMLDivElement | null>>('topMenuRef')
const topMenuBounds = useElementBounding(topMenuRef)
const overlapThreshold = 20 // pixels
const isOverlappingWithTopMenu = computed(() => {
if (!panelRef.value) {
return false
}
const { height } = panelRef.value.getBoundingClientRect()
const actionbarBottom = y.value + height
const topMenuBottom = topMenuBounds.bottom.value

const overlapPixels =
Math.min(actionbarBottom, topMenuBottom) -
Math.max(y.value, topMenuBounds.top.value)
return overlapPixels > overlapThreshold
})

watch(isDragging, (newIsDragging) => {
if (!newIsDragging) {
// Stop dragging
isDocked.value = isOverlappingWithTopMenu.value
} else {
// Start dragging
isDocked.value = false
}
})

const eventBus = useEventBus<string>('topMenu')
watch([isDragging, isOverlappingWithTopMenu], ([dragging, overlapping]) => {
eventBus.emit('updateHighlight', {
isDragging: dragging,
isOverlapping: overlapping
})
})
</script>

<style scoped>
Expand All @@ -118,6 +166,11 @@ useEventListener(window, 'resize', adjustMenuPosition)
z-index: 1000;
}

.actionbar.is-docked {
position: static;
@apply bg-transparent border-none p-0;
}

.actionbar.is-dragging {
user-select: none;
}
Expand Down
39 changes: 33 additions & 6 deletions src/components/topbar/TopMenubar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<template>
<teleport to=".comfyui-body-top">
<div class="comfyui-menu flex items-center" v-show="betaMenuEnabled">
<div
ref="topMenuRef"
class="comfyui-menu flex items-center"
v-show="betaMenuEnabled"
:class="{ dropzone: isDropZone, 'dropzone-active': isDroppable }"
>
<h1 class="comfyui-logo mx-2">ComfyUI</h1>
<Menubar
:model="items"
Expand All @@ -10,11 +15,11 @@
}"
/>
<Divider layout="vertical" class="mx-2" />
<WorkflowTabs
v-if="workflowTabsPosition === 'Topbar'"
class="flex-grow"
/>
<div class="flex-grow">
<WorkflowTabs v-if="workflowTabsPosition === 'Topbar'" />
</div>
<div class="comfyui-menu-right" ref="menuRight"></div>
<Actionbar />
</div>
</teleport>
</template>
Expand All @@ -23,10 +28,12 @@
import Menubar from 'primevue/menubar'
import Divider from 'primevue/divider'
import WorkflowTabs from '@/components/topbar/WorkflowTabs.vue'
import Actionbar from '@/components/actionbar/ComfyActionbar.vue'
import { useMenuItemStore } from '@/stores/menuItemStore'
import { computed, onMounted, ref } from 'vue'
import { computed, onMounted, provide, ref } from 'vue'
import { useSettingStore } from '@/stores/settingStore'
import { app } from '@/scripts/app'
import { useEventBus } from '@vueuse/core'

const settingStore = useSettingStore()
const workflowTabsPosition = computed(() =>
Expand All @@ -45,6 +52,18 @@ onMounted(() => {
menuRight.value.appendChild(app.menu.element)
}
})

const topMenuRef = ref<HTMLDivElement | null>(null)
provide('topMenuRef', topMenuRef)
const eventBus = useEventBus<string>('topMenu')
const isDropZone = ref(false)
const isDroppable = ref(false)
eventBus.on((event: string, payload: any) => {
if (event === 'updateHighlight') {
isDropZone.value = payload.isDragging
isDroppable.value = payload.isOverlapping && payload.isDragging
}
})
</script>

<style scoped>
Expand All @@ -61,6 +80,14 @@ onMounted(() => {
max-height: 90vh;
}

.comfyui-menu.dropzone {
background: var(--p-highlight-background);
}

.comfyui-menu.dropzone-active {
background: var(--p-highlight-background-focus);
}

.comfyui-logo {
font-size: 1.2em;
user-select: none;
Expand Down
2 changes: 0 additions & 2 deletions src/views/GraphView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<GlobalToast />
<UnloadWindowConfirmDialog />
<BrowserTabTitle />
<Actionbar />
</template>

<script setup lang="ts">
Expand Down Expand Up @@ -41,7 +40,6 @@ import ModelLibrarySidebarTab from '@/components/sidebar/tabs/ModelLibrarySideba
import GlobalToast from '@/components/toast/GlobalToast.vue'
import UnloadWindowConfirmDialog from '@/components/dialog/UnloadWindowConfirmDialog.vue'
import BrowserTabTitle from '@/components/BrowserTabTitle.vue'
import Actionbar from '@/components/actionbar/ComfyActionbar.vue'
import WorkflowsSidebarTab from '@/components/sidebar/tabs/WorkflowsSidebarTab.vue'
import TopMenubar from '@/components/topbar/TopMenubar.vue'
import { setupAutoQueueHandler } from '@/services/autoQueueService'
Expand Down
Loading