2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
2024-01-17 14:31:45 +01:00
|
|
|
* QSkinny - Copyright (C) The authors
|
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"
|
2023-06-30 14:51:03 +02:00
|
|
|
#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 )
|
|
|
|
{
|
2023-07-27 17:03:34 +02:00
|
|
|
// no qBound: scrollBarLength might be < 4.0
|
|
|
|
minHandleLength = qMax( 4.0, minHandleLength );
|
|
|
|
minHandleLength = qMin( minHandleLength, scrollBarLength );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
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 16:46:21 +02:00
|
|
|
static qreal qskScrollBarExtent(
|
|
|
|
const QskScrollView* scrollView, Qt::Orientation orientation )
|
2023-07-20 10:41:49 +02:00
|
|
|
{
|
2023-07-20 16:46:21 +02:00
|
|
|
const auto subControl = ( orientation == Qt::Horizontal )
|
|
|
|
? QskScrollView::HorizontalScrollBar : QskScrollView::VerticalScrollBar;
|
2023-07-20 10:41:49 +02:00
|
|
|
|
2023-07-20 16:46:21 +02:00
|
|
|
QskSkinStateChanger stateChanger( scrollView );
|
|
|
|
stateChanger.setStates( scrollView->scrollBarStates( subControl ) );
|
|
|
|
|
|
|
|
return scrollView->metric( subControl | QskAspect::Size );
|
2023-07-20 10:41:49 +02:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2019-04-25 14:23:39 +02:00
|
|
|
QRectF QskScrollViewSkinlet::subControlRect( const QskSkinnable* skinnable,
|
|
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2023-06-30 14:51:03 +02:00
|
|
|
using Q = QskScrollView;
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
const auto scrollView = static_cast< const QskScrollView* >( skinnable );
|
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
if ( subControl == Q::Panel )
|
2019-04-25 14:23:39 +02:00
|
|
|
return contentsRect;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
if ( subControl == Q::Viewport )
|
2019-04-25 14:23:39 +02:00
|
|
|
return viewportRect( scrollView, contentsRect );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
if ( subControl == Q::HorizontalScrollBar )
|
2019-04-25 14:23:39 +02:00
|
|
|
return scrollBarRect( scrollView, contentsRect, Qt::Horizontal );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
if ( subControl == Q::HorizontalScrollHandle )
|
2019-04-25 14:23:39 +02:00
|
|
|
return scrollHandleRect( scrollView, contentsRect, Qt::Horizontal );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
if ( subControl == Q::VerticalScrollBar )
|
2019-04-25 14:23:39 +02:00
|
|
|
return scrollBarRect( scrollView, contentsRect, Qt::Vertical );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
if ( subControl == Q::VerticalScrollHandle )
|
2019-04-25 14:23:39 +02:00
|
|
|
return scrollHandleRect( scrollView, contentsRect, Qt::Vertical );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-04-25 14:23:39 +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
|
|
|
|
{
|
2023-06-30 14:51:03 +02:00
|
|
|
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:
|
2023-06-30 14:51:03 +02:00
|
|
|
return updateBoxNode( skinnable, node, Q::Panel );
|
2023-07-20 10:41:49 +02:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
case ViewportRole:
|
2023-06-30 14:51:03 +02:00
|
|
|
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-06-30 14:51:03 +02:00
|
|
|
|
2023-07-20 10:41:49 +02:00
|
|
|
case HorizontalScrollBarRole:
|
|
|
|
return updateScrollBarNode( scrollView, Q::HorizontalScrollBar, node );
|
2023-06-30 14:51:03 +02:00
|
|
|
|
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 );
|
2023-07-19 15:18:04 +02:00
|
|
|
|
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 );
|
|
|
|
|
|
|
|
QskSkinStateChanger stateChanger( scrollView );
|
2023-07-20 16:46:21 +02:00
|
|
|
stateChanger.setStates( scrollView->scrollBarStates( subControl ) );
|
2023-07-20 10:41:49 +02:00
|
|
|
|
|
|
|
return updateBoxNode( scrollView, node, rect, subControl );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
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
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-25 14:23:39 +02:00
|
|
|
QRectF QskScrollViewSkinlet::viewportRect(
|
|
|
|
const QskScrollView* scrollView, const QRectF& contentsRect ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2020-12-15 07:21:12 +01:00
|
|
|
using Q = QskScrollView;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2020-12-15 07:21:12 +01: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 )
|
|
|
|
{
|
2020-12-15 07:21:12 +01:00
|
|
|
const auto r = subControlRect(
|
|
|
|
scrollView, contentsRect, Q::VerticalScrollBar );
|
2019-04-25 14:23:39 +02:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( r.width() > 0.0 )
|
|
|
|
vr.setWidth( vr.width() - r.width() - spacing );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( orientation & Qt::Horizontal )
|
|
|
|
{
|
2020-12-15 07:21:12 +01:00
|
|
|
const auto r = subControlRect(
|
|
|
|
scrollView, contentsRect, Q::HorizontalScrollBar );
|
2019-04-25 14:23:39 +02:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-25 14:23:39 +02:00
|
|
|
QRectF QskScrollViewSkinlet::scrollHandleRect( const QskScrollView* scrollView,
|
|
|
|
const QRectF& contentsRect, Qt::Orientation orientation ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2020-12-15 07:21:12 +01:00
|
|
|
using Q = QskScrollView;
|
2020-12-13 14:23:17 +01:00
|
|
|
|
|
|
|
const auto scrollOrientations = scrollView->scrollableOrientations();
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( !( orientation & scrollOrientations ) )
|
|
|
|
return QRectF();
|
|
|
|
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto pos = scrollView->scrollPos();
|
2019-04-25 14:23:39 +02:00
|
|
|
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto vRect = subControlRect(
|
2019-04-25 14:23:39 +02:00
|
|
|
scrollView, contentsRect, QskScrollView::Viewport );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
const auto scrollableSize = scrollView->scrollableSize();
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QRectF handleRect;
|
|
|
|
|
|
|
|
if ( orientation == Qt::Vertical )
|
|
|
|
{
|
2023-06-30 14:51:03 +02:00
|
|
|
const auto sbRect = subControlRect( scrollView, contentsRect, Q::VerticalScrollBar );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
QskSkinStateChanger stateChanger( scrollView );
|
2023-07-20 16:46:21 +02:00
|
|
|
stateChanger.setStates( scrollView->scrollBarStates( Q::VerticalScrollBar ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
const auto padding = scrollView->paddingHint( Q::VerticalScrollBar );
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto strut = scrollView->strutSizeHint( Q::VerticalScrollHandle );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +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;
|
2020-12-16 12:49:24 +01:00
|
|
|
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
|
|
|
|
{
|
2023-06-30 14:51:03 +02:00
|
|
|
const auto sbRect = subControlRect( scrollView, contentsRect, Q::HorizontalScrollBar );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
QskSkinStateChanger stateChanger( scrollView );
|
2023-07-20 16:46:21 +02:00
|
|
|
stateChanger.setStates( scrollView->scrollBarStates( Q::HorizontalScrollBar ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +02:00
|
|
|
const auto padding = scrollView->paddingHint( Q::HorizontalScrollBar );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2023-06-30 14:51:03 +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
|
|
|
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto strut = scrollView->strutSizeHint( Q::HorizontalScrollHandle );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
qreal left, right;
|
2020-12-16 12:49:24 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-25 14:23:39 +02:00
|
|
|
QRectF QskScrollViewSkinlet::scrollBarRect( const QskScrollView* scrollView,
|
|
|
|
const QRectF& contentsRect, Qt::Orientation orientation ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2020-12-15 07:21:12 +01:00
|
|
|
using Q = QskScrollView;
|
2020-12-13 14:23:17 +01:00
|
|
|
|
|
|
|
const auto scrollOrientations = scrollView->scrollableOrientations();
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( !( orientation & scrollOrientations ) )
|
|
|
|
return QRectF();
|
|
|
|
|
2020-12-15 07:21:12 +01:00
|
|
|
auto r = subControlRect( scrollView, contentsRect, Q::Panel );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( orientation == Qt::Horizontal )
|
|
|
|
{
|
2023-07-20 16:46:21 +02:00
|
|
|
const qreal h = qskScrollBarExtent( scrollView, Qt::Horizontal );
|
2017-07-21 18:21:34 +02:00
|
|
|
r.setTop( r.bottom() - h );
|
|
|
|
|
|
|
|
if ( scrollOrientations & Qt::Vertical )
|
|
|
|
{
|
2023-07-20 16:46:21 +02:00
|
|
|
const qreal w = qskScrollBarExtent( scrollView, Qt::Vertical );
|
2017-07-21 18:21:34 +02:00
|
|
|
r.setRight( r.right() - w );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-07-20 16:46:21 +02:00
|
|
|
const qreal w = qskScrollBarExtent( scrollView, Qt::Vertical );
|
2017-07-21 18:21:34 +02:00
|
|
|
r.setLeft( r.right() - w );
|
|
|
|
|
|
|
|
if ( scrollOrientations & Qt::Horizontal )
|
|
|
|
{
|
2023-07-20 16:46:21 +02:00
|
|
|
const qreal h = qskScrollBarExtent( scrollView, Qt::Horizontal );
|
2017-07-21 18:21:34 +02:00
|
|
|
r.setBottom( r.bottom() - h );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskScrollViewSkinlet.cpp"
|