6#include "QskScrollArea.h"
9#include "QskScrollViewSkinlet.h"
10#include "QskBoxBorderMetrics.h"
12#include "QskInternalMacros.h"
15#include <private/qquickitem_p.h>
16#include <private/qquickitemchangelistener_p.h>
19static inline bool qskNeedsScrollBars(
20 qreal available, qreal required, Qt::ScrollBarPolicy policy )
22 if ( policy == Qt::ScrollBarAsNeeded )
23 return required > available;
25 return policy == Qt::ScrollBarAlwaysOn;
28static inline QSizeF qskPanelInnerSize(
const QskScrollView* scrollView )
30 auto size = scrollView->
subControlRect( QskScrollView::Panel ).size();
33 const qreal bw = 2 * borderMetrics.widthAt( Qt::TopEdge );
35 size.setWidth( qMax( size.width() - bw, 0.0 ) );
36 size.setHeight( qMax( size.height() - bw, 0.0 ) );
41static inline QSizeF qskScrolledItemSize(
const QskScrollView* scrollView,
42 const QQuickItem* item,
const QSizeF& boundingSize )
46 QSizeF outerSize = boundingSize;
48 const qreal spacing = scrollView->
spacingHint( Q::Panel );
50 const auto sbV = scrollView->metric( Q::VerticalScrollBar |
QskAspect::Size );
51 const auto sbH = scrollView->metric( Q::HorizontalScrollBar |
QskAspect::Size );
53 const auto policyH = scrollView->horizontalScrollBarPolicy();
54 const auto policyV = scrollView->verticalScrollBarPolicy();
56 auto itemSize = qskConstrainedItemSize( item, outerSize );
58 bool needScrollBarV = qskNeedsScrollBars( outerSize.height(), itemSize.height(), policyV );
59 bool needScrollBarH = qskNeedsScrollBars( outerSize.width(), itemSize.width(), policyH );
61 bool hasScrollBarV = needScrollBarV;
67 outerSize.rwidth() -= sbV + spacing;
68 itemSize = qskConstrainedItemSize( item, outerSize );
70 if ( !needScrollBarH )
72 needScrollBarH = qskNeedsScrollBars(
73 outerSize.width(), itemSize.width(), policyH );
79 outerSize.rheight() -= sbH + spacing;
80 itemSize = qskConstrainedItemSize( item, outerSize );
84 needScrollBarV = qskNeedsScrollBars(
85 outerSize.height(), itemSize.height(), policyV );
91 outerSize.rwidth() -= sbV + spacing;
92 itemSize = qskConstrainedItemSize( item, outerSize );
100 class ViewportClipNode final :
public QQuickDefaultClipNode
104 : QQuickDefaultClipNode( QRectF() )
106 setGeometry(
nullptr );
111 setFlag( QSGNode::OwnsMaterial,
true );
114 void copyFrom(
const QSGClipNode* other )
116 if ( other ==
nullptr )
118 if ( !( isRectangular() && clipRect().isEmpty() ) )
120 setIsRectangular(
true );
121 setClipRect( QRectF() );
122 setGeometry(
nullptr );
124 markDirty( QSGNode::DirtyGeometry );
130 bool isDirty =
false;
132 if ( clipRect() != other->clipRect() )
134 setClipRect( other->clipRect() );
138 if ( other->isRectangular() )
140 if ( !isRectangular() )
142 setIsRectangular(
true );
143 setGeometry(
nullptr );
150 if ( isRectangular() )
152 setIsRectangular(
false );
156 if ( geometry() != other->geometry() )
159 setGeometry(
const_cast< QSGGeometry*
>( other->geometry() ) );
165 markDirty( QSGNode::DirtyGeometry );
168 void update()
override
196 class ClipItem final :
public QskControl,
public QQuickItemChangeListener
203 ~ClipItem()
override;
205 void enableGeometryListener(
bool on );
207 QQuickItem* scrolledItem()
const
209 auto children = childItems();
210 return children.isEmpty() ? nullptr : children.first();
213 bool contains(
const QPointF& pos )
const override
215 return clipRect().contains( pos );
218 QRectF clipRect()
const override
220 return scrollArea()->subControlRect( QskScrollView::Viewport );
223 inline void setItemSizeChangedEnabled(
bool on )
225 m_isSizeChangedEnabled = on;
228 QRectF focusIndicatorClipRect()
const override
230 if( scrollArea()->hasItemFocusClipping() )
239 bool event( QEvent* event )
override;
241 void itemChange( ItemChange,
const ItemChangeData& )
override;
243 void itemGeometryChanged( QQuickItem*,
244 QQuickGeometryChange change,
const QRectF& )
override
246 if ( change.sizeChange() )
247 scrolledItemGeometryChange();
252 void updateNode( QSGNode* )
override;
267 if (
auto node = QQuickItemPrivate::get(
this )->clipNode() )
269 if ( clipRect() != node->clipRect() )
274 inline void scrolledItemGeometryChange()
276 if ( m_isSizeChangedEnabled )
278 auto area = scrollArea();
282 if ( !area->isItemResizable() )
285 area->resetImplicitSize();
290 const QSGClipNode* viewPortClipNode()
const;
292 void viewportChanged()
294#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 )
295 if (
auto item = scrollArea()->scrolledItem() )
298 QCoreApplication::sendEvent( item, &ev );
303 bool m_isSizeChangedEnabled =
true;
307 : Inherited( scrollArea )
309 setObjectName( QStringLiteral(
"QskScrollAreaClipItem" ) );
313 connect( scrollArea, &QskScrollBox::scrollableSizeChanged,
314 this, &ClipItem::maybeUpdate );
317 ClipItem::~ClipItem()
319 enableGeometryListener(
false );
322 void ClipItem::updateNode( QSGNode* )
324 auto d = QQuickItemPrivate::get(
this );
326 if ( QQuickItemPrivate::get( scrollArea() )->dirtyAttributes &
327 QQuickItemPrivate::ContentUpdateMask )
335 auto clipNode = d->clipNode();
336 if ( clipNode && !clipNode->isRectangular() )
338 clipNode->setIsRectangular(
true );
339 clipNode->setGeometry(
nullptr );
347 auto clipNode = d->clipNode();
349 if ( clipNode && !( clipNode->flags() & QSGNode::OwnsMaterial ) )
353 auto parentNode = clipNode->parent();
355 auto node =
new ViewportClipNode();
356 parentNode->appendChildNode( node );
357 clipNode->reparentChildNodesTo( node );
359 parentNode->removeChildNode( clipNode );
361 if ( clipNode->flags() & QSGNode::OwnedByParent )
364 d->extra->clipNode = clipNode = node;
365 Q_ASSERT( clipNode == QQuickItemPrivate::get(
this )->clipNode() );
374 auto viewClipNode =
static_cast< ViewportClipNode*
>( clipNode );
375 viewClipNode->copyFrom( viewPortClipNode() );
379 const QSGClipNode* ClipItem::viewPortClipNode()
const
381 auto node =
const_cast< QSGNode*
>( qskPaintNode( scrollArea() ) );
383 node = QskSGNode::findChildNode( node, QskScrollViewSkinlet::ContentsRootRole );
385 if ( node && node->type() == QSGNode::ClipNodeType )
386 return static_cast< QSGClipNode*
>( node );
391 void ClipItem::itemChange(
392 QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& value )
394 if ( change == QQuickItem::ItemChildAddedChange )
396 enableGeometryListener(
true );
398 else if ( change == QQuickItem::ItemChildRemovedChange )
400 enableGeometryListener(
false );
403 Inherited::itemChange( change, value );
406 void ClipItem::enableGeometryListener(
bool on )
408 auto item = scrolledItem();
412 const QQuickItemPrivate::ChangeTypes types = QQuickItemPrivate::Geometry;
414 QQuickItemPrivate* p = QQuickItemPrivate::get( item );
416 p->addItemChangeListener(
this, types );
418 p->removeItemChangeListener(
this, types );
422 bool ClipItem::event( QEvent* event )
424 const int eventType =
event->type();
426 if ( eventType == QEvent::LayoutRequest )
428 if ( scrollArea()->isItemResizable() )
429 scrollArea()->polish();
431 else if ( eventType == QskEvent::GeometryChange )
434 if ( geometryEvent->isResized() )
442 return Inherited::event( event );
446class QskScrollArea::PrivateData
450 : isItemResizable( true )
451 , isItemFocusClipping( true )
455 void enableAutoTranslation(
QskScrollArea* scrollArea,
bool on )
459 QObject::connect( scrollArea, &QskScrollView::scrollPosChanged,
460 scrollArea, &QskScrollArea::translateItem );
464 QObject::disconnect( scrollArea, &QskScrollView::scrollPosChanged,
465 scrollArea, &QskScrollArea::translateItem );
469 ClipItem* clipItem =
nullptr;
471 bool isItemResizable : 1;
472 bool isItemFocusClipping : 1;
476QskScrollArea::QskScrollArea( QQuickItem* parentItem )
477 : Inherited( parentItem )
478 , m_data( new PrivateData() )
480 setPolishOnResize(
true );
482 m_data->clipItem =
new ClipItem(
this );
483 m_data->enableAutoTranslation(
this,
true );
485 initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Ignored );
488QskScrollArea::~QskScrollArea()
490 delete m_data->clipItem;
493void QskScrollArea::updateLayout()
495 Inherited::updateLayout();
498 m_data->clipItem->setSize( size() );
502QSizeF QskScrollArea::layoutSizeHint( Qt::SizeHint which,
const QSizeF& constraint )
const
504 if ( which == Qt::PreferredSize )
506 if (
const auto contentItem = scrolledItem() )
510 if ( m_data->isItemResizable )
512 hint = qskSizeConstraint( contentItem, which, constraint );
516 hint = contentItem->size();
519 if ( verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff )
525 if ( horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff )
535 return Inherited::layoutSizeHint( which, constraint );
538void QskScrollArea::adjustItem()
540 auto item = m_data->clipItem->scrolledItem();
542 if ( item ==
nullptr )
544 setScrollableSize( QSizeF() );
545 setScrollPos( QPointF() );
550 if ( m_data->isItemResizable )
554 const auto viewSize = qskPanelInnerSize(
this );
555 if ( !viewSize.isEmpty() )
558 itemSize = qskScrolledItemSize(
this, item, viewSize );
561 if ( itemSize.isEmpty() )
562 itemSize = QSizeF( 0.0, 0.0 );
565 m_data->clipItem->setItemSizeChangedEnabled(
false );
566 item->setSize( itemSize );
567 m_data->clipItem->setItemSizeChangedEnabled(
true );
570 m_data->enableAutoTranslation(
this,
false );
572 setScrollableSize( QSizeF( item->width(), item->height() ) );
573 setScrollPos( scrollPos() );
575 m_data->enableAutoTranslation(
this,
true );
580void QskScrollArea::setItemResizable(
bool on )
582 if ( on != m_data->isItemResizable )
584 m_data->isItemResizable = on;
585 Q_EMIT itemResizableChanged( on );
587 if ( m_data->isItemResizable )
592bool QskScrollArea::isItemResizable()
const
594 return m_data->isItemResizable;
597void QskScrollArea::setItemFocusClipping(
bool on )
599 if( m_data->isItemFocusClipping != on )
601 m_data->isItemFocusClipping = on;
606bool QskScrollArea::hasItemFocusClipping()
const
608 return m_data->isItemFocusClipping;
611void QskScrollArea::setScrolledItem( QQuickItem* item )
613 auto oldItem = m_data->clipItem->scrolledItem();
614 if ( item == oldItem )
619 if ( oldItem->parent() ==
this )
622 oldItem->setParentItem(
nullptr );
627 item->setParentItem( m_data->clipItem );
628 if ( item->parent() ==
nullptr )
629 item->setParent( m_data->clipItem );
633 Q_EMIT scrolledItemChanged();
636QQuickItem* QskScrollArea::scrolledItem()
const
638 return m_data->clipItem->scrolledItem();
641void QskScrollArea::translateItem()
643 if (
auto item = m_data->clipItem->scrolledItem() )
645 const QPointF pos = viewContentsRect().topLeft() - scrollPos();
646 item->setPosition( pos );
650#ifndef QT_NO_WHEELEVENT
652QPointF QskScrollArea::scrollOffset(
const QWheelEvent* event )
const
655 return Inherited::scrollOffset( event );
660#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.