qskinny/src/controls/QskSkinlet.cpp

805 lines
24 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 "QskSkinlet.h"
#include "QskArcNode.h"
2017-07-21 18:21:34 +02:00
#include "QskAspect.h"
2022-06-01 16:51:11 +02:00
#include "QskArcMetrics.h"
2017-10-20 13:09:30 +02:00
#include "QskBoxBorderColors.h"
2018-08-03 08:15:28 +02:00
#include "QskBoxBorderMetrics.h"
#include "QskBoxNode.h"
#include "QskBoxClipNode.h"
#include "QskBoxRectangleNode.h"
2018-08-03 08:15:28 +02:00
#include "QskBoxShapeMetrics.h"
2022-01-04 13:44:53 +01:00
#include "QskBoxHints.h"
2020-11-01 12:09:52 +01:00
#include "QskColorFilter.h"
2018-08-03 08:15:28 +02:00
#include "QskControl.h"
#include "QskFunctions.h"
#include "QskGradient.h"
2017-07-21 18:21:34 +02:00
#include "QskGraphicNode.h"
2020-11-01 12:09:52 +01:00
#include "QskGraphic.h"
#include "QskRectangleNode.h"
2020-11-22 15:27:58 +01:00
#include "QskSGNode.h"
2018-08-03 08:15:28 +02:00
#include "QskTextColors.h"
#include "QskTextNode.h"
#include "QskTextOptions.h"
2021-12-28 15:33:13 +01:00
#include "QskSkinStateChanger.h"
2022-06-01 16:51:11 +02:00
#include "QskTextureRenderer.h"
2017-07-21 18:21:34 +02:00
2018-08-03 08:15:28 +02:00
#include <qquickwindow.h>
2018-07-19 14:10:48 +02:00
#include <qsgsimplerectnode.h>
2017-07-21 18:21:34 +02:00
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
QSK_QT_PRIVATE_END
2021-12-23 19:05:59 +01:00
static inline QRectF qskSceneAlignedRect( const QQuickItem* item, const QRectF& rect )
{
const auto transform = QQuickItemPrivate::get( item )->itemToWindowTransform();
if ( transform.type() > QTransform::TxTranslate )
return rect;
/*
Aligning rect according to scene coordinates, so that
we don't run into rounding issues downstream, where values
will be floored/ceiled ending up with a slightly different
aspect ratio.
*/
const auto ratio = item->window()->devicePixelRatio();
const auto pos = transform.map( rect.topLeft() ) * ratio;
const auto size = rect.size() * ratio;
const qreal x = qRound( pos.x() ) / ratio;
const qreal y = qRound( pos.y() ) / ratio;
const qreal w = qRound( size.width() ) / ratio;
const qreal h = qRound( size.height() ) / ratio;
return QRectF( item->mapFromScene( QPointF( x, y ) ), QSizeF( w, h ) );
}
static inline QRectF qskSubControlRect( const QskSkinlet* skinlet,
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl )
{
if ( auto control = skinnable->controlCast() )
{
const auto r = control->contentsRect();
return skinlet->subControlRect( skinnable, r, subControl );
}
return QRectF();
}
static inline QSGNode* qskUpdateTextNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, Qt::Alignment alignment,
const QString& text, const QFont& font, const QskTextOptions& textOptions,
const QskTextColors& textColors, Qsk::TextStyle textStyle )
{
if ( text.isEmpty() || rect.isEmpty() )
return nullptr;
auto textNode = static_cast< QskTextNode* >( node );
if ( textNode == nullptr )
textNode = new QskTextNode();
auto effectiveFont = font;
switch ( textOptions.fontSizeMode() )
{
case QskTextOptions::FixedSize:
break;
case QskTextOptions::HorizontalFit:
Q_UNIMPLEMENTED();
break;
case QskTextOptions::VerticalFit:
effectiveFont.setPixelSize( static_cast< int >( rect.height() * 0.5 ) );
break;
case QskTextOptions::Fit:
Q_UNIMPLEMENTED();
break;
}
textNode->setTextData( skinnable->owningControl(),
text, rect, effectiveFont, textOptions, textColors, alignment, textStyle );
return textNode;
}
2017-10-23 07:46:46 +02:00
static inline QSGNode* qskUpdateGraphicNode(
const QskSkinnable* skinnable, QSGNode* node,
const QskGraphic& graphic, const QskColorFilter& colorFilter,
2020-11-01 15:44:15 +01:00
const QRectF& rect, Qt::Orientations mirrored )
2017-07-21 18:21:34 +02:00
{
if ( rect.isEmpty() )
return nullptr;
2020-10-31 08:06:41 +01:00
const auto control = skinnable->owningControl();
if ( control == nullptr )
return nullptr;
auto graphicNode = static_cast< QskGraphicNode* >( node );
if ( graphicNode == nullptr )
graphicNode = new QskGraphicNode();
const bool useRaster = control->testUpdateFlag( QskControl::PreferRasterForTextures );
graphicNode->setRenderHint( useRaster ? QskPaintedNode::Raster : QskPaintedNode::OpenGL );
graphicNode->setMirrored( mirrored );
2020-10-31 08:06:41 +01:00
const auto r = qskSceneAlignedRect( control, rect );
graphicNode->setGraphic( control->window(), graphic, colorFilter, r );
2018-07-13 15:09:25 +02:00
return graphicNode;
2017-07-21 18:21:34 +02:00
}
2017-10-20 13:09:30 +02:00
static inline bool qskIsBoxVisible( const QskBoxBorderMetrics& borderMetrics,
const QskBoxBorderColors& borderColors, const QskGradient& gradient )
{
if ( gradient.isVisible() )
return true;
return !borderMetrics.isNull() && borderColors.isVisible();
}
static inline bool qskIsArcVisible( const QskArcMetrics& arcMetrics,
const QskGradient& gradient )
{
return !arcMetrics.isNull() && gradient.isVisible();
}
2017-10-20 20:26:39 +02:00
static inline QskTextColors qskTextColors(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl )
{
/*
Would be more efficient to have QskTextColors hints instead of
storing the colors as seperated hints. TODO ...
*/
QskSkinHintStatus status;
2017-10-23 07:46:46 +02:00
2017-10-20 20:26:39 +02:00
QskTextColors c;
c.textColor = skinnable->color( subControl, &status );
#if 1
if ( !status.isValid() )
c.textColor = skinnable->color( subControl | QskAspect::TextColor );
2017-10-23 07:46:46 +02:00
#endif
2017-10-20 20:26:39 +02:00
c.styleColor = skinnable->color( subControl | QskAspect::StyleColor );
c.linkColor = skinnable->color( subControl | QskAspect::LinkColor );
2017-10-20 20:26:39 +02:00
return c;
}
static inline QSGNode* qskUpdateBoxNode(
const QskSkinnable*, QSGNode* node, const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics,
2022-06-13 17:01:15 +02:00
const QskBoxBorderColors& borderColors, const QskGradient& gradient,
const QskShadowMetrics& shadowMetrics, const QColor& shadowColor )
{
if ( rect.isEmpty() )
return nullptr;
2022-06-13 17:01:15 +02:00
const auto size = rect.size();
const auto absoluteMetrics = borderMetrics.toAbsolute( size );
if ( qskIsBoxVisible( absoluteMetrics, borderColors, gradient ) )
{
auto boxNode = static_cast< QskBoxNode* >( node );
if ( boxNode == nullptr )
boxNode = new QskBoxNode();
2022-06-13 17:01:15 +02:00
const auto absoluteShape = shape.toAbsolute( size );
const auto absoluteShadowMetrics = shadowMetrics.toAbsolute( size );
boxNode->updateNode( rect, absoluteShape, absoluteMetrics,
2022-06-13 17:01:15 +02:00
borderColors, gradient, absoluteShadowMetrics, shadowColor );
return boxNode;
}
return nullptr;
}
static inline QSGNode* qskUpdateArcNode(
const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect,
const QskGradient& fillGradient, const QskArcMetrics& metrics )
{
const auto control = skinnable->owningControl();
if ( control == nullptr || rect.isEmpty() )
return nullptr;
auto absoluteMetrics = metrics.toAbsolute( rect.size() );
if ( !qskIsArcVisible( metrics, fillGradient ) )
return nullptr;
auto arcNode = static_cast< QskArcNode* >( node );
if ( arcNode == nullptr )
arcNode = new QskArcNode();
const auto r = qskSceneAlignedRect( control, rect );
arcNode->setArcData( r, absoluteMetrics, fillGradient, control->window() );
return arcNode;
}
2017-07-21 18:21:34 +02:00
class QskSkinlet::PrivateData
{
2018-08-03 08:15:28 +02:00
public:
PrivateData( QskSkin* skin )
: skin( skin )
, ownedBySkinnable( false )
2017-07-21 18:21:34 +02:00
{
}
QskSkin* skin;
QVector< quint8 > nodeRoles;
int animatorIndex = -1;
2017-07-21 18:21:34 +02:00
bool ownedBySkinnable : 1;
};
2018-08-03 08:15:28 +02:00
QskSkinlet::QskSkinlet( QskSkin* skin )
: m_data( new PrivateData( skin ) )
2017-07-21 18:21:34 +02:00
{
}
QskSkinlet::~QskSkinlet()
{
}
QskSkin* QskSkinlet::skin() const
{
return m_data->skin;
}
void QskSkinlet::setOwnedBySkinnable( bool on )
{
m_data->ownedBySkinnable = on;
}
bool QskSkinlet::isOwnedBySkinnable() const
{
return m_data->ownedBySkinnable;
}
void QskSkinlet::setAnimatorIndex( int index )
{
m_data->animatorIndex = index;
}
void QskSkinlet::resetAnimatorIndex()
{
m_data->animatorIndex = -1;
}
int QskSkinlet::animatorIndex() const
{
return m_data->animatorIndex;
}
2017-07-21 18:21:34 +02:00
void QskSkinlet::setNodeRoles( const QVector< quint8 >& nodeRoles )
{
m_data->nodeRoles = nodeRoles;
}
void QskSkinlet::appendNodeRoles( const QVector< quint8 >& nodeRoles )
{
m_data->nodeRoles += nodeRoles;
}
const QVector< quint8 >& QskSkinlet::nodeRoles() const
{
return m_data->nodeRoles;
}
void QskSkinlet::updateNode( QskSkinnable* skinnable, QSGNode* parentNode ) const
{
2020-11-22 15:27:58 +01:00
using namespace QskSGNode;
2017-07-21 18:21:34 +02:00
QSGNode* oldNode;
QSGNode* newNode;
if ( const auto control = skinnable->controlCast() )
{
// background
2020-11-22 15:27:58 +01:00
oldNode = findChildNode( parentNode, BackgroundRole );
newNode = updateBackgroundNode( control, oldNode );
2017-07-21 18:21:34 +02:00
2020-11-22 15:27:58 +01:00
replaceChildNode( BackgroundRole, parentNode, oldNode, newNode );
2017-07-21 18:21:34 +02:00
// debug
2020-11-22 15:27:58 +01:00
oldNode = findChildNode( parentNode, DebugRole );
2017-07-21 18:21:34 +02:00
newNode = nullptr;
2021-04-23 17:31:55 +02:00
if ( control->testUpdateFlag( QskQuickItem::DebugForceBackground ) )
2017-07-21 18:21:34 +02:00
newNode = updateDebugNode( control, oldNode );
2020-11-22 15:27:58 +01:00
replaceChildNode( DebugRole, parentNode, oldNode, newNode );
2017-07-21 18:21:34 +02:00
}
2022-03-24 08:41:11 +01:00
for ( const auto nodeRole : qAsConst( m_data->nodeRoles ) )
2017-07-21 18:21:34 +02:00
{
2020-11-22 15:27:58 +01:00
Q_ASSERT( nodeRole < FirstReservedRole );
2017-07-21 18:21:34 +02:00
2020-11-22 15:27:58 +01:00
oldNode = QskSGNode::findChildNode( parentNode, nodeRole );
2017-07-21 18:21:34 +02:00
newNode = updateSubNode( skinnable, nodeRole, oldNode );
2020-11-22 15:27:58 +01:00
replaceChildNode( nodeRole, parentNode, oldNode, newNode );
2017-07-21 18:21:34 +02:00
}
}
QSGNode* QskSkinlet::updateBackgroundNode(
const QskControl* control, QSGNode* node ) const
{
2020-07-31 07:40:33 +02:00
const auto rect = control->rect();
2017-07-21 18:21:34 +02:00
if ( rect.isEmpty() )
return nullptr;
2020-07-31 07:40:33 +02:00
const auto gradient = control->background();
2017-07-21 18:21:34 +02:00
if ( !gradient.isValid() )
return nullptr;
auto rectNode = QskSGNode::ensureNode< QskRectangleNode >( node );
rectNode->updateNode( rect, gradient );
return rectNode;
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateDebugNode(
const QskControl* control, QSGNode* node ) const
{
if ( control->size().isEmpty() )
return nullptr;
2020-07-31 07:40:33 +02:00
auto rectNode = static_cast< QSGSimpleRectNode* >( node );
2017-07-21 18:21:34 +02:00
if ( rectNode == nullptr )
{
rectNode = new QSGSimpleRectNode();
QColor color;
if ( control->inherits( "QskFocusIndicator" ) )
{
color = QColor( Qt::gray );
color.setAlpha( 60 );
}
else
{
static int idx = 0;
idx = ( idx + 3 ) % 14;
// maybe using something random based on web colors ???
color = QColor( Qt::GlobalColor( 4 + idx ) );
color.setAlpha( 200 );
}
rectNode->setColor( color );
}
2020-11-22 15:27:58 +01:00
const auto r = control->rect();
2017-07-21 18:21:34 +02:00
if ( rectNode->rect() != r )
rectNode->setRect( r );
return rectNode;
}
2020-11-22 15:27:58 +01:00
void QskSkinlet::replaceChildNode( quint8 role,
QSGNode* parentNode, QSGNode* oldNode, QSGNode* newNode ) const
2017-07-21 18:21:34 +02:00
{
2020-11-22 15:27:58 +01:00
QskSGNode::replaceChildNode(
m_data->nodeRoles, role, parentNode, oldNode, newNode );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateBoxNode( const QskSkinnable* skinnable,
QSGNode* node, QskAspect::Subcontrol subControl ) const
2017-07-21 18:21:34 +02:00
{
2020-11-22 15:27:58 +01:00
const auto rect = qskSubControlRect( this, skinnable, subControl );
return updateBoxNode( skinnable, node, rect, subControl );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateBoxNode( const QskSkinnable* skinnable,
2017-10-23 07:46:46 +02:00
QSGNode* node, const QRectF& rect, QskAspect::Subcontrol subControl )
2020-07-31 07:40:33 +02:00
{
const auto fillGradient = skinnable->gradientHint( subControl );
return updateBoxNode( skinnable, node, rect, fillGradient, subControl );
}
QSGNode* QskSkinlet::updateBoxNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, const QskGradient& fillGradient,
QskAspect::Subcontrol subControl )
2017-07-21 18:21:34 +02:00
{
const auto margins = skinnable->marginHint( subControl );
2017-10-20 13:09:30 +02:00
2020-11-22 15:27:58 +01:00
const auto boxRect = rect.marginsRemoved( margins );
2017-10-20 13:09:30 +02:00
if ( boxRect.isEmpty() )
2017-07-21 18:21:34 +02:00
return nullptr;
const auto borderMetrics = skinnable->boxBorderMetricsHint( subControl );
2017-10-20 13:09:30 +02:00
const auto borderColors = skinnable->boxBorderColorsHint( subControl );
const auto shape = skinnable->boxShapeHint( subControl );
2022-06-13 17:01:15 +02:00
const auto shadowMetrics = skinnable->shadowMetricsHint( subControl );
const auto shadowColor = skinnable->shadowColorHint( subControl );
2017-10-20 13:09:30 +02:00
return qskUpdateBoxNode( skinnable, node,
2022-06-13 17:01:15 +02:00
boxRect, shape, borderMetrics, borderColors, fillGradient,
shadowMetrics, shadowColor );
2017-07-21 18:21:34 +02:00
}
2022-01-04 13:44:53 +01:00
QSGNode* QskSkinlet::updateBoxNode(
const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect,
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics,
const QskBoxBorderColors& borderColors, const QskGradient& fillGradient )
{
return qskUpdateBoxNode( skinnable, node,
2022-06-13 17:01:15 +02:00
rect, shape, borderMetrics, borderColors, fillGradient,
QskShadowMetrics(), QColor() );
2022-01-04 13:44:53 +01:00
}
QSGNode* QskSkinlet::updateBoxNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, const QskBoxHints& hints )
{
return qskUpdateBoxNode( skinnable, node, rect,
2022-06-13 17:01:15 +02:00
hints.shape, hints.borderMetrics, hints.borderColors, hints.gradient,
hints.shadowMetrics, hints.shadowColor );
2022-01-04 13:44:53 +01:00
}
QSGNode* QskSkinlet::updateInterpolatedBoxNode(
const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect,
QskAspect aspect1, QskAspect aspect2, qreal ratio )
{
QskBoxHints boxHints;
QRectF r;
ratio = qBound( 0.0, ratio, 1.0 );
if ( qFuzzyIsNull( ratio ) )
{
const auto margins = skinnable->marginHint( aspect1 );
r = rect.marginsRemoved( margins );
boxHints = skinnable->boxHints( aspect1 ).toAbsolute( r.size() );
}
else if ( qFuzzyCompare( ratio, 1.0 ) )
{
const auto margins = skinnable->marginHint( aspect2 );
r = rect.marginsRemoved( margins );
boxHints = skinnable->boxHints( aspect2 ).toAbsolute( r.size() );
}
else
{
QskMargins margins = skinnable->marginHint( aspect1 );
margins = margins.interpolated( skinnable->marginHint( aspect2 ), ratio );
r = rect.marginsRemoved( margins );
const auto boxHints1 = skinnable->boxHints( aspect1 ).toAbsolute( r.size() );
const auto boxHints2 = skinnable->boxHints( aspect2 ).toAbsolute( r.size() );
boxHints = boxHints1.interpolated( boxHints2, ratio );
}
return QskSkinlet::updateBoxNode( skinnable, node, r, boxHints );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
QSGNode* node, QskAspect::Subcontrol subControl ) const
{
const auto rect = qskSubControlRect( this, skinnable, subControl );
return updateArcNode( skinnable, node, rect, subControl );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, QskAspect::Subcontrol subControl )
{
const auto fillGradient = skinnable->gradientHint( subControl );
return updateArcNode( skinnable, node, rect, fillGradient, subControl );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, const QskGradient& fillGradient,
QskAspect::Subcontrol subControl )
{
2021-12-02 18:40:54 +01:00
const auto metrics = skinnable->arcMetricsHint( subControl );
const auto r = rect.marginsRemoved( skinnable->marginHint( subControl ) );
return qskUpdateArcNode( skinnable, node, r, fillGradient, metrics );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, const QskGradient& fillGradient,
2021-12-02 18:40:54 +01:00
const QskArcMetrics& metrics )
{
return qskUpdateArcNode( skinnable, node, rect, fillGradient, metrics );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
2021-10-20 09:27:05 +02:00
QSGNode* node, qreal startAngle, qreal spanAngle,
QskAspect::Subcontrol subControl ) const
{
const auto rect = qskSubControlRect( this, skinnable, subControl );
return updateArcNode( skinnable, node, rect, startAngle, spanAngle,
subControl );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
2021-10-20 09:27:05 +02:00
QSGNode* node, const QRectF& rect, qreal startAngle, qreal spanAngle,
QskAspect::Subcontrol subControl )
{
const auto fillGradient = skinnable->gradientHint( subControl );
2021-10-20 12:42:26 +02:00
return updateArcNode( skinnable, node, rect,
fillGradient, startAngle, spanAngle, subControl );
}
QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, const QskGradient& fillGradient,
2021-10-20 09:27:05 +02:00
qreal startAngle, qreal spanAngle, QskAspect::Subcontrol subControl )
{
auto arcMetrics = skinnable->arcMetricsHint( subControl );
arcMetrics.setStartAngle( startAngle );
arcMetrics.setSpanAngle( spanAngle );
2021-12-02 18:40:54 +01:00
const auto r = rect.marginsRemoved( skinnable->marginHint( subControl ) );
return updateArcNode( skinnable, node, r, fillGradient, arcMetrics );
}
QSGNode* QskSkinlet::updateBoxClipNode( const QskSkinnable* skinnable,
QSGNode* node, QskAspect::Subcontrol subControl ) const
{
2020-11-22 15:27:58 +01:00
const auto rect = qskSubControlRect( this, skinnable, subControl );
return updateBoxClipNode( skinnable, node, rect, subControl );
}
QSGNode* QskSkinlet::updateBoxClipNode( const QskSkinnable* skinnable,
2017-10-23 07:46:46 +02:00
QSGNode* node, const QRectF& rect, QskAspect::Subcontrol subControl )
{
2022-07-18 16:26:49 +02:00
auto clipNode = QskSGNode::ensureNode< QskBoxClipNode >( node );
const auto margins = skinnable->marginHint( subControl );
2017-10-20 13:09:30 +02:00
2020-11-22 15:27:58 +01:00
const auto clipRect = rect.marginsRemoved( margins );
2017-10-20 13:09:30 +02:00
if ( clipRect.isEmpty() )
{
clipNode->setIsRectangular( true );
clipNode->setClipRect( clipRect );
}
else
{
auto borderMetrics = skinnable->boxBorderMetricsHint( subControl );
borderMetrics = borderMetrics.toAbsolute( clipRect.size() );
auto shape = skinnable->boxShapeHint( subControl );
shape = shape.toAbsolute( clipRect.size() );
2017-10-20 13:09:30 +02:00
clipNode->setBox( clipRect, shape, borderMetrics );
}
return clipNode;
}
QSGNode* QskSkinlet::updateTextNode( const QskSkinnable* skinnable,
QSGNode* node, const QRectF& rect, Qt::Alignment alignment,
const QString& text, const QFont& font, const QskTextOptions& textOptions,
const QskTextColors& textColors, Qsk::TextStyle textStyle )
{
return qskUpdateTextNode( skinnable, node, rect, alignment,
text, font, textOptions, textColors, textStyle );
}
2017-07-21 18:21:34 +02:00
QSGNode* QskSkinlet::updateTextNode(
const QskSkinnable* skinnable, QSGNode* node,
const QRectF& rect, Qt::Alignment alignment,
2022-08-25 09:39:33 +02:00
const QString& text, QskAspect::Subcontrol subControl )
2017-07-21 18:21:34 +02:00
{
if ( text.isEmpty() || rect.isEmpty() )
return nullptr;
const auto textColors = qskTextColors( skinnable, subControl );
2022-08-25 09:39:33 +02:00
const auto textOptions = skinnable->textOptionsHint( subControl );
2017-10-20 20:26:39 +02:00
auto textStyle = Qsk::Normal;
if ( textColors.styleColor.alpha() == 0 )
2017-10-20 20:26:39 +02:00
{
textStyle = skinnable->flagHint< Qsk::TextStyle >(
subControl | QskAspect::Style, Qsk::Normal );
}
const auto font = skinnable->effectiveFont( subControl );
2017-07-21 18:21:34 +02:00
return qskUpdateTextNode( skinnable, node, rect, alignment,
text, font, textOptions, textColors, textStyle );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateTextNode(
const QskSkinnable* skinnable, QSGNode* node,
2022-08-25 09:39:33 +02:00
const QString& text, QskAspect::Subcontrol subControl ) const
2017-07-21 18:21:34 +02:00
{
2020-11-22 15:27:58 +01:00
const auto rect = qskSubControlRect( this, skinnable, subControl );
const auto alignment = skinnable->alignmentHint( subControl, Qt::AlignLeft );
2017-07-21 18:21:34 +02:00
2018-08-03 08:15:28 +02:00
return updateTextNode( skinnable, node,
2022-08-25 09:39:33 +02:00
rect, alignment, text, subControl );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateSymbolNode(
const QskSkinnable* skinnable, QSGNode* node,
QskAspect::Subcontrol subControl ) const
{
return updateGraphicNode( skinnable, node,
skinnable->symbolHint( subControl ), subControl );
}
2017-07-21 18:21:34 +02:00
QSGNode* QskSkinlet::updateGraphicNode(
const QskSkinnable* skinnable, QSGNode* node,
const QskGraphic& graphic, QskAspect::Subcontrol subControl,
2020-11-01 15:44:15 +01:00
Qt::Orientations mirrored ) const
2017-07-21 18:21:34 +02:00
{
const auto rect = qskSubControlRect( this, skinnable, subControl );
const auto alignment = skinnable->alignmentHint( subControl, Qt::AlignCenter );
const auto colorFilter = skinnable->effectiveGraphicFilter( subControl );
2017-10-23 07:46:46 +02:00
2017-07-21 18:21:34 +02:00
return updateGraphicNode( skinnable, node,
2020-11-01 15:44:15 +01:00
graphic, colorFilter, rect, alignment, mirrored );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateGraphicNode(
const QskSkinnable* skinnable, QSGNode* node,
const QskGraphic& graphic, const QskColorFilter& colorFilter,
2020-11-01 15:44:15 +01:00
const QRectF& rect, Qt::Alignment alignment, Qt::Orientations mirrored )
2017-07-21 18:21:34 +02:00
{
if ( graphic.isNull() )
return nullptr;
2020-11-22 15:27:58 +01:00
const auto size = graphic.defaultSize().scaled(
2017-07-21 18:21:34 +02:00
rect.size(), Qt::KeepAspectRatio );
2022-07-28 16:51:20 +02:00
const auto r = qskAlignedRectF( rect, size, alignment );
2020-11-01 15:44:15 +01:00
return qskUpdateGraphicNode( skinnable, node, graphic, colorFilter, r, mirrored );
2017-07-21 18:21:34 +02:00
}
QSGNode* QskSkinlet::updateGraphicNode(
const QskSkinnable* skinnable, QSGNode* node,
const QskGraphic& graphic, const QskColorFilter& colorFilter,
2020-11-01 15:44:15 +01:00
const QRectF& rect, Qt::Orientations mirrored )
2017-10-23 07:46:46 +02:00
{
if ( graphic.isNull() )
return nullptr;
2020-11-01 15:44:15 +01:00
return qskUpdateGraphicNode( skinnable, node, graphic, colorFilter, rect, mirrored );
2017-10-23 07:46:46 +02:00
}
int QskSkinlet::sampleIndexAt( const QskSkinnable* skinnable,
const QRectF& rect, QskAspect::Subcontrol subControl, const QPointF& pos ) const
2021-12-26 09:15:15 +01:00
{
// slow default implementation to be overloaded when having many cells
const auto count = sampleCount( skinnable, subControl );
for ( int i = 0; i < count; i++ )
{
const auto r = sampleRect( skinnable, rect, subControl, i );
if ( r.contains( pos ) )
return i;
}
2021-12-26 09:15:15 +01:00
return -1;
}
2021-12-28 15:33:13 +01:00
QSGNode* QskSkinlet::updateSeriesNode( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl, QSGNode* rootNode ) const
{
auto node = rootNode ? rootNode->firstChild() : nullptr;
QSGNode* lastNode = nullptr;
const auto count = sampleCount( skinnable, subControl );
2021-12-28 15:33:13 +01:00
for( int i = 0; i < count; i++ )
{
QSGNode* newNode = nullptr;
{
const auto newStates = sampleStates( skinnable, subControl, i );
2021-12-28 15:33:13 +01:00
QskSkinStateChanger stateChanger( skinnable );
stateChanger.setStates( newStates );
class IndexChanger
{
public:
inline IndexChanger( const QskSkinlet* skinlet, int index )
: m_skinlet( const_cast< QskSkinlet* >( skinlet ) )
{
m_skinlet->setAnimatorIndex( index );
}
inline ~IndexChanger()
{
m_skinlet->resetAnimatorIndex();
}
private:
QskSkinlet* m_skinlet;
};
IndexChanger indexChanger( this, i );
newNode = updateSampleNode( skinnable, subControl, i, node );
2021-12-28 15:33:13 +01:00
}
if ( newNode )
{
if ( newNode == node )
{
node = node->nextSibling();
}
else
{
if ( rootNode == nullptr )
rootNode = new QSGNode();
if ( node )
rootNode->insertChildNodeBefore( newNode, node );
else
rootNode->appendChildNode( newNode );
}
lastNode = newNode;
}
}
QskSGNode::removeAllChildNodesAfter( rootNode, lastNode );
return rootNode;
}
QSGNode* QskSkinlet::updateSampleNode( const QskSkinnable*,
2021-12-28 15:33:13 +01:00
QskAspect::Subcontrol, int index, QSGNode* ) const
{
Q_UNUSED( index )
return nullptr;
}
QskAspect::States QskSkinlet::sampleStates(
const QskSkinnable* skinnable, QskAspect::Subcontrol, int index ) const
2021-12-24 16:50:34 +01:00
{
Q_UNUSED( index )
return skinnable->skinStates();
2021-12-24 16:50:34 +01:00
}
QVariant QskSkinlet::sampleAt( const QskSkinnable*,
2021-12-28 15:33:13 +01:00
QskAspect::Subcontrol, int index ) const
{
Q_UNUSED( index )
return QVariant();
}
QSizeF QskSkinlet::hintWithoutConstraint(
const QSizeF& hint, const QSizeF& constraint ) const
{
/*
This method is useful in situations, where a hint has been calculated
from a constraint and we want to return the calculated part only
*/
QSizeF h;
if ( constraint.width() < 0.0 )
h.setWidth( hint.width() );
if ( constraint.height() < 0.0 )
h.setHeight( hint.height() );
return h;
}
2017-07-21 18:21:34 +02:00
#include "moc_QskSkinlet.cpp"