/****************************************************************************** * QSkinny - Copyright (C) 2016 Uwe Rathmann * This file may be used under the terms of the QSkinny License, Version 1.0 *****************************************************************************/ #include "QskPageIndicatorSkinlet.h" #include "QskPageIndicator.h" #include "QskBoxNode.h" QskPageIndicatorSkinlet::QskPageIndicatorSkinlet( QskSkin* skin ): QskSkinlet(skin) { setNodeRoles( { PanelRole, BulletsRole } ); } QskPageIndicatorSkinlet::~QskPageIndicatorSkinlet() { } QRectF QskPageIndicatorSkinlet::subControlRect( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const { const auto indicator = static_cast< const QskPageIndicator* >( skinnable ); if ( subControl == QskPageIndicator::Panel ) { return indicator->contentsRect(); } return Inherited::subControlRect( skinnable, subControl ); } QSGNode* QskPageIndicatorSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { const auto indicator = static_cast< const QskPageIndicator* >( skinnable ); switch( nodeRole ) { case PanelRole: { return updateBoxNode( indicator, node, QskPageIndicator::Panel ); } case BulletsRole: { return updateBulletsNode( indicator, node ); } } return Inherited::updateSubNode( skinnable, nodeRole, node ); } QRectF QskPageIndicatorSkinlet::bulletRect( const QskPageIndicator* indicator, const QRectF& rect, int index ) const { const qreal szNormal = indicator->metric( QskPageIndicator::Bullet | QskAspect::Size ); const qreal szHighlighted = indicator->metric( QskPageIndicator::Highlighted | QskAspect::Size ); // scale bullet size if we are in between a transition: qreal indexDiff = qAbs( indicator->currentIndex() - index ); if (indexDiff > ( indicator->count() - 1 ) ) indexDiff = ( indicator->count() - indicator->currentIndex() ); // wrapping const qreal sz = ( indexDiff < 1 ) ? ( 1 - indexDiff ) * szHighlighted + indexDiff * szNormal : szNormal; const qreal spacing = indicator->metric( QskPageIndicator::Panel | QskAspect::Spacing ); 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; } QRectF r( 0, 0, w, h ); r.moveCenter( rect.center() ); const qreal s = ( index > indicator->currentIndex() ) ? szHighlighted : szNormal; qreal s2; if ( indexDiff < 1 && index >= indicator->currentIndex() ) { // scrolling from or to this bullet: s2 = szNormal + qAbs( szHighlighted - szNormal ) * indexDiff; } else if ( ( indicator->currentIndex() > ( indicator->count() - 1 ) && index > ( indicator->currentIndex() - indicator->count() + 1 ) ) ) { // wrapping case: qreal wrappingDiff = indexDiff; while ( wrappingDiff > 1 ) wrappingDiff -= 1; s2 = szNormal + qAbs( szHighlighted - szNormal ) * wrappingDiff; } else { s2 = s; } qreal x = r.left() + s2 + spacing + ( index - 1 ) * ( szNormal + spacing ); qreal yAdjust; if ( indicator->currentIndex() == index ) { yAdjust = qMax( 0.0, szNormal - szHighlighted ) / 2; } else { yAdjust = qMax( 0.0, szHighlighted - szNormal ) / 2; } if ( indexDiff < 1 ) yAdjust *= indexDiff; const qreal y = ( horizontal ? r.top() : r.left() ) + yAdjust; QRectF ret = horizontal ? QRectF( x, y, sz, sz ) : QRectF( y, x, sz, sz ); return ret; } QSGNode* QskPageIndicatorSkinlet::updateBulletsNode( const QskPageIndicator* indicator, QSGNode* node) const { if ( indicator->count() == 0 ) return nullptr; if ( node == nullptr ) node = new QSGNode(); QRectF rect = subControlRect( indicator, QskPageIndicator::Panel ); rect = indicator->innerBox( QskPageIndicator::Panel, rect ); // 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 ); } // if count has decreased we need to remove superfluous nodes while ( bulletNode->nextSibling() != nullptr ) { auto sibling = bulletNode->nextSibling(); node->removeChildNode( bulletNode->nextSibling() ); delete sibling; } return node; } #include "moc_QskPageIndicatorSkinlet.cpp"