QskScrollArea once more changed, this time again without having to copy

the viewport clip node
This commit is contained in:
Uwe Rathmann 2018-01-16 20:26:18 +01:00
parent 4f6bf75426
commit 6589f7a696
4 changed files with 49 additions and 116 deletions

View File

@ -15,6 +15,7 @@
#include <QskBoxBorderMetrics.h>
#include <QskBoxShapeMetrics.h>
#include <QskAspect.h>
#include <QskBoxBorderColors.h>
#include <QskFocusIndicator.h>
#include <QGuiApplication>
@ -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();

View File

@ -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"

View File

@ -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;
}

View File

@ -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;