feat(slint): search loading fade + 1s idle-close on header menus
New LoadingDots primitive (three pulsing dots over an optional label) shown while a search is running. The result sections fade in once the results arrive instead of showing empty placeholder colors. The LoadingDots component is reusable for other heavy loads (large grids, playlist manager) when they get the same treatment. The header nav-tab menu now closes after 1s of pointer idle (was 1.5s) per the project rule that no menu stays open more than 1s idle. PopupWindow context menus already close on click-outside, which is the idle behaviour for click-triggered popups. cc
This commit is contained in:
3 files changed
+58
-8
No files matched your search
@@ -0,0 +1,38 @@
|
||||
// Loading indicator — three pulsing dots above an optional label. A
|
||||
// lightweight replacement for empty-color placeholders during heavy
|
||||
// loads (search, large grids, the playlist manager, etc.).
|
||||
|
||||
import { Theme } from "../foundation/semantic-colors.slint";
|
||||
|
||||
component Dot inherits Rectangle {
|
||||
in property <float> delay; // 0..1 phase offset
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background: Theme.text-secondary;
|
||||
property <float> phase: Math.mod((animation-tick() / 1ms) / 900.0 + root.delay, 1.0);
|
||||
property <float> tri: 1.0 - Math.abs(root.phase - 0.5) * 2.0;
|
||||
opacity: 0.25 + 0.55 * tri;
|
||||
}
|
||||
|
||||
export component LoadingDots inherits Rectangle {
|
||||
in property <string> label;
|
||||
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
spacing: 12px;
|
||||
HorizontalLayout {
|
||||
alignment: center;
|
||||
spacing: 8px;
|
||||
Dot { delay: 0.0; }
|
||||
Dot { delay: 0.18; }
|
||||
Dot { delay: 0.36; }
|
||||
}
|
||||
if root.label != "": Text {
|
||||
text: root.label;
|
||||
color: Theme.text-muted;
|
||||
font-size: 13px;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { PlaylistCard } from "../discover/PlaylistCard.slint";
|
||||
import { SearchTrackRow } from "SearchTrackRow.slint";
|
||||
import { NavButtons } from "../shell/NavButtons.slint";
|
||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { LoadingDots } from "../primitives/LoadingDots.slint";
|
||||
|
||||
// One tab in the results tab strip.
|
||||
component SearchTab inherits Rectangle {
|
||||
@@ -425,9 +426,19 @@ export component SearchResultsView inherits Rectangle {
|
||||
padding-left: 32px;
|
||||
padding-right: 32px;
|
||||
padding-bottom: 32px;
|
||||
spacing: 28px;
|
||||
spacing: 0px;
|
||||
alignment: start;
|
||||
|
||||
// Results sections — fade in once the search completes
|
||||
// so the empty placeholder colors never show through.
|
||||
VerticalLayout {
|
||||
spacing: 28px;
|
||||
opacity: SearchState.loading ? 0.0 : 1.0;
|
||||
animate opacity {
|
||||
duration: 280ms;
|
||||
easing: ease-in-out;
|
||||
}
|
||||
|
||||
// ---- Most popular + Artists (All tab) ---------------------
|
||||
if SearchState.tab == 0 && SearchState.most-popular-kind != "": HorizontalLayout {
|
||||
spacing: 24px;
|
||||
@@ -571,11 +582,11 @@ export component SearchResultsView inherits Rectangle {
|
||||
clicked => { SearchActions.load-more(4); }
|
||||
}
|
||||
|
||||
// ---- Empty / loading state --------------------------------
|
||||
if SearchState.loading && !root.has-results: Text {
|
||||
text: @tr("Searching…");
|
||||
color: Theme.text-muted;
|
||||
font-size: 14px;
|
||||
}
|
||||
// ---- Loading + empty states -------------------------------
|
||||
if SearchState.loading && !root.has-results: LoadingDots {
|
||||
label: @tr("Searching…");
|
||||
height: 140px;
|
||||
}
|
||||
if !SearchState.loading && !root.has-results: Text {
|
||||
text: @tr("No results.");
|
||||
|
||||
@@ -51,9 +51,10 @@ export component HeaderMenuOverlay inherits Rectangle {
|
||||
}
|
||||
property <int> open-index: HeaderMenuState.open-index;
|
||||
|
||||
// Closes the menu after 1.5s without pointer interaction.
|
||||
// Closes the menu after 1s without pointer interaction (project rule:
|
||||
// no menu stays open more than 1s idle).
|
||||
idle-timer := Timer {
|
||||
interval: 1500ms;
|
||||
interval: 1000ms;
|
||||
running: false;
|
||||
triggered => {
|
||||
self.running = false;
|
||||
|
||||
Reference in new issue
Block a user