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 "QskScrollViewSkinlet.h"
|
|
|
|
#include "QskAspect.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskQuick.h"
|
|
|
|
#include "QskScrollView.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qsgnode.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
static void qskAlignedHandle( qreal start, qreal end,
|
|
|
|
qreal scrollBarLength, qreal minHandleLength,
|
|
|
|
qreal& handleStart, qreal& handleEnd )
|
|
|
|
{
|
|
|
|
minHandleLength = qBound( 4.0, minHandleLength, scrollBarLength );
|
|
|
|
|
|
|
|
handleStart = start * scrollBarLength;
|
|
|
|
handleEnd = end * scrollBarLength;
|
|
|
|
|
|
|
|
const qreal handleLength = handleEnd - handleStart;
|
|
|
|
if ( handleLength < minHandleLength )
|
|
|
|
{
|
|
|
|
const qreal extra = minHandleLength - handleLength;
|
|
|
|
|
|
|
|
handleStart -= extra * handleStart / scrollBarLength;
|
|
|
|
handleEnd += extra * ( ( scrollBarLength - handleEnd ) / scrollBarLength );
|
|
|
|
}
|
|
|
|
else if ( handleLength > scrollBarLength )
|
|
|
|
{
|
|
|
|
const qreal extra = scrollBarLength - handleLength;
|
|
|
|
|
|
|
|
handleStart -= extra * handleStart / scrollBarLength;
|
|
|
|
handleEnd += extra * ( ( scrollBarLength - handleEnd ) / scrollBarLength );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskScrollViewSkinlet::QskScrollViewSkinlet( QskSkin* skin )
|
|
|
|
: Inherited( skin )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setNodeRoles( { ViewportRole, ContentsRootRole,
|
|
|
|
HorizontalScrollBarRole, HorizontalScrollHandleRole,
|
|
|
|
VerticalScrollBarRole, VerticalScrollHandleRole } );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskScrollViewSkinlet::~QskScrollViewSkinlet() = default;
|
|
|
|
|
|
|
|
QRectF QskScrollViewSkinlet::subControlRect(
|
|
|
|
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
|
|
|
|
{
|
|
|
|
const auto scrollView = static_cast< const QskScrollView* >( skinnable );
|
|
|
|
|
|
|
|
if ( subControl == QskScrollView::Panel )
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return scrollView->contentsRect();
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( subControl == QskScrollView::Viewport )
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return viewportRect( scrollView );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( subControl == QskScrollView::HorizontalScrollBar )
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return scrollBarRect( scrollView, Qt::Horizontal );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( subControl == QskScrollView::HorizontalScrollHandle )
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return scrollHandleRect( scrollView, Qt::Horizontal );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( subControl == QskScrollView::VerticalScrollBar )
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return scrollBarRect( scrollView, Qt::Vertical );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( subControl == QskScrollView::VerticalScrollHandle )
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return scrollHandleRect( scrollView, Qt::Vertical );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
return Inherited::subControlRect( skinnable, subControl );
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGNode* QskScrollViewSkinlet::updateSubNode(
|
|
|
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
|
|
|
{
|
|
|
|
const auto scrollView = static_cast< const QskScrollView* >( skinnable );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( nodeRole )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
case ViewportRole:
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
return updateBoxNode( skinnable, node, QskScrollView::Viewport );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
case HorizontalScrollHandleRole:
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return updateBoxNode( skinnable, node, QskScrollView::HorizontalScrollHandle );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
case VerticalScrollHandleRole:
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return updateBoxNode( skinnable, node, QskScrollView::VerticalScrollHandle );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
case ContentsRootRole:
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return updateContentsRootNode( scrollView, node );
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
case HorizontalScrollBarRole:
|
|
|
|
case VerticalScrollBarRole:
|
2017-09-01 11:55:55 +02:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
return nullptr;
|
2017-09-01 11:55:55 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGNode* QskScrollViewSkinlet::updateContentsRootNode(
|
|
|
|
const QskScrollView* scrollView, QSGNode* node ) const
|
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
auto* clipNode = updateBoxClipNode( scrollView, node, QskScrollView::Viewport );
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( clipNode == nullptr )
|
2017-10-17 17:34:00 +02:00
|
|
|
return nullptr;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
QSGNode* oldContentsNode = findNodeByRole( clipNode, ContentsRootRole );
|
|
|
|
QSGNode* contentsNode = updateContentsNode( scrollView, oldContentsNode );
|
|
|
|
|
|
|
|
if ( contentsNode )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Not all classes derived from QskScrollView create their content node
|
|
|
|
by updateContentsNode(). F.e QskScrollArea takes the itemNode from
|
|
|
|
another item and puts it below the clip node.
|
|
|
|
For those situations we need to set a node role, that we can decide
|
|
|
|
which child of the clip node needs to be replaced.
|
|
|
|
*/
|
|
|
|
setNodeRole( contentsNode, ContentsRootRole );
|
|
|
|
|
|
|
|
if ( contentsNode->parent() != clipNode )
|
|
|
|
clipNode->appendChildNode( contentsNode );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( oldContentsNode && oldContentsNode != contentsNode )
|
|
|
|
{
|
|
|
|
clipNode->removeChildNode( oldContentsNode );
|
|
|
|
|
|
|
|
if ( oldContentsNode->flags() & QSGNode::OwnedByParent )
|
|
|
|
delete oldContentsNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return clipNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGNode* QskScrollViewSkinlet::updateContentsNode(
|
|
|
|
const QskScrollView*, QSGNode* ) const
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGNode* QskScrollViewSkinlet::contentsNode( const QskScrollView* scrollView )
|
|
|
|
{
|
2018-01-19 10:07:05 +01:00
|
|
|
QSGNode* node = const_cast< QSGNode* >( qskPaintNode( scrollView ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( node )
|
|
|
|
{
|
|
|
|
node = findNodeByRole( node, ContentsRootRole );
|
|
|
|
if ( node )
|
|
|
|
{
|
|
|
|
node = node->firstChild();
|
|
|
|
if ( node )
|
|
|
|
return node->firstChild();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskScrollViewSkinlet::viewportRect( const QskScrollView* scrollView ) const
|
|
|
|
{
|
|
|
|
const Qt::Orientations orientation = scrollView->scrollableOrientations();
|
|
|
|
|
|
|
|
QRectF vr = subControlRect( scrollView, QskScrollView::Panel );
|
|
|
|
const qreal spacing = scrollView->metric( QskScrollView::Panel | QskAspect::Spacing );
|
|
|
|
|
|
|
|
if ( orientation & Qt::Vertical )
|
|
|
|
{
|
|
|
|
const QRectF r = subControlRect( scrollView, QskScrollView::VerticalScrollBar );
|
|
|
|
if ( r.width() > 0.0 )
|
|
|
|
vr.setWidth( vr.width() - r.width() - spacing );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( orientation & Qt::Horizontal )
|
|
|
|
{
|
|
|
|
const QRectF r = subControlRect( scrollView, QskScrollView::HorizontalScrollBar );
|
|
|
|
if ( r.height() >= 0.0 )
|
|
|
|
vr.setHeight( vr.height() - r.height() - spacing );
|
|
|
|
}
|
|
|
|
|
|
|
|
return vr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskScrollViewSkinlet::scrollHandleRect(
|
|
|
|
const QskScrollView* scrollView, Qt::Orientation orientation ) const
|
|
|
|
{
|
|
|
|
const Qt::Orientations scrollOrientations = scrollView->scrollableOrientations();
|
|
|
|
if ( !( orientation & scrollOrientations ) )
|
|
|
|
return QRectF();
|
|
|
|
|
|
|
|
const QPointF pos = scrollView->scrollPos();
|
|
|
|
const QRectF vRect = subControlRect( scrollView, QskScrollView::Viewport );
|
|
|
|
|
|
|
|
QRectF handleRect;
|
|
|
|
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
|
|
{
|
|
|
|
const auto subControlBar = QskScrollView::VerticalScrollBar;
|
|
|
|
|
|
|
|
const QRectF sbRect = subControlRect( scrollView, subControlBar );
|
2017-08-23 14:53:29 +02:00
|
|
|
const QMarginsF padding = scrollView->marginsHint( subControlBar | QskAspect::Padding );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
const qreal h = scrollView->scrollableSize().height();
|
|
|
|
|
|
|
|
const qreal y1 = pos.y() / h;
|
|
|
|
const qreal y2 = ( pos.y() + vRect.height() ) / h;
|
|
|
|
|
|
|
|
const qreal minHandleLength =
|
|
|
|
scrollView->metric( QskScrollView::VerticalScrollHandle | QskAspect::MinimumHeight );
|
|
|
|
|
|
|
|
qreal top, bottom;
|
|
|
|
qskAlignedHandle( y1, y2, sbRect.height(), minHandleLength, top, bottom );
|
|
|
|
|
|
|
|
handleRect = sbRect;
|
|
|
|
handleRect.setTop( sbRect.y() + top );
|
|
|
|
handleRect.setBottom( sbRect.y() + bottom );
|
|
|
|
handleRect.adjust( padding.left(), 0, -padding.right(), 0 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto subControlBar = QskScrollView::HorizontalScrollBar;
|
|
|
|
|
|
|
|
const QRectF sbRect = subControlRect( scrollView, subControlBar );
|
2017-08-23 14:53:29 +02:00
|
|
|
const auto padding = scrollView->marginsHint( subControlBar | QskAspect::Padding );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
const qreal w = scrollView->scrollableSize().width();
|
|
|
|
|
|
|
|
const qreal x1 = pos.x() / w;
|
|
|
|
const qreal x2 = ( pos.x() + vRect.width() ) / w;
|
|
|
|
|
|
|
|
const qreal minHandleLength =
|
|
|
|
scrollView->metric( QskScrollView::HorizontalScrollHandle | QskAspect::MinimumWidth );
|
|
|
|
|
|
|
|
qreal left, right;
|
|
|
|
qskAlignedHandle( x1, x2, sbRect.width(), minHandleLength, left, right );
|
|
|
|
|
|
|
|
handleRect = sbRect;
|
|
|
|
handleRect.setLeft( sbRect.x() + left );
|
|
|
|
handleRect.setRight( sbRect.x() + right );
|
|
|
|
handleRect.adjust( 0, padding.top(), 0, -padding.bottom() );
|
|
|
|
}
|
|
|
|
|
|
|
|
return handleRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskScrollViewSkinlet::scrollBarRect(
|
|
|
|
const QskScrollView* scrollView, Qt::Orientation orientation ) const
|
|
|
|
{
|
|
|
|
const Qt::Orientations scrollOrientations = scrollView->scrollableOrientations();
|
|
|
|
if ( !( orientation & scrollOrientations ) )
|
|
|
|
return QRectF();
|
|
|
|
|
|
|
|
QRectF r = subControlRect( scrollView, QskScrollView::Panel );
|
|
|
|
|
|
|
|
if ( orientation == Qt::Horizontal )
|
|
|
|
{
|
|
|
|
const qreal h = scrollView->metric(
|
|
|
|
QskScrollView::HorizontalScrollBar | QskAspect::Size );
|
|
|
|
|
|
|
|
r.setTop( r.bottom() - h );
|
|
|
|
|
|
|
|
if ( scrollOrientations & Qt::Vertical )
|
|
|
|
{
|
|
|
|
const qreal w = scrollView->metric(
|
|
|
|
QskScrollView::VerticalScrollBar | QskAspect::Size );
|
|
|
|
|
|
|
|
r.setRight( r.right() - w );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const qreal w = scrollView->metric(
|
|
|
|
QskScrollView::VerticalScrollBar | QskAspect::Size );
|
|
|
|
|
|
|
|
r.setLeft( r.right() - w );
|
|
|
|
|
|
|
|
if ( scrollOrientations & Qt::Horizontal )
|
|
|
|
{
|
|
|
|
const qreal h = scrollView->metric(
|
|
|
|
QskScrollView::HorizontalScrollBar | QskAspect::Size );
|
|
|
|
|
|
|
|
r.setBottom( r.bottom() - h );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskScrollViewSkinlet.cpp"
|