diff --git a/qmlUiKirigami/AlbumDelegate.qml b/qmlUiKirigami/AlbumDelegate.qml index 8642140..831ec5e 100644 --- a/qmlUiKirigami/AlbumDelegate.qml +++ b/qmlUiKirigami/AlbumDelegate.qml @@ -1,113 +1,116 @@ /* * Copyright (C) 2017 Atul Sharma * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.7 import QtQuick.Controls 2.1 as Controls import org.kde.kquickcontrolsaddons 2.0 as KQA import org.kde.kirigami 2.1 as Kirigami import org.kde.koko 0.1 as Koko Item { id: albumDelegate width: gridView.cellWidth height: gridView.cellHeight Image { id: image source: model.itemType == Koko.Types.Album ? model.cover: (model.itemType == Koko.Types.Image ? model.url: ""/*this case is for Koko.Types.Folder*/) autoTransform: true anchors.centerIn: parent width: gridView.cellWidth - (Kirigami.Units.largeSpacing ) height: gridView.cellHeight - (Kirigami.Units.largeSpacing ) fillMode: Image.PreserveAspectCrop } KQA.QImageItem { anchors.fill: image image: model.thumbnail } Kirigami.BasicListItem { visible: model.itemType == Koko.Types.Folder || model.itemType == Koko.Types.Album - label: model.fileCount == 1 ? qsTr(" %1 \n 1 Image").arg(model.display) : qsTr(" %1 \n %2 Images").arg(model.display).arg(model.fileCount); + label: model.fileCount ? (model.fileCount == 1 ? qsTr(" %1 \n 1 Image").arg(model.display) : qsTr(" %1 \n %2 Images").arg(model.display).arg(model.fileCount)) + : qsTr(" %1").arg(model.display) reserveSpaceForIcon: false width: image.width anchors.left: image.left anchors.top: image.top background: Rectangle { anchors.fill: parent opacity: 0.7 color: Kirigami.Theme.backgroundColor } } SelectionButton { id: selectionButton visible: ( albumThumbnailMouseArea.containsMouse || iconMouseArea.containsMouse ) } SelectionDelegateHighlight { id: selectionHighlight visible: model.selected } MouseArea { id: albumThumbnailMouseArea anchors.fill: parent hoverEnabled: true onClicked: activate(); } Keys.onPressed: { switch (event.key) { case Qt.Key_Enter: case Qt.Key_Return: case Qt.Key_Space: activate(); break; default: break; } } function activate( ) { gridView.model.clearSelections() switch( model.itemType) { case Koko.Types.Album: { imageListModel.imageList = model.files sortedListModel.sourceModel = imageListModel - collectionSelected(sortedListModel, model.display) + collectionSelected( sortedListModel, model.display) break; } case Koko.Types.Folder: { - console.log("Folder") + imageFolderModel.url = model.url + sortedListModel.sourceModel = imageFolderModel + folderSelected( sortedListModel, model.display) break; } case Koko.Types.Image: { imageSelected(model.index) break; } default: { console.log("Unknown") break; } } } } diff --git a/qmlUiKirigami/AlbumView.qml b/qmlUiKirigami/AlbumView.qml index b0e7250..a3eb64e 100644 --- a/qmlUiKirigami/AlbumView.qml +++ b/qmlUiKirigami/AlbumView.qml @@ -1,69 +1,81 @@ /* * Copyright (C) 2017 Atul Sharma * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.7 import QtQuick.Controls 2.1 as Controls import org.kde.kirigami 2.1 as Kirigami import org.kde.koko 0.1 as Koko Kirigami.ScrollablePage { id: page property alias model: gridView.model - signal collectionSelected(Koko.SortModel files, string cover) + signal collectionSelected(QtObject selectedModel, string cover) signal imageSelected(int currentIndex) + signal folderSelected(QtObject selectedModel, string cover) keyboardNavigationEnabled: true focus: true GridView { id: gridView property int iconSize: Kirigami.Units.iconSizes.enormous keyNavigationEnabled: true cellWidth: width / Math.floor(width / (iconSize + Kirigami.Units.largeSpacing*2)) cellHeight: iconSize + Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing*2 highlight: Rectangle { color: Kirigami.Theme.highlightColor} delegate: AlbumDelegate {} } Keys.onPressed: { switch (event.key) { case Qt.Key_Escape: gridView.model.clearSelections() break; default: break; } } Koko.SortModel { id: sortedListModel } Koko.ImageListModel { id: imageListModel } + Koko.ImageFolderModel { + id: imageFolderModel + } + onCollectionSelected: pageStack.push( Qt.resolvedUrl("AlbumView.qml"), { "model": selectedModel, "title": cover}) + onFolderSelected: pageStack.push( Qt.resolvedUrl("AlbumView.qml"), { "model": selectedModel, "title": cover}) + onImageSelected: { + currentImage.model = model + currentImage.index = currentIndex + imageViewer.state = "open"; + } + } diff --git a/qmlUiKirigami/main.qml b/qmlUiKirigami/main.qml index d04edb0..89f239f 100644 --- a/qmlUiKirigami/main.qml +++ b/qmlUiKirigami/main.qml @@ -1,180 +1,167 @@ /* * Copyright (C) 2017 Atul Sharma * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.0 as Kirigami import org.kde.koko 0.1 as Koko Kirigami.ApplicationWindow { id: root header: Kirigami.ApplicationHeader {} QtObject { id: currentImage property int index property var model property GridView view : pageStack.currentItem.flickable onIndexChanged: { view.currentIndex = currentImage.index } } pageStack.initialPage: AlbumView { id: albumView - onCollectionSelected: pageStack.push(overviewPage, { "model": files, "title": cover}) } globalDrawer: Sidebar { id: sideBar onFilterBy: { pageStack.pop(albumView) albumView.title = value previouslySelectedAction.checked = false switch( value){ case "Countries": { albumView.model = imageLocationModelCountry; break; } case "States": { albumView.model = imageLocationModelState; break; } case "Cities": { albumView.model = imageLocationModelCity; break; } case "Years": { albumView.model = imageTimeModelYear; break; } case "Months": { albumView.model = imageTimeModelMonth; break; } case "Weeks": { albumView.model = imageTimeModelWeek; break; } case "Days": { albumView.model = imageTimeModelDay; break; } case "Folders": { albumView.model = imageFolderModel; break; } } } } Koko.SortModel{ id: imageFolderModel sourceModel: Koko.ImageFolderModel {} } Koko.SortModel { id: imageTimeModelYear sourceModel: Koko.ImageTimeModel { group: Koko.ImageTimeModel.Year } sortRoleName: "date" } Koko.SortModel { id: imageTimeModelMonth sourceModel: Koko.ImageTimeModel { group: Koko.ImageTimeModel.Month } sortRoleName: "date" } Koko.SortModel { id: imageTimeModelWeek sourceModel: Koko.ImageTimeModel { group: Koko.ImageTimeModel.Week } sortRoleName: "date" } Koko.SortModel { id: imageTimeModelDay sourceModel: Koko.ImageTimeModel { group: Koko.ImageTimeModel.Day } sortRoleName: "date" } Koko.SortModel { id: imageLocationModelCountry sourceModel: Koko.ImageLocationModel { group: Koko.ImageLocationModel.Country } } Koko.SortModel { id: imageLocationModelState sourceModel: Koko.ImageLocationModel { group: Koko.ImageLocationModel.State } } Koko.SortModel { id: imageLocationModelCity sourceModel: Koko.ImageLocationModel { group: Koko.ImageLocationModel.City } - } - - Component { - id: overviewPage - AlbumView { - id: overviewPageAlbum - onImageSelected: { - currentImage.model = model - currentImage.index = currentIndex - imageViewer.state = "open"; - } - } } ImageViewer { id: imageViewer //go on top of the overlay drawer //HACK on the parent and z to go on top of the handle as well z: 2000002 parent: root.overlay.parent width: overlay.width height: overlay.height currentIndex: currentImage.index model: currentImage.model focus: true imageWidth: root.width imageHeight: root.height } Component.onCompleted: { albumView.model = imageFolderModel albumView.title = "Folders" } } diff --git a/src/imagefoldermodel.cpp b/src/imagefoldermodel.cpp index 36f542f..7fe1148 100644 --- a/src/imagefoldermodel.cpp +++ b/src/imagefoldermodel.cpp @@ -1,236 +1,232 @@ /* * Copyright 2017 by Marco Martin * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "imagefoldermodel.h" #include "types.h" #include #include #include #include #include #include #include #include #include #include #include ImageFolderModel::ImageFolderModel(QObject *parent) : KDirModel(parent), m_screenshotSize(256, 256) { QMimeDatabase db; QList mimeList = db.allMimeTypes(); m_mimeTypes << "inode/directory"; foreach (const QMimeType &mime, mimeList) { if (mime.name().startsWith(QStringLiteral("image/"))) { m_mimeTypes << mime.name(); } } dirLister()->setMimeFilter(m_mimeTypes); m_previewTimer = new QTimer(this); m_previewTimer->setSingleShot(true); connect(m_previewTimer, &QTimer::timeout, this, &ImageFolderModel::delayedPreview); //using the same cache of the engine, they index both by url m_imageCache = new KImageCache(QStringLiteral("org.kde.koko"), 10485760); connect(this, &QAbstractItemModel::rowsInserted, this, &ImageFolderModel::countChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &ImageFolderModel::countChanged); connect(this, &QAbstractItemModel::modelReset, this, &ImageFolderModel::countChanged); //TODO: don't hardcode this QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); Q_ASSERT(locations.size() >= 1); qDebug() << locations; setUrl("file://"+locations.first()); } ImageFolderModel::~ImageFolderModel() { delete m_imageCache; } QHash ImageFolderModel::roleNames() const { return { { Qt::DisplayRole, "display" }, { Qt::DecorationRole, "decoration" }, { UrlRole, "url" }, { MimeTypeRole, "mimeType" }, { Thumbnail, "thumbnail" }, - { SelectedRole, "selected" }, { ItemTypeRole, "itemType"} }; } QString ImageFolderModel::url() const { return dirLister()->url().toString(); } void ImageFolderModel::setUrl(const QString& url) { if (url.isEmpty()) { return; } if (dirLister()->url().path() == url) { dirLister()->updateDirectory(QUrl(url)); return; } beginResetModel(); dirLister()->openUrl(QUrl(url)); endResetModel(); emit urlChanged(); } int ImageFolderModel::indexForUrl(const QString &url) const { QModelIndex index = KDirModel::indexForUrl(QUrl(url)); return index.row(); } QVariantMap ImageFolderModel::get(int i) const { QModelIndex modelIndex = index(i, 0); KFileItem item = itemForIndex(modelIndex); QString url = item.url().toString(); QString mimeType = item.mimetype(); QVariantMap ret; ret.insert(QStringLiteral("url"), QVariant(url)); ret.insert(QStringLiteral("mimeType"), QVariant(mimeType)); return ret; } void ImageFolderModel::emptyTrash() { KIO::emptyTrash(); } QVariant ImageFolderModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } switch (role) { case UrlRole: { KFileItem item = itemForIndex(index); return item.url().toString(); } case MimeTypeRole: { KFileItem item = itemForIndex(index); return item.mimetype(); } case Thumbnail: { KFileItem item = itemForIndex(index); QImage preview = QImage(m_screenshotSize, QImage::Format_ARGB32_Premultiplied); if (m_imageCache->findImage(item.url().toString(), &preview)) { return preview; } m_previewTimer->start(100); const_cast(this)->m_filesToPreview[item.url()] = QPersistentModelIndex(index); } - //TODO: implement selection - case SelectedRole: - return false; case ItemTypeRole: { KFileItem item = itemForIndex(index); if( item.isDir()) { return Types::Folder; } else { return Types::Image; } } default: return KDirModel::data(index, role); } } void ImageFolderModel::delayedPreview() { QHash::const_iterator i = m_filesToPreview.constBegin(); KFileItemList list; while (i != m_filesToPreview.constEnd()) { QUrl file = i.key(); QPersistentModelIndex index = i.value(); if (!m_previewJobs.contains(file) && file.isValid()) { list.append(KFileItem(file, QString(), 0)); m_previewJobs.insert(file, QPersistentModelIndex(index)); } ++i; } if (list.size() > 0) { KIO::PreviewJob* job = KIO::filePreview(list, m_screenshotSize); job->setIgnoreMaximumSize(true); // qDebug() << "Created job" << job; connect(job, &KIO::PreviewJob::gotPreview, this, &ImageFolderModel::showPreview); connect(job, &KIO::PreviewJob::failed, this, &ImageFolderModel::previewFailed); } m_filesToPreview.clear(); } void ImageFolderModel::showPreview(const KFileItem &item, const QPixmap &preview) { QPersistentModelIndex index = m_previewJobs.value(item.url()); m_previewJobs.remove(item.url()); if (!index.isValid()) { return; } m_imageCache->insertImage(item.url().toString(), preview.toImage()); //qDebug() << "preview size:" << preview.size(); emit dataChanged(index, index); } void ImageFolderModel::previewFailed(const KFileItem &item) { m_previewJobs.remove(item.url()); } #include "moc_imagefoldermodel.cpp" diff --git a/src/imagefoldermodel.h b/src/imagefoldermodel.h index a96accd..7bb39b6 100644 --- a/src/imagefoldermodel.h +++ b/src/imagefoldermodel.h @@ -1,100 +1,99 @@ /* * Copyright 2017 by Marco Martin * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IMAGEFOLDERMODEL_H #define IMAGEFOLDERMODEL_H #include #include #include #include #include class QTimer; /** * This class provides a QML binding to KDirModel * Provides an easy way to navigate a filesystem from within QML * * @author Marco Martin */ class ImageFolderModel : public KDirModel { Q_OBJECT /** * @property string The url we want to browse. it may be an absolute path or a correct url of any protocol KIO supports */ Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) /** * @property count Total number of rows */ Q_PROPERTY(int count READ count NOTIFY countChanged) public: enum Roles { UrlRole = Qt::UserRole + 1, MimeTypeRole = Qt::UserRole + 2, Thumbnail = Qt::UserRole + 3, - SelectedRole = Qt::UserRole + 4, - ItemTypeRole = Qt::UserRole + 5 + ItemTypeRole = Qt::UserRole + 4 }; ImageFolderModel(QObject* parent=0); virtual ~ImageFolderModel(); QHash roleNames() const override; void setUrl(const QString& url); QString url() const; QVariant data(const QModelIndex &index, int role) const; int count() const {return rowCount();} Q_INVOKABLE int indexForUrl(const QString &url) const; Q_INVOKABLE QVariantMap get(int index) const; /** * Helper method to empty the trash */ Q_INVOKABLE void emptyTrash(); protected Q_SLOTS: void showPreview(const KFileItem &item, const QPixmap &preview); void previewFailed(const KFileItem &item); void delayedPreview(); Q_SIGNALS: void countChanged(); void urlChanged(); private: QStringList m_mimeTypes; //previews QTimer *m_previewTimer; QHash m_filesToPreview; QSize m_screenshotSize; QHash m_previewJobs; KImageCache* m_imageCache; }; #endif // IMAGEFOLDERMODEL_H