diff --git a/crates/qbz-qt/qml/settings/LibraryFolderTable.qml b/crates/qbz-qt/qml/settings/LibraryFolderTable.qml index 08dc5c00e..6375399b2 100644 --- a/crates/qbz-qt/qml/settings/LibraryFolderTable.qml +++ b/crates/qbz-qt/qml/settings/LibraryFolderTable.qml @@ -42,10 +42,23 @@ Column { }) } function isSelected(id) { return selectedIds.indexOf(id) >= 0 } - function toggleSelected(id) { - const next = selectedIds.slice() - const at = next.indexOf(id) - if (at >= 0) next.splice(at, 1); else next.push(id) + /// A plain or Ctrl click toggles one folder; Shift adds the range from the + /// last plain click over the rows SHOWN (the filter is a view filter) — + /// the rule every multi-select list shares (controls/SelectionModel.qml). + SelectionModel { id: folderSel } + function toggleSelected(id, mods) { + const rows = root.shown() + const current = {} + for (let i = 0; i < selectedIds.length; i++) + current[String(selectedIds[i])] = true + const picked = folderSel.next(current, id, rows, + mods === undefined ? Qt.NoModifier : mods) + // Back to the ids themselves (the removal action takes them as + // numbers), keeping earlier picks the filter currently hides. + const next = selectedIds.filter(function (sid) { return picked[String(sid)] === true }) + for (let j = 0; j < rows.length; j++) + if (picked[String(rows[j].id)] === true && next.indexOf(rows[j].id) < 0) + next.push(rows[j].id) selectedIds = next } function scanLabel(ts) { @@ -118,6 +131,7 @@ Column { QbzBridge.settingsString("library-remove-folders", JSON.stringify(root.selectedIds)) root.selectedIds = [] + folderSel.anchorId = "" } } } @@ -279,7 +293,9 @@ Column { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: root.toggleSelected(folderRow.modelData.id) + onClicked: function (mouse) { + root.toggleSelected(folderRow.modelData.id, mouse.modifiers) + } } Row { @@ -292,7 +308,9 @@ Column { width: root.kioskHost ? 44 : 20 anchors.verticalCenter: parent.verticalCenter checked: root.isSelected(folderRow.modelData.id) - onToggled: root.toggleSelected(folderRow.modelData.id) + onToggled: function (mods) { + root.toggleSelected(folderRow.modelData.id, mods) + } } // FOLDER: type glyph + name. Row { diff --git a/scripts/qml-tests/tst_multi_select.qml b/scripts/qml-tests/tst_multi_select.qml index d63370c59..7b353011a 100644 --- a/scripts/qml-tests/tst_multi_select.qml +++ b/scripts/qml-tests/tst_multi_select.qml @@ -13,6 +13,7 @@ import "../../crates/qbz-qt/qml/cards" import "../../crates/qbz-qt/qml/views" as Views import "../../crates/qbz-qt/qml/views/local" as Local import "../../crates/qbz-qt/qml/views/myqbz" as MyQbz +import "../../crates/qbz-qt/qml/settings" as Settings Item { id: root @@ -156,6 +157,18 @@ Item { view: railView } + // ---- Settings > Local Library folders --------------------------------------- + Settings.LibraryFolderTable { + id: folderTable + x: 0; y: 880 + width: 1200 + lib: ({ "folders": [1, 2, 3, 4, 5].map(function (n) { + return { "id": n, "displayName": "Music " + n, "path": "/music/" + n, + "isNetwork": false, "enabled": true, "accessible": true, + "status": "active", "lastScan": 0 } + }) }) + } + TestCase { name: "MultiSelect" when: windowShown @@ -225,6 +238,7 @@ Item { QbzLocal.treeCalls = [] railView.opened = [] railView.treeSelectMode = true + folderTable.selectedIds = [] } // A point of the row with nothing drawn over it but the body: the @@ -376,6 +390,15 @@ Item { clickOn(railCheck("/m/b"), 6, 6, Qt.ShiftModifier) compare(QbzLocal.treeCalls[3], "folder:/m/b") } + + function test_settings_folder_table_shift_range() { + var first = textItem(folderTable, "Music 1") + clickOn(first, 10, first.height / 2) + var fourth = textItem(folderTable, "Music 4") + clickOn(fourth, 10, fourth.height / 2, Qt.ShiftModifier) + compare(folderTable.selectedIds.slice().sort().join(","), "1,2,3,4") + verify(folderTable.isSelected(2), "the numeric ids stay numbers") + } } // ---- the selection rule itself ---------------------------------------------