qskinny/src/controls/QskScrollViewSkinlet.cpp

341 lines
11 KiB
C++
Raw Normal View History

2017-07-21 18:21:34 +02:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
2023-04-06 09:23:37 +02:00
* SPDX-License-Identifier: BSD-3-Clause
2017-07-21 18:21:34 +02:00
*****************************************************************************/
#include "QskScrollViewSkinlet.h"
2018-08-03 08:30:23 +02:00
#include "QskScrollView.h"
2017-07-21 18:21:34 +02:00
#include "QskAspect.h"
2018-08-03 08:15:28 +02:00
#include "QskQuick.h"
2020-11-22 15:27:58 +01:00
#include "QskSGNode.h"
#include "QskSkinStateChanger.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 );
}
}
2023-07-20 10:41:49 +02:00
static inline Qt::Orientation qskSubcontrolOrientation( QskAspect::Subcontrol subControl )
{
using Q = QskScrollView;
if ( subControl == Q::HorizontalScrollBar || subControl == Q::HorizontalScrollHandle )
return Qt::Horizontal;
else
return Qt::Vertical;
}
2018-08-03 08:15:28 +02:00
QskScrollViewSkinlet::QskScrollViewSkinlet( QskSkin* skin )
: Inherited( skin )
2017-07-21 18:21:34 +02:00
{
2022-07-12 14:21:50 +02:00
setNodeRoles( { PanelRole, ViewportRole, ContentsRootRole,
2017-07-21 18:21:34 +02:00
HorizontalScrollBarRole, HorizontalScrollHandleRole,
VerticalScrollBarRole, VerticalScrollHandleRole } );
}
QskScrollViewSkinlet::~QskScrollViewSkinlet() = default;
QRectF QskScrollViewSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
2017-07-21 18:21:34 +02:00
{
using Q = QskScrollView;
2017-07-21 18:21:34 +02:00
const auto scrollView = static_cast< const QskScrollView* >( skinnable );
if ( subControl == Q::Panel )
return contentsRect;
2017-07-21 18:21:34 +02:00
if ( subControl == Q::Viewport )
return viewportRect( scrollView, contentsRect );
2017-07-21 18:21:34 +02:00
if ( subControl == Q::HorizontalScrollBar )
return scrollBarRect( scrollView, contentsRect, Qt::Horizontal );
2017-07-21 18:21:34 +02:00
if ( subControl == Q::HorizontalScrollHandle )
return scrollHandleRect( scrollView, contentsRect, Qt::Horizontal );
2017-07-21 18:21:34 +02:00
if ( subControl == Q::VerticalScrollBar )
return scrollBarRect( scrollView, contentsRect, Qt::Vertical );
2017-07-21 18:21:34 +02:00
if ( subControl == Q::VerticalScrollHandle )
return scrollHandleRect( scrollView, contentsRect, Qt::Vertical );
2017-07-21 18:21:34 +02:00
return Inherited::subControlRect( skinnable, contentsRect, subControl );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskScrollViewSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
using Q = QskScrollView;
2017-07-21 18:21:34 +02:00
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
{
2022-07-12 14:21:50 +02:00
case PanelRole:
return updateBoxNode( skinnable, node, Q::Panel );
2023-07-20 10:41:49 +02:00
2017-07-21 18:21:34 +02:00
case ViewportRole:
return updateBoxNode( skinnable, node, Q::Viewport );
2017-07-21 18:21:34 +02:00
case HorizontalScrollHandleRole:
2023-07-20 10:41:49 +02:00
return updateScrollBarNode( scrollView, Q::HorizontalScrollHandle, node );
2017-07-21 18:21:34 +02:00
case VerticalScrollHandleRole:
2023-07-20 10:41:49 +02:00
return updateScrollBarNode( scrollView, Q::VerticalScrollHandle, node );
2023-07-20 10:41:49 +02:00
case HorizontalScrollBarRole:
return updateScrollBarNode( scrollView, Q::HorizontalScrollBar, node );
2023-07-20 10:41:49 +02:00
case VerticalScrollBarRole:
return updateScrollBarNode( scrollView, Q::VerticalScrollBar, node );
2017-07-21 18:21:34 +02:00
case ContentsRootRole:
return updateContentsRootNode( scrollView, node );
2017-07-21 18:21:34 +02:00
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
2023-07-20 10:41:49 +02:00
QSGNode* QskScrollViewSkinlet::updateScrollBarNode( const QskScrollView* scrollView,
QskAspect::Subcontrol subControl, QSGNode* node ) const
{
const auto rect = subControlRect( scrollView,
scrollView->contentsRect(), subControl );
const auto orientation = qskSubcontrolOrientation( subControl );
QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) );
return updateBoxNode( scrollView, node, rect, subControl );
}
2017-07-21 18:21:34 +02:00
QSGNode* QskScrollViewSkinlet::updateContentsRootNode(
const QskScrollView* scrollView, QSGNode* node ) const
{
auto* clipNode = updateBoxClipNode( scrollView, node, QskScrollView::Viewport );
2017-07-21 18:21:34 +02:00
if ( clipNode == nullptr )
return nullptr;
2017-07-21 18:21:34 +02:00
2020-11-22 15:27:58 +01:00
auto oldContentsNode = QskSGNode::findChildNode( clipNode, ContentsRootRole );
auto contentsNode = updateContentsNode( scrollView, oldContentsNode );
2017-07-21 18:21:34 +02:00
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.
*/
2020-11-22 15:27:58 +01:00
QskSGNode::setNodeRole( contentsNode, ContentsRootRole );
2017-07-21 18:21:34 +02:00
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 )
{
2021-12-22 15:07:51 +01:00
if ( auto node = const_cast< QSGNode* >( qskPaintNode( scrollView ) ) )
2017-07-21 18:21:34 +02:00
{
2020-11-22 15:27:58 +01:00
node = QskSGNode::findChildNode( node, ContentsRootRole );
2017-07-21 18:21:34 +02:00
if ( node )
{
node = node->firstChild();
if ( node )
return node->firstChild();
}
}
return nullptr;
}
QRectF QskScrollViewSkinlet::viewportRect(
const QskScrollView* scrollView, const QRectF& contentsRect ) const
2017-07-21 18:21:34 +02:00
{
using Q = QskScrollView;
2017-07-21 18:21:34 +02:00
const auto orientation = scrollView->scrollableOrientations();
auto vr = subControlRect( scrollView, contentsRect, Q::Panel );
const qreal spacing = scrollView->spacingHint( Q::Panel );
2017-07-21 18:21:34 +02:00
if ( orientation & Qt::Vertical )
{
const auto r = subControlRect(
scrollView, contentsRect, Q::VerticalScrollBar );
2017-07-21 18:21:34 +02:00
if ( r.width() > 0.0 )
vr.setWidth( vr.width() - r.width() - spacing );
}
if ( orientation & Qt::Horizontal )
{
const auto r = subControlRect(
scrollView, contentsRect, Q::HorizontalScrollBar );
2017-07-21 18:21:34 +02:00
if ( r.height() >= 0.0 )
vr.setHeight( vr.height() - r.height() - spacing );
}
2020-07-03 13:50:12 +02:00
if ( vr.width() < 0.0 )
vr.setWidth( 0.0 );
if ( vr.height() < 0.0 )
vr.setHeight( 0.0 );
2017-07-21 18:21:34 +02:00
return vr;
}
QRectF QskScrollViewSkinlet::scrollHandleRect( const QskScrollView* scrollView,
const QRectF& contentsRect, Qt::Orientation orientation ) const
2017-07-21 18:21:34 +02:00
{
using Q = QskScrollView;
const auto scrollOrientations = scrollView->scrollableOrientations();
2017-07-21 18:21:34 +02:00
if ( !( orientation & scrollOrientations ) )
return QRectF();
const auto pos = scrollView->scrollPos();
const auto vRect = subControlRect(
scrollView, contentsRect, QskScrollView::Viewport );
2017-07-21 18:21:34 +02:00
const auto scrollableSize = scrollView->scrollableSize();
2017-07-21 18:21:34 +02:00
QRectF handleRect;
if ( orientation == Qt::Vertical )
{
const auto sbRect = subControlRect( scrollView, contentsRect, Q::VerticalScrollBar );
2017-07-21 18:21:34 +02:00
QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) );
2017-07-21 18:21:34 +02:00
const auto padding = scrollView->paddingHint( Q::VerticalScrollBar );
const auto strut = scrollView->strutSizeHint( Q::VerticalScrollHandle );
2017-07-21 18:21:34 +02:00
const qreal y1 = pos.y() / scrollableSize.height();
const qreal y2 = ( pos.y() + vRect.height() ) / scrollableSize.height();
2017-07-21 18:21:34 +02:00
qreal top, bottom;
qskAlignedHandle( y1, y2, sbRect.height(), strut.height(), top, bottom );
2017-07-21 18:21:34 +02:00
handleRect = sbRect;
handleRect.setTop( sbRect.y() + top );
handleRect.setBottom( sbRect.y() + bottom );
handleRect.adjust( padding.left(), 0, -padding.right(), 0 );
}
else
{
const auto sbRect = subControlRect( scrollView, contentsRect, Q::HorizontalScrollBar );
2017-07-21 18:21:34 +02:00
QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) );
2017-07-21 18:21:34 +02:00
const auto padding = scrollView->paddingHint( Q::HorizontalScrollBar );
2017-07-21 18:21:34 +02:00
const qreal x1 = pos.x() / scrollableSize.width();
const qreal x2 = ( pos.x() + vRect.width() ) / scrollableSize.width();
2017-07-21 18:21:34 +02:00
const auto strut = scrollView->strutSizeHint( Q::HorizontalScrollHandle );
2017-07-21 18:21:34 +02:00
qreal left, right;
qskAlignedHandle( x1, x2, sbRect.width(), strut.width(), left, right );
2017-07-21 18:21:34 +02:00
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,
const QRectF& contentsRect, Qt::Orientation orientation ) const
2017-07-21 18:21:34 +02:00
{
2020-12-21 16:06:58 +01:00
using A = QskAspect;
using Q = QskScrollView;
const auto scrollOrientations = scrollView->scrollableOrientations();
2017-07-21 18:21:34 +02:00
if ( !( orientation & scrollOrientations ) )
return QRectF();
auto r = subControlRect( scrollView, contentsRect, Q::Panel );
2017-07-21 18:21:34 +02:00
QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) );
2017-07-21 18:21:34 +02:00
if ( orientation == Qt::Horizontal )
{
2020-12-21 16:06:58 +01:00
const qreal h = scrollView->metric( Q::HorizontalScrollBar | A::Size );
2017-07-21 18:21:34 +02:00
r.setTop( r.bottom() - h );
if ( scrollOrientations & Qt::Vertical )
{
2020-12-21 16:06:58 +01:00
const qreal w = scrollView->metric( Q::VerticalScrollBar | A::Size );
2017-07-21 18:21:34 +02:00
r.setRight( r.right() - w );
}
}
else
{
2020-12-21 16:06:58 +01:00
const qreal w = scrollView->metric( Q::VerticalScrollBar | A::Size );
2017-07-21 18:21:34 +02:00
r.setLeft( r.right() - w );
if ( scrollOrientations & Qt::Horizontal )
{
2020-12-21 16:06:58 +01:00
const qreal h = scrollView->metric( Q::HorizontalScrollBar | A::Size );
2017-07-21 18:21:34 +02:00
r.setBottom( r.bottom() - h );
}
}
return r;
}
#include "moc_QskScrollViewSkinlet.cpp"