feat(slint): extend scroll-position restore to all views
Wire the per-history-entry scroll memory (added for Local Library) into every top-level view: Home/Discover, Album, Local Album, Artist, Musician, Label, Label Releases, Location, Favorites, Mix, Playlist, Playlist Manager, Offline Manager, Search, Settings. Each view's primary scroll container reports its viewport-y and restores it on back/forward via NavState.restore-scope (scope_for in main.rs maps each entry to a stable id; tab views carry the tab, e.g. "fav:albums"). The container restores once per arm, only after content lays out, then clears the scope. A `changed sr-armed` trigger covers views that persist across sub-tab navigation (Home's Discover tabs, Favorites' shared list, Playlist A->B->A) where no fresh mount occurs. ScrollView-based views (Offline Manager, Settings) use visible-height for the overflow check; ListView/Flickable views use height. Favorites' shared albums/artists/playlists/labels Flickable matches the active tab's scope dynamically. cc
This commit is contained in:
17 files changed
+243
-21
No files matched your search
@@ -664,20 +664,31 @@ fn navigate_search(
|
|||||||
|
|
||||||
/// Apply a history entry — set the view and re-load entity pages.
|
/// Apply a history entry — set the view and re-load entity pages.
|
||||||
/// Stable scroll-restore id for an entry's primary list container, matching
|
/// Stable scroll-restore id for an entry's primary list container, matching
|
||||||
/// the `scope` strings the Slint scroll containers compare against. Returns
|
/// the `restore-scope` strings the Slint scroll containers compare against.
|
||||||
/// `""` for views without a wired scroll memory (no container will match, so
|
/// Returns `""` for views without a wired scroll memory (no container will
|
||||||
/// nothing restores). Keep in sync with the `restore-scope` checks in the
|
/// match, so nothing restores). Tab/sub-page views carry the tab in the id so
|
||||||
/// `.slint` views.
|
/// each tab keeps its own position. Keep in sync with the `.slint` views.
|
||||||
fn scope_for(entry: &nav::NavEntry) -> &'static str {
|
fn scope_for(entry: &nav::NavEntry) -> String {
|
||||||
match entry {
|
match entry {
|
||||||
nav::NavEntry::LocalLibrary { tab } => match tab.as_str() {
|
// HomeView is one persistent Flickable shared by the Discover tabs;
|
||||||
"albums" => "ll:albums",
|
// a single scope is enough (each tab entry stores its own scroll).
|
||||||
"tracks" => "ll:tracks",
|
nav::NavEntry::Home | nav::NavEntry::Discover { .. } => "home".into(),
|
||||||
"folders" => "ll:folders",
|
nav::NavEntry::Favorites { tab } => format!("fav:{tab}"),
|
||||||
"artists" => "ll:artists",
|
nav::NavEntry::LocalLibrary { tab } => format!("ll:{tab}"),
|
||||||
_ => "",
|
nav::NavEntry::DiscoverBrowse { .. } => "discover-browse".into(),
|
||||||
},
|
nav::NavEntry::Mix { .. } => "mix".into(),
|
||||||
_ => "",
|
nav::NavEntry::Playlist(_) => "playlist".into(),
|
||||||
|
nav::NavEntry::PlaylistManager => "playlist-manager".into(),
|
||||||
|
nav::NavEntry::OfflineManager => "offline-manager".into(),
|
||||||
|
nav::NavEntry::Album(_) => "album".into(),
|
||||||
|
nav::NavEntry::LocalAlbum(_) => "local-album".into(),
|
||||||
|
nav::NavEntry::Artist(_) => "artist".into(),
|
||||||
|
nav::NavEntry::Settings => "settings".into(),
|
||||||
|
nav::NavEntry::Search(_) => "search".into(),
|
||||||
|
nav::NavEntry::Musician { .. } => "musician".into(),
|
||||||
|
nav::NavEntry::Label { .. } => "label".into(),
|
||||||
|
nav::NavEntry::LabelReleases { .. } => "label-releases".into(),
|
||||||
|
nav::NavEntry::Location { .. } => "location".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,18 @@ export component AlbumPageView inherits Rectangle {
|
|||||||
interactive: !DragState.active;
|
interactive: !DragState.active;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "album" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
// Artwork-tinted gradient behind the header; scrolls with content.
|
// Artwork-tinted gradient behind the header; scrolls with content.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|||||||
@@ -7,7 +7,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 { LocalAlbumState, LocalAlbumActions, LocalAlbumVersion, NowPlayingState } from "../state.slint";
|
import { LocalAlbumState, LocalAlbumActions, LocalAlbumVersion, NowPlayingState, NavState } from "../state.slint";
|
||||||
import { NavButtons } from "../shell/NavButtons.slint";
|
import { NavButtons } from "../shell/NavButtons.slint";
|
||||||
import { CircleAction } from "../primitives/CircleAction.slint";
|
import { CircleAction } from "../primitives/CircleAction.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
@@ -320,6 +320,18 @@ export component LocalAlbumView inherits Rectangle {
|
|||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
tracks-list := ListView {
|
tracks-list := ListView {
|
||||||
vertical-scrollbar-policy: ScrollBarPolicy.always-off;
|
vertical-scrollbar-policy: ScrollBarPolicy.always-off;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "local-album" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
for track[i] in LocalAlbumState.tracks: TrackRow {
|
for track[i] in LocalAlbumState.tracks: TrackRow {
|
||||||
track: track;
|
track: track;
|
||||||
index: i;
|
index: i;
|
||||||
|
|||||||
@@ -130,6 +130,18 @@ export component ArtistPageView inherits Rectangle {
|
|||||||
interactive: !DragState.active;
|
interactive: !DragState.active;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "artist" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
// Artwork-tinted gradient behind the header; scrolls with content.
|
// Artwork-tinted gradient behind the header; scrolls with content.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|||||||
@@ -10,7 +10,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 { DiscoverBrowseState, DiscoverBrowseActions, GenreFilterState, GenreFilterActions } from "../state.slint";
|
import { DiscoverBrowseState, DiscoverBrowseActions, GenreFilterState, GenreFilterActions, NavState } from "../state.slint";
|
||||||
import { AlbumCollectionView } from "AlbumCollectionView.slint";
|
import { AlbumCollectionView } from "AlbumCollectionView.slint";
|
||||||
import { GenreFilterPopup } from "GenreFilterPopup.slint";
|
import { GenreFilterPopup } from "GenreFilterPopup.slint";
|
||||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||||
@@ -248,12 +248,24 @@ export component DiscoverBrowseView inherits Rectangle {
|
|||||||
vertical-stretch: 1;
|
vertical-stretch: 1;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "discover-browse" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
|
||||||
// Infinite scroll — when the viewport nears the bottom of the
|
// Infinite scroll — when the viewport nears the bottom of the
|
||||||
// content (within 600px), pull the next page. Guarded by
|
// content (within 600px), pull the next page. Guarded by
|
||||||
// has-more + the loading flags AND an empty search query, so a
|
// has-more + the loading flags AND an empty search query, so a
|
||||||
// client-side search filter does not keep paging in the catalog.
|
// client-side search filter does not keep paging in the catalog.
|
||||||
changed viewport-y => {
|
changed viewport-y => {
|
||||||
|
NavState.report-scroll(self.viewport-y);
|
||||||
if (-self.viewport-y + self.height >= self.viewport-height - 600px
|
if (-self.viewport-y + self.height >= self.viewport-height - 600px
|
||||||
&& DiscoverBrowseState.has-more
|
&& DiscoverBrowseState.has-more
|
||||||
&& !DiscoverBrowseState.loading-more
|
&& !DiscoverBrowseState.loading-more
|
||||||
|
|||||||
@@ -254,6 +254,18 @@ export component HomeView inherits Rectangle {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: home-content.preferred-height;
|
viewport-height: home-content.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "home" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
home-content := VerticalLayout {
|
home-content := VerticalLayout {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
GenreFilterState,
|
GenreFilterState,
|
||||||
GenreFilterActions,
|
GenreFilterActions,
|
||||||
AlphaJump,
|
AlphaJump,
|
||||||
|
NavState,
|
||||||
} from "../state.slint";
|
} from "../state.slint";
|
||||||
import { AlbumCollectionView } from "../discover/AlbumCollectionView.slint";
|
import { AlbumCollectionView } from "../discover/AlbumCollectionView.slint";
|
||||||
import { PlaylistCard } from "../discover/PlaylistCard.slint";
|
import { PlaylistCard } from "../discover/PlaylistCard.slint";
|
||||||
@@ -963,6 +964,18 @@ export component FavoritesView inherits Rectangle {
|
|||||||
track-list := ListView {
|
track-list := ListView {
|
||||||
vertical-stretch: 1;
|
vertical-stretch: 1;
|
||||||
vertical-scrollbar-policy: ScrollBarPolicy.always-off;
|
vertical-scrollbar-policy: ScrollBarPolicy.always-off;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "fav:tracks" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
for track[i] in FavoritesState.tracks-visible: VerticalLayout {
|
for track[i] in FavoritesState.tracks-visible: VerticalLayout {
|
||||||
spacing: 0px;
|
spacing: 0px;
|
||||||
// Group header rows when grouping by album / artist.
|
// Group header rows when grouping by album / artist.
|
||||||
@@ -1050,6 +1063,21 @@ export component FavoritesView inherits Rectangle {
|
|||||||
interactive: !DragState.active;
|
interactive: !DragState.active;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: other-page.preferred-height;
|
viewport-height: other-page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward. One Flickable serves the
|
||||||
|
// albums/artists/playlists/labels tabs, so match the active tab's
|
||||||
|
// scope dynamically (see nav::scope_for → "fav:<tab>").
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "fav:" + FavoritesState.active-tab
|
||||||
|
&& self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
other-page := VerticalLayout {
|
other-page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -12,7 +12,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 { LabelState, LabelActions, TrackItem, DragState } from "../state.slint";
|
import { LabelState, LabelActions, TrackItem, DragState, NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { TrackRow } from "../primitives/TrackRow.slint";
|
import { TrackRow } from "../primitives/TrackRow.slint";
|
||||||
import { CircleAction } from "../primitives/CircleAction.slint";
|
import { CircleAction } from "../primitives/CircleAction.slint";
|
||||||
@@ -52,6 +52,18 @@ export component LabelPageView inherits Rectangle {
|
|||||||
interactive: !DragState.active;
|
interactive: !DragState.active;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "label" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
page := VerticalLayout {
|
page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -10,7 +10,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 { LabelState, LabelActions } from "../state.slint";
|
import { LabelState, LabelActions, NavState } from "../state.slint";
|
||||||
import { AlbumCollectionView } from "../discover/AlbumCollectionView.slint";
|
import { AlbumCollectionView } from "../discover/AlbumCollectionView.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { QbzSelect } from "../primitives/QbzSelect.slint";
|
import { QbzSelect } from "../primitives/QbzSelect.slint";
|
||||||
@@ -32,6 +32,18 @@ export component LabelReleasesView inherits Rectangle {
|
|||||||
flick := Flickable {
|
flick := Flickable {
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "label-releases" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
page := VerticalLayout {
|
page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -7,7 +7,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 { LocationViewState, LocationViewActions, LocationArtistItem } from "../state.slint";
|
import { LocationViewState, LocationViewActions, LocationArtistItem, NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||||
import { ListScrollbar } from "../primitives/ListScrollbar.slint";
|
import { ListScrollbar } from "../primitives/ListScrollbar.slint";
|
||||||
@@ -87,6 +87,18 @@ export component ArtistsByLocationView inherits Rectangle {
|
|||||||
flick := Flickable {
|
flick := Flickable {
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "location" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
page := VerticalLayout {
|
page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -8,7 +8,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 { MixState, DragState } from "../state.slint";
|
import { MixState, DragState, NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { TrackRow } from "../primitives/TrackRow.slint";
|
import { TrackRow } from "../primitives/TrackRow.slint";
|
||||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||||
@@ -71,6 +71,18 @@ export component MixView inherits Rectangle {
|
|||||||
interactive: !DragState.active;
|
interactive: !DragState.active;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "mix" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
page := VerticalLayout {
|
page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
MusicianState,
|
MusicianState,
|
||||||
MusicianActions,
|
MusicianActions,
|
||||||
MusicianAppearanceItem,
|
MusicianAppearanceItem,
|
||||||
|
NavState,
|
||||||
} from "../state.slint";
|
} from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||||
@@ -93,6 +94,18 @@ export component MusicianPageView inherits Rectangle {
|
|||||||
flick := Flickable {
|
flick := Flickable {
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "musician" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
page := VerticalLayout {
|
page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -13,6 +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 { OfflineManagerState, OfflineManagerActions, OfflineRow, OfflineArtist } from "../state.slint";
|
import { OfflineManagerState, OfflineManagerActions, OfflineRow, OfflineArtist } from "../state.slint";
|
||||||
|
import { NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { QbzSelect } from "../primitives/QbzSelect.slint";
|
import { QbzSelect } from "../primitives/QbzSelect.slint";
|
||||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||||
@@ -394,6 +395,18 @@ export component OfflineManagerView inherits Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !OfflineManagerState.loading && OfflineManagerState.rows.length > 0: ScrollView {
|
if !OfflineManagerState.loading && OfflineManagerState.rows.length > 0: ScrollView {
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "offline-manager" && self.viewport-height > self.visible-height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
VerticalLayout {
|
VerticalLayout {
|
||||||
spacing: 2px;
|
spacing: 2px;
|
||||||
for row in OfflineManagerState.rows: Rectangle {
|
for row in OfflineManagerState.rows: Rectangle {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
PmFolderItem,
|
PmFolderItem,
|
||||||
PmPlaylistItem,
|
PmPlaylistItem,
|
||||||
PmTreeRow,
|
PmTreeRow,
|
||||||
|
NavState,
|
||||||
} from "../state.slint";
|
} from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
import { LoadingSpinner } from "../primitives/LoadingSpinner.slint";
|
||||||
@@ -1233,6 +1234,18 @@ export component PlaylistManagerView inherits Rectangle {
|
|||||||
vertical-stretch: 1;
|
vertical-stretch: 1;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: page.preferred-height;
|
viewport-height: page.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "playlist-manager" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
page := VerticalLayout {
|
page := VerticalLayout {
|
||||||
width: parent.width;
|
width: parent.width;
|
||||||
|
|||||||
@@ -6,7 +6,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 { PlaylistState, PlaylistActions, DragState } from "../state.slint";
|
import { PlaylistState, PlaylistActions, DragState, NavState } from "../state.slint";
|
||||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||||
import { TrackRow } from "../primitives/TrackRow.slint";
|
import { TrackRow } from "../primitives/TrackRow.slint";
|
||||||
import { ExpandableSearch } from "../primitives/ExpandableSearch.slint";
|
import { ExpandableSearch } from "../primitives/ExpandableSearch.slint";
|
||||||
@@ -625,6 +625,18 @@ export component PlaylistView inherits Rectangle {
|
|||||||
track-list := ListView {
|
track-list := ListView {
|
||||||
vertical-stretch: 1;
|
vertical-stretch: 1;
|
||||||
vertical-scrollbar-policy: ScrollBarPolicy.always-off;
|
vertical-scrollbar-policy: ScrollBarPolicy.always-off;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "playlist" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
for track[i] in PlaylistState.tracks: TrackRow {
|
for track[i] in PlaylistState.tracks: TrackRow {
|
||||||
track: track;
|
track: track;
|
||||||
index: i;
|
index: i;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// per-type tabs show the full loaded set as a card grid or row list with
|
// per-type tabs show the full loaded set as a card grid or row list with
|
||||||
// a load-more button.
|
// a load-more button.
|
||||||
|
|
||||||
import { SearchState, SearchActions, TrackItem, SearchPlaylistItem, AlbumCardItem, SlimItem, DragState } from "../state.slint";
|
import { SearchState, SearchActions, TrackItem, SearchPlaylistItem, AlbumCardItem, SlimItem, DragState, NavState } from "../state.slint";
|
||||||
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";
|
||||||
@@ -436,6 +436,18 @@ export component SearchResultsView inherits Rectangle {
|
|||||||
vertical-stretch: 1;
|
vertical-stretch: 1;
|
||||||
viewport-width: self.width;
|
viewport-width: self.width;
|
||||||
viewport-height: body.preferred-height;
|
viewport-height: body.preferred-height;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "search" && self.viewport-height > self.height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
|
|
||||||
body := VerticalLayout {
|
body := VerticalLayout {
|
||||||
padding-left: 32px;
|
padding-left: 32px;
|
||||||
|
|||||||
@@ -165,6 +165,18 @@ export component SettingsView inherits Rectangle {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
horizontal-stretch: 1;
|
horizontal-stretch: 1;
|
||||||
horizontal-scrollbar-policy: ScrollBarPolicy.always-off;
|
horizontal-scrollbar-policy: ScrollBarPolicy.always-off;
|
||||||
|
// Scroll-position restore on back/forward (see nav::scope_for).
|
||||||
|
property <string> sr-armed: NavState.restore-scope;
|
||||||
|
function sr-restore() {
|
||||||
|
if (NavState.restore-scope == "settings" && self.viewport-height > self.visible-height) {
|
||||||
|
self.viewport-y = NavState.scroll-restore;
|
||||||
|
NavState.restore-scope = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init => { self.sr-restore(); }
|
||||||
|
changed sr-armed => { self.sr-restore(); }
|
||||||
|
changed viewport-height => { self.sr-restore(); }
|
||||||
|
changed viewport-y => { NavState.report-scroll(self.viewport-y); }
|
||||||
VerticalLayout {
|
VerticalLayout {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
|
|||||||
Reference in new issue
Block a user