diff --git a/crates/qbz-qt/qml/views/OfflineManagerView.qml b/crates/qbz-qt/qml/views/OfflineManagerView.qml index adb303195..1c71de716 100644 --- a/crates/qbz-qt/qml/views/OfflineManagerView.qml +++ b/crates/qbz-qt/qml/views/OfflineManagerView.qml @@ -107,18 +107,22 @@ Rectangle { var p = root.livePatch[row.trackId] return (row.kind === "track" && p !== undefined) ? p.status : row.status } - function toggleSelect(trackId) { - var s = Object.assign({}, root.selected) - if (s[trackId] === true) - delete s[trackId] - else - s[trackId] = true - root.selected = s + /// The rule every multi-select list shares (controls/SelectionModel.qml): + /// a plain or Ctrl click toggles one track, Shift adds the range from the + /// last one — over the TRACK rows as drawn, never the album headers + /// between them. The Slint had this surface on its Shift-range table + /// (selection.rs SURFACE_OFFLINE); the port left it toggle-only. + SelectionModel { id: offlineSel; idKey: "trackId" } + function toggleSelect(trackId, mods) { + var tracks = root.rows.filter(function (row) { return row.kind === "track" }) + root.selected = offlineSel.next(root.selected, trackId, tracks, + mods === undefined ? Qt.NoModifier : mods) } function bulkAction(id) { if (id === "clear") { root.selected = ({}) + offlineSel.anchorId = "" return } if (id === "select-all") { @@ -507,8 +511,19 @@ Rectangle { hoverEnabled: true cursorShape: rowItem.isAlbum ? Qt.ArrowCursor : Qt.PointingHandCursor - onClicked: if (!rowItem.isAlbum) - QbzOffline.playTrack(rowItem.modelData.trackId) + // A plain click plays; a Shift or Ctrl click is + // a selection gesture, as in any file list, so + // it ticks (or ranges) instead of starting + // playback. + onClicked: function (mouse) { + if (rowItem.isAlbum) + return + if ((mouse.modifiers & (Qt.ShiftModifier | Qt.ControlModifier + | Qt.MetaModifier)) !== 0) + root.toggleSelect(rowItem.modelData.trackId, mouse.modifiers) + else + QbzOffline.playTrack(rowItem.modelData.trackId) + } } Row { @@ -526,7 +541,9 @@ Rectangle { anchors.centerIn: parent visible: !rowItem.isAlbum checked: rowItem.checked - onToggled: root.toggleSelect(rowItem.modelData.trackId) + onToggled: function (mods) { + root.toggleSelect(rowItem.modelData.trackId, mods) + } } }