From 90bfc14a0084a198bb81994cf29ad1bb94db71d6 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 3 Jul 2020 13:51:02 +0200 Subject: [PATCH] antizipate scrollbars before resizing the content item --- src/controls/QskScrollArea.cpp | 138 ++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 27 deletions(-) diff --git a/src/controls/QskScrollArea.cpp b/src/controls/QskScrollArea.cpp index 643f8d3e..7ebe75cd 100644 --- a/src/controls/QskScrollArea.cpp +++ b/src/controls/QskScrollArea.cpp @@ -7,6 +7,7 @@ #include "QskEvent.h" #include "QskQuick.h" #include "QskScrollViewSkinlet.h" +#include "QskBoxBorderMetrics.h" QSK_QT_PRIVATE_BEGIN #include @@ -15,6 +16,83 @@ QSK_QT_PRIVATE_BEGIN #include QSK_QT_PRIVATE_END +static inline bool qskNeedsScrollBars( + qreal available, qreal required, Qt::ScrollBarPolicy policy ) +{ + if ( policy == Qt::ScrollBarAsNeeded ) + return required > available; + else + return policy == Qt::ScrollBarAlwaysOn; +} + +static inline QSizeF qskPanelInnerSize( const QskScrollView* scrollView ) +{ + auto size = scrollView->subControlRect( QskScrollView::Panel ).size(); + + const auto borderMetrics = scrollView->boxBorderMetricsHint( QskScrollView::Viewport ); + const qreal bw = 2 * borderMetrics.widthAt( Qt::TopEdge ); + + size.setWidth( qMax( size.width() - bw, 0.0 ) ); + size.setHeight( qMax( size.height() - bw, 0.0 ) ); + + return size; +} + +static inline QSizeF qskScrolledItemSize( const QskScrollView* scrollView, + const QQuickItem* item, const QSizeF& boundingSize ) +{ + QSizeF outerSize = boundingSize; + + const qreal spacing = scrollView->metric( QskScrollView::Panel | QskAspect::Spacing ); + + const auto sbV = scrollView->metric( QskScrollView::VerticalScrollBar | QskAspect::Size ); + const auto sbH = scrollView->metric( QskScrollView::HorizontalScrollBar | QskAspect::Size ); + + const auto policyH = scrollView->horizontalScrollBarPolicy(); + const auto policyV = scrollView->verticalScrollBarPolicy(); + + auto itemSize = qskConstrainedItemSize( item, outerSize ); + + bool needScrollBarV = qskNeedsScrollBars( outerSize.height(), itemSize.height(), policyV ); + bool needScrollBarH = qskNeedsScrollBars( outerSize.width(), itemSize.width(), policyH ); + + bool hasScrollBarV = needScrollBarV; + + // Vertical/Horizonal scroll bars might depend on each other + + if ( needScrollBarV ) + { + outerSize.rwidth() -= sbV + spacing; + itemSize = qskConstrainedItemSize( item, outerSize ); + + if ( !needScrollBarH ) + { + needScrollBarH = qskNeedsScrollBars( + outerSize.width(), itemSize.width(), policyH ); + } + } + + if ( needScrollBarH ) + { + outerSize.rheight() -= sbH + spacing; + itemSize = qskConstrainedItemSize( item, outerSize ); + + if ( !hasScrollBarV ) + { + needScrollBarV = qskNeedsScrollBars( + outerSize.height(), itemSize.height(), policyV ); + } + } + + if ( needScrollBarV ) + { + outerSize.rwidth() -= sbV + spacing; + itemSize = qskConstrainedItemSize( item, outerSize ); + } + + return itemSize; +} + namespace { class ViewportClipNode final : public QQuickDefaultClipNode @@ -124,6 +202,11 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi return scrollArea()->subControlRect( QskScrollView::Viewport ); } + inline void setItemSizeChangedEnabled( bool on ) + { + m_isSizeChangedEnabled = on; + } + protected: bool event( QEvent* event ) override; @@ -134,7 +217,7 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi void itemGeometryChanged( QQuickItem*, QQuickGeometryChange change, const QRectF& ) override { - if ( change.sizeChange() ) + if ( m_isSizeChangedEnabled && change.sizeChange() ) scrollArea()->polish(); } @@ -142,7 +225,7 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi void itemGeometryChanged( QQuickItem*, const QRectF& newRect, const QRectF& oldRect ) override { - if ( oldRect.size() != newRect.size() ) + if ( m_isSizeChangedEnabled && ( oldRect.size() != newRect.size() ) ) scrollArea()->polish(); } #endif @@ -161,6 +244,8 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi } const QSGClipNode* viewPortClipNode() const; + + bool m_isSizeChangedEnabled = true; }; QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea ) @@ -371,39 +456,38 @@ void QskScrollArea::adjustItem() { setScrollableSize( QSizeF() ); setScrollPos( QPointF() ); + + return; } - else + + if ( m_data->isItemResizable ) { - if ( m_data->isItemResizable ) + QSizeF itemSize; + + const auto viewSize = qskPanelInnerSize( this ); + if ( !viewSize.isEmpty() ) { - auto size = viewContentsRect().size(); - if ( size.isEmpty() ) - { - size = QSizeF( 0.0, 0.0 ); - } - else - { - /* - For optional scrollbars the available space also depends - on wether the adjustedSize results in scroll bars. For the - moment we ignore this and start with a simplified code. TODO ... - */ - - size = qskConstrainedItemSize( item, size ); - } - - item->setSize( size ); + // we have to anticipate the scrollbars + itemSize = qskScrolledItemSize( this, item, viewSize ); } - m_data->enableAutoTranslation( this, false ); + if ( itemSize.isEmpty() ) + itemSize = QSizeF( 0.0, 0.0 ); - setScrollableSize( QSizeF( item->width(), item->height() ) ); - setScrollPos( scrollPos() ); - m_data->enableAutoTranslation( this, true ); - - translateItem(); + m_data->clipItem->setItemSizeChangedEnabled( false ); + item->setSize( itemSize ); + m_data->clipItem->setItemSizeChangedEnabled( true ); } + + m_data->enableAutoTranslation( this, false ); + + setScrollableSize( QSizeF( item->width(), item->height() ) ); + setScrollPos( scrollPos() ); + + m_data->enableAutoTranslation( this, true ); + + translateItem(); } void QskScrollArea::setItemResizable( bool on )