diff options
author | Hannah von Reth <hannah.vonreth@owncloud.com> | 2021-06-01 16:55:59 +0300 |
---|---|---|
committer | Hannah von Reth <vonreth@kde.org> | 2021-06-15 14:43:40 +0300 |
commit | e84605055fe576faba9d9245350e281e10403627 (patch) | |
tree | 3b5f95a2a8ca36ef0c1c51588d36370d1209987f /src | |
parent | dadf8ceb704e406175862349289e035d3af632d5 (diff) |
Change the resize behaviour of the header view
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/gui/activitywidget.cpp | 27 | ||||
-rw-r--r-- | src/gui/issueswidget.cpp | 24 | ||||
-rw-r--r-- | src/gui/models/expandingheaderview.cpp | 113 | ||||
-rw-r--r-- | src/gui/models/expandingheaderview.h | 43 | ||||
-rw-r--r-- | src/gui/models/models.cpp | 8 | ||||
-rw-r--r-- | src/gui/models/models.h | 23 | ||||
-rw-r--r-- | src/gui/protocolwidget.cpp | 33 | ||||
-rw-r--r-- | src/gui/protocolwidget.h | 3 |
9 files changed, 223 insertions, 52 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 5194120dd..4c5a9f873 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -89,6 +89,7 @@ set(client_SRCS creds/httpcredentialsgui.cpp models/activitylistmodel.cpp + models/expandingheaderview.cpp models/models.cpp models/protocolitemmodel.cpp diff --git a/src/gui/activitywidget.cpp b/src/gui/activitywidget.cpp index 51cb8ae8d..5e5014407 100644 --- a/src/gui/activitywidget.cpp +++ b/src/gui/activitywidget.cpp @@ -38,6 +38,7 @@ #include "ocsjob.h" #include "models/activitylistmodel.h" +#include "models/expandingheaderview.h" #include "models/models.h" #include "ui_activitywidget.h" @@ -62,19 +63,11 @@ ActivityWidget::ActivityWidget(QWidget *parent) _sortModel->setSourceModel(_model); _ui->_activityList->setModel(_sortModel); _sortModel->setSortRole(Models::UnderlyingDataRole); - _ui->_activityList->hideColumn(static_cast<int>(ActivityListModel::ActivityRole::Path)); - _ui->_activityList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - _ui->_activityList->horizontalHeader()->setSectionResizeMode(static_cast<int>(ActivityListModel::ActivityRole::Text), QHeaderView::Stretch); - _ui->_activityList->horizontalHeader()->setSortIndicator(static_cast<int>(ActivityListModel::ActivityRole::PointInTime), Qt::DescendingOrder); - - ConfigFile cfg; - _ui->_activityList->horizontalHeader()->setObjectName(QStringLiteral("ActivityListModelHeader")); - cfg.restoreGeometryHeader(_ui->_activityList->horizontalHeader()); - - connect(qApp, &QApplication::aboutToQuit, this, [this] { - ConfigFile cfg; - cfg.saveGeometryHeader(_ui->_activityList->horizontalHeader()); - }); + auto header = new ExpandingHeaderView(QStringLiteral("ActivityListModelHeader"), _ui->_activityList); + _ui->_activityList->setHorizontalHeader(header); + header->hideSection(static_cast<int>(ActivityListModel::ActivityRole::Path)); + header->setSectionResizeMode(QHeaderView::Interactive); + header->setSortIndicator(static_cast<int>(ActivityListModel::ActivityRole::PointInTime), Qt::DescendingOrder); _ui->_notifyLabel->hide(); _ui->_notifyScroll->hide(); @@ -107,9 +100,11 @@ ActivityWidget::ActivityWidget(QWidget *parent) connect(_model, &QAbstractItemModel::modelReset, this, &ActivityWidget::dataChanged); connect(_ui->_activityList, &QListView::customContextMenuRequested, this, &ActivityWidget::slotItemContextMenu); - _ui->_activityList->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(_ui->_activityList->horizontalHeader(), &QListView::customContextMenuRequested, this, [this] { - Models::displayFilterDialog(AccountManager::instance()->accountNames(), _sortModel, static_cast<int>(ActivityListModel::ActivityRole::Account), Qt::DisplayRole, this); + header->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header, &QListView::customContextMenuRequested, header, [header, this] { + auto menu = Models::displayFilterDialog(AccountManager::instance()->accountNames(), _sortModel, static_cast<int>(ActivityListModel::ActivityRole::Account), Qt::DisplayRole, this); + menu->addSeparator(); + menu->addAction(tr("Reset column sizes"), this, [header] { header->resizeColumns(true); }); }); connect(&_removeTimer, &QTimer::timeout, this, &ActivityWidget::slotCheckToCleanWidgets); diff --git a/src/gui/issueswidget.cpp b/src/gui/issueswidget.cpp index 01d1c9e20..0b801115f 100644 --- a/src/gui/issueswidget.cpp +++ b/src/gui/issueswidget.cpp @@ -24,6 +24,7 @@ #include "folderman.h" #include "syncfileitem.h" #include "folder.h" +#include "models/expandingheaderview.h" #include "models/models.h" #include "openfilemanager.h" #include "protocolwidget.h" @@ -76,23 +77,18 @@ IssuesWidget::IssuesWidget(QWidget *parent) _sortModel = new QSortFilterProxyModel(this); _sortModel->setSourceModel(_model); _ui->_tableView->setModel(_sortModel); - connect(_ui->_tableView, &QTreeView::customContextMenuRequested, this, &IssuesWidget::slotItemContextMenu); - _ui->_tableView->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(_ui->_tableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this] { - ProtocolWidget::showHeaderContextMenu(this, _sortModel); - }); - _ui->_tableView->horizontalHeader()->setObjectName(QStringLiteral("ActivityErrorListHeaderV2")); - _ui->_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - _ui->_tableView->horizontalHeader()->setSectionResizeMode(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Action), QHeaderView::Stretch); - _ui->_tableView->horizontalHeader()->setSortIndicator(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Time), Qt::DescendingOrder); + auto header = new ExpandingHeaderView(QStringLiteral("ActivityErrorListHeaderV2"), _ui->_tableView); + _ui->_tableView->setHorizontalHeader(header); + header->setSectionResizeMode(QHeaderView::Interactive); + header->setExpandingColumn(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Action)); + header->setSortIndicator(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Time), Qt::DescendingOrder); - ConfigFile cfg; - cfg.restoreGeometryHeader(_ui->_tableView->horizontalHeader()); - connect(qApp, &QApplication::aboutToQuit, this, [this] { - ConfigFile cfg; - cfg.saveGeometryHeader(_ui->_tableView->horizontalHeader()); + connect(_ui->_tableView, &QTreeView::customContextMenuRequested, this, &IssuesWidget::slotItemContextMenu); + _ui->_tableView->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header, &QHeaderView::customContextMenuRequested, header, [header, this] { + ProtocolWidget::showHeaderContextMenu(header, _sortModel); }); diff --git a/src/gui/models/expandingheaderview.cpp b/src/gui/models/expandingheaderview.cpp new file mode 100644 index 000000000..0916f7899 --- /dev/null +++ b/src/gui/models/expandingheaderview.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) by Hannah von Reth <hannah.vonreth@owncloud.com> + * + * 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. + */ +#include "expandingheaderview.h" +#include "models.h" + +#include "configfile.h" + +#include <QScopedValueRollback> +#include <QApplication> +#include <QDebug> + +using namespace OCC; + +ExpandingHeaderView::ExpandingHeaderView(const QString &objectName, QWidget *parent) + : QHeaderView(Qt::Horizontal, parent) +{ + setSectionsClickable(true); + setHighlightSections(true); + + connect(this, &QHeaderView::sectionCountChanged, this, &ExpandingHeaderView::resizeColumns); + connect(this, &QHeaderView::sectionResized, this, [this](int index, int oldSize, int newSize) { + if (_isResizing) { + return; + } + QScopedValueRollback<bool> guard(_isResizing, true); + if (index != _expandingColumn) { + // give/take space from _expandingColumn column + resizeSection(_expandingColumn, sectionSize(_expandingColumn) - (newSize - oldSize)); + } else { + // distribute space across all columns + // use actual width as oldSize/newSize isn't reliable here + auto visibleSections = Models::range(count()); + visibleSections.erase(std::remove_if(visibleSections.begin(), visibleSections.end(), [this](int i) { + return isSectionHidden(i); + }), + visibleSections.end()); + if (visibleSections.empty()) { + return; + } + int availableWidth = width(); + for (auto &i : visibleSections) { + availableWidth -= sectionSize(i); + } + + const int diffPerSection = availableWidth / static_cast<int>(visibleSections.size()); + const int extraDiff = availableWidth % visibleSections.size(); + const auto secondLast = visibleSections.cend()[-2]; + for (auto &i : visibleSections) { + if (_expandingColumn == i) { + continue; + } + auto newSize = sectionSize(i) + diffPerSection; + if (i == secondLast) { + newSize += extraDiff; + } + resizeSection(i, newSize); + } + } + }); + + setObjectName(objectName); + ConfigFile cfg; + cfg.restoreGeometryHeader(this); + + connect(qApp, &QApplication::aboutToQuit, this, [this] { + ConfigFile cfg; + cfg.saveGeometryHeader(this); + }); +} + +int ExpandingHeaderView::expandingColumn() const +{ + return _expandingColumn; +} + +void ExpandingHeaderView::setExpandingColumn(int newExpandingColumn) +{ + _expandingColumn = newExpandingColumn; +} + +void ExpandingHeaderView::resizeEvent(QResizeEvent *event) +{ + QHeaderView::resizeEvent(event); + resizeColumns(); +} + +void ExpandingHeaderView::resizeColumns(bool reset) +{ + QScopedValueRollback<bool> guard(_isResizing, true); + int availableWidth = width(); + const auto defaultSize = defaultSectionSize(); + for (int i = 0; i < count(); ++i) { + if (i == _expandingColumn || isSectionHidden(i)) { + continue; + } + if (reset) { + resizeSection(i, defaultSize); + } + availableWidth -= sectionSize(i); + } + resizeSection(_expandingColumn, availableWidth); +} diff --git a/src/gui/models/expandingheaderview.h b/src/gui/models/expandingheaderview.h new file mode 100644 index 000000000..bc997a398 --- /dev/null +++ b/src/gui/models/expandingheaderview.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) by Hannah von Reth <hannah.vonreth@owncloud.com> + * + * 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. + */ +#pragma once + +#include <QHeaderView> + +namespace OCC { + +// Based on https://github.com/KDAB/hotspot/blob/master/src/costheaderview.cpp +// thanks to Milian Wolff <milian.wolff@kdab.com> + +class ExpandingHeaderView : public QHeaderView +{ + Q_OBJECT +public: + ExpandingHeaderView(const QString &objectName, QWidget *parent = nullptr); + + int expandingColumn() const; + void setExpandingColumn(int newExpandingColumn); + + void resizeColumns(bool reset = false); + +protected: + void resizeEvent(QResizeEvent *event) override; + + +private: + int _expandingColumn = 0; + bool _isResizing = false; +}; + +} diff --git a/src/gui/models/models.cpp b/src/gui/models/models.cpp index e32d28594..e549aaaf8 100644 --- a/src/gui/models/models.cpp +++ b/src/gui/models/models.cpp @@ -18,6 +18,7 @@ #include <QTextStream> #include <QSortFilterProxyModel> #include <QMenu> +#include <QTimer> #include <functional> @@ -76,7 +77,7 @@ QString OCC::Models::formatSelection(const QModelIndexList &items, int dataRole) return out; } -void OCC::Models::displayFilterDialog(const QStringList &candidates, QSortFilterProxyModel *model, int column, int role, QWidget *parent) +QMenu *OCC::Models::displayFilterDialog(const QStringList &candidates, QSortFilterProxyModel *model, int column, int role, QWidget *parent) { auto menu = new QMenu(parent); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -102,5 +103,8 @@ void OCC::Models::displayFilterDialog(const QStringList &candidates, QSortFilter for (const auto &c : candidates) { addAction(c, c); } - menu->popup(QCursor::pos()); + QTimer::singleShot(0, menu, [menu] { + menu->popup(QCursor::pos()); + }); + return menu; } diff --git a/src/gui/models/models.h b/src/gui/models/models.h index 035d2fdfe..b96519fe2 100644 --- a/src/gui/models/models.h +++ b/src/gui/models/models.h @@ -18,6 +18,7 @@ #include <QtGlobal> class QSortFilterProxyModel; +class QMenu; namespace OCC { @@ -33,8 +34,28 @@ namespace Models { QString formatSelection(const QModelIndexList &items, int dataRole = Qt::DisplayRole); - void displayFilterDialog(const QStringList &candidates, QSortFilterProxyModel *model, int column, int role, QWidget *parent = nullptr); + QMenu *displayFilterDialog(const QStringList &candidates, QSortFilterProxyModel *model, int column, int role, QWidget *parent = nullptr); + /** + * Returns a vector with indices + * This is handy to iterate over the columns + */ + template <typename T> + auto range(T start, T end) + { + std::vector<T> out; + out.reserve(end - start); + for (auto i = start; i < end; ++i) { + out.push_back(i); + } + return out; + } + + template <typename T> + auto range(T end) + { + return range<T>(0, end); + } }; } diff --git a/src/gui/protocolwidget.cpp b/src/gui/protocolwidget.cpp index 3364bae1e..c8f674938 100644 --- a/src/gui/protocolwidget.cpp +++ b/src/gui/protocolwidget.cpp @@ -29,8 +29,9 @@ #include "accountstate.h" #include "syncfileitem.h" -#include "models/models.h" #include "models/activitylistmodel.h" +#include "models/expandingheaderview.h" +#include "models/models.h" #include "ui_protocolwidget.h" @@ -46,10 +47,6 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) this, &ProtocolWidget::slotItemCompleted); connect(_ui->_tableView, &QTreeWidget::customContextMenuRequested, this, &ProtocolWidget::slotItemContextMenu); - _ui->_tableView->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(_ui->_tableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this] { - showHeaderContextMenu(this, _sortModel); - }); _model = new ProtocolItemModel(this); _sortModel = new QSortFilterProxyModel(this); @@ -57,19 +54,17 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) _sortModel->setSortRole(Models::UnderlyingDataRole); _ui->_tableView->setModel(_sortModel); - _ui->_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - _ui->_tableView->horizontalHeader()->setSectionResizeMode(static_cast<int>(ProtocolItemModel::ProtocolItemRole::File), QHeaderView::Stretch); - _ui->_tableView->horizontalHeader()->setSortIndicator(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Time), Qt::DescendingOrder); - - _ui->_tableView->horizontalHeader()->setObjectName(QStringLiteral("ActivityListHeaderV2")); - ConfigFile cfg; - cfg.restoreGeometryHeader(_ui->_tableView->horizontalHeader()); - - connect(qApp, &QApplication::aboutToQuit, this, [this] { - ConfigFile cfg; - cfg.saveGeometryHeader(_ui->_tableView->horizontalHeader()); + auto header = new ExpandingHeaderView(QStringLiteral("ActivityListHeaderV2"), _ui->_tableView); + _ui->_tableView->setHorizontalHeader(header); + header->setSectionResizeMode(QHeaderView::Interactive); + header->setExpandingColumn(static_cast<int>(ProtocolItemModel::ProtocolItemRole::File)); + header->setSortIndicator(static_cast<int>(ProtocolItemModel::ProtocolItemRole::Time), Qt::DescendingOrder); + header->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header, &QHeaderView::customContextMenuRequested, header, [header, this] { + showHeaderContextMenu(header, _sortModel); }); + _ui->_headerLabel->setText(tr("Local sync protocol")); } @@ -78,9 +73,11 @@ ProtocolWidget::~ProtocolWidget() delete _ui; } -void ProtocolWidget::showHeaderContextMenu(QWidget *parent, QSortFilterProxyModel *model) +void ProtocolWidget::showHeaderContextMenu(ExpandingHeaderView *header, QSortFilterProxyModel *model) { - Models::displayFilterDialog(AccountManager::instance()->accountNames(), model, static_cast<int>(ProtocolItemModel::ProtocolItemRole::Account), Qt::DisplayRole, parent); + auto menu = Models::displayFilterDialog(AccountManager::instance()->accountNames(), model, static_cast<int>(ProtocolItemModel::ProtocolItemRole::Account), Qt::DisplayRole, header); + menu->addSeparator(); + menu->addAction(tr("Reset column sizes"), header, [header] { header->resizeColumns(true); }); } void ProtocolWidget::showContextMenu(QWidget *parent, ProtocolItemModel *model, const QModelIndexList &items) diff --git a/src/gui/protocolwidget.h b/src/gui/protocolwidget.h index fff6c0d3b..3c6a563f9 100644 --- a/src/gui/protocolwidget.h +++ b/src/gui/protocolwidget.h @@ -30,6 +30,7 @@ class QPushButton; class QSortFilterProxyModel; namespace OCC { +class ExpandingHeaderView; namespace Ui { class ProtocolWidget; @@ -47,7 +48,7 @@ public: explicit ProtocolWidget(QWidget *parent = nullptr); ~ProtocolWidget() override; - static void showHeaderContextMenu(QWidget *parent, QSortFilterProxyModel *model); + static void showHeaderContextMenu(ExpandingHeaderView *header, QSortFilterProxyModel *model); static void showContextMenu(QWidget *parent, ProtocolItemModel *model, const QModelIndexList &items); |