fix(slint): instant local queue, mouse nav buttons, narrower toolbar controls
- Local Library Tracks: build the play queue from the already-loaded rows (instant) instead of re-querying the DB + filling covers on every click. - Mouse back/forward buttons now drive history nav over track rows and album cards (a TouchArea swallows them, so each interactive surface forwards them; the AppShell catcher only saw the gaps). - ExpandableSearch open width trimmed 20px (no longer rides over left controls on small screens). - QbzSelect toolbar (Group/Sort/filter) trimmed 40px total. cc
This commit is contained in:
7 files changed
+54
-22
No files matched your search
@@ -603,6 +603,13 @@ fn tracks_current() -> std::sync::MutexGuard<'static, Vec<qbz_library::LocalTrac
|
|||||||
TRACKS_CURRENT.lock().unwrap_or_else(|e| e.into_inner())
|
TRACKS_CURRENT.lock().unwrap_or_else(|e| e.into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Snapshot of the currently-loaded Tracks-tab rows (already carry their
|
||||||
|
/// covers). Used to build the play queue instantly on a row click — avoiding
|
||||||
|
/// the full DB re-query + cover-fill that delayed queue population.
|
||||||
|
pub fn tracks_current_snapshot() -> Vec<qbz_library::LocalTrack> {
|
||||||
|
tracks_current().clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn fmt_duration(secs: u64) -> String {
|
fn fmt_duration(secs: u64) -> String {
|
||||||
format!("{}:{:02}", secs / 60, secs % 60)
|
format!("{}:{:02}", secs / 60, secs % 60)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4456,23 +4456,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
match action.as_str() {
|
match action.as_str() {
|
||||||
"play" => {
|
"play" => {
|
||||||
if let Ok(row_id) = id.parse::<i64>() {
|
if let Ok(row_id) = id.parse::<i64>() {
|
||||||
// Queue the current (searched) list so playback
|
// Queue the already-loaded rows (instant — no DB
|
||||||
// continues down it from the clicked row.
|
// re-query / cover-fill that delayed the queue) so
|
||||||
let query = weak
|
// playback continues down the list from the click.
|
||||||
.upgrade()
|
let tracks = local_library::tracks_current_snapshot();
|
||||||
.map(|w| {
|
if !tracks.is_empty() {
|
||||||
w.global::<LocalLibraryState>()
|
let start = tracks
|
||||||
.get_tracks_search()
|
.iter()
|
||||||
.to_string()
|
.position(|t| t.id == row_id)
|
||||||
})
|
.unwrap_or(0);
|
||||||
.unwrap_or_default();
|
playback::play_local_tracks(
|
||||||
playback::play_local_tracks_from(
|
runtime.clone(),
|
||||||
runtime.clone(),
|
weak.clone(),
|
||||||
weak.clone(),
|
handle.clone(),
|
||||||
handle.clone(),
|
tracks,
|
||||||
query,
|
start,
|
||||||
row_id,
|
false,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"toggle-select" => {
|
"toggle-select" => {
|
||||||
|
|||||||
@@ -332,6 +332,9 @@ pub fn play_local_folder_tracks_from(
|
|||||||
|
|
||||||
/// Play the Tracks-tab list starting at `start_track_id`: the matching set
|
/// Play the Tracks-tab list starting at `start_track_id`: the matching set
|
||||||
/// (current search) becomes the queue, so playback continues down the list.
|
/// (current search) becomes the queue, so playback continues down the list.
|
||||||
|
/// (Superseded by the instant in-memory-cache path; kept for the full-list
|
||||||
|
/// option.)
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn play_local_tracks_from(
|
pub fn play_local_tracks_from(
|
||||||
runtime: Runtime,
|
runtime: Runtime,
|
||||||
weak: slint::Weak<AppWindow>,
|
weak: slint::Weak<AppWindow>,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Theme } from "../foundation/semantic-colors.slint";
|
|||||||
import { Typography } from "../foundation/typography.slint";
|
import { Typography } from "../foundation/typography.slint";
|
||||||
import { Radius } from "../foundation/radius.slint";
|
import { Radius } from "../foundation/radius.slint";
|
||||||
import { Spacing } from "../foundation/spacing.slint";
|
import { Spacing } from "../foundation/spacing.slint";
|
||||||
import { AlbumCardItem } from "../state.slint";
|
import { AlbumCardItem, NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { QualityBadge } from "../primitives/QualityBadge.slint";
|
import { QualityBadge } from "../primitives/QualityBadge.slint";
|
||||||
import { ContextMenu } from "../primitives/ContextMenu.slint";
|
import { ContextMenu } from "../primitives/ContextMenu.slint";
|
||||||
@@ -56,6 +56,16 @@ component OverlayButton inherits Rectangle {
|
|||||||
clicked => {
|
clicked => {
|
||||||
root.clicked();
|
root.clicked();
|
||||||
}
|
}
|
||||||
|
// Forward the mouse back/forward buttons to history nav (a TouchArea
|
||||||
|
// swallows them, so they never reach the AppShell catcher).
|
||||||
|
pointer-event(e) => {
|
||||||
|
if (e.kind == PointerEventKind.down && e.button == PointerEventButton.back) {
|
||||||
|
NavState.request-back();
|
||||||
|
}
|
||||||
|
if (e.kind == PointerEventKind.down && e.button == PointerEventButton.forward) {
|
||||||
|
NavState.request-forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import { QbzIcon } from "QbzIcon.slint";
|
|||||||
|
|
||||||
export component ExpandableSearch inherits Rectangle {
|
export component ExpandableSearch inherits Rectangle {
|
||||||
in property <string> placeholder: "Search…";
|
in property <string> placeholder: "Search…";
|
||||||
in property <length> max-open-width: 216px;
|
// Trimmed 20px so the open field doesn't ride over the controls to its
|
||||||
|
// left on small screens.
|
||||||
|
in property <length> max-open-width: 196px;
|
||||||
in-out property <bool> open: false;
|
in-out property <bool> open: false;
|
||||||
in-out property <string> query;
|
in-out property <string> query;
|
||||||
// Bootstrap-style small variant. Default is the standard size.
|
// Bootstrap-style small variant. Default is the standard size.
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ export component QbzSelect inherits Rectangle {
|
|||||||
callback selected(int);
|
callback selected(int);
|
||||||
|
|
||||||
// Toolbar (sm) selects — Group / Sort / filter — were unnecessarily wide;
|
// Toolbar (sm) selects — Group / Sort / filter — were unnecessarily wide;
|
||||||
// trim 20px from the effective width (button + list).
|
// trim 40px from the effective width (button + list).
|
||||||
private property <length> eff-width: root.menu-width - (root.sm ? 20px : 0px);
|
private property <length> eff-width: root.menu-width - (root.sm ? 40px : 0px);
|
||||||
// Resolved width of the open list.
|
// Resolved width of the open list.
|
||||||
private property <length> list-width:
|
private property <length> list-width:
|
||||||
root.popup-width > root.eff-width ? root.popup-width : root.eff-width;
|
root.popup-width > root.eff-width ? root.popup-width : root.eff-width;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
import { Theme } from "../foundation/semantic-colors.slint";
|
import { Theme } from "../foundation/semantic-colors.slint";
|
||||||
import { Typography } from "../foundation/typography.slint";
|
import { Typography } from "../foundation/typography.slint";
|
||||||
import { Radius } from "../foundation/radius.slint";
|
import { Radius } from "../foundation/radius.slint";
|
||||||
import { TrackItem, DragActions, NowPlayingState } from "../state.slint";
|
import { TrackItem, DragActions, NowPlayingState, NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "QbzIcon.slint";
|
import { QbzIcon } from "QbzIcon.slint";
|
||||||
import { TrackPlayCell } from "TrackPlayCell.slint";
|
import { TrackPlayCell } from "TrackPlayCell.slint";
|
||||||
import { TrackContextMenu } from "TrackContextMenu.slint";
|
import { TrackContextMenu } from "TrackContextMenu.slint";
|
||||||
@@ -81,6 +81,15 @@ export component TrackRow inherits Rectangle {
|
|||||||
property <length> down-y;
|
property <length> down-y;
|
||||||
property <bool> dragging: false;
|
property <bool> dragging: false;
|
||||||
pointer-event(e) => {
|
pointer-event(e) => {
|
||||||
|
// The mouse back/forward buttons drive history nav. A TouchArea
|
||||||
|
// swallows these (they never reach the AppShell catcher), so each
|
||||||
|
// interactive row forwards them itself.
|
||||||
|
if (e.kind == PointerEventKind.down && e.button == PointerEventButton.back) {
|
||||||
|
NavState.request-back();
|
||||||
|
}
|
||||||
|
if (e.kind == PointerEventKind.down && e.button == PointerEventButton.forward) {
|
||||||
|
NavState.request-forward();
|
||||||
|
}
|
||||||
if (e.kind == PointerEventKind.down) {
|
if (e.kind == PointerEventKind.down) {
|
||||||
ta.down-x = root.absolute-position.x + ta.mouse-x;
|
ta.down-x = root.absolute-position.x + ta.mouse-x;
|
||||||
ta.down-y = root.absolute-position.y + ta.mouse-y;
|
ta.down-y = root.absolute-position.y + ta.mouse-y;
|
||||||
|
|||||||
Reference in new issue
Block a user