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 }