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 }