QskScrollArea once more changed, this time again without having to copy
the viewport clip node
This commit is contained in:
parent
4f6bf75426
commit
6589f7a696
|
@ -15,6 +15,7 @@
|
||||||
#include <QskBoxBorderMetrics.h>
|
#include <QskBoxBorderMetrics.h>
|
||||||
#include <QskBoxShapeMetrics.h>
|
#include <QskBoxShapeMetrics.h>
|
||||||
#include <QskAspect.h>
|
#include <QskAspect.h>
|
||||||
|
#include <QskBoxBorderColors.h>
|
||||||
#include <QskFocusIndicator.h>
|
#include <QskFocusIndicator.h>
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
@ -154,15 +155,27 @@ int main( int argc, char* argv[] )
|
||||||
But here we only want to demonstrate how QskScrollArea works.
|
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->setMargins( QMarginsF( 25, 25, 5, 5 ) );
|
||||||
scrollArea->setScrolledItem( new IconGrid() );
|
scrollArea->setScrolledItem( new IconGrid() );
|
||||||
|
|
||||||
|
auto focusIndicator = new QskFocusIndicator();
|
||||||
|
focusIndicator->setBoxBorderColorsHint( QskFocusIndicator::Panel, Qt::darkRed );
|
||||||
|
|
||||||
QskWindow window;
|
QskWindow window;
|
||||||
window.resize( 600, 600 );
|
window.resize( 600, 600 );
|
||||||
window.setColor( "SteelBlue" );
|
window.setColor( "SteelBlue" );
|
||||||
window.addItem( scrollArea );
|
window.addItem( box );
|
||||||
window.addItem( new QskFocusIndicator() );
|
window.addItem( focusIndicator );
|
||||||
|
|
||||||
window.show();
|
window.show();
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "QskScrollArea.h"
|
#include "QskScrollArea.h"
|
||||||
#include "QskScrollViewSkinlet.h"
|
#include "QskScrollViewSkinlet.h"
|
||||||
#include "QskLayoutConstraint.h"
|
#include "QskLayoutConstraint.h"
|
||||||
#include "QskBoxClipNode.h"
|
|
||||||
#include "QskEvent.h"
|
#include "QskEvent.h"
|
||||||
|
|
||||||
QSK_QT_PRIVATE_BEGIN
|
QSK_QT_PRIVATE_BEGIN
|
||||||
|
@ -77,11 +76,9 @@ namespace
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ViewportClipNode():
|
ViewportClipNode():
|
||||||
QQuickDefaultClipNode( QRectF() ),
|
QQuickDefaultClipNode( QRectF() )
|
||||||
m_otherGeometry( nullptr )
|
|
||||||
{
|
{
|
||||||
setGeometry( nullptr );
|
setGeometry( nullptr );
|
||||||
setFlag( QSGNode::OwnsGeometry, true );
|
|
||||||
|
|
||||||
// clip nodes have no material, so this flag
|
// clip nodes have no material, so this flag
|
||||||
// is available to indicate our replaced clip node
|
// is available to indicate our replaced clip node
|
||||||
|
@ -89,17 +86,15 @@ namespace
|
||||||
setFlag( QSGNode::OwnsMaterial, true );
|
setFlag( QSGNode::OwnsMaterial, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyFrom( const QSGClipNode* other, const QPointF& offset )
|
void copyFrom( const QSGClipNode* other )
|
||||||
{
|
{
|
||||||
if ( other == nullptr )
|
if ( other == nullptr )
|
||||||
{
|
{
|
||||||
if ( !( clipRect().isEmpty() && isRectangular() ) )
|
if ( !( isRectangular() && clipRect().isEmpty() ) )
|
||||||
{
|
{
|
||||||
setClipRect( QRectF() );
|
|
||||||
setIsRectangular( true );
|
setIsRectangular( true );
|
||||||
|
setClipRect( QRectF() );
|
||||||
setGeometry( nullptr );
|
setGeometry( nullptr );
|
||||||
m_otherGeometry = nullptr;
|
|
||||||
|
|
||||||
markDirty( QSGNode::DirtyGeometry );
|
markDirty( QSGNode::DirtyGeometry );
|
||||||
}
|
}
|
||||||
|
@ -109,11 +104,9 @@ namespace
|
||||||
|
|
||||||
bool isDirty = false;
|
bool isDirty = false;
|
||||||
|
|
||||||
const auto newClipRect = other->clipRect().translated( offset );
|
if ( clipRect() != other->clipRect() )
|
||||||
|
|
||||||
if ( clipRect() != newClipRect )
|
|
||||||
{
|
{
|
||||||
setClipRect( newClipRect );
|
setClipRect( other->clipRect() );
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,9 +115,7 @@ namespace
|
||||||
if ( !isRectangular() )
|
if ( !isRectangular() )
|
||||||
{
|
{
|
||||||
setIsRectangular( true );
|
setIsRectangular( true );
|
||||||
|
|
||||||
setGeometry( nullptr );
|
setGeometry( nullptr );
|
||||||
m_otherGeometry = nullptr;
|
|
||||||
|
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
|
@ -137,14 +128,10 @@ namespace
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( geometry() == nullptr )
|
if ( geometry() != other->geometry() )
|
||||||
|| ( geometry()->vertexCount() != other->geometry()->vertexCount() )
|
|
||||||
|| ( other->geometry() != m_otherGeometry ) )
|
|
||||||
{
|
{
|
||||||
setGeometry( QskBoxClipNode::translatedGeometry(
|
// both nodes share the same geometry
|
||||||
other->geometry(), offset ) );
|
setGeometry( const_cast< QSGGeometry* >( other->geometry() ) );
|
||||||
|
|
||||||
m_otherGeometry = other->geometry();
|
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,8 +149,6 @@ namespace
|
||||||
into nops.
|
into nops.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
const QSGGeometry* m_otherGeometry;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +169,16 @@ public:
|
||||||
return children.isEmpty() ? nullptr : children.first();
|
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:
|
protected:
|
||||||
virtual bool event( QEvent* event ) override final;
|
virtual bool event( QEvent* event ) override final;
|
||||||
virtual void windowChangeEvent( QskWindowChangeEvent* ) 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
|
Update the clip node with the geometry of the clip node
|
||||||
of the viewport of the scrollview.
|
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 );
|
auto viewClipNode = static_cast< ViewportClipNode* >( clipNode );
|
||||||
viewClipNode->copyFrom( viewPortClipNode(), -position() );
|
viewClipNode->copyFrom( viewPortClipNode() );
|
||||||
|
|
||||||
Q_ASSERT( viewClipNode->isRectangular() || viewClipNode->geometry() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,34 +432,13 @@ public:
|
||||||
somewhere below the paint node to have all items on the viewport being clipped.
|
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.
|
This is how it is done f.e. for the list boxes.
|
||||||
|
|
||||||
But when having QQuickItems on the viewport we run into 2 fundamental
|
But when having QQuickItems on the viewport we run into a fundamental limitation
|
||||||
limitations of the Qt/Quick design.
|
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.
|
We work around this problem, by inserting an extra item between the scroll area
|
||||||
b) The default clipNode() is always rectangular and only for the
|
and the scrollable item. This item replaces its default clip node by its own node,
|
||||||
complete boundingRect() of the item.
|
that references the geometry of the viewport clip node.
|
||||||
|
|
||||||
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 ...
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QskScrollArea::QskScrollArea( QQuickItem* parentItem ):
|
QskScrollArea::QskScrollArea( QQuickItem* parentItem ):
|
||||||
|
@ -501,7 +470,8 @@ void QskScrollArea::updateLayout()
|
||||||
{
|
{
|
||||||
Inherited::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();
|
adjustItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,9 +575,12 @@ QQuickItem* QskScrollArea::scrolledItem() const
|
||||||
|
|
||||||
void QskScrollArea::translateItem()
|
void QskScrollArea::translateItem()
|
||||||
{
|
{
|
||||||
auto item = scrolledItem();
|
auto item = m_data->clipItem->scrolledItem();
|
||||||
if ( item )
|
if ( item )
|
||||||
item->setPosition( -scrollPos() );
|
{
|
||||||
|
const QPointF pos = viewContentsRect().topLeft() - scrollPos();
|
||||||
|
item->setPosition( pos );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_QskScrollArea.cpp"
|
#include "moc_QskScrollArea.cpp"
|
||||||
|
|
|
@ -60,54 +60,3 @@ void QskBoxClipNode::setBox( const QRectF& rect,
|
||||||
|
|
||||||
markDirty( QSGNode::DirtyGeometry );
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,8 +21,6 @@ public:
|
||||||
void setBox( const QRectF&,
|
void setBox( const QRectF&,
|
||||||
const QskBoxShapeMetrics&, const QskBoxBorderMetrics& );
|
const QskBoxShapeMetrics&, const QskBoxBorderMetrics& );
|
||||||
|
|
||||||
static QSGGeometry* translatedGeometry( const QSGGeometry*, const QPointF& );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint m_hash;
|
uint m_hash;
|
||||||
QRectF m_rect;
|
QRectF m_rect;
|
||||||
|
|
Loading…
Reference in New Issue