quickshell: seamless switching between dropdowns

Remove grabFocus from BarDropdown. Centralized toggleDropdown()
closes the active dropdown before opening a new one, so clicking
between network/battery/calendar/tray is instant — no double-click.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
rope 2026-05-26 17:13:58 +01:00
parent 2b9edcb589
commit 98d1e6249c

View file

@ -458,6 +458,25 @@ in
implicitHeight: 30 implicitHeight: 30
color: "#D1${c.base00}" color: "#D1${c.base00}"
property var activeDropdown: null
function closeAllDropdowns() {
if (activeDropdown && activeDropdown.visible) {
activeDropdown.visible = false;
}
activeDropdown = null;
}
function toggleDropdown(dd, setupFn) {
if (dd.visible) {
dd.visible = false;
activeDropdown = null;
} else {
closeAllDropdowns();
if (setupFn) setupFn();
dd.visible = true;
activeDropdown = dd;
}
}
// Left workspaces // Left workspaces
Row { Row {
anchors.left: parent.left anchors.left: parent.left
@ -519,21 +538,9 @@ in
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
if (calPopup.justDismissed) return; bar.toggleDropdown(calPopup);
if (calPopup.visible) {
calPopup.open = false;
calCloseTimer.start();
} else {
calPopup.visible = true;
}
} }
} }
Timer {
id: calCloseTimer
interval: 230
onTriggered: calPopup.visible = false
}
} }
// Right network, battery, tray // Right network, battery, tray
@ -653,16 +660,12 @@ in
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
if (netDropdown.justDismissed) return; bar.toggleDropdown(netDropdown, function() {
if (netDropdown.visible) {
netDropdown.visible = false;
} else {
netWidget.wifiNetworks = []; netWidget.wifiNetworks = [];
wifiScanProc.running = true; wifiScanProc.running = true;
let pos = netWidget.mapToItem(bar.contentItem, netWidget.width / 2, 0); let pos = netWidget.mapToItem(bar.contentItem, netWidget.width / 2, 0);
netDropdown.dropdownX = pos.x; netDropdown.dropdownX = pos.x;
netDropdown.visible = true; });
}
} }
} }
} }
@ -765,16 +768,12 @@ in
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
if (batteryDropdown.justDismissed) return; bar.toggleDropdown(batteryDropdown, function() {
if (batteryDropdown.visible) {
batteryDropdown.visible = false;
} else {
batteryProc.running = true; batteryProc.running = true;
profileProc.running = true; profileProc.running = true;
let pos = batteryWidget.mapToItem(bar.contentItem, batteryWidget.width / 2, 0); let pos = batteryWidget.mapToItem(bar.contentItem, batteryWidget.width / 2, 0);
batteryDropdown.dropdownX = pos.x; batteryDropdown.dropdownX = pos.x;
batteryDropdown.visible = true; });
}
} }
} }
} }
@ -815,13 +814,13 @@ in
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (event) => { onClicked: (event) => {
if (contextMenu.justDismissed) return;
if (event.button === Qt.RightButton && modelData.hasMenu) { if (event.button === Qt.RightButton && modelData.hasMenu) {
let pos = parent.mapToItem(bar.contentItem, parent.width / 2, 0); bar.toggleDropdown(contextMenu, function() {
contextMenu.dropdownX = pos.x; let pos = parent.mapToItem(bar.contentItem, parent.width / 2, 0);
contextMenu.trayItem = modelData; contextMenu.dropdownX = pos.x;
menuOpener.menu = modelData.menu; contextMenu.trayItem = modelData;
contextMenu.visible = true; menuOpener.menu = modelData.menu;
});
} else { } else {
modelData.activate(); modelData.activate();
} }
@ -836,7 +835,6 @@ in
component BarDropdown: PopupWindow { component BarDropdown: PopupWindow {
id: dropdown id: dropdown
property bool open: false property bool open: false
property bool justDismissed: false
property real dropdownX: 0 property real dropdownX: 0
property real fullWidth: 200 property real fullWidth: 200
property real fullHeight: 200 property real fullHeight: 200
@ -848,7 +846,6 @@ in
anchor.edges: Edges.Top | Edges.Left anchor.edges: Edges.Top | Edges.Left
anchor.gravity: Edges.Bottom | Edges.Right anchor.gravity: Edges.Bottom | Edges.Right
anchor.adjustment: PopupAdjustment.Slide anchor.adjustment: PopupAdjustment.Slide
grabFocus: true
visible: false visible: false
color: "transparent" color: "transparent"
implicitWidth: fullWidth + 16 implicitWidth: fullWidth + 16
@ -859,17 +856,9 @@ in
open = true; open = true;
} else { } else {
open = false; open = false;
justDismissed = true;
_dismissGuard.start();
} }
} }
Timer {
id: _dismissGuard
interval: 100
onTriggered: dropdown.justDismissed = false
}
Item { Item {
anchors.right: _dropdownRect.left anchors.right: _dropdownRect.left
anchors.top: parent.top anchors.top: parent.top
@ -1010,7 +999,7 @@ in
enabled: !modelData.isSeparator && modelData.enabled enabled: !modelData.isSeparator && modelData.enabled
onClicked: { onClicked: {
modelData.triggered(); modelData.triggered();
contextMenu.visible = false; bar.closeAllDropdowns();
} }
} }
} }
@ -1071,7 +1060,7 @@ in
netWidget.netState = "disconnected"; netWidget.netState = "disconnected";
netWidget.netConn = ""; netWidget.netConn = "";
netWidget.netIcon = "\u{f05aa}"; netWidget.netIcon = "\u{f05aa}";
netDropdown.visible = false; bar.closeAllDropdowns();
netRefreshDelay.start(); netRefreshDelay.start();
} }
} }
@ -1160,7 +1149,7 @@ in
wifiConnectProc.running = true; wifiConnectProc.running = true;
netRefreshDelay.start(); netRefreshDelay.start();
} }
netDropdown.visible = false; bar.closeAllDropdowns();
} }
} }
} }