quickshell: fix dropdown seed gate, drop the tray menu wait

Revert the wait-for-items delay: never block opening on the menu.

The actual bug was in the rotation. toggleDropdown gated seeding on
chrome.height < 0.5, correct when height was the growth axis, but the
growth axis is width now and chrome.height is tH — never 0 (200 by
default, 32 after shrinkToButton). So seedFromButton has not run since
the bar went vertical: the chrome never seeded at the clicked widget
and tH animated from whatever the previous dropdown left behind.

Gate now tests chrome.width. seedFromButton also holds snap for the
length of the grow instead of releasing it immediately, so the panel
expands along one axis at its content's real height, and content that
lands mid-grow is followed instantly rather than starting a second
animation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-07-28 14:37:28 +01:00
parent 60e39613d6
commit 3136780cd4

View file

@ -1295,7 +1295,10 @@ in
// Opening from fully closed: seed the chrome as a
// small stub on the widget so the panel grows out of
// it (reviving mid-close morphs back instead).
if (!activeDropdown && chrome.height < 0.5) {
// width, not height: the growth axis is horizontal now.
// chrome.height is tH, which is never 0, so testing it
// meant this never fired and the panel never seeded.
if (!activeDropdown && chrome.width < 0.5) {
chrome.seedFromButton(dd);
}
// Retarget the chrome before closing the previous
@ -2049,7 +2052,19 @@ in
snap = true;
tY = stubY(dd);
tH = stubH;
snap = false;
// Stay snapped for the length of the grow, rather than
// unsnapping immediately. The panel then expands along
// ONE axis width at whatever height its content is
// right now, and content that lands mid-grow (async
// DBus tray menus) is followed instantly instead of
// kicking off a second, visibly separate animation.
_unsnap.restart();
}
Timer {
id: _unsnap
interval: Theme.animMorph
onTriggered: chrome.snap = false
}
function shrinkToButton(dd) {
tY = stubY(dd);
@ -2103,62 +2118,22 @@ in
fullHeight: menuItems.height + 2 * Theme.panelGap
onVisibleChanged: {
if (!visible) {
menuOpener.menu = null;
pendingItem = null;
_menuWait.stop();
}
if (!visible) menuOpener.menu = null;
}
QsMenuOpener {
id: menuOpener
}
// The DBus menu populates asynchronously and Steam's is
// slow. Opening on click sized the panel from an EMPTY
// body: menuItems.width is fixed, so the width was already
// final while fullHeight was just padding the panel shot
// out sideways, then lurched taller when the items landed.
// Two motions instead of one. Load first, open once there
// is something to show.
property var pendingItem: null
readonly property bool menuReady:
menuOpener.children && menuOpener.children.values.length > 0
// Opens immediately never wait on the menu. Late content
// is handled by the chrome staying snapped for the length
// of the grow (see seedFromButton), so the height follows
// instantly instead of starting a second animation.
function openFor(item, y) {
dropdownY = y;
trayItem = item;
menuOpener.menu = item.menu;
if (menuReady) {
pendingItem = null;
bar.toggleDropdown(contextMenu, null);
} else {
pendingItem = item;
_menuWait.restart();
}
}
function openPending() {
if (!pendingItem) return;
pendingItem = null;
_menuWait.stop();
if (bar.activeDropdown !== contextMenu)
bar.toggleDropdown(contextMenu, null);
}
// Never strand a click if the menu never populates.
Timer {
id: _menuWait
interval: 500
onTriggered: contextMenu.openPending()
}
Connections {
target: menuOpener.children
function onValuesChanged() {
if (contextMenu.pendingItem && contextMenu.menuReady)
contextMenu.openPending();
}
}
Card {