qskinny/src/controls/QskPageIndicatorSkinlet.cpp

226 lines
6.2 KiB
C++
Raw Normal View History

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 "QskPageIndicatorSkinlet.h"
2018-08-03 08:15:28 +02:00
#include "QskPageIndicator.h"
2017-07-21 18:21:34 +02:00
2018-08-03 08:30:23 +02:00
#include "QskBoxNode.h"
2020-11-22 15:27:58 +01:00
#include "QskSGNode.h"
2018-08-03 08:30:23 +02:00
2018-08-03 08:15:28 +02:00
QskPageIndicatorSkinlet::QskPageIndicatorSkinlet( QskSkin* skin )
: QskSkinlet( skin )
2017-07-21 18:21:34 +02:00
{
setNodeRoles( { PanelRole, BulletsRole } );
}
QskPageIndicatorSkinlet::~QskPageIndicatorSkinlet()
{
}
QRectF QskPageIndicatorSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
2017-07-21 18:21:34 +02:00
{
if ( subControl == QskPageIndicator::Panel )
{
return contentsRect;
2017-07-21 18:21:34 +02:00
}
return Inherited::subControlRect( skinnable, contentsRect, subControl );
2017-07-21 18:21:34 +02:00
}
2018-08-03 08:15:28 +02:00
QSGNode* QskPageIndicatorSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
2017-09-01 11:55:55 +02:00
{
const auto indicator = static_cast< const QskPageIndicator* >( skinnable );
2018-08-03 08:15:28 +02:00
switch ( nodeRole )
2017-09-01 11:55:55 +02:00
{
case PanelRole:
{
return updateBoxNode( indicator, node, QskPageIndicator::Panel );
}
case BulletsRole:
{
return updateBulletsNode( indicator, node );
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
2017-07-21 18:21:34 +02:00
QRectF QskPageIndicatorSkinlet::bulletRect(
const QskPageIndicator* indicator, const QRectF& rect, int index ) const
{
using Q = QskPageIndicator;
2018-08-03 08:15:28 +02:00
2020-12-17 16:14:56 +01:00
const qreal szNormal = indicator->strutSizeHint( Q::Bullet ).width();
const qreal szHighlighted = indicator->strutSizeHint( Q::Highlighted ).width();
const auto currentIndex = indicator->currentIndex();
2017-07-21 18:21:34 +02:00
// scale bullet size if we are in between a transition:
2020-12-17 16:14:56 +01:00
qreal indexDiff = qAbs( currentIndex - index );
2018-08-03 08:15:28 +02:00
if ( indexDiff > ( indicator->count() - 1 ) )
2020-12-17 16:14:56 +01:00
indexDiff = ( indicator->count() - currentIndex ); // wrapping
2017-07-21 18:21:34 +02:00
2020-12-17 16:14:56 +01:00
const qreal sz0 = ( indexDiff < 1 ) ?
2017-07-21 18:21:34 +02:00
( 1 - indexDiff ) * szHighlighted + indexDiff * szNormal : szNormal;
const qreal spacing = indicator->spacingHint( Q::Panel );
2017-07-21 18:21:34 +02:00
const bool horizontal = ( indicator->orientation() == Qt::Horizontal );
qreal w, h;
if ( horizontal )
{
w = ( indicator->count() - 1 ) * ( szNormal + spacing ) + szHighlighted;
h = rect.height();
}
else
{
w = rect.width();
h = ( indicator->count() - 1 ) * ( szNormal + spacing ) + szHighlighted;
}
2017-09-01 13:09:24 +02:00
2017-07-21 18:21:34 +02:00
QRectF r( 0, 0, w, h );
r.moveCenter( rect.center() );
qreal s2;
2020-12-17 16:14:56 +01:00
2017-07-21 18:21:34 +02:00
{
2020-12-17 16:14:56 +01:00
const qreal s = ( index > currentIndex ) ? szHighlighted : szNormal;
if ( indexDiff < 1 && index >= currentIndex )
{
// scrolling from or to this bullet:
s2 = szNormal + qAbs( szHighlighted - szNormal ) * indexDiff;
}
else if ( ( currentIndex > ( indicator->count() - 1 ) &&
index > ( currentIndex - indicator->count() + 1 ) ) )
{
// wrapping case:
qreal wrappingDiff = indexDiff;
while ( wrappingDiff > 1 )
wrappingDiff -= 1;
s2 = szNormal + qAbs( szHighlighted - szNormal ) * wrappingDiff;
}
else
{
s2 = s;
}
2017-07-21 18:21:34 +02:00
}
2020-12-17 16:14:56 +01:00
const qreal x = r.left() + s2 + spacing + ( index - 1 ) * ( szNormal + spacing );
qreal adjust = ( currentIndex == index )
? ( szNormal - szHighlighted ) : ( szHighlighted - szNormal );
adjust = 0.5 * qMax( 0.0, adjust );
2017-07-21 18:21:34 +02:00
if ( indexDiff < 1 )
2020-12-17 16:14:56 +01:00
adjust *= indexDiff;
2017-07-21 18:21:34 +02:00
2020-12-17 16:14:56 +01:00
QRectF bulletRect( 0.0, 0.0, sz0, sz0 );
if ( horizontal )
bulletRect.moveTo( x, r.top() + adjust );
else
bulletRect.moveTo( r.left() + adjust, x );
return bulletRect;
2017-07-21 18:21:34 +02:00
}
QSGNode* QskPageIndicatorSkinlet::updateBulletsNode(
2018-08-03 08:15:28 +02:00
const QskPageIndicator* indicator, QSGNode* node ) const
2017-07-21 18:21:34 +02:00
{
if ( indicator->count() == 0 )
return nullptr;
if ( node == nullptr )
node = new QSGNode();
2020-12-29 12:57:03 +01:00
const auto rect = indicator->subControlContentsRect( QskPageIndicator::Panel );
2017-07-21 18:21:34 +02:00
// index of the highlighted bullet
int currentBullet = qRound( indicator->currentIndex() );
if ( currentBullet >= indicator->count() )
currentBullet = 0;
auto bulletNode = node->firstChild();
for ( int i = 0; i < indicator->count(); i++ )
{
using Q = QskPageIndicator;
if ( i > 0 )
bulletNode = bulletNode->nextSibling();
if ( bulletNode == nullptr )
bulletNode = new QskBoxNode();
updateBoxNode( indicator, bulletNode, bulletRect( indicator, rect, i ),
( i == currentBullet ) ? Q::Highlighted : Q::Bullet );
if ( bulletNode->parent() != node )
node->appendChildNode( bulletNode );
2017-07-21 18:21:34 +02:00
}
// if count has decreased we need to remove superfluous nodes
2020-11-22 15:27:58 +01:00
QskSGNode::removeAllChildNodesAfter( node, bulletNode );
2017-07-21 18:21:34 +02:00
return node;
}
QSizeF QskPageIndicatorSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& ) const
{
using Q = QskPageIndicator;
if ( which != Qt::PreferredSize )
return QSizeF();
const auto indicator = static_cast< const QskPageIndicator* >( skinnable );
const auto bulletSize = indicator->strutSizeHint( Q::Bullet );
const auto maxSize = bulletSize.expandedTo(
indicator->strutSizeHint( Q::Highlighted ) );
const qreal spacing = indicator->spacingHint( Q::Panel );
const int n = indicator->count();
qreal w = 0;
qreal h = 0;
if ( indicator->orientation() == Qt::Horizontal )
{
if ( n > 0 )
{
w += maxSize.width();
if ( n > 1 )
w += ( n - 1 ) * ( bulletSize.width() + spacing );
}
h = maxSize.height();
}
else
{
if ( n > 0 )
{
h += maxSize.height();
if ( n > 1 )
h += ( n - 1 ) * ( bulletSize.height() + spacing );
}
w = maxSize.width();
}
const auto hint = indicator->outerBoxSize( Q::Panel, QSizeF( w, h ) );
return hint.expandedTo( indicator->strutSizeHint( Q::Panel ) );
}
2017-07-21 18:21:34 +02:00
#include "moc_QskPageIndicatorSkinlet.cpp"