Sort directories by name/ID

* Use the name as sorting criteria and fall back to the ID if there's no
  name
* Use new SyncthingSortFilterDirectoryModel also for Plasmoid's filtering
* See https://github.com/Martchus/syncthingtray/issues/75
This commit is contained in:
Martchus 2020-10-18 15:48:02 +02:00
parent d0f27bf327
commit 3e1beaa11d
15 changed files with 132 additions and 20 deletions

View File

@ -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. 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 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. 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 It is also possible to build only the CLI (syncthingctl) by adding `-DNO_MODEL:BOOL=ON` and

View File

@ -15,6 +15,7 @@ set(HEADER_FILES
syncthingdevicemodel.h syncthingdevicemodel.h
syncthingdownloadmodel.h syncthingdownloadmodel.h
syncthingrecentchangesmodel.h syncthingrecentchangesmodel.h
syncthingsortfilterdirectorymodel.h
syncthingstatusselectionmodel.h syncthingstatusselectionmodel.h
syncthingicons.h syncthingicons.h
colors.h) colors.h)
@ -24,6 +25,7 @@ set(SRC_FILES
syncthingdevicemodel.cpp syncthingdevicemodel.cpp
syncthingdownloadmodel.cpp syncthingdownloadmodel.cpp
syncthingrecentchangesmodel.cpp syncthingrecentchangesmodel.cpp
syncthingsortfilterdirectorymodel.cpp
syncthingstatusselectionmodel.cpp syncthingstatusselectionmodel.cpp
syncthingicons.cpp) syncthingicons.cpp)
set(RES_FILES resources/${META_PROJECT_NAME}icons.qrc) set(RES_FILES resources/${META_PROJECT_NAME}icons.qrc)

View File

@ -0,0 +1,27 @@
#include "./syncthingsortfilterdirectorymodel.h"
#include <QSortFilterProxyModel>
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

View File

@ -0,0 +1,55 @@
#ifndef DATA_SYNCTHINGSORTFILTERDIRECTORYMODEL_H
#define DATA_SYNCTHINGSORTFILTERDIRECTORYMODEL_H
#include "./global.h"
#include <QSortFilterProxyModel>
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

View File

@ -53,6 +53,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
, m_connection() , m_connection()
, m_notifier(m_connection) , m_notifier(m_connection)
, m_dirModel(m_connection) , m_dirModel(m_connection)
, m_sortFilterDirModel(&m_dirModel)
, m_devModel(m_connection) , m_devModel(m_connection)
, m_downloadModel(m_connection) , m_downloadModel(m_connection)
, m_recentChangesModel(m_connection) , m_recentChangesModel(m_connection)
@ -66,6 +67,7 @@ SyncthingApplet::SyncthingApplet(QObject *parent, const QVariantList &data)
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
m_notifier.setService(&m_service); m_notifier.setService(&m_service);
#endif #endif
m_sortFilterDirModel.sort(0, Qt::AscendingOrder);
qmlRegisterUncreatableMetaObject(Data::staticMetaObject, "martchus.syncthingplasmoid", 0, 6, "Data", QStringLiteral("only enums")); qmlRegisterUncreatableMetaObject(Data::staticMetaObject, "martchus.syncthingplasmoid", 0, 6, "Data", QStringLiteral("only enums"));
} }

View File

@ -9,6 +9,7 @@
#include "../../model/syncthingdirectorymodel.h" #include "../../model/syncthingdirectorymodel.h"
#include "../../model/syncthingdownloadmodel.h" #include "../../model/syncthingdownloadmodel.h"
#include "../../model/syncthingrecentchangesmodel.h" #include "../../model/syncthingrecentchangesmodel.h"
#include "../../model/syncthingsortfilterdirectorymodel.h"
#include "../../model/syncthingstatusselectionmodel.h" #include "../../model/syncthingstatusselectionmodel.h"
#include "../../connector/syncthingconnection.h" #include "../../connector/syncthingconnection.h"
@ -38,6 +39,7 @@ class SyncthingApplet : public Plasma::Applet {
Q_OBJECT Q_OBJECT
Q_PROPERTY(Data::SyncthingConnection *connection READ connection NOTIFY connectionChanged) Q_PROPERTY(Data::SyncthingConnection *connection READ connection NOTIFY connectionChanged)
Q_PROPERTY(Data::SyncthingDirectoryModel *dirModel READ dirModel NOTIFY dirModelChanged) 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::SyncthingDeviceModel *devModel READ devModel NOTIFY devModelChanged)
Q_PROPERTY(Data::SyncthingDownloadModel *downloadModel READ downloadModel NOTIFY downloadModelChanged) Q_PROPERTY(Data::SyncthingDownloadModel *downloadModel READ downloadModel NOTIFY downloadModelChanged)
Q_PROPERTY(Data::SyncthingRecentChangesModel *recentChangesModel READ recentChangesModel NOTIFY recentChangesModelChanged) Q_PROPERTY(Data::SyncthingRecentChangesModel *recentChangesModel READ recentChangesModel NOTIFY recentChangesModelChanged)
@ -71,6 +73,7 @@ public:
public: public:
Data::SyncthingConnection *connection() const; Data::SyncthingConnection *connection() const;
Data::SyncthingDirectoryModel *dirModel() const; Data::SyncthingDirectoryModel *dirModel() const;
Data::SyncthingSortFilterDirectoryModel *sortFilterDirModel() const;
Data::SyncthingDeviceModel *devModel() const; Data::SyncthingDeviceModel *devModel() const;
Data::SyncthingDownloadModel *downloadModel() const; Data::SyncthingDownloadModel *downloadModel() const;
Data::SyncthingRecentChangesModel *recentChangesModel() const; Data::SyncthingRecentChangesModel *recentChangesModel() const;
@ -171,6 +174,7 @@ private:
#endif #endif
QtGui::StatusInfo m_statusInfo; QtGui::StatusInfo m_statusInfo;
Data::SyncthingDirectoryModel m_dirModel; Data::SyncthingDirectoryModel m_dirModel;
Data::SyncthingSortFilterDirectoryModel m_sortFilterDirModel;
Data::SyncthingDeviceModel m_devModel; Data::SyncthingDeviceModel m_devModel;
Data::SyncthingDownloadModel m_downloadModel; Data::SyncthingDownloadModel m_downloadModel;
Data::SyncthingRecentChangesModel m_recentChangesModel; Data::SyncthingRecentChangesModel m_recentChangesModel;
@ -196,6 +200,11 @@ inline Data::SyncthingDirectoryModel *SyncthingApplet::dirModel() const
return const_cast<Data::SyncthingDirectoryModel *>(&m_dirModel); return const_cast<Data::SyncthingDirectoryModel *>(&m_dirModel);
} }
inline Data::SyncthingSortFilterDirectoryModel *SyncthingApplet::sortFilterDirModel() const
{
return const_cast<Data::SyncthingSortFilterDirectoryModel *>(&m_sortFilterDirModel);
}
inline Data::SyncthingDeviceModel *SyncthingApplet::devModel() const inline Data::SyncthingDeviceModel *SyncthingApplet::devModel() const
{ {
return const_cast<Data::SyncthingDeviceModel *>(&m_devModel); return const_cast<Data::SyncthingDeviceModel *>(&m_devModel);

View File

@ -20,13 +20,7 @@ ColumnLayout {
TopLevelView { TopLevelView {
id: directoryView id: directoryView
width: parent.width width: parent.width
model: plasmoid.nativeInterface.sortFilterDirModel
model: PlasmaCore.SortFilterModel {
id: directoryFilterModel
sourceModel: plasmoid.nativeInterface.dirModel
filterRole: "name"
filterRegExp: filter.text
}
delegate: TopLevelItem { delegate: TopLevelItem {
id: item id: item
@ -37,8 +31,6 @@ ColumnLayout {
property alias rescanButton: rescanButton property alias rescanButton: rescanButton
property alias resumePauseButton: resumePauseButton property alias resumePauseButton: resumePauseButton
property alias openButton: openButton property alias openButton: openButton
property int sourceIndex: directoryFilterModel.mapRowToSource(
index)
ColumnLayout { ColumnLayout {
width: parent.width width: parent.width
@ -122,7 +114,7 @@ ColumnLayout {
model: DelegateModel { model: DelegateModel {
model: plasmoid.nativeInterface.dirModel model: plasmoid.nativeInterface.dirModel
rootIndex: detailsView.model.modelIndex(sourceIndex) rootIndex: directoryView.model.mapToSource(directoryView.model.index(index, 0))
delegate: DetailItem { delegate: DetailItem {
width: detailsView.width width: detailsView.width
} }
@ -185,5 +177,6 @@ ColumnLayout {
clearButtonShown: true clearButtonShown: true
Layout.fillWidth: true Layout.fillWidth: true
visible: explicitelyShown || text !== "" visible: explicitelyShown || text !== ""
onTextChanged: directoryView.model.filterRegularExpression = new RegExp(text)
} }
} }

View File

@ -14,6 +14,7 @@ class DevView : public QTreeView {
Q_OBJECT Q_OBJECT
public: public:
using ModelType = Data::SyncthingDeviceModel; using ModelType = Data::SyncthingDeviceModel;
using SortFilterModelType = void;
DevView(QWidget *parent = nullptr); DevView(QWidget *parent = nullptr);

View File

@ -4,6 +4,7 @@
#include "../../connector/syncthingconnection.h" #include "../../connector/syncthingconnection.h"
#include "../../model/syncthingdirectorymodel.h" #include "../../model/syncthingdirectorymodel.h"
#include "../../model/syncthingsortfilterdirectorymodel.h"
#include "../../widgets/misc/direrrorsdialog.h" #include "../../widgets/misc/direrrorsdialog.h"
#include <QClipboard> #include <QClipboard>
@ -30,13 +31,15 @@ void DirView::mouseReleaseEvent(QMouseEvent *event)
{ {
QTreeView::mouseReleaseEvent(event); QTreeView::mouseReleaseEvent(event);
// get SyncthingDir object // get SyncthingDir object for clicked index
auto *const dirModel = qobject_cast<SyncthingDirectoryModel *>(model()); auto *const sortDirModel = qobject_cast<SortFilterModelType *>(model());
auto *const dirModel = qobject_cast<ModelType *>(sortDirModel ? sortDirModel->sourceModel() : model());
if (!dirModel) { if (!dirModel) {
return; return;
} }
const QPoint pos(event->pos()); const auto pos = event->pos();
const QModelIndex clickedIndex(indexAt(event->pos())); const auto clickedProxyIndex = indexAt(event->pos());
const auto clickedIndex = sortDirModel ? sortDirModel->mapToSource(clickedProxyIndex) : clickedProxyIndex;
if (!clickedIndex.isValid() || clickedIndex.column() != 1) { if (!clickedIndex.isValid() || clickedIndex.column() != 1) {
return; return;
} }
@ -47,7 +50,7 @@ void DirView::mouseReleaseEvent(QMouseEvent *event)
if (!clickedIndex.parent().isValid()) { if (!clickedIndex.parent().isValid()) {
// open/scan dir buttons // open/scan dir buttons
const QRect itemRect(visualRect(clickedIndex)); const QRect itemRect = visualRect(clickedProxyIndex);
if (pos.x() <= itemRect.right() - 58) { if (pos.x() <= itemRect.right() - 58) {
return; return;
} }

View File

@ -6,6 +6,7 @@
namespace Data { namespace Data {
struct SyncthingDir; struct SyncthingDir;
class SyncthingDirectoryModel; class SyncthingDirectoryModel;
class SyncthingSortFilterDirectoryModel;
} // namespace Data } // namespace Data
namespace QtGui { namespace QtGui {
@ -14,6 +15,7 @@ class DirView : public QTreeView {
Q_OBJECT Q_OBJECT
public: public:
using ModelType = Data::SyncthingDirectoryModel; using ModelType = Data::SyncthingDirectoryModel;
using SortFilterModelType = Data::SyncthingSortFilterDirectoryModel;
DirView(QWidget *parent = nullptr); DirView(QWidget *parent = nullptr);

View File

@ -15,6 +15,7 @@ class DownloadView : public QTreeView {
Q_OBJECT Q_OBJECT
public: public:
using ModelType = Data::SyncthingDownloadModel; using ModelType = Data::SyncthingDownloadModel;
using SortFilterModelType = void;
DownloadView(QWidget *parent = nullptr); DownloadView(QWidget *parent = nullptr);

View File

@ -9,6 +9,7 @@
#include <QTreeView> #include <QTreeView>
#include <functional> #include <functional>
#include <type_traits>
QT_FORWARD_DECLARE_CLASS(QPoint) QT_FORWARD_DECLARE_CLASS(QPoint)
QT_FORWARD_DECLARE_CLASS(QMenu) QT_FORWARD_DECLARE_CLASS(QMenu)
@ -42,7 +43,15 @@ template <typename ViewType> SelectedRow<ViewType>::SelectedRow(ViewType *view)
return; return;
} }
index = selectedRows.at(0); index = selectedRows.at(0);
model = qobject_cast<typename ViewType::ModelType *>(view->model()); if constexpr (std::is_void_v<typename ViewType::SortFilterModelType>) {
model = qobject_cast<typename ViewType::ModelType *>(view->model());
} else {
const auto *const sortFilterModel = qobject_cast<typename ViewType::SortFilterModelType *>(view->model());
if (sortFilterModel) {
index = sortFilterModel->mapToSource(index);
model = qobject_cast<typename ViewType::ModelType *>(sortFilterModel->sourceModel());
}
}
if (model) { if (model) {
data = model->info(index); data = model->info(index);
} }

View File

@ -76,6 +76,7 @@ TrayWidget::TrayWidget(TrayMenu *parent)
#endif #endif
, m_notifier(m_connection) , m_notifier(m_connection)
, m_dirModel(m_connection) , m_dirModel(m_connection)
, m_sortFilterDirModel(&m_dirModel)
, m_devModel(m_connection) , m_devModel(m_connection)
, m_dlModel(m_connection) , m_dlModel(m_connection)
, m_recentChangesModel(m_connection) , m_recentChangesModel(m_connection)
@ -86,8 +87,9 @@ TrayWidget::TrayWidget(TrayMenu *parent)
m_ui->setupUi(this); m_ui->setupUi(this);
// setup model and view // setup models and views
m_ui->dirsTreeView->setModel(&m_dirModel); m_ui->dirsTreeView->header()->setSortIndicator(0, Qt::AscendingOrder);
m_ui->dirsTreeView->setModel(&m_sortFilterDirModel);
m_ui->devsTreeView->setModel(&m_devModel); m_ui->devsTreeView->setModel(&m_devModel);
m_ui->downloadsTreeView->setModel(&m_dlModel); m_ui->downloadsTreeView->setModel(&m_dlModel);
m_ui->recentChangesTreeView->setModel(&m_recentChangesModel); m_ui->recentChangesTreeView->setModel(&m_recentChangesModel);

View File

@ -8,6 +8,7 @@
#include "../../model/syncthingdirectorymodel.h" #include "../../model/syncthingdirectorymodel.h"
#include "../../model/syncthingdownloadmodel.h" #include "../../model/syncthingdownloadmodel.h"
#include "../../model/syncthingrecentchangesmodel.h" #include "../../model/syncthingrecentchangesmodel.h"
#include "../../model/syncthingsortfilterdirectorymodel.h"
#include "../../connector/syncthingconnection.h" #include "../../connector/syncthingconnection.h"
#include "../../connector/syncthingnotifier.h" #include "../../connector/syncthingnotifier.h"
@ -118,6 +119,7 @@ private:
Data::SyncthingConnection m_connection; Data::SyncthingConnection m_connection;
Data::SyncthingNotifier m_notifier; Data::SyncthingNotifier m_notifier;
Data::SyncthingDirectoryModel m_dirModel; Data::SyncthingDirectoryModel m_dirModel;
Data::SyncthingSortFilterDirectoryModel m_sortFilterDirModel;
Data::SyncthingDeviceModel m_devModel; Data::SyncthingDeviceModel m_devModel;
Data::SyncthingDownloadModel m_dlModel; Data::SyncthingDownloadModel m_dlModel;
Data::SyncthingRecentChangesModel m_recentChangesModel; Data::SyncthingRecentChangesModel m_recentChangesModel;

View File

@ -403,7 +403,11 @@ For &lt;i&gt;all&lt;/i&gt; notifications, checkout the log</string>
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QtGui::DirView" name="dirsTreeView"/> <widget class="QtGui::DirView" name="dirsTreeView">
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>