From b63264d132a6d86c2bd4597197f6c52000d41f11 Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Mon, 14 Sep 2026 10:31:54 -0600 Subject: [PATCH] fix(qt): Ctrl+A and Escape reach the offline manager, and Escape leaves mix select mode The AppShell hotkey seam duck-types selectAll, exitMultiSelectMode and multiSelectOn on the mounted view. The offline manager exposed none of them, so Ctrl+A did nothing there although the Slint ticked every track, and MixView lacked multiSelectOn, so Escape never left its select mode. The seam's comment, which still listed Artist, Playlist, Mix and Label as views without multi-select, now names the views that implement it. cc --- crates/qbz-qt/qml/shell/AppShell.qml | 9 +++++---- crates/qbz-qt/qml/views/MixView.qml | 5 ++++- crates/qbz-qt/qml/views/OfflineManagerView.qml | 12 ++++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/crates/qbz-qt/qml/shell/AppShell.qml b/crates/qbz-qt/qml/shell/AppShell.qml index 9f9806163..885c04551 100644 --- a/crates/qbz-qt/qml/shell/AppShell.qml +++ b/crates/qbz-qt/qml/shell/AppShell.qml @@ -244,10 +244,11 @@ Rectangle { // in-view QML JS (library_bulk.rs:8: "select-all / clear never reach // Rust"), so the QbzShell signals route to the mounted view's // duck-typed interface: selectAll() / exitMultiSelectMode() / - // multiSelectOn. Implemented by LibraryView, LocalLibraryView and - // LocalAlbumView (MyQbzDetailView is EXIT-ONLY — its selection lives in - // Rust with no select-all arm); views without multi-select (Artist, - // Playlist, Mix, Label, Offline) match nothing — PARITY-DEBT (K4). + // multiSelectOn. Implemented by every view with a multi-select list: + // Library, Local Library, Local Album, Album, Artist, Playlist, Mix, + // Label and the Offline manager. MyQbzDetailView is EXIT-ONLY — its + // selection lives in Rust and its bar has no select-all arm (the Slint + // never had one there either). Connections { target: QbzShell function onSelectAllRequested() { diff --git a/crates/qbz-qt/qml/views/MixView.qml b/crates/qbz-qt/qml/views/MixView.qml index 294fbd1dd..a936422f3 100644 --- a/crates/qbz-qt/qml/views/MixView.qml +++ b/crates/qbz-qt/qml/views/MixView.qml @@ -124,7 +124,10 @@ Rectangle { if (action !== "add-to-playlist" && action !== "add-to-mixtape") root.selected = ({}) } - // Ctrl+A / Escape hotkey seam (AppShell duck-types these). + // Ctrl+A / Escape hotkey seam (AppShell duck-types these). Without + // `multiSelectOn` the Escape stack never saw this view in select mode, so + // Escape did not leave it here while it did on every other track list. + readonly property bool multiSelectOn: root.multiSelect function selectAll() { if (!root.multiSelect) root.setMultiSelect(true) root.bulkAction("select-all") diff --git a/crates/qbz-qt/qml/views/OfflineManagerView.qml b/crates/qbz-qt/qml/views/OfflineManagerView.qml index 1c71de716..952f80d15 100644 --- a/crates/qbz-qt/qml/views/OfflineManagerView.qml +++ b/crates/qbz-qt/qml/views/OfflineManagerView.qml @@ -119,6 +119,18 @@ Rectangle { mods === undefined ? Qt.NoModifier : mods) } + // Ctrl+A / Escape hotkey seam (AppShell duck-types these). The selection + // here is always on (no select mode), so Ctrl+A ticks every track — as + // the Slint's select_all_active_surface did for this view — and Escape + // drops a non-empty selection. + readonly property bool multiSelectOn: Object.keys(root.selected).length > 0 + function selectAll() { + root.bulkAction("select-all") + } + function exitMultiSelectMode() { + root.bulkAction("clear") + } + function bulkAction(id) { if (id === "clear") { root.selected = ({})