2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskScrollArea.h"
|
|
|
|
#include "QskScrollViewSkinlet.h"
|
|
|
|
#include "QskLayoutConstraint.h"
|
2018-01-12 15:46:15 +01:00
|
|
|
#include "QskBoxClipNode.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qquickitem_p.h>
|
|
|
|
#include <private/qquickclipnode_p.h>
|
|
|
|
#include <private/qquickitemchangelistener_p.h>
|
|
|
|
QSK_QT_PRIVATE_END
|
|
|
|
|
|
|
|
static QSizeF qskAdjustedSize( const QQuickItem* item, const QSizeF& targetSize )
|
|
|
|
{
|
|
|
|
using namespace QskLayoutConstraint;
|
|
|
|
|
|
|
|
QSizeF sz = effectiveConstraint( item, Qt::PreferredSize );
|
|
|
|
|
|
|
|
qreal w = sz.width();
|
|
|
|
qreal h = sz.height();
|
|
|
|
|
|
|
|
if ( targetSize != sz )
|
|
|
|
{
|
|
|
|
const QSizeF minSize = effectiveConstraint( item, Qt::MinimumSize );
|
|
|
|
const QSizeF maxSize = effectiveConstraint( item, Qt::MaximumSize );
|
|
|
|
|
|
|
|
const auto policy = sizePolicy( item );
|
|
|
|
|
|
|
|
if ( targetSize.width() > w )
|
|
|
|
{
|
|
|
|
if ( policy.horizontalPolicy() & QskSizePolicy::GrowFlag )
|
|
|
|
w = qMin( maxSize.width(), targetSize.width() );
|
|
|
|
}
|
|
|
|
else if ( targetSize.width() < w )
|
|
|
|
{
|
|
|
|
if ( policy.horizontalPolicy() & QskSizePolicy::ShrinkFlag )
|
|
|
|
w = qMax( minSize.width(), w );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( targetSize.height() > h )
|
|
|
|
{
|
|
|
|
if ( policy.verticalPolicy() & QskSizePolicy::GrowFlag )
|
|
|
|
h = qMin( maxSize.height(), targetSize.height() );
|
|
|
|
}
|
|
|
|
else if ( targetSize.height() < h )
|
|
|
|
{
|
|
|
|
if ( policy.verticalPolicy() & QskSizePolicy::ShrinkFlag )
|
|
|
|
h = qMax( minSize.height(), h );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QSizeF( w, h );
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class ViewportClipNode final : public QQuickDefaultClipNode
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ViewportClipNode():
|
2018-01-12 15:46:15 +01:00
|
|
|
QQuickDefaultClipNode( QRectF() ),
|
|
|
|
m_otherGeometry( nullptr )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setGeometry( nullptr );
|
2018-01-12 15:46:15 +01:00
|
|
|
setFlag( QSGNode::OwnsGeometry, true );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
// clip nodes have no material, so this flag
|
|
|
|
// is available to indicate our replaced clip node
|
|
|
|
|
|
|
|
setFlag( QSGNode::OwnsMaterial, true );
|
|
|
|
}
|
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
void copyFrom( const QSGClipNode* other, const QPointF& offset )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( other == nullptr )
|
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
if ( !( clipRect().isEmpty() && isRectangular() ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setClipRect( QRectF() );
|
2018-01-12 15:46:15 +01:00
|
|
|
setIsRectangular( true );
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
setGeometry( nullptr );
|
2018-01-12 15:46:15 +01:00
|
|
|
m_otherGeometry = nullptr;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-01-12 15:46:15 +01:00
|
|
|
|
|
|
|
return;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-01-12 15:46:15 +01:00
|
|
|
|
|
|
|
bool isDirty = false;
|
|
|
|
|
|
|
|
const auto newClipRect = other->clipRect().translated( offset );
|
|
|
|
|
|
|
|
if ( clipRect() != newClipRect )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
setClipRect( newClipRect );
|
|
|
|
isDirty = true;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
if ( other->isRectangular() )
|
|
|
|
{
|
|
|
|
if ( !isRectangular() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
setIsRectangular( true );
|
|
|
|
|
|
|
|
setGeometry( nullptr );
|
|
|
|
m_otherGeometry = nullptr;
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
isDirty = true;
|
|
|
|
}
|
2018-01-12 15:46:15 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( isRectangular() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
setIsRectangular( false );
|
2017-07-21 18:21:34 +02:00
|
|
|
isDirty = true;
|
|
|
|
}
|
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
if ( ( geometry() == nullptr )
|
|
|
|
|| ( geometry()->vertexCount() != other->geometry()->vertexCount() )
|
|
|
|
|| ( other->geometry() != m_otherGeometry ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
setGeometry( QskBoxClipNode::translatedGeometry(
|
|
|
|
other->geometry(), offset ) );
|
|
|
|
|
|
|
|
m_otherGeometry = other->geometry();
|
|
|
|
isDirty = true;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( isDirty )
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void update() override final
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The Qt-Quick framework is limited to setting clipNodes from
|
|
|
|
the bounding rectangle. As we need a different clipping
|
|
|
|
we turn any updates of the clip done by QQuickWindow
|
|
|
|
into nops.
|
|
|
|
*/
|
|
|
|
}
|
2018-01-12 15:46:15 +01:00
|
|
|
|
|
|
|
const QSGGeometry* m_otherGeometry;
|
2017-07-21 18:21:34 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeListener
|
|
|
|
{
|
|
|
|
// when inheriting from QskControl we participate in node cleanups
|
|
|
|
using Inherited = QskControl;
|
|
|
|
|
|
|
|
public:
|
|
|
|
QskScrollAreaClipItem( QskScrollArea* );
|
|
|
|
virtual ~QskScrollAreaClipItem();
|
|
|
|
|
|
|
|
void enableGeometryListener( bool on );
|
|
|
|
|
|
|
|
QQuickItem* scrolledItem() const
|
|
|
|
{
|
|
|
|
auto children = childItems();
|
|
|
|
return children.isEmpty() ? nullptr : children.first();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool event( QEvent* event ) override final;
|
|
|
|
|
|
|
|
virtual void itemChange( ItemChange, const ItemChangeData& ) override final;
|
|
|
|
virtual void geometryChanged( const QRectF&, const QRectF& ) override final;
|
|
|
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
|
|
|
|
virtual void itemGeometryChanged( QQuickItem*,
|
|
|
|
QQuickGeometryChange change, const QRectF& ) override final
|
|
|
|
{
|
|
|
|
if ( change.sizeChange() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2018-01-12 15:46:15 +01:00
|
|
|
virtual void itemGeometryChanged( QQuickItem*,
|
2017-07-21 18:21:34 +02:00
|
|
|
const QRectF& newRect, const QRectF& oldRect ) override final
|
|
|
|
{
|
|
|
|
if ( oldRect.size() != newRect.size() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
virtual void updateNode( QSGNode* ) override final;
|
|
|
|
|
|
|
|
private:
|
|
|
|
inline QskScrollArea* scrollArea()
|
|
|
|
{
|
|
|
|
return static_cast< QskScrollArea* >( parentItem() );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const QskScrollArea* scrollArea() const
|
|
|
|
{
|
|
|
|
return static_cast< const QskScrollArea* >( parentItem() );
|
|
|
|
}
|
|
|
|
|
|
|
|
const QSGClipNode* viewPortClipNode() const;
|
|
|
|
};
|
|
|
|
|
|
|
|
QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea ):
|
|
|
|
Inherited( scrollArea )
|
|
|
|
{
|
|
|
|
setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) );
|
|
|
|
setClip( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskScrollAreaClipItem::~QskScrollAreaClipItem()
|
|
|
|
{
|
|
|
|
enableGeometryListener( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollAreaClipItem::updateNode( QSGNode* )
|
|
|
|
{
|
|
|
|
auto* d = QQuickItemPrivate::get( this );
|
|
|
|
|
|
|
|
if ( QQuickItemPrivate::get( scrollArea() )->dirtyAttributes
|
|
|
|
& QQuickItemPrivate::ContentUpdateMask )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The update order depends on who calls update first and we
|
|
|
|
have to handle being called before a new clip node has
|
|
|
|
been created by the scrollview.
|
|
|
|
But better invalidate the unguarded clip geometry until then ...
|
|
|
|
*/
|
|
|
|
auto clipNode = d->clipNode();
|
|
|
|
if ( clipNode && !clipNode->isRectangular() )
|
|
|
|
{
|
|
|
|
clipNode->setIsRectangular( true );
|
|
|
|
clipNode->setGeometry( nullptr );
|
|
|
|
}
|
|
|
|
|
|
|
|
// in the next cycle we will find a valid clip
|
|
|
|
update();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto clipNode = d->clipNode();
|
|
|
|
|
|
|
|
if ( clipNode && !( clipNode->flags() & QSGNode::OwnsMaterial ) )
|
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
// Replace the clip node being inserted from QQuickWindow
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
auto parentNode = clipNode->parent();
|
|
|
|
|
|
|
|
auto node = new ViewportClipNode();
|
|
|
|
parentNode->appendChildNode( node );
|
|
|
|
clipNode->reparentChildNodesTo( node );
|
|
|
|
|
|
|
|
parentNode->removeChildNode( clipNode );
|
|
|
|
|
|
|
|
if ( clipNode->flags() & QSGNode::OwnedByParent )
|
|
|
|
delete clipNode;
|
|
|
|
|
|
|
|
d->extra->clipNode = clipNode = node;
|
|
|
|
Q_ASSERT( clipNode == QQuickItemPrivate::get( this )->clipNode() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( clipNode )
|
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2017-07-21 18:21:34 +02:00
|
|
|
auto viewClipNode = static_cast< ViewportClipNode* >( clipNode );
|
2018-01-12 15:46:15 +01:00
|
|
|
viewClipNode->copyFrom( viewPortClipNode(), -position() );
|
|
|
|
|
|
|
|
Q_ASSERT( viewClipNode->isRectangular() || viewClipNode->geometry() );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const QSGClipNode* QskScrollAreaClipItem::viewPortClipNode() const
|
|
|
|
{
|
|
|
|
auto node = const_cast< QSGNode* >( QskControl::paintNode( scrollArea() ) );
|
|
|
|
if ( node )
|
|
|
|
node = QskSkinlet::findNodeByRole( node, QskScrollViewSkinlet::ContentsRootRole );
|
|
|
|
|
|
|
|
if ( node && node->type() == QSGNode::ClipNodeType )
|
|
|
|
return static_cast< QSGClipNode* >( node );
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollAreaClipItem::geometryChanged(
|
|
|
|
const QRectF& newRect, const QRectF& oldRect )
|
|
|
|
{
|
|
|
|
Inherited::geometryChanged( newRect, oldRect );
|
|
|
|
|
|
|
|
if ( newRect.size() != oldRect.size() )
|
|
|
|
{
|
|
|
|
// we need to restore the clip node
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollAreaClipItem::itemChange(
|
|
|
|
QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& value )
|
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
if ( change == QQuickItem::ItemChildAddedChange )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
enableGeometryListener( true );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-01-12 15:46:15 +01:00
|
|
|
else if ( change == QQuickItem::ItemChildRemovedChange )
|
|
|
|
{
|
|
|
|
enableGeometryListener( false );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
Inherited::itemChange( change, value );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollAreaClipItem::enableGeometryListener( bool on )
|
|
|
|
{
|
|
|
|
auto item = scrolledItem();
|
|
|
|
if ( item )
|
|
|
|
{
|
|
|
|
// we might also be interested in ImplicitWidth/ImplicitHeight
|
|
|
|
const QQuickItemPrivate::ChangeTypes types = QQuickItemPrivate::Geometry;
|
|
|
|
|
|
|
|
QQuickItemPrivate* p = QQuickItemPrivate::get( item );
|
|
|
|
if ( on )
|
|
|
|
p->addItemChangeListener( this, types );
|
|
|
|
else
|
|
|
|
p->removeItemChangeListener( this, types );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskScrollAreaClipItem::event( QEvent* event )
|
|
|
|
{
|
|
|
|
if( event->type() == QEvent::LayoutRequest )
|
|
|
|
{
|
|
|
|
if ( scrollArea()->isItemResizable() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::event( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskScrollArea::PrivateData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PrivateData():
|
|
|
|
isItemResizable( true )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void enableAutoTranslation( QskScrollArea* scrollArea, bool on )
|
|
|
|
{
|
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
QObject::connect( scrollArea, &QskScrollView::scrollPosChanged,
|
|
|
|
scrollArea, &QskScrollArea::translateItem );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QObject::disconnect( scrollArea, &QskScrollView::scrollPosChanged,
|
|
|
|
scrollArea, &QskScrollArea::translateItem );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QskScrollAreaClipItem* clipItem;
|
|
|
|
bool isItemResizable : 1;
|
|
|
|
};
|
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
/*
|
|
|
|
When doing scene graph composition it is quite easy to insert a clip node
|
|
|
|
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.
|
|
|
|
|
|
|
|
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 ...
|
|
|
|
*/
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QskScrollArea::QskScrollArea( QQuickItem* parentItem ):
|
|
|
|
Inherited( parentItem ),
|
|
|
|
m_data( new PrivateData() )
|
|
|
|
{
|
|
|
|
setPolishOnResize( true );
|
|
|
|
|
|
|
|
m_data->clipItem = new QskScrollAreaClipItem( this );
|
|
|
|
m_data->enableAutoTranslation( this, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskScrollArea::~QskScrollArea()
|
|
|
|
{
|
2017-11-09 19:59:51 +01:00
|
|
|
delete m_data->clipItem;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollArea::updateLayout()
|
|
|
|
{
|
|
|
|
Inherited::updateLayout();
|
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
m_data->clipItem->setGeometry( viewContentsRect() );
|
2017-07-21 18:21:34 +02:00
|
|
|
adjustItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollArea::adjustItem()
|
|
|
|
{
|
|
|
|
QQuickItem* item = m_data->clipItem->scrolledItem();
|
2018-01-12 15:46:15 +01:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( item == nullptr )
|
|
|
|
{
|
|
|
|
setScrollableSize( QSizeF() );
|
|
|
|
setScrollPos( QPointF() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( m_data->isItemResizable )
|
|
|
|
{
|
|
|
|
const QRectF rect = viewContentsRect();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
For optional scrollbars the available space also depends
|
|
|
|
on wether the adjustedSize results in scroll bars. For the
|
|
|
|
moment we ignore this and start with a simplified code.
|
|
|
|
*/
|
|
|
|
#endif
|
|
|
|
auto newSize = qskAdjustedSize( item, rect.size() );
|
|
|
|
item->setSize( newSize );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->enableAutoTranslation( this, false );
|
|
|
|
|
|
|
|
setScrollableSize( QSizeF( item->width(), item->height() ) );
|
|
|
|
setScrollPos( scrollPos() );
|
|
|
|
|
|
|
|
m_data->enableAutoTranslation( this, true );
|
|
|
|
|
|
|
|
translateItem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollArea::setItemResizable( bool on )
|
|
|
|
{
|
|
|
|
if ( on != m_data->isItemResizable )
|
|
|
|
{
|
|
|
|
m_data->isItemResizable = on;
|
|
|
|
Q_EMIT itemResizableChanged();
|
|
|
|
|
|
|
|
if ( m_data->isItemResizable )
|
|
|
|
polish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskScrollArea::isItemResizable() const
|
|
|
|
{
|
|
|
|
return m_data->isItemResizable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollArea::setScrolledItem( QQuickItem* item )
|
|
|
|
{
|
|
|
|
auto oldItem = m_data->clipItem->scrolledItem();
|
|
|
|
if ( item == oldItem )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( oldItem )
|
|
|
|
{
|
|
|
|
if ( oldItem->parent() == this )
|
|
|
|
delete oldItem;
|
|
|
|
else
|
|
|
|
oldItem->setParentItem( nullptr );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( item )
|
|
|
|
{
|
|
|
|
item->setParentItem( m_data->clipItem );
|
|
|
|
if ( item->parent() == nullptr )
|
|
|
|
item->setParent( m_data->clipItem );
|
|
|
|
}
|
|
|
|
|
|
|
|
polish();
|
|
|
|
Q_EMIT scrolledItemChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QQuickItem* QskScrollArea::scrolledItem() const
|
|
|
|
{
|
|
|
|
return m_data->clipItem->scrolledItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollArea::translateItem()
|
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
auto item = scrolledItem();
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( item )
|
2018-01-12 15:46:15 +01:00
|
|
|
item->setPosition( -scrollPos() );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskScrollArea.cpp"
|