diff --git a/examples/thumbnails/main.cpp b/examples/thumbnails/main.cpp index 5e48d40a..ec47e987 100644 --- a/examples/thumbnails/main.cpp +++ b/examples/thumbnails/main.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -154,15 +155,27 @@ int main( int argc, char* argv[] ) But here we only want to demonstrate how QskScrollArea works. */ - auto scrollArea = new ScrollArea(); + auto box = new QskLinearBox( Qt::Vertical ); + box->setMargins( 20 ); + + auto buttonBox = new QskLinearBox( Qt::Horizontal, box ); + buttonBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); + + new QskPushButton( "Push Me", buttonBox ); + new QskPushButton( "Push Me", buttonBox ); + + auto scrollArea = new ScrollArea( box ); scrollArea->setMargins( QMarginsF( 25, 25, 5, 5 ) ); scrollArea->setScrolledItem( new IconGrid() ); + auto focusIndicator = new QskFocusIndicator(); + focusIndicator->setBoxBorderColorsHint( QskFocusIndicator::Panel, Qt::darkRed ); + QskWindow window; window.resize( 600, 600 ); window.setColor( "SteelBlue" ); - window.addItem( scrollArea ); - window.addItem( new QskFocusIndicator() ); + window.addItem( box ); + window.addItem( focusIndicator ); window.show(); diff --git a/src/controls/QskScrollArea.cpp b/src/controls/QskScrollArea.cpp index 4ac59dca..02e64080 100644 --- a/src/controls/QskScrollArea.cpp +++ b/src/controls/QskScrollArea.cpp @@ -6,7 +6,6 @@ #include "QskScrollArea.h" #include "QskScrollViewSkinlet.h" #include "QskLayoutConstraint.h" -#include "QskBoxClipNode.h" #include "QskEvent.h" QSK_QT_PRIVATE_BEGIN @@ -77,11 +76,9 @@ namespace { public: ViewportClipNode(): - QQuickDefaultClipNode( QRectF() ), - m_otherGeometry( nullptr ) + QQuickDefaultClipNode( QRectF() ) { setGeometry( nullptr ); - setFlag( QSGNode::OwnsGeometry, true ); // clip nodes have no material, so this flag // is available to indicate our replaced clip node @@ -89,17 +86,15 @@ namespace setFlag( QSGNode::OwnsMaterial, true ); } - void copyFrom( const QSGClipNode* other, const QPointF& offset ) + void copyFrom( const QSGClipNode* other ) { if ( other == nullptr ) { - if ( !( clipRect().isEmpty() && isRectangular() ) ) + if ( !( isRectangular() && clipRect().isEmpty() ) ) { - setClipRect( QRectF() ); setIsRectangular( true ); - + setClipRect( QRectF() ); setGeometry( nullptr ); - m_otherGeometry = nullptr; markDirty( QSGNode::DirtyGeometry ); } @@ -109,11 +104,9 @@ namespace bool isDirty = false; - const auto newClipRect = other->clipRect().translated( offset ); - - if ( clipRect() != newClipRect ) + if ( clipRect() != other->clipRect() ) { - setClipRect( newClipRect ); + setClipRect( other->clipRect() ); isDirty = true; } @@ -122,9 +115,7 @@ namespace if ( !isRectangular() ) { setIsRectangular( true ); - setGeometry( nullptr ); - m_otherGeometry = nullptr; isDirty = true; } @@ -137,14 +128,10 @@ namespace isDirty = true; } - if ( ( geometry() == nullptr ) - || ( geometry()->vertexCount() != other->geometry()->vertexCount() ) - || ( other->geometry() != m_otherGeometry ) ) + if ( geometry() != other->geometry() ) { - setGeometry( QskBoxClipNode::translatedGeometry( - other->geometry(), offset ) ); - - m_otherGeometry = other->geometry(); + // both nodes share the same geometry + setGeometry( const_cast< QSGGeometry* >( other->geometry() ) ); isDirty = true; } } @@ -162,8 +149,6 @@ namespace into nops. */ } - - const QSGGeometry* m_otherGeometry; }; } @@ -184,6 +169,16 @@ public: return children.isEmpty() ? nullptr : children.first(); } + virtual bool contains( const QPointF& pos ) const override final + { + return clipRect().contains( pos ); + } + + virtual QRectF clipRect() const override final + { + return scrollArea()->subControlRect( QskScrollView::Viewport ); + } + protected: virtual bool event( QEvent* event ) override final; virtual void windowChangeEvent( QskWindowChangeEvent* ) override final; @@ -309,14 +304,9 @@ void QskScrollAreaClipItem::updateNode( QSGNode* ) /* Update the clip node with the geometry of the clip node of the viewport of the scrollview. - - Maybe it would be better to ask the skinlet for translated clip node - but we would have a dependency for QskScrollViewSkinlet then. */ auto viewClipNode = static_cast< ViewportClipNode* >( clipNode ); - viewClipNode->copyFrom( viewPortClipNode(), -position() ); - - Q_ASSERT( viewClipNode->isRectangular() || viewClipNode->geometry() ); + viewClipNode->copyFrom( viewPortClipNode() ); } } @@ -442,34 +432,13 @@ public: somewhere below the paint node to have all items on the viewport being clipped. This is how it is done f.e. for the list boxes. - But when having QQuickItems on the viewport we run into 2 fundamental - limitations of the Qt/Quick design. + But when having QQuickItems on the viewport we run into a fundamental limitation + of the Qt/Quick design: node subtrees for the children have to be in parallel to + the paint node. - a) The node subtrees for the children are in parallel to the paint node. - b) The default clipNode() is always rectangular and only for the - complete boundingRect() of the item. - - Both limitations are hardcoded in QQuickWindow without offering ways - to customize the operations. Even worse: obviously the code was once started - with having more flexible APIs in mind, but for some reasons it was never - finalized and not even the existing APIs are internally used properly. - - ( F.e there would be a virtual method QQuickItem::clipRect(), but QQuickWindow - uses erroneously QQuickItem::contains() to filter events - grmpf. ) - - -- - - This class works around those limitations, by inserting a clip item - that replaces its default clip node by copying out the geometry of clip node - for view port. - - This clip item needs to have exactly the same position + size as the - viewport, so that clipping of the mouse/touch/hover/wheel in QQuickWindow - works properly. Unfortunately we then have to copy + translate the geometry of - the view port instead of simply sharing it between the 2 clip nodes. - - But even then, filtering of events does not yet work perfect for non rectangular - clip regions. Maybe it could be done by adding a childMouseEventFilter(). TODO ... + We work around this problem, by inserting an extra item between the scroll area + and the scrollable item. This item replaces its default clip node by its own node, + that references the geometry of the viewport clip node. */ QskScrollArea::QskScrollArea( QQuickItem* parentItem ): @@ -501,7 +470,8 @@ void QskScrollArea::updateLayout() { Inherited::updateLayout(); - m_data->clipItem->setGeometry( viewContentsRect() ); + // the clipItem always has the same geometry as the scroll area + m_data->clipItem->setSize( size() ); adjustItem(); } @@ -605,9 +575,12 @@ QQuickItem* QskScrollArea::scrolledItem() const void QskScrollArea::translateItem() { - auto item = scrolledItem(); + auto item = m_data->clipItem->scrolledItem(); if ( item ) - item->setPosition( -scrollPos() ); + { + const QPointF pos = viewContentsRect().topLeft() - scrollPos(); + item->setPosition( pos ); + } } #include "moc_QskScrollArea.cpp" diff --git a/src/nodes/QskBoxClipNode.cpp b/src/nodes/QskBoxClipNode.cpp index 087d2193..81149da3 100644 --- a/src/nodes/QskBoxClipNode.cpp +++ b/src/nodes/QskBoxClipNode.cpp @@ -60,54 +60,3 @@ void QskBoxClipNode::setBox( const QRectF& rect, markDirty( QSGNode::DirtyGeometry ); } - -template< class Point > -static inline void qskCopyPoints( const Point* from, Point* to, - int numPoints, const QPointF& offset ) -{ - if ( offset.isNull() ) - { - memcpy( to, from, numPoints * sizeof( Point ) ); - } - else - { - const float dx = offset.x(); - const float dy = offset.y(); - - for ( int i = 0; i < numPoints; i++ ) - { - to[i].x = from[i].x + dx; - to[i].y = from[i].y + dy; - } - } -} - -QSGGeometry* QskBoxClipNode::translatedGeometry( - const QSGGeometry* geometry, const QPointF& offset ) -{ - if ( geometry == nullptr || geometry->vertexCount() == 0 ) - return nullptr; - - QSGGeometry* g = nullptr; - - if ( geometry->sizeOfVertex() == sizeof( QSGGeometry::Point2D ) ) - { - g = new QSGGeometry( - QSGGeometry::defaultAttributes_Point2D(), geometry->vertexCount(), - geometry->indexCount(), geometry->indexType() ); - - qskCopyPoints( geometry->vertexDataAsPoint2D(), - g->vertexDataAsPoint2D(), g->vertexCount(), offset ); - } - else if ( geometry->sizeOfVertex() == sizeof( QSGGeometry::ColoredPoint2D ) ) - { - g = new QSGGeometry( - QSGGeometry::defaultAttributes_ColoredPoint2D(), geometry->vertexCount(), - geometry->indexCount(), geometry->indexType() ); - - qskCopyPoints( geometry->vertexDataAsColoredPoint2D(), - g->vertexDataAsColoredPoint2D(), g->vertexCount(), offset ); - } - - return g; -} diff --git a/src/nodes/QskBoxClipNode.h b/src/nodes/QskBoxClipNode.h index 59222fc4..38954408 100644 --- a/src/nodes/QskBoxClipNode.h +++ b/src/nodes/QskBoxClipNode.h @@ -21,8 +21,6 @@ public: void setBox( const QRectF&, const QskBoxShapeMetrics&, const QskBoxBorderMetrics& ); - static QSGGeometry* translatedGeometry( const QSGGeometry*, const QPointF& ); - private: uint m_hash; QRectF m_rect;