fix(slint): flyouts/idle, header nav layout, toasts, settings, search

Flyouts & idle (main effort):
- Header dropdowns now glue under the tab: open-menu resets from-sidebar +
  title (a stale sidebar flyout state was flinging the panel far away).
- Idle-close now pauses while the cursor rests on the TRIGGER too (full
  header tabs gained the trigger-hovered logic the others already had).
- Dropped the dropdown caret (saves width on small screens).

Header nav layout:
- Section nav moved INTO the left-controls cluster, after the three sacred
  buttons, so it never overlaps them. Full text tabs when nav-in-sidebar off
  and sidebar not closed; compact icons when closed — mutually exclusive (no
  more duplicate/cluttered header).
- Search field gives up 60px (animated) when the header hosts the nav.

Settings made functional:
- Toast component mounted in AppShell (it was never rendered) + the in-app
  toasts toggle drives ToastState.enabled.
- "Show album artwork in Local Library track list" now drives the Tracks/
  Folders rows. Both rows un-flagged as unsupported.

Collapsible search (ExpandableSearch, app-wide):
- Smooth open AND close (width animates both ways; magnifier fades).
- Reliable auto-focus on open (deferred via a one-shot timer).

cc
This commit is contained in:
vicrodh committed 2026-05-30 15:59:25 -06:00
1 parent 682719cab0
commit 13f43b802b
5 files changed
+128 -95

No files matched your search

@@ -19,9 +19,21 @@ export component ExpandableSearch inherits Rectangle {
in property <bool> sm: false;
callback edited(string);
// Defer the focus: focusing the input synchronously inside `changed open`
// races the open animation/layout (and can panic with "Recursion
// detected"). A one-shot timer grabs focus a tick later, reliably.
focus-timer := Timer {
interval: 30ms;
running: false;
triggered => {
input.focus();
self.running = false;
}
}
changed open => {
if (root.open) {
input.focus();
focus-timer.running = true;
focus-timer.restart();
}
}
@@ -29,9 +41,11 @@ export component ExpandableSearch inherits Rectangle {
width: root.sm ? 30px : 34px;
height: root.sm ? 30px : 34px;
// Closed: the magnifier toggle.
// Closed: the magnifier toggle. Fades (not toggled) so open/close is
// smooth in both directions.
Rectangle {
visible: !root.open;
opacity: root.open ? 0.0 : 1.0;
animate opacity { duration: 120ms; easing: ease-in-out; }
width: root.sm ? 30px : 34px;
height: root.sm ? 30px : 34px;
border-radius: 6px;
@@ -45,6 +59,7 @@ export component ExpandableSearch inherits Rectangle {
tint: open-ta.has-hover ? Theme.text-primary : Theme.text-muted;
}
open-ta := TouchArea {
enabled: !root.open;
mouse-cursor: pointer;
clicked => {
root.open = true;
@@ -52,15 +67,15 @@ export component ExpandableSearch inherits Rectangle {
}
}
// Open: right-anchored overlay that grows left.
// Open: right-anchored overlay that grows left. Always rendered; its width
// animates to 0 when closed (clip hides it), so the shrink is also smooth.
overlay := Rectangle {
visible: root.open;
x: parent.width - self.width;
width: root.open ? root.max-open-width : 0px;
height: root.sm ? 30px : 34px;
background: Theme.surface-card;
border-radius: 6px;
border-width: 1px;
border-width: root.open ? 1px : 0px;
border-color: input.has-focus ? Theme.accent : Theme.border-subtle;
clip: true;
animate width { duration: 200ms; easing: ease-in-out; }