refactor(qbz-qt): split QbzLibrary out of the God-object
library_bridge.rs has sat in the tree unreferenced since the phase-23 work stopped: no mod declaration, not in build.rs, zero call sites, and duplicating members that were still live in bridge.rs. It could not have been wired as it stood — its module was named qbz_library, which is ambiguous with the qbz-library crate this binary depends on, so it never compiled. The module is now qbz_library_bridge; the QML type name comes from the QObject, so QML is unaffected. Registering it and deleting the duplicates had to happen together. Wiring it alone would have compiled cleanly and produced two independent Library states — QML reading one object while Rust published to the other — with the view simply frozen and nothing in the log. Moved with it: four properties, five invokables, three signals, the seven library-only ui() hops in main.rs, and thirty-four QML references. The Connections blocks in LibraryView, Sidebar, QueuePanel, AlbumView and PlaylistCollage retarget wholesale because every handler in them is a library signal. ArtistView's does not: it mixes onLibraryArtworkReady with onReleaseSectionReady, so it becomes two blocks — retargeting it whole would have orphaned the releases pager, and QML resolves handlers lazily enough that the discography would just have stopped loading with nothing in the log. bridge.rs is down to 389 lines and five domains. cc
This commit is contained in:
18 files changed
+65
-122
No files matched your search
@@ -35,7 +35,7 @@ fn main() {
|
|||||||
.qt_module("QuickControls2")
|
.qt_module("QuickControls2")
|
||||||
.qml_module(QmlModule {
|
.qml_module(QmlModule {
|
||||||
uri: "com.blitzfc.qbz",
|
uri: "com.blitzfc.qbz",
|
||||||
rust_files: &["src/bridge.rs", "src/session_bridge.rs", "src/shell_bridge.rs", "src/player_bridge.rs", "src/queue_bridge.rs", "src/home_bridge.rs", "src/viz_bridge.rs", "src/local_bridge.rs"],
|
rust_files: &["src/bridge.rs", "src/session_bridge.rs", "src/shell_bridge.rs", "src/player_bridge.rs", "src/queue_bridge.rs", "src/home_bridge.rs", "src/viz_bridge.rs", "src/local_bridge.rs", "src/library_bridge.rs"],
|
||||||
qml_files: &[
|
qml_files: &[
|
||||||
"qml/LoginScreen.qml",
|
"qml/LoginScreen.qml",
|
||||||
"qml/Main.qml",
|
"qml/Main.qml",
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ ApplicationWindow {
|
|||||||
QbzHome.boot()
|
QbzHome.boot()
|
||||||
QbzViz.boot()
|
QbzViz.boot()
|
||||||
QbzLocal.boot()
|
QbzLocal.boot()
|
||||||
|
QbzLibrary.boot()
|
||||||
QbzBridge.boot()
|
QbzBridge.boot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ Rectangle {
|
|||||||
|
|
||||||
function toggleFavorite() {
|
function toggleFavorite() {
|
||||||
root.isFavorite = !root.isFavorite
|
root.isFavorite = !root.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("album", root.albumId)
|
QbzLibrary.libraryToggleFavorite("album", root.albumId)
|
||||||
}
|
}
|
||||||
function togglePin() {
|
function togglePin() {
|
||||||
root.isPinned = !root.isPinned
|
root.isPinned = !root.isPinned
|
||||||
QbzBridge.togglePin("album", root.albumId, root.title, root.artist, "")
|
QbzLibrary.togglePin("album", root.albumId, root.title, root.artist, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.isPinned = !root.isPinned
|
root.isPinned = !root.isPinned
|
||||||
QbzBridge.togglePin("artist", root.item.id, root.item.title,
|
QbzLibrary.togglePin("artist", root.item.id, root.item.title,
|
||||||
root.item.subtitle || "", root.artworkUrl)
|
root.item.subtitle || "", root.artworkUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ Rectangle {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
if (root.item.playlistOwned) {
|
if (root.item.playlistOwned) {
|
||||||
root.item.isFavorite = !root.item.isFavorite
|
root.item.isFavorite = !root.item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("playlist", root.item.id)
|
QbzLibrary.libraryToggleFavorite("playlist", root.item.id)
|
||||||
} else {
|
} else {
|
||||||
root.item.playlistFollowing = !root.item.playlistFollowing
|
root.item.playlistFollowing = !root.item.playlistFollowing
|
||||||
QbzBridge.playlistSetFollowById(root.item.id, root.item.playlistFollowing)
|
QbzBridge.playlistSetFollowById(root.item.id, root.item.playlistFollowing)
|
||||||
@@ -166,7 +166,7 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.isPinned = !root.isPinned
|
root.isPinned = !root.isPinned
|
||||||
QbzBridge.togglePin("playlist", root.item.id, root.item.title,
|
QbzLibrary.togglePin("playlist", root.item.id, root.item.title,
|
||||||
root.item.subtitle || "", root.artworkUrl)
|
root.item.subtitle || "", root.artworkUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ Rectangle {
|
|||||||
else if (a === "favorite") {
|
else if (a === "favorite") {
|
||||||
if (root.item.playlistOwned) {
|
if (root.item.playlistOwned) {
|
||||||
root.item.isFavorite = !root.item.isFavorite
|
root.item.isFavorite = !root.item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("playlist", root.item.id)
|
QbzLibrary.libraryToggleFavorite("playlist", root.item.id)
|
||||||
} else {
|
} else {
|
||||||
root.item.playlistFollowing = !root.item.playlistFollowing
|
root.item.playlistFollowing = !root.item.playlistFollowing
|
||||||
QbzBridge.playlistSetFollowById(root.item.id, root.item.playlistFollowing)
|
QbzBridge.playlistSetFollowById(root.item.id, root.item.playlistFollowing)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: QbzBridge
|
target: QbzLibrary
|
||||||
// Shared with the feed's artKey-keyed emissions — ignore anything
|
// Shared with the feed's artKey-keyed emissions — ignore anything
|
||||||
// that is not one of OUR urls.
|
// that is not one of OUR urls.
|
||||||
function onLibraryArtworkReady(key, path) {
|
function onLibraryArtworkReady(key, path) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.item.isFavorite = !root.item.isFavorite
|
root.item.isFavorite = !root.item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("track", root.item.id)
|
QbzLibrary.libraryToggleFavorite("track", root.item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CardOverlayButton {
|
CardOverlayButton {
|
||||||
@@ -193,7 +193,7 @@ Rectangle {
|
|||||||
else if (a === "go-album") QbzBridge.openAlbum(root.item.albumId)
|
else if (a === "go-album") QbzBridge.openAlbum(root.item.albumId)
|
||||||
else if (a === "favorite") {
|
else if (a === "favorite") {
|
||||||
root.item.isFavorite = !root.item.isFavorite
|
root.item.isFavorite = !root.item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("track", root.item.id)
|
QbzLibrary.libraryToggleFavorite("track", root.item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +257,7 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.item.isFavorite = !root.item.isFavorite
|
root.item.isFavorite = !root.item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("track", root.item.id)
|
QbzLibrary.libraryToggleFavorite("track", root.item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +329,7 @@ Rectangle {
|
|||||||
else if (a === "go-album") QbzBridge.openAlbum(root.item.albumId)
|
else if (a === "go-album") QbzBridge.openAlbum(root.item.albumId)
|
||||||
else if (a === "favorite") {
|
else if (a === "favorite") {
|
||||||
root.item.isFavorite = !root.item.isFavorite
|
root.item.isFavorite = !root.item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("track", root.item.id)
|
QbzLibrary.libraryToggleFavorite("track", root.item.id)
|
||||||
} else if (a === "remove") root.removeRequested()
|
} else if (a === "remove") root.removeRequested()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ Rectangle {
|
|||||||
if (id !== "") QbzPlayer.enqueueTrack(id, a)
|
if (id !== "") QbzPlayer.enqueueTrack(id, a)
|
||||||
} else if (a === "album-favorite") {
|
} else if (a === "album-favorite") {
|
||||||
if (QbzPlayer.npAlbumId !== "")
|
if (QbzPlayer.npAlbumId !== "")
|
||||||
QbzBridge.libraryToggleFavorite("album", QbzPlayer.npAlbumId)
|
QbzLibrary.libraryToggleFavorite("album", QbzPlayer.npAlbumId)
|
||||||
}
|
}
|
||||||
// TODO(qt-bridge): "playlist" (add-to-playlist modal) and
|
// TODO(qt-bridge): "playlist" (add-to-playlist modal) and
|
||||||
// "mixtape" have no invokable in the Qt port yet — the rows are
|
// "mixtape" have no invokable in the Qt port yet — the rows are
|
||||||
|
|||||||
@@ -755,7 +755,7 @@ Rectangle {
|
|||||||
if (id !== "") QbzPlayer.enqueueTrack(id, a)
|
if (id !== "") QbzPlayer.enqueueTrack(id, a)
|
||||||
} else if (a === "album-favorite") {
|
} else if (a === "album-favorite") {
|
||||||
if (QbzPlayer.npAlbumId !== "")
|
if (QbzPlayer.npAlbumId !== "")
|
||||||
QbzBridge.libraryToggleFavorite("album", QbzPlayer.npAlbumId)
|
QbzLibrary.libraryToggleFavorite("album", QbzPlayer.npAlbumId)
|
||||||
}
|
}
|
||||||
// TODO(qt-bridge): "playlist" (add-to-playlist modal) and
|
// TODO(qt-bridge): "playlist" (add-to-playlist modal) and
|
||||||
// "mixtape" have no invokable in the Qt port yet — the rows are
|
// "mixtape" have no invokable in the Qt port yet — the rows are
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Rectangle {
|
|||||||
QbzTheme { id: theme }
|
QbzTheme { id: theme }
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: QbzBridge
|
target: QbzLibrary
|
||||||
function onLibraryArtworkReady(key, path) {
|
function onLibraryArtworkReady(key, path) {
|
||||||
var m = root.coverMap
|
var m = root.coverMap
|
||||||
m[key] = path
|
m[key] = path
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: QbzBridge
|
target: QbzLibrary
|
||||||
function onLibraryArtworkReady(key, path) {
|
function onLibraryArtworkReady(key, path) {
|
||||||
var m = root.coverMap
|
var m = root.coverMap
|
||||||
m[key] = path
|
m[key] = path
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: QbzBridge
|
target: QbzLibrary
|
||||||
function onLibraryArtworkReady(key, path) {
|
function onLibraryArtworkReady(key, path) {
|
||||||
var m = root.coverMap
|
var m = root.coverMap
|
||||||
m[key] = path
|
m[key] = path
|
||||||
@@ -350,7 +350,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: {
|
onClicked: {
|
||||||
header.isFavorite = !header.isFavorite
|
header.isFavorite = !header.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("album", header.id)
|
QbzLibrary.libraryToggleFavorite("album", header.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Radio / Mixtape / Info — INERT stubs (POC-NOTE:
|
// Radio / Mixtape / Info — INERT stubs (POC-NOTE:
|
||||||
@@ -710,10 +710,10 @@ Rectangle {
|
|||||||
else if (a === "queue") QbzPlayer.enqueueAlbum(header.id, "later")
|
else if (a === "queue") QbzPlayer.enqueueAlbum(header.id, "later")
|
||||||
else if (a === "favorite") {
|
else if (a === "favorite") {
|
||||||
header.isFavorite = !header.isFavorite
|
header.isFavorite = !header.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("album", header.id)
|
QbzLibrary.libraryToggleFavorite("album", header.id)
|
||||||
} else if (a === "pin") {
|
} else if (a === "pin") {
|
||||||
header.isPinned = !header.isPinned
|
header.isPinned = !header.isPinned
|
||||||
QbzBridge.togglePin("album", header.id, header.title, header.artist, header.artUrl)
|
QbzLibrary.togglePin("album", header.id, header.title, header.artist, header.artUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,13 +66,21 @@ Rectangle {
|
|||||||
return tabs
|
return tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Two blocks, not one: artwork is QbzLibrary's signal and the releases
|
||||||
|
// pager is still QbzBridge's. Retargeting a mixed block wholesale would
|
||||||
|
// silently orphan the other half — QML resolves handlers lazily, so the
|
||||||
|
// discography would just stop loading with nothing in the log.
|
||||||
Connections {
|
Connections {
|
||||||
target: QbzBridge
|
target: QbzLibrary
|
||||||
function onLibraryArtworkReady(key, path) {
|
function onLibraryArtworkReady(key, path) {
|
||||||
var m = root.coverMap
|
var m = root.coverMap
|
||||||
m[key] = path
|
m[key] = path
|
||||||
root.coverMap = Object.assign({}, m)
|
root.coverMap = Object.assign({}, m)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: QbzBridge
|
||||||
function onReleaseSectionReady(releaseType, cardsJson, hasMore) {
|
function onReleaseSectionReady(releaseType, cardsJson, hasMore) {
|
||||||
var cards = JSON.parse(cardsJson)
|
var cards = JSON.parse(cardsJson)
|
||||||
var sections = root.releaseSections
|
var sections = root.releaseSections
|
||||||
@@ -306,7 +314,7 @@ Rectangle {
|
|||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
row.isFavorite = !row.isFavorite
|
row.isFavorite = !row.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("track", row.id)
|
QbzLibrary.libraryToggleFavorite("track", row.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,7 +604,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: {
|
onClicked: {
|
||||||
artist.isFollowing = !artist.isFollowing
|
artist.isFollowing = !artist.isFollowing
|
||||||
QbzBridge.libraryToggleFavorite("artist", artist.id)
|
QbzLibrary.libraryToggleFavorite("artist", artist.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QbzCircleAction {
|
QbzCircleAction {
|
||||||
@@ -1124,7 +1132,7 @@ Rectangle {
|
|||||||
|
|
||||||
// Library feed access (the phase-5 document, parsed in LibraryView).
|
// Library feed access (the phase-5 document, parsed in LibraryView).
|
||||||
function libraryFeed() {
|
function libraryFeed() {
|
||||||
return JSON.parse(QbzBridge.libraryJson)
|
return JSON.parse(QbzLibrary.libraryJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Network sidebar (300px, surface-card + 1px left border) ---------
|
// --- Network sidebar (300px, surface-card + 1px left border) ---------
|
||||||
@@ -1385,7 +1393,7 @@ Rectangle {
|
|||||||
overflowMenu.close()
|
overflowMenu.close()
|
||||||
if (modelData.action === "pin") {
|
if (modelData.action === "pin") {
|
||||||
artist.isPinned = !artist.isPinned
|
artist.isPinned = !artist.isPinned
|
||||||
QbzBridge.togglePin("artist", artist.id, artist.name, "", artist.artUrl)
|
QbzLibrary.togglePin("artist", artist.id, artist.name, "", artist.artUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Library view — QML port of crates/qbz-ui/ui/favorites/FavoritesView.slint
|
// Library view — QML port of crates/qbz-ui/ui/favorites/FavoritesView.slint
|
||||||
// + the Library "All" mixed feed (library_all.rs semantics).
|
// + the Library "All" mixed feed (library_all.rs semantics).
|
||||||
//
|
//
|
||||||
// Data: QbzBridge.libraryJson (ONE JSON document — the full merged feed;
|
// Data: QbzLibrary.libraryJson (ONE JSON document — the full merged feed;
|
||||||
// tabs/search/sort/source-filters derive HERE in JS, measured per the
|
// tabs/search/sort/source-filters derive HERE in JS, measured per the
|
||||||
// phase brief) + libraryCountsJson (tab badges). Artwork is id-keyed
|
// phase brief) + libraryCountsJson (tab badges). Artwork is id-keyed
|
||||||
// through the libraryArtworkReady signal into `artMap` (never a
|
// through the libraryArtworkReady signal into `artMap` (never a
|
||||||
@@ -44,9 +44,9 @@ Rectangle {
|
|||||||
// ============================ state ==================================
|
// ============================ state ==================================
|
||||||
property string activeTab: "all"
|
property string activeTab: "all"
|
||||||
|
|
||||||
readonly property var counts: JSON.parse(QbzBridge.libraryCountsJson)
|
readonly property var counts: JSON.parse(QbzLibrary.libraryCountsJson)
|
||||||
// Full merged feed (parsed once per publish — timed for the report).
|
// Full merged feed (parsed once per publish — timed for the report).
|
||||||
readonly property var feed: parseFeed(QbzBridge.libraryJson)
|
readonly property var feed: parseFeed(QbzLibrary.libraryJson)
|
||||||
function parseFeed(json) {
|
function parseFeed(json) {
|
||||||
var t = Date.now()
|
var t = Date.now()
|
||||||
var f = JSON.parse(json)
|
var f = JSON.parse(json)
|
||||||
@@ -162,11 +162,11 @@ Rectangle {
|
|||||||
if (!keep[key]) { delete m[key]; changed = true }
|
if (!keep[key]) { delete m[key]; changed = true }
|
||||||
}
|
}
|
||||||
if (changed) artMap = Object.assign({}, m)
|
if (changed) artMap = Object.assign({}, m)
|
||||||
QbzBridge.libraryArtworkWindow(JSON.stringify(keys))
|
QbzLibrary.libraryArtworkWindow(JSON.stringify(keys))
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: QbzBridge
|
target: QbzLibrary
|
||||||
function onLibraryArtworkReady(key, path) {
|
function onLibraryArtworkReady(key, path) {
|
||||||
var m = root.artMap
|
var m = root.artMap
|
||||||
m[key] = path
|
m[key] = path
|
||||||
@@ -315,7 +315,7 @@ Rectangle {
|
|||||||
else if (a === "go-album") QbzBridge.openAlbum(item.albumId)
|
else if (a === "go-album") QbzBridge.openAlbum(item.albumId)
|
||||||
else if (a === "favorite") {
|
else if (a === "favorite") {
|
||||||
item.isFavorite = !item.isFavorite
|
item.isFavorite = !item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite("track", item.id)
|
QbzLibrary.libraryToggleFavorite("track", item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ Rectangle {
|
|||||||
QbzBridge.openArtist(item.id)
|
QbzBridge.openArtist(item.id)
|
||||||
} else if (a === "favorite") {
|
} else if (a === "favorite") {
|
||||||
item.isFavorite = !item.isFavorite
|
item.isFavorite = !item.isFavorite
|
||||||
QbzBridge.libraryToggleFavorite(item.kind, item.id)
|
QbzLibrary.libraryToggleFavorite(item.kind, item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -871,12 +871,12 @@ Rectangle {
|
|||||||
QbzSpinner {
|
QbzSpinner {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
size: 36
|
size: 36
|
||||||
visible: QbzBridge.libraryLoading
|
visible: QbzLibrary.libraryLoading
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error + retry.
|
// Error + retry.
|
||||||
Column {
|
Column {
|
||||||
visible: !QbzBridge.libraryLoading && QbzBridge.libraryError !== ""
|
visible: !QbzLibrary.libraryLoading && QbzLibrary.libraryError !== ""
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 10
|
spacing: 10
|
||||||
Text {
|
Text {
|
||||||
@@ -886,7 +886,7 @@ Rectangle {
|
|||||||
font.weight: theme.weightSemibold
|
font.weight: theme.weightSemibold
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: QbzBridge.libraryError
|
text: QbzLibrary.libraryError
|
||||||
color: theme.textMuted
|
color: theme.textMuted
|
||||||
font.pixelSize: theme.fontLegal
|
font.pixelSize: theme.fontLegal
|
||||||
}
|
}
|
||||||
@@ -910,7 +910,7 @@ Rectangle {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: QbzBridge.reloadLibrary()
|
onClicked: QbzLibrary.reloadLibrary()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -922,7 +922,7 @@ Rectangle {
|
|||||||
anchors.leftMargin: 32
|
anchors.leftMargin: 32
|
||||||
anchors.rightMargin: 32
|
anchors.rightMargin: 32
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
visible: !QbzBridge.libraryLoading && QbzBridge.libraryError === ""
|
visible: !QbzLibrary.libraryLoading && QbzLibrary.libraryError === ""
|
||||||
&& (root.activeTab !== "all" || root.viewMode === "grid")
|
&& (root.activeTab !== "all" || root.viewMode === "grid")
|
||||||
&& root.activeTab !== "tracks"
|
&& root.activeTab !== "tracks"
|
||||||
cellWidth: 220
|
cellWidth: 220
|
||||||
@@ -1007,7 +1007,7 @@ Rectangle {
|
|||||||
anchors.leftMargin: 32
|
anchors.leftMargin: 32
|
||||||
anchors.rightMargin: 32
|
anchors.rightMargin: 32
|
||||||
anchors.topMargin: 10
|
anchors.topMargin: 10
|
||||||
visible: !QbzBridge.libraryLoading && QbzBridge.libraryError === ""
|
visible: !QbzLibrary.libraryLoading && QbzLibrary.libraryError === ""
|
||||||
&& ((root.activeTab === "all" && root.viewMode === "list")
|
&& ((root.activeTab === "all" && root.viewMode === "list")
|
||||||
|| root.activeTab === "tracks")
|
|| root.activeTab === "tracks")
|
||||||
cacheBuffer: 44 * 10
|
cacheBuffer: 44 * 10
|
||||||
|
|||||||
@@ -24,16 +24,6 @@ pub mod qbz_bridge {
|
|||||||
#[qobject]
|
#[qobject]
|
||||||
#[qml_element]
|
#[qml_element]
|
||||||
#[qml_singleton]
|
#[qml_singleton]
|
||||||
// --- Library view --------------------------------------------------
|
|
||||||
#[qproperty(bool, library_loading)]
|
|
||||||
#[qproperty(QString, library_error)]
|
|
||||||
// One JSON document: the FULL merged feed (tabs/search/sort/source
|
|
||||||
// filters derive QML-side from the parsed array — see library_qt.rs
|
|
||||||
// for the rationale + the measured parse cost).
|
|
||||||
#[qproperty(QString, library_json)]
|
|
||||||
// {tracks, albums, artists, playlists, labels, all} — tab badges.
|
|
||||||
#[qproperty(QString, library_counts_json)]
|
|
||||||
|
|
||||||
// --- Album / Artist detail views (phase 8) -------------------------
|
// --- Album / Artist detail views (phase 8) -------------------------
|
||||||
#[qproperty(bool, album_loading)]
|
#[qproperty(bool, album_loading)]
|
||||||
#[qproperty(QString, album_json)]
|
#[qproperty(QString, album_json)]
|
||||||
@@ -75,39 +65,6 @@ pub mod qbz_bridge {
|
|||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
fn boot(self: Pin<&mut QbzBridge>);
|
fn boot(self: Pin<&mut QbzBridge>);
|
||||||
|
|
||||||
/// Library retry button / manual refresh.
|
|
||||||
#[qinvokable]
|
|
||||||
fn reload_library(self: Pin<&mut QbzBridge>);
|
|
||||||
|
|
||||||
/// Windowed artwork: the grid/list reports the mounted window as a
|
|
||||||
/// JSON array of artKeys (tab views filter the feed, so raw indices
|
|
||||||
/// don't map); Rust dispatches covers for those keys (id-keyed via
|
|
||||||
/// `libraryArtworkReady`).
|
|
||||||
#[qinvokable]
|
|
||||||
fn library_artwork_window(self: Pin<&mut QbzBridge>, keys_json: QString);
|
|
||||||
|
|
||||||
/// Card heart: toggle favorite (Qobuz API or the local store,
|
|
||||||
/// routed by id shape); the result arrives via
|
|
||||||
/// `libraryFavoriteChanged`.
|
|
||||||
#[qinvokable]
|
|
||||||
fn library_toggle_favorite(self: Pin<&mut QbzBridge>, kind: QString, id: QString);
|
|
||||||
|
|
||||||
/// Emitted when a dispatched cover lands on disk (id-keyed —
|
|
||||||
/// `{kind}:{id}`); QML updates its artwork map.
|
|
||||||
#[qsignal]
|
|
||||||
fn library_artwork_ready(self: Pin<&mut QbzBridge>, key: QString, path: QString);
|
|
||||||
|
|
||||||
/// Emitted after a heart toggle (optimistic flip + rollback).
|
|
||||||
#[qsignal]
|
|
||||||
fn library_favorite_changed(self: Pin<&mut QbzBridge>, key: QString, value: bool);
|
|
||||||
|
|
||||||
/// AlbumCard pin badge: toggle pin (album/artist/playlist).
|
|
||||||
#[qinvokable]
|
|
||||||
fn toggle_pin(self: Pin<&mut QbzBridge>, kind: QString, id: QString, title: QString, subtitle: QString, artwork_url: QString);
|
|
||||||
/// Emitted after a pin toggle (`{kind}:{id}` key like artKey).
|
|
||||||
#[qsignal]
|
|
||||||
fn pin_changed(self: Pin<&mut QbzBridge>, key: QString, value: bool);
|
|
||||||
|
|
||||||
/// Open the album detail view (pushes "album" on the nav stack).
|
/// Open the album detail view (pushes "album" on the nav stack).
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
fn open_album(self: Pin<&mut QbzBridge>, album_id: QString);
|
fn open_album(self: Pin<&mut QbzBridge>, album_id: QString);
|
||||||
@@ -237,10 +194,6 @@ use cxx_qt_lib::QString;
|
|||||||
/// generated `set_*` methods on the Qt thread; the struct itself is plain
|
/// generated `set_*` methods on the Qt thread; the struct itself is plain
|
||||||
/// storage (as required by cxx-qt's Default-constructed qobjects).
|
/// storage (as required by cxx-qt's Default-constructed qobjects).
|
||||||
pub struct QbzBridgeRust {
|
pub struct QbzBridgeRust {
|
||||||
library_loading: bool,
|
|
||||||
library_error: QString,
|
|
||||||
library_json: QString,
|
|
||||||
library_counts_json: QString,
|
|
||||||
album_loading: bool,
|
album_loading: bool,
|
||||||
album_json: QString,
|
album_json: QString,
|
||||||
artist_loading: bool,
|
artist_loading: bool,
|
||||||
@@ -260,10 +213,6 @@ pub struct QbzBridgeRust {
|
|||||||
impl Default for QbzBridgeRust {
|
impl Default for QbzBridgeRust {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
library_loading: false,
|
|
||||||
library_error: QString::default(),
|
|
||||||
library_json: QString::from("[]"),
|
|
||||||
library_counts_json: QString::from("{}"),
|
|
||||||
album_loading: false,
|
album_loading: false,
|
||||||
album_json: QString::from("{}"),
|
album_json: QString::from("{}"),
|
||||||
artist_loading: false,
|
artist_loading: false,
|
||||||
@@ -287,28 +236,6 @@ impl qbz_bridge::QbzBridge {
|
|||||||
crate::register_qt_thread(self.qt_thread());
|
crate::register_qt_thread(self.qt_thread());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload_library(self: Pin<&mut Self>) {
|
|
||||||
crate::reload_library();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn library_artwork_window(self: Pin<&mut Self>, keys_json: QString) {
|
|
||||||
crate::library_artwork_window(keys_json.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn library_toggle_favorite(self: Pin<&mut Self>, kind: QString, id: QString) {
|
|
||||||
crate::library_toggle_favorite(kind.to_string(), id.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_pin(self: Pin<&mut Self>, kind: QString, id: QString, title: QString, subtitle: QString, artwork_url: QString) {
|
|
||||||
crate::toggle_pin(
|
|
||||||
kind.to_string(),
|
|
||||||
id.to_string(),
|
|
||||||
title.to_string(),
|
|
||||||
subtitle.to_string(),
|
|
||||||
artwork_url.to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_album(self: Pin<&mut Self>, album_id: QString) {
|
pub fn open_album(self: Pin<&mut Self>, album_id: QString) {
|
||||||
crate::open_album(album_id.to_string());
|
crate::open_album(album_id.to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
//! QbzLibrary — Library view domain bridge (phase 23 split of the QbzBridge
|
//! QbzLibrary — Library view domain bridge.
|
||||||
|
//!
|
||||||
|
//! The module is `qbz_library_bridge`, NOT `qbz_library`: this crate depends
|
||||||
|
//! on the `qbz-library` crate, whose Rust name is `qbz_library`, and a bridge
|
||||||
|
//! module by that name is ambiguous at every use site. The QML type name comes
|
||||||
|
//! from the QObject (`QbzLibrary`), not from the module, so QML is unaffected.
|
||||||
|
//! Original note: Library view domain bridge (phase 23 split of the QbzBridge
|
||||||
//! God-object; the pattern is documented in main.rs). Props: the merged-feed
|
//! God-object; the pattern is documented in main.rs). Props: the merged-feed
|
||||||
//! document + counts + loading/error. Invokables: reload, windowed artwork
|
//! document + counts + loading/error. Invokables: reload, windowed artwork
|
||||||
//! dispatch, favorite/pin toggles. Signals: artwork-ready / favorite-changed
|
//! dispatch, favorite/pin toggles. Signals: artwork-ready / favorite-changed
|
||||||
@@ -12,7 +18,7 @@ use cxx_qt::Threading as _;
|
|||||||
use cxx_qt_lib::QString;
|
use cxx_qt_lib::QString;
|
||||||
|
|
||||||
#[cxx_qt::bridge]
|
#[cxx_qt::bridge]
|
||||||
pub mod qbz_library {
|
pub mod qbz_library_bridge {
|
||||||
extern "C++" {
|
extern "C++" {
|
||||||
include!("cxx-qt-lib/qstring.h");
|
include!("cxx-qt-lib/qstring.h");
|
||||||
type QString = cxx_qt_lib::QString;
|
type QString = cxx_qt_lib::QString;
|
||||||
@@ -77,7 +83,7 @@ pub mod qbz_library {
|
|||||||
impl cxx_qt::Threading for QbzLibrary {}
|
impl cxx_qt::Threading for QbzLibrary {}
|
||||||
}
|
}
|
||||||
|
|
||||||
use qbz_library::QbzLibrary;
|
use qbz_library_bridge::QbzLibrary;
|
||||||
|
|
||||||
/// Rust side of the library bridge (plain storage, phase-1 pattern).
|
/// Rust side of the library bridge (plain storage, phase-1 pattern).
|
||||||
pub struct QbzLibraryRust {
|
pub struct QbzLibraryRust {
|
||||||
@@ -112,7 +118,7 @@ pub(crate) fn ui(f: impl FnOnce(Pin<&mut QbzLibrary>) + Send + 'static) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl qbz_library::QbzLibrary {
|
impl qbz_library_bridge::QbzLibrary {
|
||||||
pub fn boot(self: Pin<&mut Self>) {
|
pub fn boot(self: Pin<&mut Self>) {
|
||||||
if QT_THREAD.set(self.qt_thread()).is_err() {
|
if QT_THREAD.set(self.qt_thread()).is_err() {
|
||||||
log::warn!("[qbz-qt] library Qt thread already registered");
|
log::warn!("[qbz-qt] library Qt thread already registered");
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ mod queue_bridge;
|
|||||||
mod home_bridge;
|
mod home_bridge;
|
||||||
mod viz_bridge;
|
mod viz_bridge;
|
||||||
mod local_bridge;
|
mod local_bridge;
|
||||||
|
mod library_bridge;
|
||||||
mod artwork_qt;
|
mod artwork_qt;
|
||||||
mod bridge;
|
mod bridge;
|
||||||
mod home_qt;
|
mod home_qt;
|
||||||
@@ -282,7 +283,7 @@ pub(crate) fn do_logout() {
|
|||||||
b.as_mut().set_home_error(QString::from(""));
|
b.as_mut().set_home_error(QString::from(""));
|
||||||
b.as_mut().set_home_loading(false);
|
b.as_mut().set_home_loading(false);
|
||||||
});
|
});
|
||||||
ui(|mut b| {
|
library_bridge::ui(|mut b| {
|
||||||
b.as_mut().set_library_json(QString::from("[]"));
|
b.as_mut().set_library_json(QString::from("[]"));
|
||||||
b.as_mut().set_library_counts_json(QString::from("{}"));
|
b.as_mut().set_library_counts_json(QString::from("{}"));
|
||||||
b.as_mut().set_library_error(QString::from(""));
|
b.as_mut().set_library_error(QString::from(""));
|
||||||
@@ -516,7 +517,7 @@ pub(crate) fn enqueue_album(album_id: String, mode: String) {
|
|||||||
pub(crate) fn toggle_pin(kind: String, id: String, title: String, subtitle: String, artwork_url: String) {
|
pub(crate) fn toggle_pin(kind: String, id: String, title: String, subtitle: String, artwork_url: String) {
|
||||||
if let Some(value) = sidebar_qt::toggle_pin(&kind, &id, &title, &subtitle, &artwork_url) {
|
if let Some(value) = sidebar_qt::toggle_pin(&kind, &id, &title, &subtitle, &artwork_url) {
|
||||||
let key = format!("{kind}:{id}");
|
let key = format!("{kind}:{id}");
|
||||||
ui(move |mut b| {
|
library_bridge::ui(move |mut b| {
|
||||||
b.as_mut().pin_changed(QString::from(key.as_str()), value);
|
b.as_mut().pin_changed(QString::from(key.as_str()), value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1183,7 +1184,7 @@ pub(crate) fn reload_library() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log_rss("library load start");
|
log_rss("library load start");
|
||||||
ui(|mut b| {
|
library_bridge::ui(|mut b| {
|
||||||
b.as_mut().set_library_loading(true);
|
b.as_mut().set_library_loading(true);
|
||||||
b.as_mut().set_library_error(QString::from(""));
|
b.as_mut().set_library_error(QString::from(""));
|
||||||
});
|
});
|
||||||
@@ -1209,7 +1210,7 @@ pub(crate) fn reload_library() {
|
|||||||
"[qbz-qt][perf] library load total: {:?} ({total} items)",
|
"[qbz-qt][perf] library load total: {:?} ({total} items)",
|
||||||
t.elapsed(),
|
t.elapsed(),
|
||||||
);
|
);
|
||||||
ui(move |mut b| {
|
library_bridge::ui(move |mut b| {
|
||||||
b.as_mut().set_library_json(QString::from(feed_json.as_str()));
|
b.as_mut().set_library_json(QString::from(feed_json.as_str()));
|
||||||
b.as_mut()
|
b.as_mut()
|
||||||
.set_library_counts_json(QString::from(counts_json.as_str()));
|
.set_library_counts_json(QString::from(counts_json.as_str()));
|
||||||
@@ -1219,7 +1220,7 @@ pub(crate) fn reload_library() {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("[qbz-qt] library load failed: {e}");
|
log::warn!("[qbz-qt] library load failed: {e}");
|
||||||
ui(move |mut b| {
|
library_bridge::ui(move |mut b| {
|
||||||
b.as_mut().set_library_error(QString::from(e.as_str()));
|
b.as_mut().set_library_error(QString::from(e.as_str()));
|
||||||
b.as_mut().set_library_loading(false);
|
b.as_mut().set_library_loading(false);
|
||||||
});
|
});
|
||||||
@@ -1285,7 +1286,7 @@ pub(crate) fn library_artwork_window(keys_json: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_library_artwork(key: String, path: String) {
|
fn emit_library_artwork(key: String, path: String) {
|
||||||
ui(move |mut b| {
|
library_bridge::ui(move |mut b| {
|
||||||
b.as_mut()
|
b.as_mut()
|
||||||
.library_artwork_ready(QString::from(key.as_str()), QString::from(path.as_str()));
|
.library_artwork_ready(QString::from(key.as_str()), QString::from(path.as_str()));
|
||||||
});
|
});
|
||||||
@@ -1298,7 +1299,7 @@ pub(crate) fn library_toggle_favorite(kind: String, id: String) {
|
|||||||
let runtime = app();
|
let runtime = app();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
if let Some(value) = library_qt::toggle_favorite(&runtime, &kind, &id).await {
|
if let Some(value) = library_qt::toggle_favorite(&runtime, &kind, &id).await {
|
||||||
ui(move |mut b| {
|
library_bridge::ui(move |mut b| {
|
||||||
b.as_mut()
|
b.as_mut()
|
||||||
.library_favorite_changed(QString::from(key.as_str()), value);
|
.library_favorite_changed(QString::from(key.as_str()), value);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user