Improve QListView scroll bar calculation.

When both scroll bar's policies are set to ScrollBarAsNeeded, make sure
the scroll bars are shown if needed and not show if not. Even the corner
case, where one scroll bar's visibility depends on the other, is handled
properly.

Task-number: QTBUG-45470
Change-Id: I11d6ccf7c0b51644a5ce2d5c3fc59e2e4812755d
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Thorbjørn Lund Martsum <tmartsum@gmail.com>
Reviewed-by: Thorbjørn Lindeijer <bjorn@lindeijer.nl>
This commit is contained in:
Christoph Schleifenbaum 2015-04-22 13:06:32 +02:00 committed by Thorbjørn Lund Martsum
parent cca5cce205
commit 52c122e616
2 changed files with 84 additions and 4 deletions

View File

@ -1846,8 +1846,17 @@ void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step)
const bool bothScrollBarsAuto = qq->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded &&
qq->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded;
if (bothScrollBarsAuto && contentsSize.width() - qq->verticalScrollBar()->width() <= viewport()->width()
&& contentsSize.height() - qq->horizontalScrollBar()->height() <= viewport()->height()) {
const QSize viewportSize(viewport()->width() + (qq->verticalScrollBar()->maximum() > 0 ? qq->verticalScrollBar()->width() : 0),
viewport()->height() + (qq->horizontalScrollBar()->maximum() > 0 ? qq->horizontalScrollBar()->height() : 0));
bool verticalWantsToShow = contentsSize.height() > viewportSize.height();
bool horizontalWantsToShow;
if (verticalWantsToShow)
horizontalWantsToShow = contentsSize.width() > viewportSize.width() - qq->verticalScrollBar()->width();
else
horizontalWantsToShow = contentsSize.width() > viewportSize.width();
if (bothScrollBarsAuto && !horizontalWantsToShow) {
// break the infinite loop described above by setting the range to 0, 0.
// QAbstractScrollArea will then hide the scroll bar for us
horizontalScrollBar()->setRange(0, 0);
@ -1868,8 +1877,17 @@ void QCommonListViewBase::updateVerticalScrollBar(const QSize &step)
const bool bothScrollBarsAuto = qq->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded &&
qq->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded;
if (bothScrollBarsAuto && contentsSize.width() - qq->verticalScrollBar()->width() <= viewport()->width()
&& contentsSize.height() - qq->horizontalScrollBar()->height() <= viewport()->height()) {
const QSize viewportSize(viewport()->width() + (qq->verticalScrollBar()->maximum() > 0 ? qq->verticalScrollBar()->width() : 0),
viewport()->height() + (qq->horizontalScrollBar()->maximum() > 0 ? qq->horizontalScrollBar()->height() : 0));
bool horizontalWantsToShow = contentsSize.width() > viewportSize.width();
bool verticalWantsToShow;
if (horizontalWantsToShow)
verticalWantsToShow = contentsSize.height() > viewportSize.height() - qq->horizontalScrollBar()->height();
else
verticalWantsToShow = contentsSize.height() > viewportSize.height();
if (bothScrollBarsAuto && !verticalWantsToShow) {
// break the infinite loop described above by setting the range to 0, 0.
// QAbstractScrollArea will then hide the scroll bar for us
verticalScrollBar()->setRange(0, 0);

View File

@ -47,6 +47,7 @@
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QDialog>
#include <QtWidgets/QStyledItemDelegate>
#include <QtWidgets/QStyleFactory>
#if defined(Q_OS_WIN) || defined(Q_OS_WINCE)
# include <windows.h>
@ -2348,11 +2349,34 @@ void tst_QListView::testViewOptions()
QCOMPARE(options.decorationPosition, QStyleOptionViewItem::Top);
}
// make sure we have no transient scroll bars
class TempStyleSetter
{
public:
TempStyleSetter()
: m_oldStyle(qApp->style())
{
m_oldStyle->setParent(0);
QListView tempView;
if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient, 0, tempView.horizontalScrollBar()))
QApplication::setStyle(QStyleFactory::create("Fusion"));
}
~TempStyleSetter()
{
QApplication::setStyle(m_oldStyle);
}
private:
QStyle* m_oldStyle;
};
void tst_QListView::taskQTBUG_39902_mutualScrollBars()
{
QWidget window;
window.resize(400, 300);
QListView *view = new QListView(&window);
// make sure we have no transient scroll bars
TempStyleSetter styleSetter;
QStandardItemModel model(200, 1);
const QSize itemSize(100, 20);
for (int i = 0; i < model.rowCount(); ++i)
@ -2370,6 +2394,44 @@ void tst_QListView::taskQTBUG_39902_mutualScrollBars()
view->resize(itemSize.width() + view->frameWidth() * 2, model.rowCount() * itemSize.height() + view->frameWidth() * 2);
// this will end up in a stack overflow, if QTBUG-39902 is not fixed
QTest::qWait(100);
// these tests do not apply with transient scroll bars enabled
QVERIFY (!view->style()->styleHint(QStyle::SH_ScrollBar_Transient, 0, view->horizontalScrollBar()));
// make it double as large, no scroll bars should be visible
view->resize((itemSize.width() + view->frameWidth() * 2) * 2, (model.rowCount() * itemSize.height() + view->frameWidth() * 2) * 2);
QTRY_VERIFY(!view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(!view->verticalScrollBar()->isVisible());
// make it half the size, both scroll bars should be visible
view->resize((itemSize.width() + view->frameWidth() * 2) / 2, (model.rowCount() * itemSize.height() + view->frameWidth() * 2) / 2);
QTRY_VERIFY(view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(view->verticalScrollBar()->isVisible());
// make it double as large, no scroll bars should be visible
view->resize((itemSize.width() + view->frameWidth() * 2) * 2, (model.rowCount() * itemSize.height() + view->frameWidth() * 2) * 2);
QTRY_VERIFY(!view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(!view->verticalScrollBar()->isVisible());
// now, coming from the double size, resize it to the exactly matching size, still no scroll bars should be visible again
view->resize(itemSize.width() + view->frameWidth() * 2, model.rowCount() * itemSize.height() + view->frameWidth() * 2);
QTRY_VERIFY(!view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(!view->verticalScrollBar()->isVisible());
// now remove just one single pixel in height -> both scroll bars will show up since they depend on each other
view->resize(itemSize.width() + view->frameWidth() * 2, model.rowCount() * itemSize.height() + view->frameWidth() * 2 - 1);
QTRY_VERIFY(view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(view->verticalScrollBar()->isVisible());
// now remove just one single pixel in with -> both scroll bars will show up since they depend on each other
view->resize(itemSize.width() + view->frameWidth() * 2 - 1, model.rowCount() * itemSize.height() + view->frameWidth() * 2);
QTRY_VERIFY(view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(view->verticalScrollBar()->isVisible());
// finally, coming from a size being to small, resize back to the exactly matching size -> both scroll bars should disappear again
view->resize(itemSize.width() + view->frameWidth() * 2, model.rowCount() * itemSize.height() + view->frameWidth() * 2);
QTRY_VERIFY(!view->horizontalScrollBar()->isVisible());
QTRY_VERIFY(!view->verticalScrollBar()->isVisible());
}
QTEST_MAIN(tst_QListView)