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) { function score(name, extra, q) {
let n = name.toLowerCase(); let n = name.toLowerCase();
if (n.startsWith(q)) return 3; if (n.startsWith(q)) return 5;
if (n.includes(" " + q)) return 2; if (n.includes(" " + q)) return 4;
if (n.includes(q)) return 1; if (n.includes(q)) return 3;
if (extra && extra.toLowerCase().includes(q)) return 1; 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; return 0;
} }
@ -575,8 +586,12 @@ in
if (dd.visible && !dd.closing) { if (dd.visible && !dd.closing) {
dd.animateClose(); dd.animateClose();
} else { } else {
if (activeDropdown && activeDropdown !== dd && activeDropdown.visible) { // Retarget the chrome before closing the previous
activeDropdown.animateClose(); // 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 (setupFn) setupFn();
if (dd.closing) { if (dd.closing) {
@ -584,7 +599,6 @@ in
} else { } else {
dd.visible = true; dd.visible = true;
} }
activeDropdown = dd;
} }
} }
@ -1080,6 +1094,11 @@ in
if (!visible || closing) return; if (!visible || closing) return;
closing = true; closing = true;
open = false; 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(); _autoClose.stop();
_closeDelay.start(); _closeDelay.start();
} }
@ -1126,7 +1145,7 @@ in
Timer { Timer {
id: _closeDelay id: _closeDelay
interval: 230 interval: 300
onTriggered: { dropdown.visible = false; dropdown.closing = false; if (bar.activeDropdown === dropdown) bar.activeDropdown = null; } 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 // Content is clipped to the chrome's ANIMATED geometry
// reveals/hides with the morph; fades on open/close. // 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 { Item {
id: _dropdownRect id: _dropdownRect
anchors.left: parent.left x: (chrome.x + 8) - dropdown.x
anchors.leftMargin: 8 y: 0
anchors.top: parent.top width: Math.max(0, chrome.width - (chrome.flushRight ? 8 : 16))
width: dropdown.fullWidth
height: Math.min(dropdown.fullHeight, chrome.height) height: Math.min(dropdown.fullHeight, chrome.height)
clip: true clip: true
opacity: dropdown.open ? 1 : 0 opacity: dropdown.open ? 1 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: 150; easing.type: Easing.OutCubic } NumberAnimation { duration: 200; easing.type: Easing.OutCubic }
} }
Item { Item {
id: dropdownContent id: dropdownContent
x: 8 - _dropdownRect.x
width: dropdown.fullWidth width: dropdown.fullWidth
height: dropdown.fullHeight height: dropdown.fullHeight
} }