From 06327772455bb94235026909b8d383ef0542d4e5 Mon Sep 17 00:00:00 2001 From: rope Date: Fri, 12 Jun 2026 08:52:53 +0100 Subject: [PATCH] quickshell: dropdowns grow out of and shrink into their widget Co-Authored-By: Claude Fable 5 --- settings/quickshell.nix | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/settings/quickshell.nix b/settings/quickshell.nix index 9f2343a..d820459 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -586,6 +586,13 @@ in if (dd.visible && !dd.closing) { dd.animateClose(); } else { + if (setupFn) setupFn(); + // 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) { + chrome.seedFromButton(dd); + } // Retarget the chrome before closing the previous // dropdown so it morphs instead of dipping closed. const prev = activeDropdown; @@ -593,7 +600,6 @@ in if (prev && prev !== dd && prev.visible) { prev.animateClose(); } - if (setupFn) setupFn(); if (dd.closing) { dd.revive(); } else { @@ -1109,7 +1115,11 @@ in // and the panel animation run together (when switching // dropdowns, toggleDropdown retargets activeDropdown // first, so this doesn't fire and the chrome morphs). - if (bar.activeDropdown === dropdown) bar.activeDropdown = null; + // The panel also shrinks back toward its widget. + if (bar.activeDropdown === dropdown) { + bar.activeDropdown = null; + chrome.shrinkToButton(dropdown); + } _autoClose.stop(); _closeDelay.start(); } @@ -1279,6 +1289,26 @@ in property real tH: 0 property bool flushRight: false property real openH: bar.activeDropdown ? tH : 0 + property bool snap: false + readonly property real stubW: 32 + + // Grow-from / shrink-to the widget that owns the dropdown: + // the panel opens as a small stub on the button and + // expands; closing retargets back to the stub while the + // height collapses. + function stubX(dd) { + return Math.round(Math.min(bar.width - Theme.frameWidth - stubW, Math.max(Theme.frameWidth, dd.dropdownX - stubW / 2))); + } + function seedFromButton(dd) { + snap = true; + tX = stubX(dd); + tW = stubW; + snap = false; + } + function shrinkToButton(dd) { + tX = stubX(dd); + tW = stubW; + } x: tX y: 30 @@ -1312,11 +1342,11 @@ in } Behavior on tX { - enabled: chrome.height > 0.5 + enabled: !chrome.snap NumberAnimation { duration: 280; easing.type: Easing.OutExpo } } Behavior on tW { - enabled: chrome.height > 0.5 + enabled: !chrome.snap NumberAnimation { duration: 280; easing.type: Easing.OutExpo } } Behavior on openH {