diff --git a/README.md b/README.md index 1d25540..b6cdae9 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ To build the plugin for Dolphin integration KIO is also requried. Additionally, building the plugin, add `-DNO_FILE_ITEM_ACTION_PLUGIN:BOOL=ON` to the CMake arguments. To build the Plasmoid for the Plasma 5 desktop, the Qt module QML and the KF5 module -Plasma are required as well. Additionally, the Plasmoid requires Qt 5.8 or newer. To skip +Plasma are required as well. Additionally, the Plasmoid requires Qt 5.12 or newer. To skip building the Plasmoid, add `-DNO_PLASMOID:BOOL=ON` to the CMake arguments. It is also possible to build only the CLI (syncthingctl) by adding `-DNO_MODEL:BOOL=ON` and diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index aeeed8d..0ad6b09 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -15,6 +15,7 @@ set(HEADER_FILES syncthingdevicemodel.h syncthingdownloadmodel.h syncthingrecentchangesmodel.h + syncthingsortfilterdirectorymodel.h syncthingstatusselectionmodel.h syncthingicons.h colors.h) @@ -24,6 +25,7 @@ set(SRC_FILES syncthingdevicemodel.cpp syncthingdownloadmodel.cpp syncthingrecentchangesmodel.cpp + syncthingsortfilterdirectorymodel.cpp syncthingstatusselectionmodel.cpp syncthingicons.cpp) set(RES_FILES resources/${META_PROJECT_NAME}icons.qrc) diff --git a/model/syncthingsortfilterdirectorymodel.cpp b/model/syncthingsortfilterdirectorymodel.cpp new file mode 100644 index 0000000..badeab7 --- /dev/null +++ b/model/syncthingsortfilterdirectorymodel.cpp @@ -0,0 +1,27 @@ +#include "./syncthingsortfilterdirectorymodel.h" + +#include + +namespace Data { + +bool SyncthingSortFilterDirectoryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + // show all nested structures + if (sourceParent.isValid()) { + return true; + } + // use default filtering for top-level + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); +} + +bool SyncthingSortFilterDirectoryModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + // keep order within nested structures + if (m_behavior == SyncthingDirectorySortBehavior::KeepRawOrder || left.parent().isValid() || right.parent().isValid()) { + return left.row() < right.row(); + } + // use the default sorting for the top-level + return QSortFilterProxyModel::lessThan(left, right); +} + +} // namespace Data diff --git a/model/syncthingsortfilterdirectorymodel.h b/model/syncthingsortfilterdirectorymodel.h new file mode 100644 index 0000000..7d78e25 --- /dev/null +++ b/model/syncthingsortfilterdirectorymodel.h @@ -0,0 +1,55 @@ +#ifndef DATA_SYNCTHINGSORTFILTERDIRECTORYMODEL_H +#define DATA_SYNCTHINGSORTFILTERDIRECTORYMODEL_H + +#include "./global.h" + +#include + +namespace Data { + +enum class SyncthingDirectorySortBehavior { + KeepRawOrder, + Alphabetically, +}; + +class LIB_SYNCTHING_MODEL_EXPORT SyncthingSortFilterDirectoryModel : public QSortFilterProxyModel { + Q_OBJECT +public: + explicit SyncthingSortFilterDirectoryModel(QAbstractItemModel *sourceModel = nullptr, QObject *parent = nullptr); + + SyncthingDirectorySortBehavior behavior() const; + void setBehavior(SyncthingDirectorySortBehavior behavior); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + +private: + SyncthingDirectorySortBehavior m_behavior; +}; + +inline SyncthingSortFilterDirectoryModel::SyncthingSortFilterDirectoryModel(QAbstractItemModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) + , m_behavior(SyncthingDirectorySortBehavior::Alphabetically) +{ + setSortCaseSensitivity(Qt::CaseInsensitive); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setSourceModel(sourceModel); +} + +inline SyncthingDirectorySortBehavior SyncthingSortFilterDirectoryModel::behavior() const +{ + return m_behavior; +} + +inline void SyncthingSortFilterDirectoryModel::setBehavior(SyncthingDirectorySortBehavior behavior) +{ + if (behavior != m_behavior) { + m_behavior = behavior; + invalidate(); + } +} + +} // namespace Data + +#endif // DATA_SYNCTHINGSORTFILTERDIRECTORYMODEL_H diff --git a/plasmoid/lib/syncthingapplet.cpp b/plasmoid/lib/syncthingapplet.cpp index fe8cf37..3d6b898 100644 --- a/plasmoid/lib/syncthingapplet.cpp +++ b/plasmoid/lib/syncthingapplet.cpp @@ -53,6 +53,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data) , m_connection() , m_notifier(m_connection) , m_dirModel(m_connection) + , m_sortFilterDirModel(&m_dirModel) , m_devModel(m_connection) , m_downloadModel(m_connection) , m_recentChangesModel(m_connection) @@ -66,6 +67,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data) #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD m_notifier.setService(&m_service); #endif + m_sortFilterDirModel.sort(0, Qt::AscendingOrder); qmlRegisterUncreatableMetaObject(Data::staticMetaObject, "martchus.syncthingplasmoid", 0, 6, "Data", QStringLiteral("only enums")); } diff --git a/plasmoid/lib/syncthingapplet.h b/plasmoid/lib/syncthingapplet.h index 971b000..062fbc5 100644 --- a/plasmoid/lib/syncthingapplet.h +++ b/plasmoid/lib/syncthingapplet.h @@ -9,6 +9,7 @@ #include "../../model/syncthingdirectorymodel.h" #include "../../model/syncthingdownloadmodel.h" #include "../../model/syncthingrecentchangesmodel.h" +#include "../../model/syncthingsortfilterdirectorymodel.h" #include "../../model/syncthingstatusselectionmodel.h" #include "../../connector/syncthingconnection.h" @@ -38,6 +39,7 @@ class SyncthingApplet : public Plasma::Applet { Q_OBJECT Q_PROPERTY(Data::SyncthingConnection *connection READ connection NOTIFY connectionChanged) Q_PROPERTY(Data::SyncthingDirectoryModel *dirModel READ dirModel NOTIFY dirModelChanged) + Q_PROPERTY(Data::SyncthingSortFilterDirectoryModel *sortFilterDirModel READ sortFilterDirModel NOTIFY dirModelChanged) Q_PROPERTY(Data::SyncthingDeviceModel *devModel READ devModel NOTIFY devModelChanged) Q_PROPERTY(Data::SyncthingDownloadModel *downloadModel READ downloadModel NOTIFY downloadModelChanged) Q_PROPERTY(Data::SyncthingRecentChangesModel *recentChangesModel READ recentChangesModel NOTIFY recentChangesModelChanged) @@ -71,6 +73,7 @@ public: public: Data::SyncthingConnection *connection() const; Data::SyncthingDirectoryModel *dirModel() const; + Data::SyncthingSortFilterDirectoryModel *sortFilterDirModel() const; Data::SyncthingDeviceModel *devModel() const; Data::SyncthingDownloadModel *downloadModel() const; Data::SyncthingRecentChangesModel *recentChangesModel() const; @@ -171,6 +174,7 @@ private: #endif QtGui::StatusInfo m_statusInfo; Data::SyncthingDirectoryModel m_dirModel; + Data::SyncthingSortFilterDirectoryModel m_sortFilterDirModel; Data::SyncthingDeviceModel m_devModel; Data::SyncthingDownloadModel m_downloadModel; Data::SyncthingRecentChangesModel m_recentChangesModel; @@ -196,6 +200,11 @@ inline Data::SyncthingDirectoryModel *SyncthingApplet::dirModel() const return const_cast(&m_dirModel); } +inline Data::SyncthingSortFilterDirectoryModel *SyncthingApplet::sortFilterDirModel() const +{ + return const_cast(&m_sortFilterDirModel); +} + inline Data::SyncthingDeviceModel *SyncthingApplet::devModel() const { return const_cast(&m_devModel); diff --git a/plasmoid/package/contents/ui/DirectoriesPage.qml b/plasmoid/package/contents/ui/DirectoriesPage.qml index c1603a8..976fee4 100644 --- a/plasmoid/package/contents/ui/DirectoriesPage.qml +++ b/plasmoid/package/contents/ui/DirectoriesPage.qml @@ -20,13 +20,7 @@ ColumnLayout { TopLevelView { id: directoryView width: parent.width - - model: PlasmaCore.SortFilterModel { - id: directoryFilterModel - sourceModel: plasmoid.nativeInterface.dirModel - filterRole: "name" - filterRegExp: filter.text - } + model: plasmoid.nativeInterface.sortFilterDirModel delegate: TopLevelItem { id: item @@ -37,8 +31,6 @@ ColumnLayout { property alias rescanButton: rescanButton property alias resumePauseButton: resumePauseButton property alias openButton: openButton - property int sourceIndex: directoryFilterModel.mapRowToSource( - index) ColumnLayout { width: parent.width @@ -122,7 +114,7 @@ ColumnLayout { model: DelegateModel { model: plasmoid.nativeInterface.dirModel - rootIndex: detailsView.model.modelIndex(sourceIndex) + rootIndex: directoryView.model.mapToSource(directoryView.model.index(index, 0)) delegate: DetailItem { width: detailsView.width } @@ -185,5 +177,6 @@ ColumnLayout { clearButtonShown: true Layout.fillWidth: true visible: explicitelyShown || text !== "" + onTextChanged: directoryView.model.filterRegularExpression = new RegExp(text) } } diff --git a/tray/gui/devview.h b/tray/gui/devview.h index 887cc76..9aab01b 100644 --- a/tray/gui/devview.h +++ b/tray/gui/devview.h @@ -14,6 +14,7 @@ class DevView : public QTreeView { Q_OBJECT public: using ModelType = Data::SyncthingDeviceModel; + using SortFilterModelType = void; DevView(QWidget *parent = nullptr); diff --git a/tray/gui/dirview.cpp b/tray/gui/dirview.cpp index 885c40f..382ca78 100644 --- a/tray/gui/dirview.cpp +++ b/tray/gui/dirview.cpp @@ -4,6 +4,7 @@ #include "../../connector/syncthingconnection.h" #include "../../model/syncthingdirectorymodel.h" +#include "../../model/syncthingsortfilterdirectorymodel.h" #include "../../widgets/misc/direrrorsdialog.h" #include @@ -30,13 +31,15 @@ void DirView::mouseReleaseEvent(QMouseEvent *event) { QTreeView::mouseReleaseEvent(event); - // get SyncthingDir object - auto *const dirModel = qobject_cast(model()); + // get SyncthingDir object for clicked index + auto *const sortDirModel = qobject_cast(model()); + auto *const dirModel = qobject_cast(sortDirModel ? sortDirModel->sourceModel() : model()); if (!dirModel) { return; } - const QPoint pos(event->pos()); - const QModelIndex clickedIndex(indexAt(event->pos())); + const auto pos = event->pos(); + const auto clickedProxyIndex = indexAt(event->pos()); + const auto clickedIndex = sortDirModel ? sortDirModel->mapToSource(clickedProxyIndex) : clickedProxyIndex; if (!clickedIndex.isValid() || clickedIndex.column() != 1) { return; } @@ -47,7 +50,7 @@ void DirView::mouseReleaseEvent(QMouseEvent *event) if (!clickedIndex.parent().isValid()) { // open/scan dir buttons - const QRect itemRect(visualRect(clickedIndex)); + const QRect itemRect = visualRect(clickedProxyIndex); if (pos.x() <= itemRect.right() - 58) { return; } diff --git a/tray/gui/dirview.h b/tray/gui/dirview.h index 7ab35b3..5c7bfdd 100644 --- a/tray/gui/dirview.h +++ b/tray/gui/dirview.h @@ -6,6 +6,7 @@ namespace Data { struct SyncthingDir; class SyncthingDirectoryModel; +class SyncthingSortFilterDirectoryModel; } // namespace Data namespace QtGui { @@ -14,6 +15,7 @@ class DirView : public QTreeView { Q_OBJECT public: using ModelType = Data::SyncthingDirectoryModel; + using SortFilterModelType = Data::SyncthingSortFilterDirectoryModel; DirView(QWidget *parent = nullptr); diff --git a/tray/gui/downloadview.h b/tray/gui/downloadview.h index 4c255c8..8c69d58 100644 --- a/tray/gui/downloadview.h +++ b/tray/gui/downloadview.h @@ -15,6 +15,7 @@ class DownloadView : public QTreeView { Q_OBJECT public: using ModelType = Data::SyncthingDownloadModel; + using SortFilterModelType = void; DownloadView(QWidget *parent = nullptr); diff --git a/tray/gui/helper.h b/tray/gui/helper.h index 0cb7772..0547645 100644 --- a/tray/gui/helper.h +++ b/tray/gui/helper.h @@ -9,6 +9,7 @@ #include #include +#include QT_FORWARD_DECLARE_CLASS(QPoint) QT_FORWARD_DECLARE_CLASS(QMenu) @@ -42,7 +43,15 @@ template SelectedRow::SelectedRow(ViewType *view) return; } index = selectedRows.at(0); - model = qobject_cast(view->model()); + if constexpr (std::is_void_v) { + model = qobject_cast(view->model()); + } else { + const auto *const sortFilterModel = qobject_cast(view->model()); + if (sortFilterModel) { + index = sortFilterModel->mapToSource(index); + model = qobject_cast(sortFilterModel->sourceModel()); + } + } if (model) { data = model->info(index); } diff --git a/tray/gui/traywidget.cpp b/tray/gui/traywidget.cpp index c15b853..320c1ad 100644 --- a/tray/gui/traywidget.cpp +++ b/tray/gui/traywidget.cpp @@ -76,6 +76,7 @@ TrayWidget::TrayWidget(TrayMenu *parent) #endif , m_notifier(m_connection) , m_dirModel(m_connection) + , m_sortFilterDirModel(&m_dirModel) , m_devModel(m_connection) , m_dlModel(m_connection) , m_recentChangesModel(m_connection) @@ -86,8 +87,9 @@ TrayWidget::TrayWidget(TrayMenu *parent) m_ui->setupUi(this); - // setup model and view - m_ui->dirsTreeView->setModel(&m_dirModel); + // setup models and views + m_ui->dirsTreeView->header()->setSortIndicator(0, Qt::AscendingOrder); + m_ui->dirsTreeView->setModel(&m_sortFilterDirModel); m_ui->devsTreeView->setModel(&m_devModel); m_ui->downloadsTreeView->setModel(&m_dlModel); m_ui->recentChangesTreeView->setModel(&m_recentChangesModel); diff --git a/tray/gui/traywidget.h b/tray/gui/traywidget.h index 7f821f6..2c6b13e 100644 --- a/tray/gui/traywidget.h +++ b/tray/gui/traywidget.h @@ -8,6 +8,7 @@ #include "../../model/syncthingdirectorymodel.h" #include "../../model/syncthingdownloadmodel.h" #include "../../model/syncthingrecentchangesmodel.h" +#include "../../model/syncthingsortfilterdirectorymodel.h" #include "../../connector/syncthingconnection.h" #include "../../connector/syncthingnotifier.h" @@ -118,6 +119,7 @@ private: Data::SyncthingConnection m_connection; Data::SyncthingNotifier m_notifier; Data::SyncthingDirectoryModel m_dirModel; + Data::SyncthingSortFilterDirectoryModel m_sortFilterDirModel; Data::SyncthingDeviceModel m_devModel; Data::SyncthingDownloadModel m_dlModel; Data::SyncthingRecentChangesModel m_recentChangesModel; diff --git a/tray/gui/traywidget.ui b/tray/gui/traywidget.ui index 9957fef..03f2fac 100644 --- a/tray/gui/traywidget.ui +++ b/tray/gui/traywidget.ui @@ -403,7 +403,11 @@ For <i>all</i> notifications, checkout the log 0 - + + + true + +