quickshell: dropdowns grow out of and shrink into their widget

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-06-12 08:52:53 +01:00
parent 28c64286bd
commit 0632777245

View file

@ -586,6 +586,13 @@ in
if (dd.visible && !dd.closing) { if (dd.visible && !dd.closing) {
dd.animateClose(); dd.animateClose();
} else { } 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 // Retarget the chrome before closing the previous
// dropdown so it morphs instead of dipping closed. // dropdown so it morphs instead of dipping closed.
const prev = activeDropdown; const prev = activeDropdown;
@ -593,7 +600,6 @@ in
if (prev && prev !== dd && prev.visible) { if (prev && prev !== dd && prev.visible) {
prev.animateClose(); prev.animateClose();
} }
if (setupFn) setupFn();
if (dd.closing) { if (dd.closing) {
dd.revive(); dd.revive();
} else { } else {
@ -1109,7 +1115,11 @@ in
// and the panel animation run together (when switching // and the panel animation run together (when switching
// dropdowns, toggleDropdown retargets activeDropdown // dropdowns, toggleDropdown retargets activeDropdown
// first, so this doesn't fire and the chrome morphs). // 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(); _autoClose.stop();
_closeDelay.start(); _closeDelay.start();
} }
@ -1279,6 +1289,26 @@ in
property real tH: 0 property real tH: 0
property bool flushRight: false property bool flushRight: false
property real openH: bar.activeDropdown ? tH : 0 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 x: tX
y: 30 y: 30
@ -1312,11 +1342,11 @@ in
} }
Behavior on tX { Behavior on tX {
enabled: chrome.height > 0.5 enabled: !chrome.snap
NumberAnimation { duration: 280; easing.type: Easing.OutExpo } NumberAnimation { duration: 280; easing.type: Easing.OutExpo }
} }
Behavior on tW { Behavior on tW {
enabled: chrome.height > 0.5 enabled: !chrome.snap
NumberAnimation { duration: 280; easing.type: Easing.OutExpo } NumberAnimation { duration: 280; easing.type: Easing.OutExpo }
} }
Behavior on openH { Behavior on openH {