From 19eefe66821d26a00d24fbe44aa870309d80ad4b Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 4 May 2018 13:00:18 +0200 Subject: Use dedicated QtExpandedListView in new roster UI QtExpandedListView is always high enough to show all entries in the model. It also correctly hands off scrolling events to the parent widget for smooth scrolling. Test-Information: Tested on macOS 10.13.4 with a well sized roster, that sizing and scrolling works as expected. Tested with Qt 5.5.1. Change-Id: I6d93db3045e1c2f343b89c0d45874d8f85a20c0a 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 #include #include -#include #include #include #include @@ -19,6 +18,7 @@ #include #include #include +#include #include 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 + +#include +#include + +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 +T getParentOfType(QWidget* start) { + auto parentW = start->parentWidget(); + if (parentW == nullptr) { + return nullptr; + } + T result = dynamic_cast(parentW); + if (result) { + return result; + } + return getParentOfType(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(parentWidget()); + if (scrollArea) { + QList 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 + +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", -- cgit v0.10.2-6-g49f6