[graphics/koko/release/26.08] src: Disallow selection and file actions when presenting collections

Oliver Beard <[email protected]> Tue, 4 Aug 2026 13:30:43 +0000 (UTC)
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit d5cf4d6dc7fe07c6eeaa61162ac489881cfe8b9c by Oliver Beard.
Committed on 04/08/2026 at 13:25.
Pushed by olib into branch 'release/26.08'.

Disallow selection and file actions when presenting collections

Collections are virtual folders. Their URL is reported so as to show a preview, but corresponds to an actual file. That file shouldn't be acted upon for file actions nor is selection relevant.

M  +5    -0    src/models/abstractgallerymodel.cpp
M  +4    -0    src/models/abstractgallerymodel.h
M  +6    -0    src/models/gallerylocationmodel.cpp
M  +2    -0    src/models/gallerylocationmodel.h
M  +6    -0    src/models/gallerytagsmodel.cpp
M  +2    -0    src/models/gallerytagsmodel.h
M  +6    -0    src/models/gallerytimemodel.cpp
M  +2    -0    src/models/gallerytimemodel.h
M  +28   -12   src/qml/gallery/GalleryPage.qml

https://invent.kde.org/graphics/koko/-/commit/d5cf4d6dc7fe07c6eeaa61162ac489881cfe8b9c

diff --git a/src/models/abstractgallerymodel.cpp b/src/models/abstractgallerymodel.cpp
index afa74806..72dc7f8b 100644
--- a/src/models/abstractgallerymodel.cpp
+++ b/src/models/abstractgallerymodel.cpp
@@ -17,6 +17,11 @@ AbstractGalleryModel::Status AbstractGalleryModel::status() const
     return Loaded;
 }
 
+bool AbstractGalleryModel::showingCollections() const
+{
+    return false;
+}
+
 QHash<int, QByteArray> AbstractGalleryModel::roleNames() const
 {
     QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
diff --git a/src/models/abstractgallerymodel.h b/src/models/abstractgallerymodel.h
index edc16f46..dd96ff2a 100644
--- a/src/models/abstractgallerymodel.h
+++ b/src/models/abstractgallerymodel.h
@@ -23,6 +23,7 @@ class AbstractGalleryModel : public QAbstractListModel
 
     Q_PROPERTY(QString title READ title NOTIFY titleChanged)
     Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+    Q_PROPERTY(bool showingCollections READ showingCollections NOTIFY showingCollectionsChanged)
 
 public:
     ~AbstractGalleryModel() = default;
@@ -60,11 +61,14 @@ public:
     // For models which are not immediately populated
     virtual Status status() const;
 
+    virtual bool showingCollections() const;
+
     QHash<int, QByteArray> roleNames() const override;
 
 Q_SIGNALS:
     void titleChanged();
     void statusChanged();
+    void showingCollectionsChanged();
 
 protected:
     explicit AbstractGalleryModel(QObject *parent = nullptr);
diff --git a/src/models/gallerylocationmodel.cpp b/src/models/gallerylocationmodel.cpp
index bae8dc88..462e9e0e 100644
--- a/src/models/gallerylocationmodel.cpp
+++ b/src/models/gallerylocationmodel.cpp
@@ -35,6 +35,7 @@ GalleryLocationModel::GalleryLocationModel(QObject *parent)
     , m_mode(None)
 {
     connect(this, &GalleryLocationModel::pathChanged, this, &GalleryLocationModel::titleChanged);
+    connect(this, &GalleryLocationModel::pathChanged, this, &GalleryLocationModel::showingCollectionsChanged);
     connect(ImageStorage::instance(), &ImageStorage::storageModified, this, [this]() {
         populate(m_path);
     });
@@ -104,6 +105,11 @@ QVariant GalleryLocationModel::pathForIndex(const QModelIndex &index) const
     }
 }
 
+bool GalleryLocationModel::showingCollections() const
+{
+    return m_mode != FileItemMode;
+}
+
 QVariant GalleryLocationModel::data(const QModelIndex &index, int role) const
 {
     Q_ASSERT(checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid));
diff --git a/src/models/gallerylocationmodel.h b/src/models/gallerylocationmodel.h
index 438c9cfb..2cf007e6 100644
--- a/src/models/gallerylocationmodel.h
+++ b/src/models/gallerylocationmodel.h
@@ -32,6 +32,8 @@ public:
 
     Q_INVOKABLE QVariant pathForIndex(const QModelIndex &index) const override;
 
+    bool showingCollections() const override;
+
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
     int rowCount(const QModelIndex &parent = {}) const override;
 
diff --git a/src/models/gallerytagsmodel.cpp b/src/models/gallerytagsmodel.cpp
index 07e6ed5f..2f4686eb 100644
--- a/src/models/gallerytagsmodel.cpp
+++ b/src/models/gallerytagsmodel.cpp
@@ -15,6 +15,7 @@ GalleryTagsModel::GalleryTagsModel(QObject *parent)
     , m_mode(None)
 {
     connect(this, &GalleryTagsModel::pathChanged, this, &GalleryTagsModel::titleChanged);
+    connect(this, &GalleryTagsModel::pathChanged, this, &GalleryTagsModel::showingCollectionsChanged);
     connect(ImageStorage::instance(), &ImageStorage::storageModified, this, [this]() {
         populate(m_path);
     });
@@ -67,6 +68,11 @@ QVariant GalleryTagsModel::pathForIndex(const QModelIndex &index) const
     }
 }
 
+bool GalleryTagsModel::showingCollections() const
+{
+    return m_mode != FileItemMode;
+}
+
 QVariant GalleryTagsModel::data(const QModelIndex &index, int role) const
 {
     Q_ASSERT(checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid));
diff --git a/src/models/gallerytagsmodel.h b/src/models/gallerytagsmodel.h
index baa05d39..3469830a 100644
--- a/src/models/gallerytagsmodel.h
+++ b/src/models/gallerytagsmodel.h
@@ -30,6 +30,8 @@ public:
 
     Q_INVOKABLE QVariant pathForIndex(const QModelIndex &index) const override;
 
+    bool showingCollections() const override;
+
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
     int rowCount(const QModelIndex &parent = {}) const override;
 
diff --git a/src/models/gallerytimemodel.cpp b/src/models/gallerytimemodel.cpp
index 26fe3cfc..45c14f70 100644
--- a/src/models/gallerytimemodel.cpp
+++ b/src/models/gallerytimemodel.cpp
@@ -37,6 +37,7 @@ GalleryTimeModel::GalleryTimeModel(QObject *parent)
     , m_mode(None)
 {
     connect(this, &GalleryTimeModel::pathChanged, this, &GalleryTimeModel::titleChanged);
+    connect(this, &GalleryTimeModel::pathChanged, this, &GalleryTimeModel::showingCollectionsChanged);
     connect(ImageStorage::instance(), &ImageStorage::storageModified, this, [this]() {
         populate(m_path);
     });
@@ -108,6 +109,11 @@ QVariant GalleryTimeModel::pathForIndex(const QModelIndex &index) const
     }
 }
 
+bool GalleryTimeModel::showingCollections() const
+{
+    return m_mode != FileItemMode;
+}
+
 QVariant GalleryTimeModel::data(const QModelIndex &index, int role) const
 {
     Q_ASSERT(checkIndex(index, CheckIndexOption::ParentIsInvalid | CheckIndexOption::IndexIsValid));
diff --git a/src/models/gallerytimemodel.h b/src/models/gallerytimemodel.h
index 3f7d22bc..5358d3ea 100644
--- a/src/models/gallerytimemodel.h
+++ b/src/models/gallerytimemodel.h
@@ -32,6 +32,8 @@ public:
 
     Q_INVOKABLE QVariant pathForIndex(const QModelIndex &index) const override;
 
+    bool showingCollections() const override;
+
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
     int rowCount(const QModelIndex &parent = {}) const override;
 
diff --git a/src/qml/gallery/GalleryPage.qml b/src/qml/gallery/GalleryPage.qml
index 28c8e659..815c8079 100644
--- a/src/qml/gallery/GalleryPage.qml
+++ b/src/qml/gallery/GalleryPage.qml
@@ -39,6 +39,7 @@ Kirigami.ScrollablePage {
     property list<var> navigationHistory: []
     property int navigationIndex: -1
 
+    property bool showingCollections: galleryModel.showingCollections
     property bool selectionMode: selectionModel.hasSelection
 
     Component.onCompleted: {
@@ -365,56 +366,56 @@ Kirigami.ScrollablePage {
     readonly property list<Kirigami.Action> fileMenuActions: [
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canSaveAs
+            enabled: fileMenuManager.enabled && fileMenuManager.canSaveAs && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: AC.StandardActionData.SaveAs
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canOpenFolder
+            enabled: fileMenuManager.enabled && fileMenuManager.canOpenFolder && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: "OpenFolder"
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canOpenWith
+            enabled: fileMenuManager.enabled && fileMenuManager.canOpenWith && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: "OpenWith"
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canCopy
+            enabled: fileMenuManager.enabled && fileMenuManager.canCopy && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: AC.StandardActionData.Copy
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canCopyPath
+            enabled: fileMenuManager.enabled && fileMenuManager.canCopyPath && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: "CopyPath"
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canMoveToTrash
+            enabled: fileMenuManager.enabled && fileMenuManager.canMoveToTrash && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: AC.StandardActionData.MoveToTrash
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canDeleteFile
+            enabled: fileMenuManager.enabled && fileMenuManager.canDeleteFile && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: AC.StandardActionData.DeleteFile
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
         },
         Kirigami.Action {
             displayHint: Kirigami.DisplayHint.AlwaysHide
-            enabled: fileMenuManager.enabled && fileMenuManager.canPrint
+            enabled: fileMenuManager.enabled && fileMenuManager.canPrint && !page.showingCollections
             visible: enabled
             AC.ActionCollection.action: AC.StandardActionData.Print
             AC.ActionCollection.collection: "org.kde.koko.mediaview"
@@ -430,16 +431,15 @@ Kirigami.ScrollablePage {
             id: selectAllAction
             AC.ActionCollection.action: AC.StandardActionData.SelectAll
             AC.ActionCollection.collection: "org.kde.koko.gallery"
-            enabled: page.visible && !page.isEmpty && !page.disallowMassSelection
+            enabled: page.visible && !page.isEmpty && !page.showingCollections && !page.disallowMassSelection
             displayHint: Kirigami.DisplayHint.AlwaysHide
             onTriggered: selectionModel.select(gridView.model.index(0, 0), ItemSelectionModel.Select | ItemSelectionModel.Columns)
-
         },
         Kirigami.Action {
             id: deselectAllAction
             AC.ActionCollection.action: AC.StandardActionData.Deselect
             AC.ActionCollection.collection: "org.kde.koko.gallery"
-            enabled: page.visible && !page.isEmpty && !page.disallowMassSelection
+            enabled: page.visible && !page.isEmpty && !page.showingCollections && !page.disallowMassSelection
             displayHint: Kirigami.DisplayHint.AlwaysHide
             onTriggered: selectionModel.clearSelection()
         },
@@ -447,7 +447,7 @@ Kirigami.ScrollablePage {
             id: invertSelectionAction
             AC.ActionCollection.action: "InvertSelection"
             AC.ActionCollection.collection: "org.kde.koko.gallery"
-            enabled: page.visible && !page.isEmpty && !page.disallowMassSelection
+            enabled: page.visible && !page.isEmpty && !page.showingCollections && !page.disallowMassSelection
             displayHint: Kirigami.DisplayHint.AlwaysHide
             onTriggered: selectionModel.select(gridView.model.index(0, 0), ItemSelectionModel.Toggle | ItemSelectionModel.Columns)
         }
@@ -712,11 +712,19 @@ Kirigami.ScrollablePage {
             }
 
             function select() {
+                if (page.showingCollections) {
+                    return;
+                }
+
                 gridView.currentIndex = delegate.index;
                 selectionModel.select(gridView.model.index(index, 0), ItemSelectionModel.ClearAndSelect);
             }
 
             function shiftSelect() {
+                if (page.showingCollections) {
+                    return;
+                }
+
                 let fromIndex = Math.min(gridView.currentIndex, delegate.index);
                 let toIndex = Math.max(gridView.currentIndex, delegate.index);
 
@@ -727,11 +735,19 @@ Kirigami.ScrollablePage {
             }
 
             function ctrlSelect() {
+                if (page.showingCollections) {
+                    return;
+                }
+
                 gridView.currentIndex = delegate.index;
                 selectionModel.select(gridView.model.index(index, 0), ItemSelectionModel.Toggle);
             }
 
             function showMenu() {
+                if (page.showingCollections) {
+                    return;
+                }
+
                 gridView.currentIndex = delegate.index;
                 if (!selectionModel.selectedIndexes.includes(gridView.model.index(index, 0))) {
                     selectionModel.select(gridView.model.index(index, 0), ItemSelectionModel.ClearAndSelect);