From a4259223e09d00f7d4a510ccc13bc1eb735c7683 Mon Sep 17 00:00:00 2001 From: Martchus Date: Wed, 28 Sep 2016 00:06:21 +0200 Subject: [PATCH] Improve displaying downloads --- data/syncthingconnection.cpp | 7 +-- data/syncthingdownloadmodel.cpp | 26 ++++++++- data/syncthingdownloadmodel.h | 12 +++- gui/downloaditemdelegate.cpp | 89 ++++++++++++++++++++--------- gui/downloaditemdelegate.h | 1 + gui/downloadview.cpp | 10 ++-- testdata/downloadprogressevent.json | 2 +- 7 files changed, 106 insertions(+), 41 deletions(-) diff --git a/data/syncthingconnection.cpp b/data/syncthingconnection.cpp index ae53b91..c617b15 100644 --- a/data/syncthingconnection.cpp +++ b/data/syncthingconnection.cpp @@ -102,7 +102,7 @@ bool SyncthingDir::assignStatus(DirStatus newStatus, DateTime time) SyncthingItemDownloadProgress::SyncthingItemDownloadProgress(const QString &containingDirPath, const QString &relativeItemPath, const QJsonObject &values) : relativePath(relativeItemPath), - fileInfo(containingDirPath % QChar('/') % relativeItemPath), + fileInfo(containingDirPath % QChar('/') % QString(relativeItemPath).replace(QChar('\\'), QChar('/'))), blocksCurrentlyDownloading(values.value(QStringLiteral("Pulling")).toInt()), blocksAlreadyDownloaded(values.value(QStringLiteral("Pulled")).toInt()), totalNumberOfBlocks(values.value(QStringLiteral("Total")).toInt()), @@ -681,7 +681,7 @@ void SyncthingConnection::readConfig() void SyncthingConnection::readDirs(const QJsonArray &dirs) { m_dirs.clear(); - m_dirs.reserve(dirs.size()); + m_dirs.reserve(static_cast(dirs.size())); for(const QJsonValue &dirVal : dirs) { const QJsonObject dirObj(dirVal.toObject()); SyncthingDir dirItem; @@ -712,7 +712,7 @@ void SyncthingConnection::readDirs(const QJsonArray &dirs) void SyncthingConnection::readDevs(const QJsonArray &devs) { m_devs.clear(); - m_devs.reserve(devs.size()); + m_devs.reserve(static_cast(devs.size())); for(const QJsonValue &devVal: devs) { const QJsonObject devObj(devVal.toObject()); SyncthingDev devItem; @@ -1121,7 +1121,6 @@ void SyncthingConnection::readStatusChangedEvent(DateTime eventTime, const QJson /*! * \brief Reads results of requestEvents(). - * \remarks TODO */ void SyncthingConnection::readDownloadProgressEvent(DateTime eventTime, const QJsonObject &eventData) { diff --git a/data/syncthingdownloadmodel.cpp b/data/syncthingdownloadmodel.cpp index 20d6fd5..83e1432 100644 --- a/data/syncthingdownloadmodel.cpp +++ b/data/syncthingdownloadmodel.cpp @@ -13,7 +13,8 @@ SyncthingDownloadModel::SyncthingDownloadModel(SyncthingConnection &connection, m_connection(connection), m_dirs(connection.dirInfo()), m_unknownIcon(QIcon::fromTheme(QStringLiteral("text-x-generic"), QIcon(QStringLiteral(":/icons/hicolor/scalable/mimetypes/text-x-generic.svg")))), - m_pendingDirs(0) + m_pendingDirs(0), + m_singleColumnMode(true) { connect(&m_connection, &SyncthingConnection::newConfig, this, &SyncthingDownloadModel::newConfig); connect(&m_connection, &SyncthingConnection::newDirs, this, &SyncthingDownloadModel::newDirs); @@ -112,6 +113,8 @@ QVariant SyncthingDownloadModel::data(const QModelIndex &index, int role) const break; case ItemPercentage: return progress.downloadPercentage; + case ItemProgressLabel: + return progress.label; default: ; } @@ -136,6 +139,8 @@ QVariant SyncthingDownloadModel::data(const QModelIndex &index, int role) const break; case ItemPercentage: return dir.downloadPercentage; + case ItemProgressLabel: + return dir.downloadLabel; default: ; } @@ -164,9 +169,9 @@ int SyncthingDownloadModel::rowCount(const QModelIndex &parent) const int SyncthingDownloadModel::columnCount(const QModelIndex &parent) const { if(!parent.isValid()) { - return 2; // label/ID, status/progress + return singleColumnMode() ? 1 : 2; // label/ID, status/progress } else if(!parent.parent().isValid()) { - return 2; // file, progress + return singleColumnMode() ? 1 : 2; // file, progress } else { return 0; } @@ -210,4 +215,19 @@ void SyncthingDownloadModel::downloadProgressChanged() } } +void SyncthingDownloadModel::setSingleColumnMode(bool singleColumnModeEnabled) +{ + if(m_singleColumnMode != singleColumnModeEnabled) { + if(m_singleColumnMode) { + beginInsertColumns(QModelIndex(), 1, 1); + m_singleColumnMode = true; + endInsertColumns(); + } else { + beginRemoveColumns(QModelIndex(), 1, 1); + m_singleColumnMode = false; + endRemoveColumns(); + } + } +} + } // namespace Data diff --git a/data/syncthingdownloadmodel.h b/data/syncthingdownloadmodel.h index 2030de3..d186203 100644 --- a/data/syncthingdownloadmodel.h +++ b/data/syncthingdownloadmodel.h @@ -17,12 +17,14 @@ class SyncthingDownloadModel : public QAbstractItemModel { Q_OBJECT Q_PROPERTY(unsigned int pendingDownloads READ pendingDownloads NOTIFY pendingDownloadsChanged) + Q_PROPERTY(bool singleColumnMode READ singleColumnMode WRITE setSingleColumnMode) public: explicit SyncthingDownloadModel(SyncthingConnection &connection, QObject *parent = nullptr); enum SyncthingDownloadModelRole { - ItemPercentage = Qt::UserRole + 1 + ItemPercentage = Qt::UserRole + 1, + ItemProgressLabel }; public Q_SLOTS: @@ -36,6 +38,8 @@ public Q_SLOTS: const SyncthingDir *dirInfo(const QModelIndex &index) const; const SyncthingItemDownloadProgress *progressInfo(const QModelIndex &index) const; unsigned int pendingDownloads() const; + bool singleColumnMode() const; + void setSingleColumnMode(bool singleColumnModeEnabled); Q_SIGNALS: void pendingDownloadsChanged(unsigned int pendingDownloads); @@ -52,6 +56,7 @@ private: const QFileIconProvider m_fileIconProvider; std::vector m_pendingDirs; unsigned int m_pendingDownloads; + bool m_singleColumnMode; }; inline unsigned int SyncthingDownloadModel::pendingDownloads() const @@ -59,6 +64,11 @@ inline unsigned int SyncthingDownloadModel::pendingDownloads() const return m_pendingDownloads; } +inline bool SyncthingDownloadModel::singleColumnMode() const +{ + return m_singleColumnMode; +} + } // namespace Data #endif // DATA_SYNCTHINGDOWNLOADMODEL_H diff --git a/gui/downloaditemdelegate.cpp b/gui/downloaditemdelegate.cpp index 1ff5e99..2345330 100644 --- a/gui/downloaditemdelegate.cpp +++ b/gui/downloaditemdelegate.cpp @@ -10,7 +10,11 @@ #include #include #include +#include +#include + +using namespace std; using namespace Data; namespace QtGui { @@ -27,37 +31,66 @@ DownloadItemDelegate::DownloadItemDelegate(QObject* parent) : void DownloadItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - // use the customization only on top-level rows - //if(!index.parent().isValid()) { - // QStyledItemDelegate::paint(painter, option, index); - //} else { - // init style options to use drawControl(), except for the text - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - opt.text.clear(); - opt.features = QStyleOptionViewItem::None; - QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter); + // init style options to use drawControl(), except for the text + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + opt.textElideMode = Qt::ElideNone; // elide manually + opt.features = QStyleOptionViewItem::None; + if(index.parent().isValid()) { + opt.displayAlignment = Qt::AlignTop | Qt::AlignLeft; + opt.decorationSize = QSize(option.rect.height(), option.rect.height()); + opt.features |= QStyleOptionViewItem::HasDecoration; + opt.text = option.fontMetrics.elidedText(opt.text, Qt::ElideMiddle, opt.rect.width() - opt.rect.height() - 26); + } else { + opt.text = option.fontMetrics.elidedText(opt.text, Qt::ElideMiddle, opt.rect.width() / 2 - 4); + } + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter); - // draw progress bar - const QAbstractItemModel *model = index.model(); - QStyleOptionProgressBar progressBarOption; - progressBarOption.state = QStyle::State_Enabled; - progressBarOption.direction = QApplication::layoutDirection(); - progressBarOption.rect = option.rect; - progressBarOption.rect.setWidth(option.rect.width() - 20); - progressBarOption.textAlignment = Qt::AlignCenter; - progressBarOption.textVisible = true; - progressBarOption.progress = model->data(index, SyncthingDownloadModel::ItemPercentage).toInt(); - progressBarOption.minimum = 0; - progressBarOption.maximum = 100; - progressBarOption.text = model->data(index, Qt::DisplayRole).toString(); - QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); + // draw progress bar + const QAbstractItemModel *model = index.model(); + QStyleOptionProgressBar progressBarOption; + progressBarOption.state = option.state; + progressBarOption.direction = option.direction; + progressBarOption.rect = option.rect; + if(index.parent().isValid()) { + progressBarOption.rect.setX(opt.rect.x() + opt.rect.height() + 4); + progressBarOption.rect.setY(opt.rect.y() + opt.rect.height() / 2); + } else { + progressBarOption.rect.setX(opt.rect.x() + opt.fontMetrics.width(opt.text) + 6); + progressBarOption.rect.setWidth(progressBarOption.rect.width() - 18); + } + progressBarOption.textAlignment = Qt::AlignCenter; + progressBarOption.textVisible = true; + if(option.state & QStyle::State_Selected) { + progressBarOption.palette.setBrush(QPalette::Foreground, option.palette.brush(QPalette::HighlightedText)); + } + progressBarOption.progress = model->data(index, SyncthingDownloadModel::ItemPercentage).toInt(); + progressBarOption.minimum = 0; + progressBarOption.maximum = 100; + progressBarOption.text = model->data(index, SyncthingDownloadModel::ItemProgressLabel).toString(); + QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); - // draw buttons - const int buttonY = option.rect.y() + centerObj(option.rect.height(), 16); - painter->drawPixmap(option.rect.right() - 16, buttonY, 16, 16, m_folderIcon); - //} + // draw buttons + int buttonY = option.rect.y(); + if(!index.parent().isValid()) { + buttonY += centerObj(progressBarOption.rect.height(), 16); + } + painter->drawPixmap(option.rect.right() - 16, buttonY, 16, 16, m_folderIcon); + // draw file icon + if(index.parent().isValid()) { + const int fileIconHeight = option.rect.height() - 2; + painter->drawPixmap(option.rect.left(), option.rect.y() + 1, fileIconHeight, fileIconHeight, model->data(index, Qt::DecorationRole).value().pixmap(fileIconHeight)); + } +} + +QSize DownloadItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize defaultSize(QStyledItemDelegate::sizeHint(option, index)); + if(index.parent().isValid()) { + defaultSize.setHeight(defaultSize.height() + defaultSize.height() - 12); + } + return defaultSize; } } diff --git a/gui/downloaditemdelegate.h b/gui/downloaditemdelegate.h index 4471d97..41aa55e 100644 --- a/gui/downloaditemdelegate.h +++ b/gui/downloaditemdelegate.h @@ -13,6 +13,7 @@ public: DownloadItemDelegate(QObject *parent); void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; private: const QPixmap m_folderIcon; diff --git a/gui/downloadview.cpp b/gui/downloadview.cpp index 78e0ad3..ae8c812 100644 --- a/gui/downloadview.cpp +++ b/gui/downloadview.cpp @@ -19,7 +19,7 @@ DownloadView::DownloadView(QWidget *parent) : { header()->setSectionResizeMode(QHeaderView::ResizeToContents); header()->hide(); - setItemDelegateForColumn(1, new DownloadItemDelegate(this)); + setItemDelegateForColumn(0, new DownloadItemDelegate(this)); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &DownloadView::customContextMenuRequested, this, &DownloadView::showContextMenu); } @@ -30,12 +30,14 @@ void DownloadView::mouseReleaseEvent(QMouseEvent *event) if(const SyncthingDownloadModel *dlModel = qobject_cast(model())) { const QPoint pos(event->pos()); const QModelIndex clickedIndex(indexAt(event->pos())); - if(clickedIndex.isValid() && clickedIndex.column() == 1) { + if(clickedIndex.isValid() && clickedIndex.column() == 0) { const QRect itemRect(visualRect(clickedIndex)); if(pos.x() > itemRect.right() - 17) { if(clickedIndex.parent().isValid()) { - if(const SyncthingItemDownloadProgress *progress = dlModel->progressInfo(clickedIndex)) { - emit openItemDir(*progress); + if(pos.y() < itemRect.y() + itemRect.height() / 2) { + if(const SyncthingItemDownloadProgress *progress = dlModel->progressInfo(clickedIndex)) { + emit openItemDir(*progress); + } } } else if(const SyncthingDir *dir = dlModel->dirInfo(clickedIndex)) { emit openDir(*dir); diff --git a/testdata/downloadprogressevent.json b/testdata/downloadprogressevent.json index 0839d74..b04ce83 100644 --- a/testdata/downloadprogressevent.json +++ b/testdata/downloadprogressevent.json @@ -36,7 +36,7 @@ "BytesTotal": 104792064, "BytesDone": 87883776 }, - "dir\\file4": { + "directory1\\directory\\directory3\\directory4\\directory5\\directory3\\directory4\\directory5\\file4": { "Total": 80, "Pulling": 2, "CopiedFromOrigin": 0,