diff options
-rw-r--r-- | Swift/QtUI/QtChatOverviewBundle.cpp | 22 | ||||
-rw-r--r-- | Swift/QtUI/QtChatOverviewBundle.h | 5 | ||||
-rw-r--r-- | Swift/QtUI/QtExpandedListView.cpp | 109 | ||||
-rw-r--r-- | Swift/QtUI/QtExpandedListView.h | 34 | ||||
-rw-r--r-- | Swift/QtUI/SConscript | 1 |
5 files changed, 149 insertions, 22 deletions
diff --git a/Swift/QtUI/QtChatOverviewBundle.cpp b/Swift/QtUI/QtChatOverviewBundle.cpp index bf99b0c..8505541 100644 --- a/Swift/QtUI/QtChatOverviewBundle.cpp +++ b/Swift/QtUI/QtChatOverviewBundle.cpp @@ -9,7 +9,6 @@ #include <QDebug> #include <QHBoxLayout> #include <QLabel> -#include <QListView> #include <QPalette> #include <QSortFilterProxyModel> #include <QVBoxLayout> @@ -19,6 +18,7 @@ #include <Swift/QtUI/ChattablesModel.h> #include <Swift/QtUI/QtChatOverviewDelegate.h> #include <Swift/QtUI/QtClickableLabel.h> +#include <Swift/QtUI/QtExpandedListView.h> #include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { @@ -100,34 +100,16 @@ QtChatOverviewBundle::QtChatOverviewBundle(ChattablesModel* rootModel, QString n headerLayout->addWidget(filterLabel_); connect(filterLabel_, SIGNAL(clicked()), this, SLOT(handleFilterClicked())); } - listView_ = new QListView(this); + listView_ = new QtExpandedListView(this); listView_->setModel(proxyModel_); listView_->setFrameStyle(QFrame::NoFrame); listView_->setItemDelegate(new QtChatOverviewDelegate(this)); connect(listView_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleItemClicked(const QModelIndex&))); - recalculateSize(); mainLayout->addWidget(listView_); - connect(proxyModel_, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this, SLOT(recalculateSize())); - connect(proxyModel_, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(recalculateSize())); - connect(proxyModel_, SIGNAL(modelReset()), this, SLOT(recalculateSize())); } QtChatOverviewBundle::~QtChatOverviewBundle() {} -void QtChatOverviewBundle::recalculateSize() { - int totalHeight = 0; - for (int i = 0; i < proxyModel_->rowCount(); i++) { - totalHeight += listView_->sizeHintForRow(i); - } - listView_->setFixedHeight(totalHeight); - if (hideWhenEmpty_ && totalHeight == 0) { - hide(); - } - else { - show(); - } -} - void QtChatOverviewBundle::handleFilterClicked() { if (proxyModel_->hasFilter(BundleFilter::Filter::Online)) { proxyModel_->removeFilter(BundleFilter::Filter::Online); diff --git a/Swift/QtUI/QtChatOverviewBundle.h b/Swift/QtUI/QtChatOverviewBundle.h index f469fea..6e232ca 100644 --- a/Swift/QtUI/QtChatOverviewBundle.h +++ b/Swift/QtUI/QtChatOverviewBundle.h @@ -19,6 +19,7 @@ class QListView; namespace Swift { class ChattablesModel; class QtClickableLabel; + class QtExpandedListView; class BundleFilter : public QSortFilterProxyModel { Q_OBJECT @@ -44,12 +45,12 @@ namespace Swift { void clicked(JID jid); private slots: - void recalculateSize(); void handleFilterClicked(); void handleItemClicked(const QModelIndex&); + private: ChattablesModel* rootModel_; - QListView* listView_; + QtExpandedListView* listView_; BundleFilter* proxyModel_; bool hideWhenEmpty_; QtClickableLabel* filterLabel_ = nullptr; diff --git a/Swift/QtUI/QtExpandedListView.cpp b/Swift/QtUI/QtExpandedListView.cpp new file mode 100644 index 0000000..8cbfab6 --- /dev/null +++ b/Swift/QtUI/QtExpandedListView.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swift/QtUI/QtExpandedListView.h> + +#include <QWheelEvent> +#include <QScrollArea> + +namespace Swift { + +QtExpandedListView::QtExpandedListView(QWidget* parent) : QListView(parent) { + // Disable macOS focus rectangle due to bad performance. + setAttribute(Qt::WA_MacShowFocusRect, 0); + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); +} + +void QtExpandedListView::setModel(QAbstractItemModel* newModel) { + if (model()) { + disconnectFromModel(model()); + } + if (newModel) { + connectToModel(newModel); + } + QListView::setModel(newModel); + adjustHeightToModelChange(); +} + +QtExpandedListView::~QtExpandedListView() { + if (model()) { + disconnectFromModel(model()); + } +} + +bool QtExpandedListView::viewportEvent(QEvent* event) { + // Ignore wheel events for faster mouse scrolling. + if (event && event->type() == QEvent::Wheel) { + return false; + } + + return QListView::viewportEvent(event); +} + +template <typename T> +T getParentOfType(QWidget* start) { + auto parentW = start->parentWidget(); + if (parentW == nullptr) { + return nullptr; + } + T result = dynamic_cast<T>(parentW); + if (result) { + return result; + } + return getParentOfType<T>(parentW); +} + +void QtExpandedListView::currentChanged(const QModelIndex ¤t, const QModelIndex &) { + // Make sure that the current selected index is visible in the parent QScrollArea. + auto scrollArea = getParentOfType<QScrollArea*>(parentWidget()); + if (scrollArea) { + QList<QPoint> points; + auto visRect = visualRect(current); + points << pos() + visRect.topLeft(); + points << pos() + visRect.topRight(); + points << pos() + visRect.bottomLeft(); + points << pos() + visRect.bottomRight(); + + for (auto&& point : points) { + scrollArea->ensureVisible(point.x(), point.y(), 0, 0); + } + } +} + +void QtExpandedListView::adjustHeightToModelChange() { + updateGeometry(); +} + +QSize QtExpandedListView::minimumSizeHint() const { + auto sh = sizeHint(); + return QSize(0, sh.height()); +} + +QSize QtExpandedListView::sizeHint() const { + auto listViewSH = QListView::sizeHint(); + if (model()) { + auto lastRect = rectForIndex(model()->index(model()->rowCount()-1, 0, rootIndex())); + auto idealHeight = lastRect.y() + lastRect.height() + frameWidth() * 2; + listViewSH.setHeight(idealHeight); + } + return listViewSH; +} + +void QtExpandedListView::connectToModel(QAbstractItemModel* model) { + connect(model, &QAbstractItemModel::dataChanged, this, &QtExpandedListView::adjustHeightToModelChange); + connect(model, &QAbstractItemModel::modelReset, this, &QtExpandedListView::adjustHeightToModelChange); + connect(model, &QAbstractItemModel::rowsInserted, this, &QtExpandedListView::adjustHeightToModelChange); + connect(model, &QAbstractItemModel::rowsRemoved, this, &QtExpandedListView::adjustHeightToModelChange); +} + +void QtExpandedListView::disconnectFromModel(QAbstractItemModel* model) { + disconnect(model, &QAbstractItemModel::dataChanged, this, &QtExpandedListView::adjustHeightToModelChange); + disconnect(model, &QAbstractItemModel::modelReset, this, &QtExpandedListView::adjustHeightToModelChange); + disconnect(model, &QAbstractItemModel::rowsInserted, this, &QtExpandedListView::adjustHeightToModelChange); + disconnect(model, &QAbstractItemModel::rowsRemoved, this, &QtExpandedListView::adjustHeightToModelChange); +} + +} diff --git a/Swift/QtUI/QtExpandedListView.h b/Swift/QtUI/QtExpandedListView.h new file mode 100644 index 0000000..df78376 --- /dev/null +++ b/Swift/QtUI/QtExpandedListView.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <QListView> + +namespace Swift { + +class QtExpandedListView : public QListView { +public: + QtExpandedListView(QWidget* parent); + ~QtExpandedListView() override; + + void setModel(QAbstractItemModel* model) override; + bool viewportEvent(QEvent* event) override; + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; + +private slots: + void adjustHeightToModelChange(); + +private: + void connectToModel(QAbstractItemModel* model); + void disconnectFromModel(QAbstractItemModel* model); +}; + +} diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index e61bdcf..b8ec3ba 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -169,6 +169,7 @@ sources = [ "QtEmojisScroll.cpp", "QtEmojisSelector.cpp", "QtEmoticonsGrid.cpp", + "QtExpandedListView.cpp", "QtFileTransferListItemModel.cpp", "QtFileTransferListWidget.cpp", "QtFormResultItemModel.cpp", |