feat(qt): playlist manager, folders, importer and the Library parity round
Ports the whole playlist-organisation domain, which had no counterpart in this frontend, plus the Library work the owner asked not to defer. Playlist Manager. New controller (playlist_manager_qt, _rows, _ops) over the already-ported folders_qt repository, which until now had zero consumers. Grid, list and folder-tree modes; sort, filter and search; folder cards and chips; drag reorder and up/down arrows. Reached from the sidebar's "..." menu, whose New folder / Manage playlists / Import rows had been rendering dimmed and inert behind a comment saying the seam did not exist. Folders. QbzFolderEdit plus the create/edit modals and PmFolderIcon: preset or custom-image icons, colour, hidden flag, delete. Deleting a folder nulls its LOCAL members' folder_id explicitly, because the app keeps the foreign_keys pragma off, so the schema's ON DELETE SET NULL never fires for them. QbzPlaylistEdit. One shared rename/description/delete editor replacing the inline popup in PlaylistView. Three corrections the old path had wrong: the description is carried through a rename instead of being written as NULL, a FOLLOWED playlist routes to unsubscribe (playlist/delete returns 200 and silently no-ops on a playlist you do not own), and the post-delete back navigation is conditional on actually being on that playlist's page, so deleting from the manager no longer navigates you off it. Importer. Spotify / Apple Music / Tidal / Deezer, over the shared qbz-playlist-import crate. Picker local mode. The reference's four-case matrix — target local or Qobuz, payload local refs or Qobuz ids. Local playlists list unconditionally and Qobuz ones only when online. Local refs onto a Qobuz playlist write the library.db sidecar at next_playlist_sidecar_position(pid, qobuz_count); the 0-based enumerate the reference used before collided slots and lost rows. The carried payload can no longer be parsed as a catalog id when it is a library row id. Unblocks the local bulk add-to-playlist. Library. A-Z jump strip, Albums list mode, Artists side panel, per-tab persistence and multi-select. Preferences land in the same <data-dir>/qbz/favorites_ui.json the shipping build writes, so switching between the two frontends keeps them. LibraryView is split into views/library/ on the way past 1800 lines. Local library. Group-by now persists; playback queues the order on screen rather than the SQL page order; switching album-identity mode invalidates the Artists tab; artist matching folds diacritics and compares normalized credit parts, so "Air" stops listing "Airbourne" and "Beyonce" stops being two artists. Offline. activate_offline never persisted a last-user marker, so every per-user path resolved to None and all local reads came back empty; a session that started offline saw no local playlists at all. The sidebar's load-once latch was function-local and therefore unreachable from the logout reset, so a second login never rebuilt the tree. Both are session-scoped now, and the sidebar Refresh row runs the local-safe verb instead of being dimmed with no way to recover. Also fixes, found while verifying rather than reported: artwork derivatives were written straight to their final path, so a QML Image resolving one mid-encode opened a truncated png and Qt cached that failure against the URL for the rest of the session; Library's track group separators referenced a Component declared in another delegate's scope and could never render; the genre filter was drawn on Tracks and Albums but applied only on All; an All-feed row called a player method that does not exist; and the sidebar's drop-hot highlight used Slint's #RRGGBBAA literals in Qt's #AARRGGBB slot. 17 new msgids across all 8 locales. cc
This commit is contained in:
115 files changed
+20778
-1637
No files matched your search
@@ -37,33 +37,54 @@ Rectangle {
|
||||
property bool active: false
|
||||
/// Active = accent FILL (labelled buttons) vs accent BORDER (icon-only).
|
||||
property bool fillActive: false
|
||||
/// THE SECOND SCALE — the Playlist Manager toolbar's `PmToolButton` /
|
||||
/// `PmIconButton` (playlist/PlaylistManagerView.slint:162-230), which is the
|
||||
/// same control 4 px taller with more air. The port's established idiom for
|
||||
/// a second size on one control (`QbzLineEdit.sm`, `QbzSelect.sm`,
|
||||
/// `QbzToggleButton.sm`), rather than a 90 %-identical fork.
|
||||
///
|
||||
/// Under `large`: height 34, padH 11 (the reference's `row { x: 11 }` and
|
||||
/// `width: row.preferred-width + 22`), row spacing 7, label 13, NO border
|
||||
/// (except the accent-border arm, which the PM never uses), and an
|
||||
/// icon-only button is a 34 × 34 square with a 16 px glyph — the 15 → 16
|
||||
/// delta between the labelled and icon-only forms IS the reference
|
||||
/// (`:180-184` vs `:216-222`); do not unify it.
|
||||
///
|
||||
/// The idle GLYPH tint also changes, and it is the largest of the deltas:
|
||||
/// this control tints idle glyphs `muted` (#888888) where the reference
|
||||
/// paints `Theme.text-secondary` (#cccccc) on five of its six toolbar
|
||||
/// controls. Defaults are untouched, so the MyQBZ toolbars cannot regress.
|
||||
property bool large: false
|
||||
signal clicked()
|
||||
|
||||
QbzTheme { id: theme }
|
||||
|
||||
readonly property bool filled: root.active && root.fillActive
|
||||
readonly property int padH: root.label === "" ? 8 : 10
|
||||
readonly property bool iconOnly: root.label === ""
|
||||
readonly property int padH: root.large ? 11 : (root.iconOnly ? 8 : 10)
|
||||
|
||||
height: 30
|
||||
implicitWidth: 2 * root.padH + row.implicitWidth
|
||||
height: root.large ? 34 : 30
|
||||
implicitWidth: (root.large && root.iconOnly)
|
||||
? 34
|
||||
: 2 * root.padH + row.implicitWidth
|
||||
radius: 6
|
||||
color: root.filled ? theme.accent
|
||||
: (toolArea.containsMouse ? theme.surfaceHover : theme.surfaceElevated)
|
||||
border.width: 1
|
||||
border.width: (root.large && !(root.active && !root.fillActive)) ? 0 : 1
|
||||
border.color: (root.active && !root.fillActive) ? theme.accent : theme.borderSubtle
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
spacing: root.large ? 7 : 6
|
||||
|
||||
QbzIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: root.name
|
||||
width: 15
|
||||
height: 15
|
||||
width: (root.large && root.iconOnly) ? 16 : 15
|
||||
height: (root.large && root.iconOnly) ? 16 : 15
|
||||
tintName: root.filled ? theme.accentGlyphTint
|
||||
: (root.active ? "textPrimary" : "muted")
|
||||
: (root.active ? "textPrimary" : (root.large ? "secondary" : "muted"))
|
||||
}
|
||||
Text {
|
||||
visible: root.label !== ""
|
||||
@@ -71,7 +92,7 @@ Rectangle {
|
||||
text: root.label
|
||||
color: root.filled ? theme.accentGlyphColor
|
||||
: (root.active ? theme.textPrimary : theme.textSecondary)
|
||||
font.pixelSize: 12
|
||||
font.pixelSize: root.large ? 13 : 12
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Rectangle {
|
||||
|
||||
Reference in new issue
Block a user