[utilities/keepsecret] src: WIP: Add multi-select with Ctrl+click and Shift+click
Roshani Kumari <[email protected]> Tue, 4 Aug 2026 13:48:02 +0000 (UTC)
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit f3d7322f61a08c4576b440f5b3a4d54b6bc9d50c by Roshani Kumari.
Committed on 04/08/2026 at 13:39.
Pushed by roshani into branch 'master'.
WIP: Add multi-select with Ctrl+click and Shift+click
M +8 -0 src/collectionmodel.cpp
M +1 -0 src/collectionmodel.h
M +93 -17 src/qml/CollectionContentsPage.qml
https://invent.kde.org/utilities/keepsecret/-/commit/f3d7322f61a08c4576b440f5b3a4d54b6bc9d50c
diff --git a/src/collectionmodel.cpp b/src/collectionmodel.cpp
index 9da47ba..02b5dff 100644
--- a/src/collectionmodel.cpp
+++ b/src/collectionmodel.cpp
@@ -306,4 +306,12 @@ QVariantList CollectionModel::exportItems()
return result;
}
+QString CollectionModel::dbusPathAt(int row) const
+{
+ if (row < 0 || row >= m_items.count()) {
+ return QString();
+ }
+ return m_items.at(row).dbusPath;
+}
+
#include "moc_collectionmodel.cpp"
diff --git a/src/collectionmodel.h b/src/collectionmodel.h
index fdf1f05..3b5c0fb 100644
--- a/src/collectionmodel.h
+++ b/src/collectionmodel.h
@@ -37,6 +37,7 @@ public:
Q_INVOKABLE void lock();
Q_INVOKABLE void unlock();
+ Q_INVOKABLE QString dbusPathAt(int row) const;
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
diff --git a/src/qml/CollectionContentsPage.qml b/src/qml/CollectionContentsPage.qml
index 3e5a059..ff9f785 100644
--- a/src/qml/CollectionContentsPage.qml
+++ b/src/qml/CollectionContentsPage.qml
@@ -15,6 +15,9 @@ Kirigami.ScrollablePage {
id: page
property alias currentEntry: view.currentIndex
+ property var selectedIndices: []
+ property int lastSelectedIndex: -1
+ property int selectedCount: 0
title: App.collectionModel.collectionName
@@ -94,6 +97,36 @@ Kirigami.ScrollablePage {
AC.ActionCollection.action: "export-wallet"
onTriggered: exportDialog.open()
},
+ Kirigami.Action {
+ id: deleteSelectedAction
+ text: i18nc("@action:button Delete selected secrets", "Delete Selected")
+ icon.name: "delete-symbolic"
+ displayHint: Kirigami.DisplayHint.AlwaysHide
+ visible: true
+ enabled: page.selectedCount > 1
+ onTriggered: {
+ console.log("Delete Selected triggered, count:", page.selectedIndices.length)
+ showDeleteDialog(
+ i18nc("@title:window", "Delete Secrets"),
+ i18nc("@label", "Are you sure you want to delete %1 items?", page.selectedIndices.length),
+ i18nc("@action:check", "I understand that the items will be permanently deleted"),
+ () => {
+ const indices = [...page.selectedIndices]
+ // First collect all dbus paths
+ console.log("Selected indices:", JSON.stringify(indices))
+ const paths = indices.map(idx => App.collectionModel.dbusPathAt(idx)).filter(p => p)
+ // Then delete all
+ console.log("Paths to delete:", JSON.stringify(paths))
+ paths.forEach(dbusPath => {
+ console.log("Deleting:", dbusPath)
+ App.secretItemForContextMenu.loadItem(App.collectionModel.collectionPath, dbusPath)
+ App.secretItemForContextMenu.deleteItem()
+ })
+ page.selectedIndices = []
+ }
+ );
+ }
+ },
Kirigami.Action {
text: i18nc("@action:inmenu", "Import")
icon.name: "document-import"
@@ -306,27 +339,57 @@ Kirigami.ScrollablePage {
required property var model
required property int index
width: view.width
- // FIXME: this imitates an item with the space for the icon even if there is none, there should be something to do that more cleanly
leftPadding: Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.largeSpacing * 2
text: model.display
- highlighted: view.currentIndex == index
-
- function click() {
- if (contextMenu.visible) {
- return;
+ highlighted: view.currentIndex == index || page.selectedIndices.indexOf(index) !== -1
+
+ TapHandler {
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ acceptedModifiers: Qt.NoModifier
+ onTapped: {
+ if (contextMenu.visible) return
+ page.selectedIndices = []
+ page.selectedCount = 0
+ page.lastSelectedIndex = index
+ view.currentIndex = index
+ App.secretItem.loadItem(App.collectionModel.collectionPath, model.dbusPath)
+ view.forceActiveFocus()
}
- view.currentIndex = index
- App.secretItem.loadItem(App.collectionModel.collectionPath, model.dbusPath);
- view.forceActiveFocus();
}
- onClicked: click()
- Keys.onPressed: (event) => {
- if (contextMenu.visible) {
- return;
+ TapHandler {
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ acceptedModifiers: Qt.ControlModifier
+ onTapped: {
+ if (contextMenu.visible) return
+ let newSelection = [...page.selectedIndices]
+ const idx = newSelection.indexOf(index)
+ if (idx === -1) {
+ newSelection.push(index)
+ } else {
+ newSelection.splice(idx, 1)
}
- if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) {
- delegate.click();
+ page.selectedIndices = newSelection
+ page.selectedCount = page.selectedIndices.length
+ page.lastSelectedIndex = index
+ console.log("selectedCount:", page.selectedCount)
+ }
+ }
+
+ TapHandler {
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ acceptedModifiers: Qt.ShiftModifier
+ onTapped: {
+ if (contextMenu.visible) return
+ if (page.lastSelectedIndex === -1) return
+ const start = Math.min(page.lastSelectedIndex, index)
+ const end = Math.max(page.lastSelectedIndex, index)
+ let newSelection = []
+ for (let i = start; i <= end; i++) {
+ newSelection.push(i)
+ }
+ page.selectedIndices = newSelection
+ page.selectedCount = page.selectedIndices.length
}
}
@@ -335,11 +398,13 @@ Kirigami.ScrollablePage {
acceptedButtons: Qt.RightButton
onPressedChanged: {
if (pressed) {
- contextMenu.model = model
- contextMenu.popup(delegate)
+ contextMenu.model = model
+ contextMenu.popup(delegate)
}
}
}
+
+ // Long press (touch)
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: {
@@ -347,6 +412,17 @@ Kirigami.ScrollablePage {
contextMenu.popup(delegate)
}
}
+
+ Keys.onPressed: (event) => {
+ if (contextMenu.visible) return
+ if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) {
+ page.selectedIndices = []
+ page.selectedCount = 0
+ view.currentIndex = index
+ App.secretItem.loadItem(App.collectionModel.collectionPath, model.dbusPath)
+ view.forceActiveFocus()
+ }
+ }
}
Kirigami.PlaceholderMessage {