diff --git a/examples/thumbnails/main.cpp b/examples/thumbnails/main.cpp index f19eca4d..7f858244 100644 --- a/examples/thumbnails/main.cpp +++ b/examples/thumbnails/main.cpp @@ -26,8 +26,6 @@ #include -#define HIDE_NODES 1 - const int gridSize = 20; const int thumbnailSize = 150; @@ -81,18 +79,6 @@ class Thumbnail : public QskPushButton setStrutSizeHint( QskPushButton::Icon, -1, -1 ); } -#if 0 - void mousePressEvent( QMouseEvent* event ) override - { - /* - ignore events: to check if the pan gesture recoognizer of the scroll - area works, when the event arrives as regular event - ( not via childMouseEventFilter ) - */ - event->setAccepted( false ); - } -#endif - private: QskGraphic thumbnailGraphic( const QColor& color, int shape, const QSizeF& size ) const @@ -124,54 +110,67 @@ class IconGrid : public QskLinearBox IconGrid( QQuickItem* parentItem = nullptr ) : QskLinearBox( Qt::Horizontal, gridSize, parentItem ) { +#if QT_VERSION >= QT_VERSION_CHECK( 6, 3, 0 ) + setFlag( QQuickItem::ItemObservesViewport, true ); +#endif + setMargins( 20 ); setSpacing( 20 ); for ( int col = 0; col < gridSize; col++ ) { for ( int row = 0; row < gridSize; row++ ) - ( void ) new Thumbnail( randomColor(), randomShape(), this ); + { + auto thumbnail = new Thumbnail( randomColor(), randomShape(), this ); + thumbnail->setPlacementPolicy( Qsk::Hidden, QskPlacementPolicy::Reserve ); + thumbnail->setVisible( false ); + } } -#if HIDE_NODES + setSize( sizeConstraint() ); + } + + protected: + void viewportChangeEvent( QskViewportChangeEvent* ) override + { + if ( isEmpty() ) + return; + /* When having too many nodes, the scene graph becomes horribly slow. So we explicitely hide all items outside the visible area ( see updateVisibilities below ) and make use of the DeferredUpdate and CleanupOnVisibility features of QskItem. */ - setSize( sizeConstraint() ); - updateLayout(); // so that every item has its initial geometry - for ( int i = 0; i < elementCount(); i++ ) - { - if ( auto control = qskControlCast( itemAtIndex( i ) ) ) - { - // to support the optimizations in ScrollArea::updateVisibilities - control->setPlacementPolicy( Qsk::Hidden, QskPlacementPolicy::Reserve ); - control->setVisible( false ); - } - } + QQuickItem* item; + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 3, 0 ) + item = viewportItem(); +#else + for ( item = parentItem(); item && !item->clip(); item = item->parentItem() ); #endif - } -#if HIDE_NODES - void updateVisibilities( const QRectF& viewPort ) - { - if ( !isEmpty() && viewPort != m_viewPort ) + if ( item ) { - setItemsVisible( m_viewPort, false ); - setItemsVisible( viewPort, true ); + auto r = item->clipRect(); + r.moveTo( mapFromItem( item, r.topLeft() ) ); - m_viewPort = viewPort; + const auto viewPort = this->viewPort( r ); + + if ( m_viewPort != viewPort ) + { + setItemsVisible( m_viewPort, false ); + setItemsVisible( viewPort, true ); + + m_viewPort = viewPort; + } } } private: - void setItemsVisible( const QRectF& rect, bool on ) + QRect viewPort( const QRectF& rect ) const { - const auto dim = dimension(); - // we know, that all items have the same size const auto itemSize = itemAtIndex( 0 )->size(); @@ -181,9 +180,16 @@ class IconGrid : public QskLinearBox const int colMin = rect.left() / ( itemSize.width() + spacing() ); const int colMax = rect.right() / ( itemSize.height() + spacing() ); - for ( int row = rowMin; row <= rowMax; row++ ) + return QRect( colMin, rowMin, colMax - colMin + 1, rowMax - rowMin + 1 ); + } + + void setItemsVisible( const QRect& viewPort, bool on ) + { + const auto dim = dimension(); + + for ( int row = viewPort.top(); row <= viewPort.bottom(); row++ ) { - for ( int col = colMin; col <= colMax; col++ ) + for ( int col = viewPort.left(); col <= viewPort.right(); col++ ) { if ( auto item = itemAtIndex( row * dim + col ) ) item->setVisible( on ); @@ -191,8 +197,7 @@ class IconGrid : public QskLinearBox } } - QRectF m_viewPort; -#endif + QRect m_viewPort; }; class ScrollArea : public QskScrollArea @@ -218,29 +223,6 @@ class ScrollArea : public QskScrollArea setBoxShapeHint( HorizontalScrollHandle, 8 ); setFlickRecognizerTimeout( 300 ); - - connect( this, &QskScrollView::scrollPosChanged, - this, &ScrollArea::updateVisibilities ); - } - - protected: - void geometryChangeEvent( QskGeometryChangeEvent* event ) override - { - QskScrollArea::geometryChangeEvent( event ); - updateVisibilities(); - } - - private: - void updateVisibilities() - { -#if HIDE_NODES - const auto box = static_cast< IconGrid* >( scrolledItem() ); - if ( box ) - { - const QRectF viewPort( scrollPos(), viewContentsRect().size() ); - box->updateVisibilities( viewPort ); - } -#endif } }; @@ -291,7 +273,7 @@ int main( int argc, char* argv[] ) scrollArea->setScrolledItem( iconGrid ); #if 0 - // for testing nested gestures + // for testing nested gestures auto swipeView = new QskSwipeView(); swipeView->addItem( scrollArea ); diff --git a/src/controls/QskEvent.cpp b/src/controls/QskEvent.cpp index b143bcd4..37945d2e 100644 --- a/src/controls/QskEvent.cpp +++ b/src/controls/QskEvent.cpp @@ -189,6 +189,7 @@ QskEvent* QskEvent::clone() const } #endif + // -- QskGeometryChangeEvent QskGeometryChangeEvent::QskGeometryChangeEvent( @@ -216,6 +217,19 @@ bool QskGeometryChangeEvent::isMoved() const ( m_rect.y() != m_oldRect.y() ); } +// -- QskViewportChangeEvent + +QskViewportChangeEvent::QskViewportChangeEvent( QQuickItem* modifiedParent ) + : QskEvent( QskEvent::ViewportChange ) + , m_modifiedParent( modifiedParent ) +{ +} + +QskViewportChangeEvent* QskViewportChangeEvent::clone() const +{ + return new QskViewportChangeEvent( *this ); +} + // -- QskWindowChangeEvent QskWindowChangeEvent::QskWindowChangeEvent( diff --git a/src/controls/QskEvent.h b/src/controls/QskEvent.h index 93609953..5ebb8f89 100644 --- a/src/controls/QskEvent.h +++ b/src/controls/QskEvent.h @@ -37,6 +37,7 @@ class QSK_EXPORT QskEvent : public QEvent NoEvent = 53800, GeometryChange, + ViewportChange, WindowChange, /* @@ -85,6 +86,22 @@ class QSK_EXPORT QskGeometryChangeEvent : public QskEvent QRectF m_oldRect; }; +class QSK_EXPORT QskViewportChangeEvent : public QskEvent +{ + public: + QskViewportChangeEvent( QQuickItem* ); + + inline QQuickItem* modifiedParent() const { return m_modifiedParent; } + + QskViewportChangeEvent* clone() const override; + + protected: + QSK_EVENT_DISABLE_COPY( QskViewportChangeEvent ) + + private: + QQuickItem* m_modifiedParent; +}; + class QSK_EXPORT QskWindowChangeEvent : public QskEvent { public: diff --git a/src/controls/QskItem.cpp b/src/controls/QskItem.cpp index dfa0aef9..9a89189b 100644 --- a/src/controls/QskItem.cpp +++ b/src/controls/QskItem.cpp @@ -781,6 +781,11 @@ bool QskItem::event( QEvent* event ) changeEvent( event ); return true; } + case QskEvent::ViewportChange: + { + viewportChangeEvent( static_cast< QskViewportChangeEvent* >( event ) ); + return true; + } case QskEvent::GeometryChange: { geometryChangeEvent( static_cast< QskGeometryChangeEvent* >( event ) ); @@ -858,6 +863,11 @@ void QskItem::windowChangeEvent( QskWindowChangeEvent* ) { } +void QskItem::viewportChangeEvent( QskViewportChangeEvent* event ) +{ + event->ignore(); +} + void QskItem::geometryChangeEvent( QskGeometryChangeEvent* ) { } diff --git a/src/controls/QskItem.h b/src/controls/QskItem.h index 3360d1e8..d921a740 100644 --- a/src/controls/QskItem.h +++ b/src/controls/QskItem.h @@ -11,6 +11,7 @@ class QskItemPrivate; class QskGeometryChangeEvent; +class QskViewportChangeEvent; class QskWindowChangeEvent; class QSK_EXPORT QskItem : public QQuickItem @@ -154,6 +155,7 @@ class QSK_EXPORT QskItem : public QQuickItem virtual void changeEvent( QEvent* ); virtual void geometryChangeEvent( QskGeometryChangeEvent* ); + virtual void viewportChangeEvent( QskViewportChangeEvent* ); virtual void windowChangeEvent( QskWindowChangeEvent* ); void mouseUngrabEvent() override; diff --git a/src/controls/QskItemPrivate.cpp b/src/controls/QskItemPrivate.cpp index 514bf512..40f0cca7 100644 --- a/src/controls/QskItemPrivate.cpp +++ b/src/controls/QskItemPrivate.cpp @@ -6,6 +6,7 @@ #include "QskItemPrivate.h" #include "QskTreeNode.h" #include "QskSetup.h" +#include "QskEvent.h" static inline void qskSendEventTo( QObject* object, QEvent::Type type ) { @@ -251,8 +252,24 @@ QSGTransformNode* QskItemPrivate::createTransformNode() return new QskItemNode(); } -/* - Can we do something useful with overloading: +#if QT_VERSION >= QT_VERSION_CHECK( 6, 3, 0 ) - - QQuickItemPrivate::transformChanged - */ +bool QskItemPrivate::transformChanged( QQuickItem* transformedItem ) +{ + bool accepted = false; + + if ( q_func()->flags() & QQuickItem::ItemObservesViewport ) + { + QskViewportChangeEvent event( transformedItem ); + QCoreApplication::sendEvent( q_func(), &event ); + + accepted = event.isAccepted(); + } + + accepted |= Inherited::transformChanged( transformedItem ); + + + return accepted; +} + +#endif diff --git a/src/controls/QskItemPrivate.h b/src/controls/QskItemPrivate.h index b210d4e3..4e828f69 100644 --- a/src/controls/QskItemPrivate.h +++ b/src/controls/QskItemPrivate.h @@ -45,6 +45,10 @@ class QskItemPrivate : public QQuickItemPrivate void setImplicitSize( qreal width, qreal height, bool doNotify ); virtual QSizeF implicitSizeHint() const = 0; +#if QT_VERSION >= QT_VERSION_CHECK( 6, 3, 0 ) + bool transformChanged( QQuickItem* ) override final; +#endif + private: Q_DECLARE_PUBLIC( QskItem ) diff --git a/src/controls/QskScrollArea.cpp b/src/controls/QskScrollArea.cpp index 42ea661b..07d53370 100644 --- a/src/controls/QskScrollArea.cpp +++ b/src/controls/QskScrollArea.cpp @@ -245,6 +245,8 @@ namespace { if ( change.sizeChange() ) scrolledItemGeometryChange(); + + viewportChanged(); } void updateNode( QSGNode* ) override; @@ -287,6 +289,17 @@ namespace const QSGClipNode* viewPortClipNode() const; + void viewportChanged() + { +#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 ) + if ( auto item = scrollArea()->scrolledItem() ) + { + QskViewportChangeEvent ev( this ); + QCoreApplication::sendEvent( item, &ev ); + } +#endif + } + bool m_isSizeChangedEnabled = true; }; @@ -422,6 +435,7 @@ namespace { // we need to restore the clip node update(); + viewportChanged(); } }