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-08-03 08:15:28 +02:00
|
|
|
#include "QskEvent.h"
|
2018-05-01 12:41:20 +02:00
|
|
|
#include "QskQuick.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "QskScrollViewSkinlet.h"
|
|
|
|
|
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qquickclipnode_p.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <private/qquickitem_p.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
#include <private/qquickitemchangelistener_p.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <private/qquickwindow_p.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
QSK_QT_PRIVATE_END
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class ViewportClipNode final : public QQuickDefaultClipNode
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
ViewportClipNode()
|
|
|
|
: 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;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-07-21 18:21:34 +02:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
protected:
|
2018-07-31 17:32:25 +02:00
|
|
|
bool event( QEvent* event ) 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
|
|
|
|
2018-08-03 08:15:28 +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
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
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;
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea )
|
|
|
|
: Inherited( scrollArea )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) );
|
|
|
|
setClip( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskScrollAreaClipItem::~QskScrollAreaClipItem()
|
|
|
|
{
|
|
|
|
enableGeometryListener( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollAreaClipItem::updateNode( QSGNode* )
|
|
|
|
{
|
|
|
|
auto* d = QQuickItemPrivate::get( this );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( QQuickItemPrivate::get( scrollArea() )->dirtyAttributes &
|
2019-01-07 09:13:53 +01:00
|
|
|
QQuickItemPrivate::ContentUpdateMask )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskScrollAreaClipItem::event( QEvent* event )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( event->type() == QEvent::LayoutRequest )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( scrollArea()->isItemResizable() )
|
|
|
|
scrollArea()->polish();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::event( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskScrollArea::PrivateData
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
PrivateData()
|
|
|
|
: isItemResizable( 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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:14:32 +02:00
|
|
|
QskScrollAreaClipItem* clipItem = nullptr;
|
2018-01-16 12:13:38 +01:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
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.
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskScrollArea::QskScrollArea( QQuickItem* parentItem )
|
|
|
|
: Inherited( parentItem )
|
|
|
|
, m_data( new PrivateData() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
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-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()
|
|
|
|
{
|
2019-09-10 17:01:47 +02:00
|
|
|
auto 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 )
|
|
|
|
{
|
2019-09-10 17:01:47 +02:00
|
|
|
auto size = viewContentsRect().size();
|
2020-06-09 06:56:20 +02:00
|
|
|
if ( size.isEmpty() )
|
|
|
|
{
|
|
|
|
size = QSizeF( 0.0, 0.0 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
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. TODO ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
size = qskConstrainedItemSize( item, size );
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
item->setSize( size );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_data->enableAutoTranslation( this, false );
|
|
|
|
|
2019-09-13 06:46:16 +02:00
|
|
|
setScrollableSize( QSizeF( item->width(), item->height() ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
setScrollPos( scrollPos() );
|
|
|
|
|
|
|
|
m_data->enableAutoTranslation( this, true );
|
|
|
|
|
|
|
|
translateItem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskScrollArea::setItemResizable( bool on )
|
|
|
|
{
|
|
|
|
if ( on != m_data->isItemResizable )
|
|
|
|
{
|
|
|
|
m_data->isItemResizable = on;
|
2020-03-13 07:39:31 +01:00
|
|
|
Q_EMIT itemResizableChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2020-03-13 07:39:31 +01:00
|
|
|
if ( auto item = m_data->clipItem->scrolledItem() )
|
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"
|