quickshell: fuzzy launcher search; content reveals/wipes with the chrome morph

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-06-11 20:32:19 +01:00
parent 21db9825d5
commit f2cf842ace

View file

@ -230,10 +230,21 @@ in
function score(name, extra, q) {
let n = name.toLowerCase();
if (n.startsWith(q)) return 3;
if (n.includes(" " + q)) return 2;
if (n.includes(q)) return 1;
if (extra && extra.toLowerCase().includes(q)) return 1;
if (n.startsWith(q)) return 5;
if (n.includes(" " + q)) return 4;
if (n.includes(q)) return 3;
if (extra && extra.toLowerCase().includes(q)) return 2;
// Fuzzy: q as an in-order subsequence of n (vktop
// vesktop); fewer skipped characters scores higher.
let qi = 0, gaps = 0, last = -1;
for (let i = 0; i < n.length && qi < q.length; i++) {
if (n[i] === q[qi]) {
if (last >= 0) gaps += i - last - 1;
last = i;
qi++;
}
}
if (qi === q.length) return 1 / (1 + gaps);
return 0;
}
@ -575,8 +586,12 @@ in
if (dd.visible && !dd.closing) {
dd.animateClose();
} else {
if (activeDropdown && activeDropdown !== dd && activeDropdown.visible) {
activeDropdown.animateClose();
// Retarget the chrome before closing the previous
// dropdown so it morphs instead of dipping closed.
const prev = activeDropdown;
activeDropdown = dd;
if (prev && prev !== dd && prev.visible) {
prev.animateClose();
}
if (setupFn) setupFn();
if (dd.closing) {
@ -584,7 +599,6 @@ in
} else {
dd.visible = true;
}
activeDropdown = dd;
}
}
@ -1080,6 +1094,11 @@ in
if (!visible || closing) return;
closing = true;
open = false;
// Collapse the chrome immediately so the content fade
// 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;
_autoClose.stop();
_closeDelay.start();
}
@ -1126,7 +1145,7 @@ in
Timer {
id: _closeDelay
interval: 230
interval: 300
onTriggered: { dropdown.visible = false; dropdown.closing = false; if (bar.activeDropdown === dropdown) bar.activeDropdown = null; }
}
@ -1137,23 +1156,26 @@ in
}
}
// Content clipped to the chrome's animated height so it
// reveals/hides with the morph; fades on open/close.
// Content is clipped to the chrome's ANIMATED geometry
// revealed as the panel slides/grows over it and wiped as
// the panel leaves, instead of popping in place. The inner
// item counter-offsets so content stays put while the clip
// window moves across it.
Item {
id: _dropdownRect
anchors.left: parent.left
anchors.leftMargin: 8
anchors.top: parent.top
width: dropdown.fullWidth
x: (chrome.x + 8) - dropdown.x
y: 0
width: Math.max(0, chrome.width - (chrome.flushRight ? 8 : 16))
height: Math.min(dropdown.fullHeight, chrome.height)
clip: true
opacity: dropdown.open ? 1 : 0
Behavior on opacity {
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
}
Item {
id: dropdownContent
x: 8 - _dropdownRect.x
width: dropdown.fullWidth
height: dropdown.fullHeight
}