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"
|
2018-05-01 12:41:20 +02:00
|
|
|
#include "QskQuick.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "QskScrollViewSkinlet.h"
|
|
|
|
#include "QskLayoutConstraint.h"
|
2018-01-16 12:13:38 +01:00
|
|
|
#include "QskEvent.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qquickitem_p.h>
|
2018-01-16 12:13:38 +01:00
|
|
|
#include <private/qquickwindow_p.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
#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-16 20:26:18 +01:00
|
|
|
QQuickDefaultClipNode( QRectF() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setGeometry( nullptr );
|
|
|
|
|
|
|
|
// clip nodes have no material, so this flag
|
|
|
|
// is available to indicate our replaced clip node
|
|
|
|
|
|
|
|
setFlag( QSGNode::OwnsMaterial, true );
|
|
|
|
}
|
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
void copyFrom( const QSGClipNode* other )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( other == nullptr )
|
|
|
|
{
|
2018-01-16 20:26:18 +01:00
|
|
|
if ( !( isRectangular() && clipRect().isEmpty() ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-12 15:46:15 +01:00
|
|
|
setIsRectangular( true );
|
2018-01-16 20:26:18 +01:00
|
|
|
setClipRect( QRectF() );
|
2017-07-21 18:21:34 +02:00
|
|
|
setGeometry( nullptr );
|
|
|
|
|
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;
|
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
if ( clipRect() != other->clipRect() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-16 20:26:18 +01:00
|
|
|
setClipRect( other->clipRect() );
|
2018-01-12 15:46:15 +01:00
|
|
|
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 );
|
|
|
|
|
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-16 20:26:18 +01:00
|
|
|
if ( geometry() != other->geometry() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-01-16 20:26:18 +01:00
|
|
|
// both nodes share the same geometry
|
|
|
|
setGeometry( const_cast< QSGGeometry* >( other->geometry() ) );
|
2018-01-12 15:46:15 +01:00
|
|
|
isDirty = true;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( isDirty )
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
|
2018-07-31 17:32:25 +02:00
|
|
|
void update() override
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-07-31 17:32:25 +02:00
|
|
|
bool contains( const QPointF& pos ) const override
|
2018-01-16 20:26:18 +01:00
|
|
|
{
|
|
|
|
return clipRect().contains( pos );
|
|
|
|
}
|
|
|
|
|
2018-07-31 17:32:25 +02:00
|
|
|
QRectF clipRect() const override
|
2018-01-16 20:26:18 +01:00
|
|
|
{
|
|
|
|
return scrollArea()->subControlRect( QskScrollView::Viewport );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
protected:
|
2018-07-31 17:32:25 +02:00
|
|
|
bool event( QEvent* event ) override;
|
|
|
|
void windowChangeEvent( QskWindowChangeEvent* ) override;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-07-31 17:32:25 +02:00
|
|
|
void itemChange( ItemChange, const ItemChangeData& ) override;
|
|
|
|
void geometryChanged( const QRectF&, const QRectF& ) override;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
|
2018-07-31 17:32:25 +02:00
|
|
|
void itemGeometryChanged( QQuickItem*,
|
|
|
|
QQuickGeometryChange change, const QRectF& ) override
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( change.sizeChange() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2018-07-31 17:32:25 +02:00
|
|
|
void itemGeometryChanged( QQuickItem*,
|
|
|
|
const QRectF& newRect, const QRectF& oldRect ) override
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( oldRect.size() != newRect.size() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-07-31 17:32:25 +02:00
|
|
|
void updateNode( QSGNode* ) override;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
private:
|
2018-01-16 12:13:38 +01:00
|
|
|
void connectWindow( const QQuickWindow*, bool on );
|
|
|
|
void onFocusItemChanged();
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
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 );
|
2018-01-16 12:13:38 +01:00
|
|
|
|
|
|
|
connectWindow( window(), true );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskScrollAreaClipItem::~QskScrollAreaClipItem()
|
|
|
|
{
|
|
|
|
enableGeometryListener( false );
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:13:38 +01:00
|
|
|
void QskScrollAreaClipItem::connectWindow( const QQuickWindow* window, bool on )
|
|
|
|
{
|
|
|
|
if ( window == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
connect( window, &QQuickWindow::activeFocusItemChanged,
|
|
|
|
this, &QskScrollAreaClipItem::onFocusItemChanged );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
disconnect( window, &QQuickWindow::activeFocusItemChanged,
|
|
|
|
this, &QskScrollAreaClipItem::onFocusItemChanged );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
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.
|
|
|
|
*/
|
2017-07-21 18:21:34 +02:00
|
|
|
auto viewClipNode = static_cast< ViewportClipNode* >( clipNode );
|
2018-01-16 20:26:18 +01:00
|
|
|
viewClipNode->copyFrom( viewPortClipNode() );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const QSGClipNode* QskScrollAreaClipItem::viewPortClipNode() const
|
|
|
|
{
|
2018-01-19 10:07:05 +01:00
|
|
|
auto node = const_cast< QSGNode* >( qskPaintNode( scrollArea() ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
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 );
|
|
|
|
}
|
2018-01-16 12:13:38 +01:00
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:13:38 +01:00
|
|
|
void QskScrollAreaClipItem::onFocusItemChanged()
|
|
|
|
{
|
|
|
|
if ( window() == nullptr || !scrollArea()->autoScrollFocusItem() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto focusItem = window()->activeFocusItem();
|
|
|
|
if ( focusItem )
|
|
|
|
{
|
|
|
|
auto reason = QQuickWindowPrivate::get( window() )->lastFocusReason;
|
|
|
|
if ( reason == Qt::TabFocusReason || reason == Qt::BacktabFocusReason )
|
|
|
|
scrollArea()->ensureItemVisible( focusItem );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
bool QskScrollAreaClipItem::event( QEvent* event )
|
|
|
|
{
|
|
|
|
if( event->type() == QEvent::LayoutRequest )
|
|
|
|
{
|
|
|
|
if ( scrollArea()->isItemResizable() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::event( event );
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:13:38 +01:00
|
|
|
void QskScrollAreaClipItem::windowChangeEvent( QskWindowChangeEvent* event )
|
|
|
|
{
|
|
|
|
Inherited::windowChangeEvent( event );
|
|
|
|
|
|
|
|
connectWindow( event->oldWindow(), false );
|
|
|
|
connectWindow( event->window(), true );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
class QskScrollArea::PrivateData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PrivateData():
|
2018-01-16 12:13:38 +01:00
|
|
|
isItemResizable( true ),
|
|
|
|
autoScrollFocusItem( true )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2018-01-16 12:13:38 +01:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
bool isItemResizable : 1;
|
2018-01-16 12:13:38 +01:00
|
|
|
bool autoScrollFocusItem : 1;
|
2017-07-21 18:21:34 +02:00
|
|
|
};
|
|
|
|
|
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.
|
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
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.
|
2018-01-12 15:46:15 +01:00
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
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.
|
2018-01-12 15:46:15 +01:00
|
|
|
*/
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-01-16 12:13:38 +01:00
|
|
|
void QskScrollArea::ensureItemVisible( const QQuickItem* item )
|
|
|
|
{
|
|
|
|
const QQuickItem* scrolledItem = this->scrolledItem();
|
|
|
|
if ( scrolledItem && qskIsAncestorOf( scrolledItem, item ) )
|
|
|
|
{
|
|
|
|
const auto pos = scrolledItem->mapFromItem( item, QPointF() );
|
|
|
|
ensureVisible( QRectF( pos.x(), pos.y(), item->width(), item->height() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskScrollArea::updateLayout()
|
|
|
|
{
|
|
|
|
Inherited::updateLayout();
|
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
// the clipItem always has the same geometry as the scroll area
|
|
|
|
m_data->clipItem->setSize( size() );
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 12:13:38 +01:00
|
|
|
void QskScrollArea::setAutoScrollFocusedItem( bool on )
|
|
|
|
{
|
|
|
|
if ( m_data->autoScrollFocusItem != on )
|
|
|
|
{
|
|
|
|
m_data->autoScrollFocusItem = on;
|
|
|
|
Q_EMIT autoScrollFocusedItemChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskScrollArea::autoScrollFocusItem() const
|
|
|
|
{
|
|
|
|
return m_data->autoScrollFocusItem;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
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-16 20:26:18 +01:00
|
|
|
auto item = m_data->clipItem->scrolledItem();
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( item )
|
2018-01-16 20:26:18 +01:00
|
|
|
{
|
|
|
|
const QPointF pos = viewContentsRect().topLeft() - scrollPos();
|
|
|
|
item->setPosition( pos );
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskScrollArea.cpp"
|