From 8c29273ff058f90dfa16a30b3d8457b842c0954d Mon Sep 17 00:00:00 2001 From: gubatron Date: Fri, 21 Mar 2014 01:45:47 -0400 Subject: [PATCH] [QT] Fixes feel when resizing the last column on tables (issue #2862) Re-submitting this pull request with a single commit. This patch introduces a GUIUtil class that is used when setting up the 2 tables we have so far on the Qt-GUI. In the past you could only resize the last column, which has BTC amounts from the right border of the column header, something that was rather unnatural. If a new table were ever to be added to the interface, fixing the last columns resizing behavior is rather simple. Just look at how we initialize here a TableViewLastColumnResizingFixer object when setting up the table header's behavior, and then how we override the resize event of the component (can be the table, or the dialog) and we invoke columnResizingFixer->stretchColumnWidth(columnIndex); --- src/qt/guiutil.cpp | 115 ++++++++++++++++++++++++++++++++++ src/qt/guiutil.h | 40 ++++++++++++ src/qt/receivecoinsdialog.cpp | 41 +++++++----- src/qt/receivecoinsdialog.h | 13 +++- src/qt/transactionview.cpp | 22 ++++--- src/qt/transactionview.h | 13 ++++ 6 files changed, 217 insertions(+), 27 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 7e1831f037..4a4fece3e1 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -379,6 +379,121 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) return QObject::eventFilter(obj, evt); } +void TableViewLastColumnResizingFixer::connectViewHeadersSignals() +{ + connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int))); + connect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged())); +} + +//we need to disconnect these while handling the resize events, otherwise we can enter infinite loops +void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals() +{ + disconnect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int))); + disconnect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged())); +} + +//setup the resize mode, handles compatibility for QT5 and below as the method signatures changed. (refactored here for readability) +void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode) +{ +#if QT_VERSION < 0x050000 + tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode); +#else + tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode); +#endif +} + +void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width) { + tableView->setColumnWidth(nColumnIndex, width); + tableView->horizontalHeader()->resizeSection(nColumnIndex, width); +} + +int TableViewLastColumnResizingFixer::getColumnsWidth() +{ + int nColumnsWidthSum = 0; + for (int i = 0; i < columnCount; i++) + { + nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i); + } + return nColumnsWidthSum; +} + +int TableViewLastColumnResizingFixer::getAvailableWidthForColumn(int column) +{ + int nResult = lastColumnMinimumWidth; + int nTableWidth = tableView->horizontalHeader()->width(); + + if (nTableWidth > 0) + { + int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column); + nResult = std::max(nResult, nTableWidth - nOtherColsWidth); + } + + return nResult; +} + +//make sure we don't make the columns wider than the table's viewport's width. +void TableViewLastColumnResizingFixer::adjustTableColumnsWidth() +{ + disconnectViewHeadersSignals(); + resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex)); + connectViewHeadersSignals(); + + int nTableWidth = tableView->horizontalHeader()->width(); + int nColsWidth = getColumnsWidth(); + if (nColsWidth > nTableWidth) + { + resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex)); + } +} + +//make column use all the space available, useful during window resizing. +void TableViewLastColumnResizingFixer::stretchColumnWidth(int column) { + disconnectViewHeadersSignals(); + resizeColumn(column, getAvailableWidthForColumn(column)); + connectViewHeadersSignals(); +} + +//when a section is resized this is a slot-proxy for ajustAmountColumnWidth() +void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int oldSize, int newSize) +{ + adjustTableColumnsWidth(); + int remainingWidth = getAvailableWidthForColumn(logicalIndex); + if (newSize > remainingWidth) + { + resizeColumn(logicalIndex, remainingWidth); + } +} + +//when the table's geometry is ready, we manually perform the Stretch of the "Message" column +//as the "Stretch" resize mode does not allow for interactive resizing. +void TableViewLastColumnResizingFixer::on_geometriesChanged() +{ + if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0) + { + disconnectViewHeadersSignals(); + resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex)); + connectViewHeadersSignals(); + } +} + +/** + * Initializes all internal variables and prepares the + * the resize modes of the last 2 columns of the table and + */ +TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth) : + tableView(table), + lastColumnMinimumWidth(lastColMinimumWidth), + allColumnsMinimumWidth(allColsMinimumWidth) +{ + columnCount = tableView->horizontalHeader()->count(); + lastColumnIndex = columnCount - 1; + secondToLastColumnIndex = columnCount - 2; + tableView->horizontalHeader()->setMinimumSectionSize(allColumnsMinimumWidth); + setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive); + setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive); +} + + #ifdef WIN32 boost::filesystem::path static StartupShortcutPath() { diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 52124ff20a..26202e8d41 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include class QValidatedLineEdit; class SendCoinsRecipient; @@ -116,6 +118,44 @@ namespace GUIUtil int size_threshold; }; + /** + * Makes a QTableView last column feel as if it was being resized from its left border. + * Also makes sure the column widths are never larger than the table's viewport. + * In Qt, all columns are resizable from the right, but it's not intuitive resizing the last column from the right. + * Usually our second to last columns behave as if stretched, and when on strech mode, columns aren't resizable + * interactively or programatically. + * + * This helper object takes care of this issue. + * + */ + class TableViewLastColumnResizingFixer: public QObject + { + Q_OBJECT + public: + TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth); + void stretchColumnWidth(int column); + + private: + QTableView* tableView; + int lastColumnMinimumWidth; + int allColumnsMinimumWidth; + int lastColumnIndex; + int columnCount; + int secondToLastColumnIndex; + + void adjustTableColumnsWidth(); + int getAvailableWidthForColumn(int column); + int getColumnsWidth(); + void connectViewHeadersSignals(); + void disconnectViewHeadersSignals(); + void setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode); + void resizeColumn(int nColumnIndex, int width); + + private slots: + void on_sectionResized(int logicalIndex, int oldSize, int newSize); + void on_geometriesChanged(); + }; + bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 2af3949ae4..f630324947 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -55,34 +55,35 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); } + + void ReceiveCoinsDialog::setModel(WalletModel *model) { this->model = model; if(model && model->getOptionsModel()) { - connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - updateDisplayUnit(); - - ui->recentRequestsView->setModel(model->getRecentRequestsTableModel()); - ui->recentRequestsView->setAlternatingRowColors(true); - ui->recentRequestsView->setSelectionBehavior(QAbstractItemView::SelectRows); - ui->recentRequestsView->setSelectionMode(QAbstractItemView::ContiguousSelection); - ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Date, 130); - ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Label, 120); -#if QT_VERSION < 0x050000 - ui->recentRequestsView->horizontalHeader()->setResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); -#else - ui->recentRequestsView->horizontalHeader()->setSectionResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); -#endif - ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Amount, 100); - model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder); - + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); connect(ui->recentRequestsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(on_recentRequestsView_selectionChanged(QItemSelection, QItemSelection))); + updateDisplayUnit(); + + QTableView* tableView = ui->recentRequestsView; + + tableView->verticalHeader()->hide(); + tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + tableView->setModel(model->getRecentRequestsTableModel()); + tableView->setAlternatingRowColors(true); + tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + tableView->setSelectionMode(QAbstractItemView::ContiguousSelection); + tableView->setColumnWidth(RecentRequestsTableModel::Date, DATE_COLUMN_WIDTH); + tableView->setColumnWidth(RecentRequestsTableModel::Label, LABEL_COLUMN_WIDTH); + + //(last 2 columns are set when the table geometry is ready) by the columnResizingFixer. + columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH); } } @@ -200,6 +201,12 @@ void ReceiveCoinsDialog::on_removeRequestButton_clicked() model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent()); } +//We override the virtual resizeEvent of the QWidget to adjust tablet's column sizes as the table's width is proportional to the dialog's. +void ReceiveCoinsDialog::resizeEvent(QResizeEvent* event) { + QWidget::resizeEvent(event); + columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message); +} + void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return) diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index bfe8b3401f..1d051d9324 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include "guiutil.h" namespace Ui { class ReceiveCoinsDialog; @@ -28,11 +30,18 @@ class ReceiveCoinsDialog : public QDialog Q_OBJECT public: + enum ColumnWidths { + DATE_COLUMN_WIDTH = 130, + LABEL_COLUMN_WIDTH = 120, + AMOUNT_MINIMUM_COLUMN_WIDTH = 160, + MINIMUM_COLUMN_WIDTH = 130 + }; + explicit ReceiveCoinsDialog(QWidget *parent = 0); ~ReceiveCoinsDialog(); - void setModel(WalletModel *model); + public slots: void clear(); void reject(); @@ -43,9 +52,11 @@ protected: private: Ui::ReceiveCoinsDialog *ui; + GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer; WalletModel *model; QMenu *contextMenu; void copyColumnToClipboard(int column); + virtual void resizeEvent(QResizeEvent* event); private slots: void on_receiveButton_clicked(); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index a0c3ce62aa..d178efe8d5 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -168,6 +168,7 @@ void TransactionView::setModel(WalletModel *model) transactionProxyModel->setSortRole(Qt::EditRole); + transactionView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); transactionView->setModel(transactionProxyModel); transactionView->setAlternatingRowColors(true); transactionView->setSelectionBehavior(QAbstractItemView::SelectRows); @@ -176,15 +177,12 @@ void TransactionView::setModel(WalletModel *model) transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); transactionView->verticalHeader()->hide(); - transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Status, 23); - transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Date, 120); - transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Type, 120); -#if QT_VERSION < 0x050000 - transactionView->horizontalHeader()->setResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); -#else - transactionView->horizontalHeader()->setSectionResizeMode(TransactionTableModel::ToAddress, QHeaderView::Stretch); -#endif - transactionView->horizontalHeader()->resizeSection(TransactionTableModel::Amount, 100); + transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH); + transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH); + transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH); + transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH); + + columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH); } } @@ -439,3 +437,9 @@ void TransactionView::focusTransaction(const QModelIndex &idx) transactionView->setCurrentIndex(targetIdx); transactionView->setFocus(); } + +//We override the virtual resizeEvent of the QWidget to adjust tablet's column sizes as the table's width is proportional to the dialog's. +void TransactionView::resizeEvent(QResizeEvent* event) { + QWidget::resizeEvent(event); + columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 18f2b9bfc9..fe8e205d6c 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -6,6 +6,7 @@ #define TRANSACTIONVIEW_H #include +#include "guiutil.h" class TransactionFilterProxy; class WalletModel; @@ -44,6 +45,14 @@ public: Range }; + enum ColumnWidths { + STATUS_COLUMN_WIDTH = 23, + DATE_COLUMN_WIDTH = 120, + TYPE_COLUMN_WIDTH = 120, + AMOUNT_MINIMUM_COLUMN_WIDTH = 120, + MINIMUM_COLUMN_WIDTH = 23 + }; + private: WalletModel *model; TransactionFilterProxy *transactionProxyModel; @@ -62,6 +71,10 @@ private: QWidget *createDateRangeWidget(); + GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer; + + virtual void resizeEvent(QResizeEvent* event); + private slots: void contextualMenu(const QPoint &); void dateRangeChanged();