6#include "QskScrollArea.h"
9#include "QskScrollViewSkinlet.h"
10#include "QskBoxBorderMetrics.h"
14#include <private/qquickitem_p.h>
15#include <private/qquickitemchangelistener_p.h>
18static inline bool qskNeedsScrollBars(
19 qreal available, qreal required, Qt::ScrollBarPolicy policy )
21 if ( policy == Qt::ScrollBarAsNeeded )
22 return required > available;
24 return policy == Qt::ScrollBarAlwaysOn;
27static inline QSizeF qskPanelInnerSize(
const QskScrollView* scrollView )
29 auto size = scrollView->
subControlRect( QskScrollView::Panel ).size();
32 const qreal bw = 2 * borderMetrics.widthAt( Qt::TopEdge );
34 size.setWidth( qMax( size.width() - bw, 0.0 ) );
35 size.setHeight( qMax( size.height() - bw, 0.0 ) );
40static inline QSizeF qskScrolledItemSize(
const QskScrollView* scrollView,
41 const QQuickItem* item,
const QSizeF& boundingSize )
45 QSizeF outerSize = boundingSize;
47 const qreal spacing = scrollView->
spacingHint( Q::Panel );
52 const auto policyH = scrollView->horizontalScrollBarPolicy();
53 const auto policyV = scrollView->verticalScrollBarPolicy();
55 auto itemSize = qskConstrainedItemSize( item, outerSize );
57 bool needScrollBarV = qskNeedsScrollBars( outerSize.height(), itemSize.height(), policyV );
58 bool needScrollBarH = qskNeedsScrollBars( outerSize.width(), itemSize.width(), policyH );
60 bool hasScrollBarV = needScrollBarV;
66 outerSize.rwidth() -= sbV + spacing;
67 itemSize = qskConstrainedItemSize( item, outerSize );
69 if ( !needScrollBarH )
71 needScrollBarH = qskNeedsScrollBars(
72 outerSize.width(), itemSize.width(), policyH );
78 outerSize.rheight() -= sbH + spacing;
79 itemSize = qskConstrainedItemSize( item, outerSize );
83 needScrollBarV = qskNeedsScrollBars(
84 outerSize.height(), itemSize.height(), policyV );
90 outerSize.rwidth() -= sbV + spacing;
91 itemSize = qskConstrainedItemSize( item, outerSize );
99 class ViewportClipNode final :
public QQuickDefaultClipNode
103 : QQuickDefaultClipNode( QRectF() )
105 setGeometry(
nullptr );
110 setFlag( QSGNode::OwnsMaterial,
true );
113 void copyFrom(
const QSGClipNode* other )
115 if ( other ==
nullptr )
117 if ( !( isRectangular() && clipRect().isEmpty() ) )
119 setIsRectangular(
true );
120 setClipRect( QRectF() );
121 setGeometry(
nullptr );
123 markDirty( QSGNode::DirtyGeometry );
129 bool isDirty =
false;
131 if ( clipRect() != other->clipRect() )
133 setClipRect( other->clipRect() );
137 if ( other->isRectangular() )
139 if ( !isRectangular() )
141 setIsRectangular(
true );
142 setGeometry(
nullptr );
149 if ( isRectangular() )
151 setIsRectangular(
false );
155 if ( geometry() != other->geometry() )
158 setGeometry(
const_cast< QSGGeometry*
>( other->geometry() ) );
164 markDirty( QSGNode::DirtyGeometry );
167 void update()
override
195 class ClipItem final :
public QskControl,
public QQuickItemChangeListener
202 ~ClipItem()
override;
204 void enableGeometryListener(
bool on );
206 QQuickItem* scrolledItem()
const
208 auto children = childItems();
209 return children.isEmpty() ? nullptr : children.first();
212 bool contains(
const QPointF& pos )
const override
214 return clipRect().contains( pos );
217 QRectF clipRect()
const override
219 return scrollArea()->subControlRect( QskScrollView::Viewport );
222 inline void setItemSizeChangedEnabled(
bool on )
224 m_isSizeChangedEnabled = on;
227 QRectF focusIndicatorClipRect()
const override
229 if( scrollArea()->hasItemFocusClipping() )
238 bool event( QEvent* event )
override;
240 void itemChange( ItemChange,
const ItemChangeData& )
override;
242 void itemGeometryChanged( QQuickItem*,
243 QQuickGeometryChange change,
const QRectF& )
override
245 if ( change.sizeChange() )
246 scrolledItemGeometryChange();
249 void updateNode( QSGNode* )
override;
264 if (
auto node = QQuickItemPrivate::get(
this )->clipNode() )
266 if ( clipRect() != node->clipRect() )
271 inline void scrolledItemGeometryChange()
273 if ( m_isSizeChangedEnabled )
275 auto area = scrollArea();
279 if ( !area->isItemResizable() )
282 area->resetImplicitSize();
287 const QSGClipNode* viewPortClipNode()
const;
289 bool m_isSizeChangedEnabled =
true;
293 : Inherited( scrollArea )
295 setObjectName( QStringLiteral(
"QskScrollAreaClipItem" ) );
299 connect( scrollArea, &QskScrollBox::scrollableSizeChanged,
300 this, &ClipItem::maybeUpdate );
303 ClipItem::~ClipItem()
305 enableGeometryListener(
false );
308 void ClipItem::updateNode( QSGNode* )
310 auto d = QQuickItemPrivate::get(
this );
312 if ( QQuickItemPrivate::get( scrollArea() )->dirtyAttributes &
313 QQuickItemPrivate::ContentUpdateMask )
321 auto clipNode = d->clipNode();
322 if ( clipNode && !clipNode->isRectangular() )
324 clipNode->setIsRectangular(
true );
325 clipNode->setGeometry(
nullptr );
333 auto clipNode = d->clipNode();
335 if ( clipNode && !( clipNode->flags() & QSGNode::OwnsMaterial ) )
339 auto parentNode = clipNode->parent();
341 auto node =
new ViewportClipNode();
342 parentNode->appendChildNode( node );
343 clipNode->reparentChildNodesTo( node );
345 parentNode->removeChildNode( clipNode );
347 if ( clipNode->flags() & QSGNode::OwnedByParent )
350 d->extra->clipNode = clipNode = node;
351 Q_ASSERT( clipNode == QQuickItemPrivate::get(
this )->clipNode() );
360 auto viewClipNode =
static_cast< ViewportClipNode*
>( clipNode );
361 viewClipNode->copyFrom( viewPortClipNode() );
365 const QSGClipNode* ClipItem::viewPortClipNode()
const
367 auto node =
const_cast< QSGNode*
>( qskPaintNode( scrollArea() ) );
369 node = QskSGNode::findChildNode( node, QskScrollViewSkinlet::ContentsRootRole );
371 if ( node && node->type() == QSGNode::ClipNodeType )
372 return static_cast< QSGClipNode*
>( node );
377 void ClipItem::itemChange(
378 QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& value )
380 if ( change == QQuickItem::ItemChildAddedChange )
382 enableGeometryListener(
true );
384 else if ( change == QQuickItem::ItemChildRemovedChange )
386 enableGeometryListener(
false );
389 Inherited::itemChange( change, value );
392 void ClipItem::enableGeometryListener(
bool on )
394 auto item = scrolledItem();
398 const QQuickItemPrivate::ChangeTypes types = QQuickItemPrivate::Geometry;
400 QQuickItemPrivate* p = QQuickItemPrivate::get( item );
402 p->addItemChangeListener(
this, types );
404 p->removeItemChangeListener(
this, types );
408 bool ClipItem::event( QEvent* event )
410 const int eventType =
event->type();
412 if ( eventType == QEvent::LayoutRequest )
414 if ( scrollArea()->isItemResizable() )
415 scrollArea()->polish();
417 else if ( eventType == QskEvent::GeometryChange )
420 if ( geometryEvent->isResized() )
427 return Inherited::event( event );
431class QskScrollArea::PrivateData
435 : isItemResizable( true )
436 , isItemFocusClipping( true )
440 void enableAutoTranslation(
QskScrollArea* scrollArea,
bool on )
444 QObject::connect( scrollArea, &QskScrollView::scrollPosChanged,
445 scrollArea, &QskScrollArea::translateItem );
449 QObject::disconnect( scrollArea, &QskScrollView::scrollPosChanged,
450 scrollArea, &QskScrollArea::translateItem );
454 ClipItem* clipItem =
nullptr;
456 bool isItemResizable : 1;
457 bool isItemFocusClipping : 1;
461QskScrollArea::QskScrollArea( QQuickItem* parentItem )
462 : Inherited( parentItem )
463 , m_data( new PrivateData() )
465 setPolishOnResize(
true );
467 m_data->clipItem =
new ClipItem(
this );
468 m_data->enableAutoTranslation(
this,
true );
470 initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Ignored );
473QskScrollArea::~QskScrollArea()
475 delete m_data->clipItem;
478void QskScrollArea::updateLayout()
480 Inherited::updateLayout();
483 m_data->clipItem->setSize( size() );
487QSizeF QskScrollArea::layoutSizeHint( Qt::SizeHint which,
const QSizeF& constraint )
const
489 if ( which == Qt::PreferredSize )
491 if (
const auto contentItem = scrolledItem() )
495 if ( m_data->isItemResizable )
497 hint = qskSizeConstraint( contentItem, which, constraint );
501 hint = contentItem->size();
504 if ( verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff )
510 if ( horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff )
520 return Inherited::layoutSizeHint( which, constraint );
523void QskScrollArea::adjustItem()
525 auto item = m_data->clipItem->scrolledItem();
527 if ( item ==
nullptr )
529 setScrollableSize( QSizeF() );
530 setScrollPos( QPointF() );
535 if ( m_data->isItemResizable )
539 const auto viewSize = qskPanelInnerSize(
this );
540 if ( !viewSize.isEmpty() )
543 itemSize = qskScrolledItemSize(
this, item, viewSize );
546 if ( itemSize.isEmpty() )
547 itemSize = QSizeF( 0.0, 0.0 );
550 m_data->clipItem->setItemSizeChangedEnabled(
false );
551 item->setSize( itemSize );
552 m_data->clipItem->setItemSizeChangedEnabled(
true );
555 m_data->enableAutoTranslation(
this,
false );
557 setScrollableSize( QSizeF( item->width(), item->height() ) );
558 setScrollPos( scrollPos() );
560 m_data->enableAutoTranslation(
this,
true );
565void QskScrollArea::setItemResizable(
bool on )
567 if ( on != m_data->isItemResizable )
569 m_data->isItemResizable = on;
570 Q_EMIT itemResizableChanged( on );
572 if ( m_data->isItemResizable )
577bool QskScrollArea::isItemResizable()
const
579 return m_data->isItemResizable;
582void QskScrollArea::setItemFocusClipping(
bool on )
584 if( m_data->isItemFocusClipping != on )
586 m_data->isItemFocusClipping = on;
591bool QskScrollArea::hasItemFocusClipping()
const
593 return m_data->isItemFocusClipping;
596void QskScrollArea::setScrolledItem( QQuickItem* item )
598 auto oldItem = m_data->clipItem->scrolledItem();
599 if ( item == oldItem )
604 if ( oldItem->parent() ==
this )
607 oldItem->setParentItem(
nullptr );
612 item->setParentItem( m_data->clipItem );
613 if ( item->parent() ==
nullptr )
614 item->setParent( m_data->clipItem );
618 Q_EMIT scrolledItemChanged();
621QQuickItem* QskScrollArea::scrolledItem()
const
623 return m_data->clipItem->scrolledItem();
626void QskScrollArea::translateItem()
628 if (
auto item = m_data->clipItem->scrolledItem() )
630 const QPointF pos = viewContentsRect().topLeft() - scrollPos();
631 item->setPosition( pos );
635#ifndef QT_NO_WHEELEVENT
637QPointF QskScrollArea::scrollOffset(
const QWheelEvent* event )
const
640 return Inherited::scrollOffset( event );
645#include "moc_QskScrollArea.cpp"
Base class of all controls.
QRectF subControlRect(QskAspect::Subcontrol) const
void focusIndicatorRectChanged()
virtual QRectF focusIndicatorClipRect() const
qreal spacingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a spacing hint.
QskBoxBorderMetrics boxBorderMetricsHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a border hint.
qreal metric(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a metric hint.