Warning, /graphics/koko/src/qml/AlbumView.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: (C) 2017 Atul Sharma <atulsharma406@gmail.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Controls 2.15 as Controls
0009 import QtQuick.Layouts 1.15
0010 
0011 import org.kde.kirigami 2.12 as Kirigami
0012 import org.kde.koko 0.1 as Koko
0013 import org.kde.koko.private 0.1
0014 
0015 Kirigami.ScrollablePage {
0016     id: page
0017 
0018     property alias model: gridView.model
0019     property bool isFolderView: false
0020     property bool isTrashView: gridView.url.toString().startsWith("trash:")
0021     signal collectionSelected(QtObject selectedModel, string cover)
0022     signal folderSelected(QtObject selectedModel, string cover, string path)
0023 
0024     property bool bookmarked: isFolderView && kokoConfig.savedFolders.includes(model.sourceModel.url.toString().replace("file:///", "file:/"))
0025     property var backUrls: [];
0026     property var backUrlsPosition: 0;
0027 
0028     property alias gridViewItem: gridView
0029 
0030     focus: true
0031 
0032     Component {
0033         id: normalTitleComponent
0034         Kirigami.Heading {
0035              level: 1
0036              Layout.fillWidth: true
0037              Layout.maximumWidth: implicitWidth + 1 // The +1 is to make sure we do not trigger eliding at max width
0038              Layout.minimumWidth: 0
0039              opacity: page.isCurrentPage ? 1 : 0.4
0040              maximumLineCount: 1
0041              elide: Text.ElideRight
0042              text: page.title
0043          }
0044      }
0045 
0046      
0047     property bool wideMode: Controls.ApplicationWindow.window.width > applicationWindow().wideScreenWidth
0048      
0049     // doesn't work without loader
0050     header: Loader {
0051         height: active ? implicitHeight : 0 // fix issue where space is being reserved even if not active
0052         active: page.wideMode
0053         sourceComponent: mobileHeader
0054     }
0055     footer: Loader {
0056         height: active ? implicitHeight : 0 // fix issue where space is being reserved even if not active
0057         active: !page.wideMode
0058         sourceComponent: mobileHeader 
0059     }
0060     
0061     Component {
0062         id: mobileHeader
0063         Rectangle {
0064             Kirigami.Theme.colorSet: Kirigami.Theme.View
0065             Kirigami.Theme.inherit: false
0066             color: Kirigami.Theme.backgroundColor
0067             
0068             visible: Kirigami.Settings.isMobile && page.isFolderView;
0069             height: Kirigami.Settings.isMobile && page.isFolderView ? implicitHeight : 0
0070             
0071             implicitHeight: column.implicitHeight
0072             
0073             ColumnLayout {
0074                 id: column
0075                 spacing: 0
0076                 anchors.left: parent.left
0077                 anchors.right: parent.right
0078                 Kirigami.Separator {
0079                     Layout.fillWidth: true
0080                     visible: !page.wideMode
0081                 }
0082                 Loader { 
0083                     active: Kirigami.Settings.isMobile && page.isFolderView; sourceComponent: folderTitle 
0084                     Layout.fillHeight: true
0085                     Layout.fillWidth: true
0086                     Layout.margins: page.wideMode ? 0 : Kirigami.Units.smallSpacing
0087                 }
0088                 Kirigami.Separator {
0089                     Layout.fillWidth: true
0090                     visible: page.wideMode
0091                 }
0092             }
0093         }
0094     }
0095 
0096     property alias folderTitle: folderTitleComponent
0097     property alias normalTitle: normalTitleComponent
0098 
0099     Component {
0100         id: folderTitleComponent
0101 
0102         RowLayout {
0103             id: folderLayout
0104             visible: page.isFolderView
0105             Controls.ToolButton {
0106                 id: backButton
0107                 visible: page.wideMode
0108                 Layout.maximumWidth: height
0109                 Layout.leftMargin: (Kirigami.Settings.isMobile || !page.wideMode && applicationWindow().globalDrawer) ? 0 : -Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
0110                 
0111                 icon.name: (LayoutMirroring.enabled ? "go-previous-symbolic-rtl" : "go-previous-symbolic")
0112                 enabled: page.backUrlsPosition > 0
0113                 onClicked: {
0114                     page.backUrlsPosition--;
0115                     model.sourceModel.url = page.backUrls[page.backUrlsPosition];
0116                 }
0117             }
0118 
0119             Controls.ToolButton {
0120                 implicitHeight: Kirigami.Units.gridUnit * 2
0121                 implicitWidth: Kirigami.Units.gridUnit * 2
0122                 visible: page.wideMode
0123                 icon.name: (LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic")
0124                 enabled: page.backUrls.length < page.backUrlsPosition
0125                 onClicked: {
0126                     page.backUrlsPosition++;
0127                     model.sourceModel.url = page.backUrls[page.backUrlsPosition];
0128                 }
0129             }
0130 
0131             Controls.ScrollView {
0132                 id: scrollView
0133                 clip: true
0134                 Layout.fillWidth: true
0135                 Layout.fillHeight: true
0136                 Layout.maximumWidth: Kirigami.Settings.isMobile ? -1 : folderRow.implicitWidth + 1
0137                 Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff
0138                 Controls.ScrollBar.vertical.policy: Controls.ScrollBar.AlwaysOff
0139                 
0140                 RowLayout {
0141                     id: folderRow
0142                     spacing: 0
0143 
0144                     Controls.ToolButton {
0145                         implicitHeight: Kirigami.Units.gridUnit * 2
0146                         implicitWidth: Kirigami.Units.gridUnit * 2
0147                         property bool canBeSimplified: page.isFolderView && Koko.DirModelUtils.inHome(page.model.sourceModel.url)
0148                         icon.name: canBeSimplified ? "go-home" : "folder-root-symbolic"
0149                         DragHandler {
0150                             enabled: scrollView.contentWidth > scrollView.width
0151                             yAxis.enabled: false
0152                             xAxis.enabled: false
0153                         }
0154                         onClicked: {
0155                             const tmp = page.backUrls;
0156                             while (page.backUrlsPosition < page.backUrls.length) {
0157                                 tmp.pop();
0158                             }
0159                             tmp.push(page.model.sourceModel.url);
0160                             page.backUrlsPosition++;
0161                             page.backUrls = tmp;
0162                             if (canBeSimplified) {
0163                                 model.sourceModel.url = "file:///" + Koko.DirModelUtils.home;
0164                             } else {
0165                                 model.sourceModel.url = "file:///";
0166                             }
0167                         }
0168                     }
0169                     Kirigami.Icon {
0170                         visible: page.model.sourceModel.url.toString() !== "file:///"
0171                         source: LayoutMirroring.enabled ? "arrow-left" : "arrow-right"
0172                         // adds visual balance
0173                         Layout.leftMargin: Kirigami.Units.smallSpacing
0174                         Layout.preferredWidth: visible ? Kirigami.Units.iconSizes.small : 0
0175                         height: width
0176                     }
0177                     Repeater {
0178                         id: repeater
0179                         model: page.isFolderView ? Koko.DirModelUtils.getUrlParts(page.model.sourceModel.url) : 0
0180                         Row {
0181                             DragHandler {
0182                                 enabled: scrollView.contentWidth > scrollView.width
0183                                 yAxis.enabled: false
0184                                 xAxis.enabled: false
0185                             }
0186                             Controls.ToolButton {
0187                                 height: Kirigami.Units.gridUnit * 2
0188                                 anchors.verticalCenter: parent.verticalCenter
0189                                 text: modelData
0190                                 onClicked: {
0191                                     const nextUrl = Koko.DirModelUtils.partialUrlForIndex(page.model.sourceModel.url, index + 1);
0192 
0193                                     if (String(nextUrl) === page.model.sourceModel.url + "/") {
0194                                         return;
0195                                     }
0196                                     const tmp = page.backUrls;
0197                                     while (page.backUrlsPosition < page.backUrls.length) {
0198                                         tmp.pop();
0199                                     }
0200                                     page.backUrlsPosition++;
0201                                     tmp.push(page.model.sourceModel.url);
0202                                     page.backUrls = tmp;
0203                                     page.model.sourceModel.url = nextUrl;
0204                                 }
0205                             }
0206                             Kirigami.Icon {
0207                                 anchors.verticalCenter: parent.verticalCenter
0208                                 visible: index != repeater.model.length - 1
0209                                 source: LayoutMirroring.enabled ? "arrow-left" : "arrow-right"
0210                                 width: height
0211                                 height: visible ? Kirigami.Units.iconSizes.small : 0
0212                             }
0213                         }
0214                     }
0215                 }
0216             }
0217             
0218             // bookmark button for footer
0219             Controls.ToolButton {
0220                 implicitHeight: Kirigami.Units.gridUnit * 2
0221                 display: page.wideMode ? Controls.AbstractButton.TextBesideIcon : Controls.AbstractButton.IconOnly
0222                 icon.name: page.bookmarked ? "bookmark-remove" : "bookmark-add-folder"
0223                 text: page.bookmarked ? i18n("Remove Bookmark") : i18nc("@action:button Bookmarks the current folder", "Bookmark Folder")
0224                 visible: Kirigami.Settings.isMobile && bookmarkActionVisible
0225                 onClicked: {
0226                     if (page.model.sourceModel.url == undefined) {
0227                         return
0228                     }
0229                     if (page.bookmarked) {
0230                         const index = kokoConfig.savedFolders.indexOf(model.sourceModel.url.toString().replace("file:///", "file:/"));
0231                         if (index !== -1) {
0232                             kokoConfig.savedFolders.splice(index, 1);
0233                         }
0234                     } else {
0235                         kokoConfig.savedFolders.push(model.sourceModel.url.toString().replace("file:///", "file:/"));
0236                     }
0237                 }
0238             }
0239         }
0240     }
0241 
0242     states: [
0243         State {
0244             name: "browsing"
0245             when: !model.hasSelectedImages
0246         },
0247         State {
0248             name: "selecting"
0249             when: model.hasSelectedImages && Kirigami.Settings.tabletMode
0250         }
0251     ]
0252 
0253 
0254     property bool bookmarkActionVisible: page.isFolderView && !model.hasSelectedImages && model.sourceModel.url.toString() !== ("file://" + Koko.DirModelUtils.pictures)
0255                                                                                        && model.sourceModel.url.toString() !== ("file://" + Koko.DirModelUtils.videos)
0256 
0257     actions: [
0258         Kirigami.Action {
0259             id: bookmarkAction
0260             icon.name: page.bookmarked ? "bookmark-remove" : "bookmark-add-folder"
0261             text: page.bookmarked ? i18n("Remove Bookmark") : i18nc("@action:button Bookmarks the current folder", "Bookmark Folder")
0262             visible: Kirigami.Settings.isMobile && page.isFolderView && !model.hasSelectedImages
0263                 && model.sourceModel.url.toString() !== ("file://" + Koko.DirModelUtils.pictures)
0264                 && model.sourceModel.url.toString() !== ("file://" + Koko.DirModelUtils.videos)
0265             onTriggered: {
0266                 if (page.model.sourceModel.url == undefined) {
0267                     return
0268                 }
0269                 if (page.bookmarked) {
0270                     const index = kokoConfig.savedFolders.indexOf(model.sourceModel.url.toString().replace("file:///", "file:/"));
0271                     if (index !== -1) {
0272                         kokoConfig.savedFolders.splice(index, 1);
0273                     }
0274                 } else {
0275                     kokoConfig.savedFolders.push(model.sourceModel.url.toString().replace("file:///", "file:/"));
0276                 }
0277             }
0278         },
0279         Kirigami.Action {
0280             id: goUpAction
0281             icon.name: "go-up"
0282             text: i18n("Go Up")
0283             visible: page.isFolderView && Kirigami.Settings.isMobile
0284             onTriggered: {
0285                 const tmp = page.backUrls;
0286                 while (page.backUrlsPosition < page.backUrls.length) {
0287                     tmp.pop();
0288                 }
0289                 tmp.push(page.model.sourceModel.url);
0290                 page.backUrlsPosition++;
0291                 page.backUrls = tmp;
0292                 var str = String(model.sourceModel.url).split("/")
0293                 str.pop()
0294                 if (str.join("/") == "file://") {
0295                     model.sourceModel.url = "file:///"
0296                 } else {
0297                     model.sourceModel.url = str.join("/")
0298                 }
0299             }
0300         },
0301         Kirigami.Action {
0302             visible: page.isFolderView && Kirigami.Settings.isMobile
0303             property bool canBeSimplified: page.isFolderView && Koko.DirModelUtils.canBeSimplified(page.model.sourceModel.url)
0304             icon.name: canBeSimplified ? "go-home" : "folder-root-symbolic"
0305             text: canBeSimplified ? i18n("Home") : i18n("Root")
0306             onTriggered: {
0307                 const tmp = page.backUrls;
0308                 while (page.backUrlsPosition < page.backUrls.length) {
0309                     tmp.pop();
0310                 }
0311                 tmp.push(page.model.sourceModel.url);
0312                 page.backUrlsPosition++;
0313                 page.backUrls = tmp;
0314                 if (canBeSimplified) {
0315                     model.sourceModel.url = "file:///" + Koko.DirModelUtils.home;
0316                 } else {
0317                     model.sourceModel.url = "file:///";
0318                 }
0319             }
0320         },
0321         ShareAction {
0322             id: shareAction
0323             visible: model.hasSelectedImages
0324 
0325             property Connections connection: Connections {
0326                 target: model
0327                 function onSelectedImagesChanged() {
0328                     shareAction.inputData = {
0329                         urls: model.selectedImages(),
0330                         mimeType: model.selectedImagesMimeTypes()
0331                     };
0332                 }
0333             }
0334 
0335         },
0336         Kirigami.Action {
0337             icon.name: "group-delete"
0338             text: i18n("Delete Selection")
0339             tooltip: i18n("Move selected items to trash")
0340             visible: model.hasSelectedImages && !page.isTrashView
0341             onTriggered: model.deleteSelection()
0342         },
0343         Kirigami.Action {
0344             icon.name: "restoration"
0345             text: i18n("Restore Selection")
0346             tooltip: i18n("Restore selected items from trash")
0347             visible: model.hasSelectedImages && page.isTrashView
0348             onTriggered: model.restoreSelection()
0349         },
0350         Kirigami.Action {
0351             visible: Kirigami.Settings.isMobile && root.width <= applicationWindow().wideScreenWidth
0352             icon.name: "configure"
0353             text: i18n("Configureā€¦")
0354             onTriggered: applicationWindow().openSettingsPage();
0355         },
0356         Kirigami.Action {
0357             icon.name: "edit-select-all"
0358             text: i18n("Select All")
0359             tooltip: i18n("Selects all the media in the current view")
0360             visible: model.containImages
0361             onTriggered: model.selectAll()
0362 
0363         },
0364         Kirigami.Action {
0365             icon.name: "edit-select-none"
0366             text: i18n("Deselect All")
0367             tooltip: i18n("De-selects all the selected media")
0368             onTriggered: model.clearSelections()
0369             visible: model.hasSelectedImages
0370         }
0371     ]
0372 
0373     background: Rectangle {
0374         Kirigami.Theme.colorSet: Kirigami.Theme.View
0375         color: Kirigami.Theme.backgroundColor
0376     }
0377 
0378     Keys.onPressed: {
0379         switch (event.key) {
0380             case Qt.Key_Escape:
0381                 gridView.model.clearSelections()
0382                 break;
0383             default:
0384                 break;
0385         }
0386     }
0387 
0388     GridView {
0389         id: gridView
0390 
0391         property real widthToApproximate: (applicationWindow().wideScreen ? applicationWindow().pageStack.defaultColumnWidth : page.width) - (1||Kirigami.Settings.tabletMode ? Kirigami.Units.gridUnit : 0)
0392         property string url: model.sourceModel.url ? model.sourceModel.url : ""
0393 
0394         cellWidth: Math.floor(width/Math.floor(width/(kokoConfig.iconSize + Kirigami.Units.largeSpacing * 2)))
0395         cellHeight: kokoConfig.iconSize + Kirigami.Units.largeSpacing * 2
0396 
0397         topMargin: Kirigami.Units.gridUnit
0398 
0399         highlightMoveDuration: 0
0400         keyNavigationEnabled: true
0401         focus: true
0402 
0403         // always clean selection
0404         onUrlChanged: model.clearSelections()
0405 
0406         delegate: AlbumDelegate {
0407             id: delegate
0408             highlighted: gridView.currentIndex == index
0409 
0410             Controls.ToolTip.text: Koko.DirModelUtils.fileNameOfUrl(model.imageurl)
0411             Controls.ToolTip.visible: hovered && model.itemType === Koko.Types.Image
0412             Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
0413 
0414             onPressAndHold: gridView.model.toggleSelected(delegate.index)
0415 
0416             onClicked: if (page.state === "selecting" || Controller.keyboardModifiers() & Qt.ControlModifier) {
0417                 gridView.model.toggleSelected(delegate.index)
0418             } else {
0419                 gridView.model.clearSelections()
0420                 gridView.currentIndex = delegate.index;
0421                 switch(delegate.itemType) {
0422                     case Koko.Types.Album: {
0423                         imageListModel.query = imageListModel.queryForIndex( model.sourceIndex)
0424                         sortedListModel.sourceModel = imageListModel
0425                         collectionSelected( sortedListModel, delegate.content)
0426                         break;
0427                     }
0428                     case Koko.Types.Folder: {
0429                         if (!page.isFolderView) {
0430                             imageFolderModel.url = delegate.imageurl
0431                             sortedListModel.sourceModel = imageFolderModel
0432                             folderSelected(sortedListModel, delegate.contetn, delegate.imageurl)
0433                             return
0434                         }
0435                         const tmp = page.backUrls;
0436                         while (page.backUrlsPosition < page.backUrls.length) {
0437                             tmp.pop();
0438                         }
0439                         tmp.push(page.model.sourceModel.url);
0440                         page.backUrls = tmp;
0441                         page.backUrlsPosition++;
0442                         page.model.sourceModel.url = delegate.imageurl;
0443                         break;
0444                     }
0445                     case Koko.Types.Image: {
0446                         if (gridView.url.toString().startsWith("trash:")) {
0447                             break
0448                         }
0449                         applicationWindow().pageStack.layers.push(Qt.resolvedUrl("ImageViewPage.qml"), {
0450                             startIndex: page.model.index(gridView.currentIndex, 0),
0451                             imagesModel: page.model
0452                         })
0453                         break;
0454                     }
0455                     default: {
0456                         console.log("Unknown")
0457                         break;
0458                     }
0459                 }
0460             }
0461             SelectionButton {
0462                 id: selectionButton
0463 
0464                 selected: delegate.selected
0465                 index: delegate.index
0466                 opacity: delegate.hovered || page.state === "selecting"
0467                 visible: delegate.itemType !== Koko.Types.Folder && delegate.itemType !== Koko.Types.Album
0468 
0469                 anchors {
0470                     top: delegate.top
0471                     left: delegate.left
0472                 }
0473             }
0474         }
0475 
0476         Kirigami.PlaceholderMessage {
0477             anchors.centerIn: parent
0478             text: i18n("No Media Found")
0479             visible: gridView.count === 0
0480             width: parent.width - (Kirigami.Units.largeSpacing * 4)
0481         }
0482 
0483         //FIXME: right now if those two objects are out of this, the whole page breaks
0484         Koko.SortModel {
0485             id: sortedListModel
0486         }
0487         Koko.ImageFolderModel {
0488             id: imageFolderModel
0489         }
0490     }
0491 
0492     onCollectionSelected: pageStack.push(Qt.resolvedUrl("AlbumView.qml"), {
0493         model: selectedModel,
0494         title: cover,
0495     })
0496     onFolderSelected: pageStack.push(Qt.resolvedUrl("AlbumView.qml"), {
0497         model: selectedModel,
0498         title: cover,
0499         url: path
0500     })
0501 }