quickshell: open tray menus at full size, not empty-then-grown
The DBus menu populates asynchronously and Steam's is slow, so the panel was sized from an empty body: menuItems.width is fixed, making the width final immediately while fullHeight was only padding. The panel shot out sideways, then lurched taller when the items arrived — two motions instead of one. contextMenu now loads the menu first and opens once it has items, falling back to a 500ms timeout so a menu that never populates cannot strand the click. Hovering a tray icon also prefetches its menu, which covers the wait in practice since you cannot click without hovering. Clicking the open menu's own icon still closes it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0a6a23aa8a
commit
60e39613d6
1 changed files with 77 additions and 26 deletions
|
|
@ -1849,23 +1849,24 @@ in
|
|||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onEntered: {
|
||||
if (bar.activeDropdown) {
|
||||
if (modelData.hasMenu && !(bar.activeDropdown === contextMenu && contextMenu.trayItem === modelData)) {
|
||||
if (bar.activeDropdown === contextMenu) {
|
||||
// Same dropdown, just switch content
|
||||
let pos = parent.mapToItem(bar.contentItem, 0, parent.height / 2);
|
||||
contextMenu.dropdownY = pos.y;
|
||||
contextMenu.trayItem = modelData;
|
||||
menuOpener.menu = modelData.menu;
|
||||
} else {
|
||||
bar.toggleDropdown(contextMenu, function() {
|
||||
let pos = parent.mapToItem(bar.contentItem, 0, parent.height / 2);
|
||||
contextMenu.dropdownY = pos.y;
|
||||
contextMenu.trayItem = modelData;
|
||||
menuOpener.menu = modelData.menu;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!modelData.hasMenu) return;
|
||||
// Prefetch: you cannot click without
|
||||
// hovering first, so start the DBus
|
||||
// round-trip now and the menu is
|
||||
// usually loaded before the click.
|
||||
if (!bar.activeDropdown && menuOpener.menu !== modelData.menu) {
|
||||
menuOpener.menu = modelData.menu;
|
||||
}
|
||||
if (!bar.activeDropdown) return;
|
||||
if (bar.activeDropdown === contextMenu && contextMenu.trayItem === modelData) return;
|
||||
let pos = parent.mapToItem(bar.contentItem, 0, parent.height / 2);
|
||||
if (bar.activeDropdown === contextMenu) {
|
||||
// Same dropdown, just switch content
|
||||
contextMenu.dropdownY = pos.y;
|
||||
contextMenu.trayItem = modelData;
|
||||
menuOpener.menu = modelData.menu;
|
||||
} else {
|
||||
contextMenu.openFor(modelData, pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1873,15 +1874,14 @@ in
|
|||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: (event) => {
|
||||
if (modelData.hasMenu) {
|
||||
bar.toggleDropdown(contextMenu, function() {
|
||||
let pos = parent.mapToItem(bar.contentItem, 0, parent.height / 2);
|
||||
contextMenu.dropdownY = pos.y;
|
||||
contextMenu.trayItem = modelData;
|
||||
menuOpener.menu = modelData.menu;
|
||||
});
|
||||
} else {
|
||||
if (!modelData.hasMenu) {
|
||||
modelData.activate();
|
||||
} else if (bar.activeDropdown === contextMenu && contextMenu.trayItem === modelData) {
|
||||
// Clicking the open menu's own icon still closes it
|
||||
contextMenu.animateClose();
|
||||
} else {
|
||||
let pos = parent.mapToItem(bar.contentItem, 0, parent.height / 2);
|
||||
contextMenu.openFor(modelData, pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2103,13 +2103,64 @@ in
|
|||
fullHeight: menuItems.height + 2 * Theme.panelGap
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) menuOpener.menu = null;
|
||||
if (!visible) {
|
||||
menuOpener.menu = null;
|
||||
pendingItem = null;
|
||||
_menuWait.stop();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
id: menuItems
|
||||
anchors.centerIn: parent
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue