From f89aeb79b4810672d95a2b0efe6e67e5f32c3f76 Mon Sep 17 00:00:00 2001 From: Kian Dadkhah Date: Sat, 7 Nov 2026 15:07:30 -0500 Subject: [PATCH] [ryoku] shell: keep clicks in an expanded Controls drawer inside Opening the Brightness or Volume drawer grows the Controls panel, and the panel window grows with it, but a press on the grown part closed the panel. The click-away window sits above the panel and passes presses through only where its mask cuts a hole. It never redraws after its first frame, so the mask edit for the taller panel never reached the compositor: a WAYLAND_DEBUG trace shows one set_input_region with the first frame and no later commit, and presses closed the panel from the old bottom edge down. The same applied to the media section appearing and to a panel that shrank. The hole is now the panel's whole column, which does not change while the panel is open. The panel window takes that column and places the card in it; a press in the clear part above or below the card closes the panel from there. The settings popup is bounded by the card instead of the window, so at the Center placement it no longer rises above the card. Note: Fixed: clicking inside the expanded Brightness or Volume drawer in Controls no longer closes the panel --- ryoku/shell/CHANGELOG.md | 4 +++ .../shell/modules/sidebar/ControlsBar.qml | 17 ++++++----- .../shell/modules/sidebar/ControlsBoard.qml | 2 ++ .../shell/modules/sidebar/Sidebar.qml | 29 +++++++++++++++---- .../shell/modules/sidebar/SidebarChrome.qml | 1 + 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/ryoku/shell/CHANGELOG.md b/ryoku/shell/CHANGELOG.md index 5f08410cf9..50f2a5e0e5 100644 --- a/ryoku/shell/CHANGELOG.md +++ b/ryoku/shell/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog: ryoku/shell/ ## Unreleased +- **Clicking an expanded Controls drawer no longer closes the panel.** Opening the + Brightness or Volume drawer grew the panel, but clicks on the newly grown part + still counted as clicks outside it. The whole drawer now takes clicks, and the + settings popup stays inside the panel when it is centred. - **Windows reach the Shima dock again.** Under the Shima bar style the frame and the dock both reserved the dock's band, which left a dock-tall gap between windows and the dock. The frame's reservation is now the only one there. diff --git a/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBar.qml b/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBar.qml index 200d4f22f3..825d9737df 100644 --- a/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBar.qml +++ b/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBar.qml @@ -11,6 +11,8 @@ Item { required property real s required property var screen required property bool active + // the panel's window can reach past its card, so the card bounds the settings popup + property Item card: null signal session(string action) signal openExtensions() signal requestClose() @@ -169,25 +171,26 @@ Item { ControlsSettingsPopup { id: settings + readonly property Item bounds: root.card ?? host parent: root x: { - if (!host) + if (!bounds) return 0; - const origin = root.mapToItem(host, 0, 0); - return Math.max(0, Math.min(host.width - width, origin.x + root.width - width)) - origin.x; + const origin = root.mapToItem(bounds, 0, 0); + return Math.max(0, Math.min(bounds.width - width, origin.x + root.width - width)) - origin.x; } y: { - if (!host) + if (!bounds) return 0; - const origin = root.mapToItem(host, 0, 0); + const origin = root.mapToItem(bounds, 0, 0); const preferred = origin.y - height - Tokens.s2 * root.s; - return Math.max(0, Math.min(host.height - height, preferred)) - origin.y; + return Math.max(0, Math.min(bounds.height - height, preferred)) - origin.y; } s: root.s active: root.active // keep the popup wholly above the bar so it never covers the gear that closes // it, measured on every open because the bar settles after the popup exists - onAboutToShow: maxHeight = host ? Math.max(1, root.mapToItem(host, 0, 0).y - Tokens.s2 * root.s) : hostHeight + onAboutToShow: maxHeight = bounds ? Math.max(1, root.mapToItem(bounds, 0, 0).y - Tokens.s2 * root.s) : hostHeight onOpenExtensions: root.openExtensions() onRequestClose: root.requestClose() } diff --git a/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBoard.qml b/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBoard.qml index e96a56ee6a..818263b098 100644 --- a/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBoard.qml +++ b/ryoku/shell/quickshell/shell/modules/sidebar/ControlsBoard.qml @@ -14,6 +14,7 @@ Item { required property var screen required property bool active property string page: "controls" + property Item card: null signal requestClose() readonly property string currentPage: ["wifi", "bluetooth"].indexOf(page) >= 0 ? page : "controls" @@ -136,6 +137,7 @@ Item { height: implicitHeight s: root.s screen: root.screen + card: root.card active: root.overviewActive onSession: action => root.session(action) onOpenExtensions: root.showPage("plugins") diff --git a/ryoku/shell/quickshell/shell/modules/sidebar/Sidebar.qml b/ryoku/shell/quickshell/shell/modules/sidebar/Sidebar.qml index fa986009f6..bc12d77b2d 100644 --- a/ryoku/shell/quickshell/shell/modules/sidebar/Sidebar.qml +++ b/ryoku/shell/quickshell/shell/modules/sidebar/Sidebar.qml @@ -81,6 +81,9 @@ Scope { } // The dismiss surface never animates. Only the small corner buffer redraws. + // Since it does not redraw, a mask edited after its first frame never reaches + // the compositor, so its hole is the panel's whole column, which stays fixed + // while an inline drawer or section grows the panel inside it. PanelWindow { id: dismiss screen: root.screen @@ -99,9 +102,9 @@ Scope { Region { intersection: Intersection.Subtract x: root.panelX - y: root.panelY + y: root.roomY width: root.wantedWidth - height: root.wantedHeight + height: root.availableHeight } Region { intersection: Intersection.Subtract; width: dismiss.width; height: root.clearances.top } Region { intersection: Intersection.Subtract; y: dismiss.height - root.clearances.bottom; width: dismiss.width; height: root.clearances.bottom } @@ -125,17 +128,33 @@ Scope { exclusionMode: ExclusionMode.Ignore exclusiveZone: 0 anchors { top: true; left: true } - margins.top: root.panelY + margins.top: root.roomY margins.left: root.panelX implicitWidth: root.wantedWidth - implicitHeight: root.wantedHeight + implicitHeight: root.availableHeight WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.namespace: "ryoku-sidebar-left" WlrLayershell.keyboardFocus: root.active ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + // The dismiss hole spans the whole column, so a press in its clear part + // above or below the panel has to dismiss here instead. + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + onPressed: mouse => { + if (mouse.y >= surface.y && mouse.y < surface.y + surface.height) { + mouse.accepted = false; + return; + } + Qt.callLater(() => SidebarState.close(root.screen)); + } + } + FocusScope { id: surface - anchors.fill: parent + y: root.panelY - root.roomY + width: parent.width + height: root.wantedHeight opacity: root.reveal scale: 0.985 + root.reveal * 0.015 transformOrigin: root.panelTransformOrigin diff --git a/ryoku/shell/quickshell/shell/modules/sidebar/SidebarChrome.qml b/ryoku/shell/quickshell/shell/modules/sidebar/SidebarChrome.qml index 9727a79924..c74ea5c388 100644 --- a/ryoku/shell/quickshell/shell/modules/sidebar/SidebarChrome.qml +++ b/ryoku/shell/quickshell/shell/modules/sidebar/SidebarChrome.qml @@ -121,6 +121,7 @@ Item { ControlsBoard { s: root.s screen: root.screen + card: root active: root.active && root.page !== "plugins" page: root.page onRequestClose: root.closeRequested()