diff --git a/cuttlefish/autotests/CMakeLists.txt b/cuttlefish/autotests/CMakeLists.txt --- a/cuttlefish/autotests/CMakeLists.txt +++ b/cuttlefish/autotests/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories(../src) -ecm_add_test(iconmodeltest.cpp ../src/iconmodel.cpp +ecm_add_test(iconmodeltest.cpp ../src/iconmodel.cpp ../src/sortfiltermodel.cpp TEST_NAME iconmodeltest LINK_LIBRARIES Qt5::Gui diff --git a/cuttlefish/autotests/iconmodeltest.cpp b/cuttlefish/autotests/iconmodeltest.cpp --- a/cuttlefish/autotests/iconmodeltest.cpp +++ b/cuttlefish/autotests/iconmodeltest.cpp @@ -26,6 +26,7 @@ #include #include "iconmodel.h" +#include "sortfiltermodel.h" using namespace CuttleFish; @@ -40,11 +41,14 @@ void init() { m_iconModel = new IconModel(this); + m_proxyModel = new SortFilterModel(this); + m_proxyModel->setSourceModel(m_iconModel); } void cleanup() { delete m_iconModel; + delete m_proxyModel; } void initTestCase() @@ -55,14 +59,14 @@ { const int _all = m_iconModel->rowCount(QModelIndex()); - m_iconModel->setFilter("edit"); - const int _edit = m_iconModel->rowCount(QModelIndex()); + m_proxyModel->setFilter("edit"); + const int _edit = m_proxyModel->rowCount(QModelIndex()); - m_iconModel->setCategory("actions"); - const int _editactions = m_iconModel->rowCount(QModelIndex()); + m_proxyModel->setCategory("actions"); + const int _editactions = m_proxyModel->rowCount(QModelIndex()); - m_iconModel->setCategory("all"); - const int _alledit = m_iconModel->rowCount(QModelIndex()); + m_proxyModel->setCategory("all"); + const int _alledit = m_proxyModel->rowCount(QModelIndex()); QVERIFY(_all > _edit); QVERIFY(_all > _editactions); @@ -80,6 +84,7 @@ QJsonArray m_empty; IconModel* m_iconModel; + SortFilterModel* m_proxyModel; }; diff --git a/cuttlefish/package/contents/ui/IconGrid.qml b/cuttlefish/package/contents/ui/IconGrid.qml --- a/cuttlefish/package/contents/ui/IconGrid.qml +++ b/cuttlefish/package/contents/ui/IconGrid.qml @@ -35,7 +35,8 @@ cacheBuffer: 20 highlightMoveDuration: 0 boundsBehavior: Flickable.StopAtBounds - model: iconModel + model: proxyModel + currentIndex: proxyModel.currentIndex highlight: Item {} @@ -48,4 +49,7 @@ width: Kirigami.Units.gridUnit * 8 height: width } + Component.onCompleted: { + currentItem.setAsPreview() + } } diff --git a/cuttlefish/package/contents/ui/IconGridDelegate.qml b/cuttlefish/package/contents/ui/IconGridDelegate.qml --- a/cuttlefish/package/contents/ui/IconGridDelegate.qml +++ b/cuttlefish/package/contents/ui/IconGridDelegate.qml @@ -91,9 +91,9 @@ } } onClicked: (mouse) => { - iconGrid.currentIndex = index; + proxyModel.currentIndex = index iconGrid.forceActiveFocus(); - if (mouse.button == Qt.RightButton) { + if (mouse.button == Qt.RightButton) { cuttlefish.itemRightClicked() } } diff --git a/cuttlefish/package/contents/ui/Tools.qml b/cuttlefish/package/contents/ui/Tools.qml --- a/cuttlefish/package/contents/ui/Tools.qml +++ b/cuttlefish/package/contents/ui/Tools.qml @@ -56,7 +56,7 @@ repeat: false interval: 100 onTriggered: { - iconModel.filter = filterInput.text + proxyModel.filter = filterInput.text } } Component.onCompleted: { @@ -70,11 +70,10 @@ model: iconModel.categories onActivated: { if (currentText == "all") { - iconModel.category = ""; + proxyModel.category = ""; } else if (currentText != "") { - iconModel.category = currentText + proxyModel.category = currentText } - iconModel.sort() } popup.modal: false } diff --git a/cuttlefish/src/CMakeLists.txt b/cuttlefish/src/CMakeLists.txt --- a/cuttlefish/src/CMakeLists.txt +++ b/cuttlefish/src/CMakeLists.txt @@ -3,6 +3,7 @@ main.cpp iconmodel.cpp colorschemes.cpp + sortfiltermodel.cpp ) add_executable(cuttlefish ${cuttlefish_SRCS}) diff --git a/cuttlefish/src/iconmodel.h b/cuttlefish/src/iconmodel.h --- a/cuttlefish/src/iconmodel.h +++ b/cuttlefish/src/iconmodel.h @@ -36,8 +36,6 @@ { Q_OBJECT - Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged) - Q_PROPERTY(QString category READ category WRITE setCategory NOTIFY categoryChanged) Q_PROPERTY(QStringList categories READ categories NOTIFY categoriesChanged) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged); @@ -64,39 +62,27 @@ QString key(int role) const; - bool matchIcons(const QFileInfo &info); void add(const QFileInfo &info, const QString &cat); void remove(const QString &iconFile); - void setCategory(const QString &cat); - QString category() const; - - void setFilter(const QString &filter); - QString filter() const; - QStringList categories() const; bool loading(); void load(); Q_INVOKABLE void output(const QString &text); - Q_INVOKABLE void sort(); Q_SIGNALS: - void filterChanged(); - void categoryChanged(); void categoriesChanged(); void loadingChanged(); private: QHash m_roleNames; QStringList m_icons; - QString m_category; QStringList m_categories; - QString m_filter; QHash m_data; QHash m_categoryTranslations; diff --git a/cuttlefish/src/iconmodel.cpp b/cuttlefish/src/iconmodel.cpp --- a/cuttlefish/src/iconmodel.cpp +++ b/cuttlefish/src/iconmodel.cpp @@ -60,8 +60,6 @@ m_roleNames.insert(Theme, "iconTheme"); m_roleNames.insert(Type, "type"); - connect(this, &IconModel::categoryChanged, this, &IconModel::load); - m_categories = QStringList() << "all" \ << "actions" << "animations" @@ -169,39 +167,11 @@ } } -QString IconModel::category() const -{ - return m_category; -} - QStringList IconModel::categories() const { return m_categories; } -void IconModel::setCategory(const QString& cat) -{ - if (cat != m_category) { - m_category = cat; - emit categoryChanged(); - } -} - -QString IconModel::filter() const -{ - return m_filter; -} - -void IconModel::setFilter(const QString &filter) -{ - //qDebug() << "Filter: " << filter; - if (m_filter != filter) { - m_filter = filter; - load(); - emit filterChanged(); - } -} - void IconModel::load() { //qDebug() << "\n -- Loading (category / filter) : " << m_category << m_filter; @@ -247,47 +217,18 @@ while (it.hasNext()) { it.next(); const QFileInfo &info = it.fileInfo(); - if (matchIcons(info)) { - add(info, categoryFromPath(info.absoluteFilePath())); - } + add(info, categoryFromPath(info.absoluteFilePath())); } } } } - sort(); - endResetModel(); m_loading = false; emit loadingChanged(); } -void IconModel::sort() -{ - std::sort(m_icons.begin(), m_icons.end()); -} - -bool IconModel::matchIcons(const QFileInfo& info) -{ - bool ok = false; - - // Category is empty or all? Skip further matching. - bool catmatch = m_category.isEmpty() || m_category == QStringLiteral("all"); - // category match? - if (!catmatch && m_category == categoryFromPath(info.absoluteFilePath())) { - catmatch = true; - } - - // name filter - if (m_filter.isEmpty() || info.fileName().indexOf(m_filter) != -1) { - if (catmatch) { - ok = true; - } - } - return ok; -} - QString IconModel::categoryFromPath(const QString& path) { QStringList cats; diff --git a/cuttlefish/src/main.cpp b/cuttlefish/src/main.cpp --- a/cuttlefish/src/main.cpp +++ b/cuttlefish/src/main.cpp @@ -37,6 +37,7 @@ // Own #include "colorschemes.h" #include "iconmodel.h" +#include "sortfiltermodel.h" void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { @@ -121,8 +122,13 @@ } auto iconModel = new CuttleFish::IconModel(engine.rootContext()); + auto proxyModel = new CuttleFish::SortFilterModel(engine.rootContext()); + proxyModel->setSourceModel(iconModel); + proxyModel->sort(0); auto colorSchemes = new CuttleFish::ColorSchemes(engine.rootContext()); + engine.rootContext()->setContextProperty("iconModel", iconModel); + engine.rootContext()->setContextProperty("proxyModel", proxyModel); engine.rootContext()->setContextProperty("pickerMode", parser.isSet("picker")); engine.rootContext()->setContextProperty("colorSchemes", colorSchemes); diff --git a/cuttlefish/package/contents/ui/IconGrid.qml b/cuttlefish/src/sortfiltermodel.h copy from cuttlefish/package/contents/ui/IconGrid.qml copy to cuttlefish/src/sortfiltermodel.h --- a/cuttlefish/package/contents/ui/IconGrid.qml +++ b/cuttlefish/src/sortfiltermodel.h @@ -1,6 +1,6 @@ /*************************************************************************** * * - * Copyright 2014-2015 Sebastian Kügler * + * Copyright 2020 David Redondo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,33 +19,39 @@ * * ***************************************************************************/ -import QtQuick 2.5 -import QtQuick.Controls 2.5 as QQC2 -import QtQuick.Layouts 1.0 -import org.kde.kirigami 2.8 as Kirigami +#include -GridView { - id: iconGrid - focus: true +namespace CuttleFish { - cellWidth: iconSize + Math.round(Kirigami.Units.gridUnit * 1.5) - cellHeight: cellWidth + Math.round(Kirigami.Units.gridUnit * 2) +class SortFilterModel : public QSortFilterProxyModel { + Q_OBJECT + Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged) + Q_PROPERTY(QString category READ category WRITE setCategory NOTIFY categoryChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) +public: + SortFilterModel(QObject *parent); - cacheBuffer: 20 - highlightMoveDuration: 0 - boundsBehavior: Flickable.StopAtBounds - model: iconModel + void setCategory(const QString &category); + QString category() const; - highlight: Item {} + void setFilter(const QString &filter); + QString filter() const; - delegate: IconGridDelegate {} + void setCurrentIndex(int index); + int currentIndex(); + +Q_SIGNALS: + void filterChanged(); + void categoryChanged(); + void currentIndexChanged(); + +private: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; + + QString m_category; + QString m_filter; + QModelIndex m_currentSourceIndex; +}; - QQC2.BusyIndicator { - running: iconModel.loading - visible: running - anchors.centerIn: parent - width: Kirigami.Units.gridUnit * 8 - height: width - } } diff --git a/cuttlefish/src/sortfiltermodel.cpp b/cuttlefish/src/sortfiltermodel.cpp new file mode 100644 --- /dev/null +++ b/cuttlefish/src/sortfiltermodel.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * * + * Copyright 2020 David Redondo * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * + * * + * You should have received a copy of the GNU 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 "iconmodel.h" +#include "sortfiltermodel.h" + +using namespace CuttleFish; + +SortFilterModel::SortFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSortRole(IconModel::IconName); + setSortCaseSensitivity(Qt::CaseInsensitive); +} + + +bool SortFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const +{ + + const QModelIndex sourceIndex = sourceModel()->index(source_row, 0, source_parent); + if (!(m_category.isEmpty() || m_category == QLatin1String("all")) + && (m_category != sourceIndex.data(IconModel::Category))) { + return false; + } + if (!m_filter.isEmpty()) { + return sourceIndex.data(IconModel::IconName).toString().contains(m_filter, Qt::CaseInsensitive); + } + return true; +} + +int SortFilterModel::currentIndex() +{ + const QModelIndex index = mapFromSource(m_currentSourceIndex); + if (index.isValid()) { + return index.row(); + } + m_currentSourceIndex = mapFromSource(this->index(0, 0)); + return 0; +} + +void SortFilterModel::setCurrentIndex(int index) +{ + if (mapFromSource(m_currentSourceIndex).row() != index) { + m_currentSourceIndex = mapToSource(this->index(index, 0)); + emit currentIndexChanged(); + } + +} + +QString SortFilterModel::category() const +{ + return m_category; +} + +void SortFilterModel::setCategory(const QString &category) +{ + if (category == m_category) { + return; + } + int oldIndex = currentIndex(); + m_category = category; + invalidateFilter(); + emit categoryChanged(); + if (currentIndex() != oldIndex) { + emit currentIndexChanged(); + } +} + +QString SortFilterModel::filter() const +{ + return m_filter; +} + +void SortFilterModel::setFilter(const QString& filter) +{ + if (filter == m_filter) { + return; + } + int oldIndex = currentIndex(); + m_filter = filter; + invalidateFilter(); + emit filterChanged(); + if (currentIndex() != oldIndex) { + emit currentIndexChanged(); + } +} + +