From 890302e40e7cf0c4b08b9c336bba6bda7951e918 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sat, 21 Dec 2024 16:26:47 +0100 Subject: [PATCH] Improve directory and device models * Show number and size of out of sync items * Bring order of rows in device model more in-line with the official UI * Bring wording and icons more in-line with the official UI * Improve naming of ForkAwesome icon fields --- syncthingmodel/syncthingdevicemodel.cpp | 82 +++++++++++++--------- syncthingmodel/syncthingdirectorymodel.cpp | 27 ++++--- syncthingmodel/syncthingicons.cpp | 7 +- syncthingmodel/syncthingicons.h | 7 +- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/syncthingmodel/syncthingdevicemodel.cpp b/syncthingmodel/syncthingdevicemodel.cpp index c267d446..4a985714 100644 --- a/syncthingmodel/syncthingdevicemodel.cpp +++ b/syncthingmodel/syncthingdevicemodel.cpp @@ -17,7 +17,7 @@ namespace Data { static int computeDeviceRowCount(const SyncthingDev &dev) { // hide connection type, last seen and everything after introducer (eg. traffic) unless connected - return dev.isConnected() ? 10 : 5; + return dev.isConnected() ? 11 : 6; } SyncthingDeviceModel::SyncthingDeviceModel(SyncthingConnection &connection, QObject *parent) @@ -115,7 +115,11 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const return QVariant(); } const auto &dev = m_devs[static_cast(index.parent().row())]; - const auto row = !dev.isConnected() && index.row() >= 2 ? index.row() + 2 : index.row(); + const auto skipRows = !dev.isConnected(); + auto row = skipRows && index.row() >= 2 ? index.row() + 2 : index.row(); + if (skipRows && row >= 5) { + row += 2; + } switch (role) { case Qt::DisplayRole: case Qt::EditRole: @@ -125,22 +129,24 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const case 0: return tr("ID"); case 1: - return tr("Address"); + return tr("Out of Sync items"); case 2: - return tr("Connection type"); + return tr("Incoming traffic"); case 3: - return tr("Last seen"); + return tr("Outgoing traffic"); case 4: - return tr("Compression"); + return tr("Address"); case 5: - return tr("Certificate"); + return tr("Connection type"); case 6: - return tr("Introducer"); + return tr("Last seen"); case 7: - return tr("Incoming traffic"); + return tr("Compression"); case 8: - return tr("Outgoing traffic"); + return tr("Certificate"); case 9: + return tr("Introducer"); + case 10: return tr("Version"); } break; @@ -153,33 +159,38 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const case 0: return dev.id; case 1: + if (dev.overallCompletion.needed.isNull()) { + return tr("none"); + } + return tr("%1 item(s), ~ %2", nullptr, trQuandity(dev.overallCompletion.needed.items)).arg(dev.overallCompletion.needed.items).arg(dataSizeToString(dev.overallCompletion.needed.bytes).data()); + case 2: + return QString::fromStdString(dataSizeToString(dev.totalIncomingTraffic)); + case 3: + return QString::fromStdString(dataSizeToString(dev.totalOutgoingTraffic)); + case 4: if (dev.connectionAddress.isEmpty()) { return dev.addresses.join(QStringLiteral(", ")); } else { return QVariant( dev.connectionAddress % QStringLiteral(" (") % dev.addresses.join(QStringLiteral(", ")) % QStringLiteral(")")); } - case 2: + case 5: if (!dev.connectionType.isEmpty()) { return QVariant( dev.connectionType % QStringLiteral(" (") % (dev.connectionLocal ? tr("local") : tr("remote")) % QStringLiteral(")")); } else { return QVariant(); } - case 3: + case 6: return dev.lastSeen.isNull() ? tr("unknown or this device") : QString::fromLatin1(dev.lastSeen.toString(DateTimeOutputFormat::DateAndTime, true).data()); - case 4: - return dev.compression; - case 5: - return dev.certName.isEmpty() ? tr("none") : dev.certName; - case 6: - return dev.introducer ? tr("yes") : tr("no"); case 7: - return QString::fromStdString(dataSizeToString(dev.totalIncomingTraffic)); + return dev.compression; case 8: - return QString::fromStdString(dataSizeToString(dev.totalOutgoingTraffic)); + return dev.certName.isEmpty() ? tr("none") : dev.certName; case 9: + return dev.introducer ? tr("yes") : tr("no"); + case 10: return dev.clientVersion; } } @@ -193,22 +204,24 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const case 0: return icons.hashtag; case 1: - return icons.link; - case 2: return icons.exchange; + case 2: + return icons.cloudDownload; case 3: - return icons.eye; + return icons.cloudUpload; case 4: - return icons.fileArchive; + return icons.link; case 5: - return icons.certificate; + return icons.signal; case 6: - return icons.networkWired; + return icons.eye; case 7: - return icons.cloudDownloadAlt; + return icons.fileArchive; case 8: - return icons.cloudUploadAlt; + return icons.certificate; case 9: + return icons.networkWired; + case 10: return icons.tag; } } @@ -217,12 +230,17 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const switch (index.column()) { case 1: switch (row) { - case 3: + case 1: + if (dev.overallCompletion.needed.isNull()) { + return Colors::gray(m_brightColors); + } + break; + case 6: if (dev.lastSeen.isNull()) { return Colors::gray(m_brightColors); } break; - case 5: + case 8: if (dev.certName.isEmpty()) { return Colors::gray(m_brightColors); } @@ -234,12 +252,12 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const switch (index.column()) { case 1: switch (row) { - case 1: + case 4: if (!dev.connectionType.isEmpty()) { return dev.connectionType; } break; - case 3: + case 6: if (!dev.lastSeen.isNull()) { return agoString(dev.lastSeen); } diff --git a/syncthingmodel/syncthingdirectorymodel.cpp b/syncthingmodel/syncthingdirectorymodel.cpp index 0f0f0aa1..f044307e 100644 --- a/syncthingmodel/syncthingdirectorymodel.cpp +++ b/syncthingmodel/syncthingdirectorymodel.cpp @@ -16,7 +16,7 @@ namespace Data { static int computeDirectoryRowCount(const SyncthingDir &dir) { - return dir.paused ? 8 : 10; + return dir.paused ? 8 : 11; } SyncthingDirectoryModel::SyncthingDirectoryModel(SyncthingConnection &connection, QObject *parent) @@ -140,7 +140,9 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const case 8: return tr("Last file"); case 9: - return tr("Errors"); + return tr("Out of Sync items"); + case 10: + return tr("Failed items"); } break; } @@ -175,6 +177,11 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const case 8: return dir.lastFileName.isEmpty() ? tr("unknown") : dir.lastFileName; case 9: + if (dir.neededStats.isNull()) { + return tr("none"); + } + return tr("%1 item(s), ~ %2", nullptr, trQuandity(dir.neededStats.total)).arg(dir.neededStats.total).arg(dataSizeToString(dir.neededStats.bytes).data()); + case 10: if (dir.globalError.isEmpty() && !dir.pullErrorCount) { return tr("none"); } @@ -182,9 +189,9 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const return dir.globalError; } if (dir.globalError.isEmpty()) { - return tr("%1 item(s) out of sync", nullptr, trQuandity(dir.pullErrorCount)).arg(dir.pullErrorCount); + return tr("%1 item(s)", nullptr, trQuandity(dir.pullErrorCount)).arg(dir.pullErrorCount); } - return tr("%1 and %2 item(s) out of sync", nullptr, trQuandity(dir.pullErrorCount)).arg(dir.globalError).arg(dir.pullErrorCount); + return tr("%1 and %2 item(s)", nullptr, trQuandity(dir.pullErrorCount)).arg(dir.globalError).arg(dir.pullErrorCount); } } break; @@ -211,9 +218,11 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const case 7: return icons.clock; case 8: - return icons.exchangeAlt; + return icons.exchange; case 9: - return icons.exclamationTriangle; + return icons.cloudDownload; + case 10: + return icons.exclamationCircle; } } break; @@ -235,6 +244,8 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const return dir.lastFileName.isEmpty() ? Colors::gray(m_brightColors) : (dir.lastFileDeleted ? Colors::red(m_brightColors) : QVariant()); case 9: + return dir.neededStats.total == 0 ? Colors::gray(m_brightColors) : Colors::red(m_brightColors); + case 10: return dir.globalError.isEmpty() && !dir.pullErrorCount ? Colors::gray(m_brightColors) : Colors::red(m_brightColors); } } @@ -266,9 +277,9 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const } } break; - case 9: + case 10: if (!dir.itemErrors.empty()) { - QStringList errors; + auto errors = QStringList(); errors.reserve(static_cast(dir.itemErrors.size())); for (const auto &error : dir.itemErrors) { errors << error.path; diff --git a/syncthingmodel/syncthingicons.cpp b/syncthingmodel/syncthingicons.cpp index e4133242..c5954375 100644 --- a/syncthingmodel/syncthingicons.cpp +++ b/syncthingmodel/syncthingicons.cpp @@ -319,8 +319,8 @@ ForkAwesomeIcons::ForkAwesomeIcons(QtForkAwesome::Renderer &renderer, const QCol , shareAlt(renderer.pixmap(QtForkAwesome::Icon::ShareAlt, size, color)) , refresh(renderer.pixmap(QtForkAwesome::Icon::Refresh, size, color)) , clock(renderer.pixmap(QtForkAwesome::Icon::ClockO, size, color)) - , exchangeAlt(renderer.pixmap(QtForkAwesome::Icon::Exchange, size, color)) , exclamation(renderer.pixmap(QtForkAwesome::Icon::Exclamation, size, color)) + , exclamationCircle(renderer.pixmap(QtForkAwesome::Icon::ExclamationCircle, size, color)) , exclamationTriangle(renderer.pixmap(QtForkAwesome::Icon::ExclamationTriangle, size, color)) , cogs(renderer.pixmap(QtForkAwesome::Icon::Cogs, size, color)) , link(renderer.pixmap(QtForkAwesome::Icon::Link, size, color)) @@ -330,10 +330,11 @@ ForkAwesomeIcons::ForkAwesomeIcons(QtForkAwesome::Renderer &renderer, const QCol , folder(renderer.pixmap(QtForkAwesome::Icon::Folder, size, color)) , certificate(renderer.pixmap(QtForkAwesome::Icon::Certificate, size, color)) , networkWired(renderer.pixmap(QtForkAwesome::Icon::Sitemap, size, color)) - , cloudDownloadAlt(renderer.pixmap(QtForkAwesome::Icon::CloudDownload, size, color)) - , cloudUploadAlt(renderer.pixmap(QtForkAwesome::Icon::CloudUpload, size, color)) + , cloudDownload(renderer.pixmap(QtForkAwesome::Icon::CloudDownload, size, color)) + , cloudUpload(renderer.pixmap(QtForkAwesome::Icon::CloudUpload, size, color)) , tag(renderer.pixmap(QtForkAwesome::Icon::Tag, size, color)) , exchange(renderer.pixmap(QtForkAwesome::Icon::Exchange, size, color)) + , signal(renderer.pixmap(QtForkAwesome::Icon::Signal, size, color)) { } diff --git a/syncthingmodel/syncthingicons.h b/syncthingmodel/syncthingicons.h index d6671b5f..215875e5 100644 --- a/syncthingmodel/syncthingicons.h +++ b/syncthingmodel/syncthingicons.h @@ -138,8 +138,8 @@ struct LIB_SYNCTHING_MODEL_EXPORT ForkAwesomeIcons { QIcon shareAlt; QIcon refresh; QIcon clock; - QIcon exchangeAlt; QIcon exclamation; + QIcon exclamationCircle; QIcon exclamationTriangle; QIcon cogs; QIcon link; @@ -149,10 +149,11 @@ struct LIB_SYNCTHING_MODEL_EXPORT ForkAwesomeIcons { QIcon folder; QIcon certificate; QIcon networkWired; - QIcon cloudDownloadAlt; - QIcon cloudUploadAlt; + QIcon cloudDownload; + QIcon cloudUpload; QIcon tag; QIcon exchange; + QIcon signal; }; class LIB_SYNCTHING_MODEL_EXPORT IconManager : public QObject {