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 "QskSkinnable.h"
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskAnimationHint.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "QskAspect.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskColorFilter.h"
|
|
|
|
#include "QskControl.h"
|
|
|
|
#include "QskHintAnimator.h"
|
|
|
|
#include "QskMargins.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "QskSetup.h"
|
|
|
|
#include "QskSkin.h"
|
2017-08-22 20:50:55 +02:00
|
|
|
#include "QskSkinHintTable.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "QskSkinTransition.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskSkinlet.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qfont.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
#define DEBUG_MAP 0
|
|
|
|
#define DEBUG_ANIMATOR 0
|
|
|
|
#define DEBUG_STATE 0
|
2017-09-06 10:22:59 +02:00
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
static inline bool qskIsControl( const QskSkinnable* skinnable )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK( 5, 7, 0 )
|
2017-07-21 18:21:34 +02:00
|
|
|
return skinnable->metaObject()->inherits( &QskControl::staticMetaObject );
|
|
|
|
#else
|
|
|
|
for ( auto mo = skinnable->metaObject();
|
|
|
|
mo != nullptr; mo = mo->superClass() )
|
|
|
|
{
|
|
|
|
if ( mo == &QskControl::staticMetaObject )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
static inline bool qskCompareResolvedStates(
|
|
|
|
QskAspect::Aspect& aspect1, QskAspect::Aspect& aspect2,
|
|
|
|
const QskSkinHintTable& table )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( !table.hasStates() )
|
|
|
|
return false;
|
2017-09-06 10:22:59 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
const QskAspect::Aspect a1 = aspect1;
|
|
|
|
const QskAspect::Aspect a2 = aspect2;
|
|
|
|
|
|
|
|
Q_FOREVER
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
const auto s1 = aspect1.topState();
|
|
|
|
const auto s2 = aspect2.topState();
|
2017-09-06 10:22:59 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( s1 > s2 )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( table.hasHint( aspect1 ) )
|
|
|
|
return false;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect1.clearState( s1 );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2017-10-17 17:34:00 +02:00
|
|
|
else if ( s2 > s1 )
|
|
|
|
{
|
|
|
|
if ( table.hasHint( aspect2 ) )
|
|
|
|
return false;
|
2017-09-06 10:22:59 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect2.clearState( s2 );
|
|
|
|
}
|
|
|
|
else
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( aspect1 == aspect2 )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( table.hasHint( aspect1 ) )
|
|
|
|
return true;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( s1 == 0 )
|
|
|
|
{
|
2019-04-17 16:33:17 +02:00
|
|
|
if ( aspect1.placement() == QskAspect::NoPlacement )
|
2017-10-17 17:34:00 +02:00
|
|
|
return true;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-04-17 16:33:17 +02:00
|
|
|
// clear the placement bits and restart with the initial state
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect1 = a1;
|
2019-04-17 16:33:17 +02:00
|
|
|
aspect1.setPlacement( QskAspect::NoPlacement );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect2 = a2;
|
2019-04-17 16:33:17 +02:00
|
|
|
aspect2.setPlacement( QskAspect::NoPlacement );
|
2017-10-17 17:34:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( table.hasHint( aspect1 ) || table.hasHint( aspect2 ) )
|
|
|
|
return false;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect1.clearState( s1 );
|
|
|
|
aspect2.clearState( s2 );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskSkinnable::PrivateData
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
PrivateData()
|
|
|
|
: skinlet( nullptr )
|
|
|
|
, skinState( QskAspect::NoState )
|
|
|
|
, hasLocalSkinlet( false )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~PrivateData()
|
|
|
|
{
|
2017-12-08 13:56:35 +01:00
|
|
|
if ( hasLocalSkinlet )
|
|
|
|
{
|
|
|
|
if ( skinlet && skinlet->isOwnedBySkinnable() )
|
|
|
|
delete skinlet;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 20:50:55 +02:00
|
|
|
QskSkinHintTable hintTable;
|
2017-07-21 18:21:34 +02:00
|
|
|
QskHintAnimatorTable animators;
|
|
|
|
|
|
|
|
const QskSkinlet* skinlet;
|
|
|
|
|
|
|
|
QskAspect::State skinState;
|
|
|
|
bool hasLocalSkinlet : 1;
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskSkinnable::QskSkinnable()
|
|
|
|
: m_data( new PrivateData() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QskSkinnable::~QskSkinnable()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setSkinlet( const QskSkinlet* skinlet )
|
|
|
|
{
|
|
|
|
if ( skinlet == m_data->skinlet )
|
|
|
|
{
|
|
|
|
if ( skinlet )
|
|
|
|
{
|
|
|
|
// now we don't depend on global skin changes anymore
|
|
|
|
m_data->hasLocalSkinlet = true;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_data->skinlet && m_data->skinlet->isOwnedBySkinnable() )
|
|
|
|
delete m_data->skinlet;
|
|
|
|
|
|
|
|
m_data->skinlet = skinlet;
|
|
|
|
m_data->hasLocalSkinlet = ( skinlet != nullptr );
|
|
|
|
|
|
|
|
owningControl()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
const QskSkinlet* QskSkinnable::skinlet() const
|
|
|
|
{
|
|
|
|
return m_data->hasLocalSkinlet ? m_data->skinlet : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QskSkinlet* QskSkinnable::effectiveSkinlet() const
|
|
|
|
{
|
|
|
|
if ( m_data->skinlet == nullptr )
|
|
|
|
{
|
|
|
|
m_data->skinlet = qskSetup->skin()->skinlet( this );
|
|
|
|
m_data->hasLocalSkinlet = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_data->skinlet;
|
|
|
|
}
|
|
|
|
|
2018-03-22 11:22:13 +01:00
|
|
|
QskSkinHintTable& QskSkinnable::hintTable()
|
2017-08-22 20:50:55 +02:00
|
|
|
{
|
|
|
|
return m_data->hintTable;
|
|
|
|
}
|
|
|
|
|
2018-03-22 11:22:13 +01:00
|
|
|
const QskSkinHintTable& QskSkinnable::hintTable() const
|
2017-08-22 20:50:55 +02:00
|
|
|
{
|
|
|
|
return m_data->hintTable;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskSkinnable::setFlagHint( QskAspect::Aspect aspect, int flag )
|
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
m_data->hintTable.setHint( aspect, QVariant( flag ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int QskSkinnable::flagHint( QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
return effectiveHint( aspect ).toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setColor( QskAspect::Aspect aspect, const QColor& color )
|
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setColor( aspect, color );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setColor( QskAspect::Aspect aspect, Qt::GlobalColor color )
|
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setColor( aspect, color );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 20:50:55 +02:00
|
|
|
void QskSkinnable::setColor( QskAspect::Aspect aspect, QRgb rgb )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setColor( aspect, rgb );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2017-08-22 20:50:55 +02:00
|
|
|
QColor QskSkinnable::color( QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
return effectiveHint( aspect | QskAspect::Color, status ).value< QColor >();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setMetric( QskAspect::Aspect aspect, qreal metric )
|
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setMetric( aspect, metric );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskSkinnable::metric( QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
|
|
|
return effectiveHint( aspect | QskAspect::Metric, status ).toReal();
|
|
|
|
}
|
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
void QskSkinnable::setMarginsHint( QskAspect::Aspect aspect, qreal margins )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setMargins( aspect, QskMargins( margins ) );
|
|
|
|
}
|
|
|
|
|
2017-08-23 14:53:29 +02:00
|
|
|
void QskSkinnable::setMarginsHint( QskAspect::Aspect aspect, const QMarginsF& margins )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setMargins( aspect, margins );
|
|
|
|
}
|
|
|
|
|
|
|
|
QMarginsF QskSkinnable::marginsHint(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
|
|
|
return effectiveHint( aspect | QskAspect::Metric, status ).value< QskMargins >();
|
|
|
|
}
|
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
void QskSkinnable::setGradientHint(
|
|
|
|
QskAspect::Aspect aspect, const QskGradient& gradient )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setGradient( aspect, gradient );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskGradient QskSkinnable::gradientHint(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
|
|
|
return effectiveHint( aspect | QskAspect::Color, status ).value< QskGradient >();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setBoxShapeHint(
|
|
|
|
QskAspect::Aspect aspect, const QskBoxShapeMetrics& shape )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setBoxShape( aspect, shape );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskBoxShapeMetrics QskSkinnable::boxShapeHint(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
2017-10-18 20:00:06 +02:00
|
|
|
using namespace QskAspect;
|
|
|
|
return effectiveHint( aspect | Metric | Shape, status ).value< QskBoxShapeMetrics >();
|
2017-10-17 17:34:00 +02:00
|
|
|
}
|
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
void QskSkinnable::setBoxBorderMetricsHint(
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAspect::Aspect aspect, const QskBoxBorderMetrics& border )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setBoxBorder( aspect, border );
|
|
|
|
}
|
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
QskBoxBorderMetrics QskSkinnable::boxBorderMetricsHint(
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
using namespace QskAspect;
|
2017-10-18 20:00:06 +02:00
|
|
|
return effectiveHint( aspect | Metric | Border, status ).value< QskBoxBorderMetrics >();
|
2017-10-17 17:34:00 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
void QskSkinnable::setBoxBorderColorsHint(
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAspect::Aspect aspect, const QskBoxBorderColors& colors )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setBoxBorderColors( aspect, colors );
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
QskBoxBorderColors QskSkinnable::boxBorderColorsHint(
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
2018-03-22 11:22:13 +01:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
using namespace QskAspect;
|
2017-10-18 20:00:06 +02:00
|
|
|
return effectiveHint( aspect | Color | Border, status ).value< QskBoxBorderColors >();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2020-08-09 10:45:48 +02:00
|
|
|
void QskSkinnable::setIntervalHint(
|
|
|
|
QskAspect::Aspect aspect, const QskIntervalF& interval )
|
|
|
|
{
|
|
|
|
m_data->hintTable.setInterval( aspect, interval );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskIntervalF QskSkinnable::intervalHint(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
|
|
|
return effectiveHint( aspect | QskAspect::Metric, status ).value< QskIntervalF >();
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskSkinnable::setFontRole( QskAspect::Aspect aspect, int role )
|
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setFontRole( aspect, role );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2020-03-18 12:49:57 +01:00
|
|
|
int QskSkinnable::fontRole(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2020-03-18 12:49:57 +01:00
|
|
|
return effectiveHint( aspect | QskAspect::FontRole, status ).toInt();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QFont QskSkinnable::effectiveFont( QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
return effectiveSkin()->font( fontRole( aspect ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setGraphicRole( QskAspect::Aspect aspect, int role )
|
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setGraphicRole( aspect, role );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2020-03-18 12:49:57 +01:00
|
|
|
int QskSkinnable::graphicRole(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2020-03-18 12:49:57 +01:00
|
|
|
return effectiveHint( aspect | QskAspect::GraphicRole, status ).toInt();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskColorFilter QskSkinnable::effectiveGraphicFilter(
|
|
|
|
QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
aspect.setSubControl( effectiveSubcontrol( aspect.subControl() ) );
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect.setPlacement( effectivePlacement() );
|
2017-07-21 18:21:34 +02:00
|
|
|
aspect = aspect | QskAspect::GraphicRole;
|
|
|
|
|
|
|
|
QskSkinHintStatus status;
|
|
|
|
|
2017-08-22 19:47:06 +02:00
|
|
|
const QVariant hint = storedHint( aspect | skinState(), &status );
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( status.isValid() )
|
|
|
|
{
|
|
|
|
// we need to know about how the aspect gets resolved
|
|
|
|
// before checking for animators
|
|
|
|
|
|
|
|
aspect.setSubControl( status.aspect.subControl() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !aspect.isAnimator() )
|
|
|
|
{
|
2018-03-22 11:22:13 +01:00
|
|
|
QVariant v = animatedValue( aspect, nullptr );
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( v.canConvert< QskColorFilter >() )
|
|
|
|
return v.value< QskColorFilter >();
|
2018-03-22 11:22:13 +01:00
|
|
|
|
2019-02-03 12:48:43 +01:00
|
|
|
if ( auto control = owningControl() )
|
2018-03-22 11:22:13 +01:00
|
|
|
{
|
2019-02-03 12:48:43 +01:00
|
|
|
v = QskSkinTransition::animatedGraphicFilter(
|
|
|
|
control->window(), hint.toInt() );
|
2018-03-22 11:22:13 +01:00
|
|
|
|
|
|
|
if ( v.canConvert< QskColorFilter >() )
|
|
|
|
{
|
2019-02-03 12:48:43 +01:00
|
|
|
/*
|
|
|
|
As it is hard to find out which controls depend
|
|
|
|
on the animated graphic filters we reschedule
|
|
|
|
our updates here.
|
|
|
|
*/
|
|
|
|
control->update();
|
2018-03-22 11:22:13 +01:00
|
|
|
return v.value< QskColorFilter >();
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return effectiveSkin()->graphicFilter( hint.toInt() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::setAnimation(
|
|
|
|
QskAspect::Aspect aspect, QskAnimationHint animation )
|
|
|
|
{
|
2017-08-22 20:50:55 +02:00
|
|
|
m_data->hintTable.setAnimation( aspect, animation );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskAnimationHint QskSkinnable::animation(
|
2018-08-03 08:15:28 +02:00
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
aspect.setAnimator( true );
|
|
|
|
return effectiveHint( aspect, status ).value< QskAnimationHint >();
|
|
|
|
}
|
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAnimationHint QskSkinnable::effectiveAnimation(
|
2018-03-22 11:22:13 +01:00
|
|
|
QskAspect::Type type, QskAspect::Subcontrol subControl,
|
2018-02-06 14:58:24 +01:00
|
|
|
QskAspect::State state, QskSkinHintStatus* status ) const
|
2017-10-17 17:34:00 +02:00
|
|
|
{
|
2018-02-06 14:58:24 +01:00
|
|
|
QskAspect::Aspect aspect = subControl | type | state;
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect.setAnimator( true );
|
|
|
|
|
|
|
|
QskAnimationHint hint;
|
|
|
|
|
|
|
|
const auto a = m_data->hintTable.resolvedAnimator( aspect, hint );
|
|
|
|
if ( a.isAnimator() )
|
|
|
|
{
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::Skinnable;
|
|
|
|
status->aspect = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
|
2019-04-19 17:04:36 +02:00
|
|
|
if ( auto skin = effectiveSkin() )
|
2017-10-17 17:34:00 +02:00
|
|
|
{
|
|
|
|
const auto a = skin->hintTable().resolvedAnimator( aspect, hint );
|
|
|
|
if ( a.isAnimator() )
|
|
|
|
{
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::Skin;
|
|
|
|
status->aspect = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::NoSource;
|
|
|
|
status->aspect = QskAspect::Aspect();
|
|
|
|
}
|
|
|
|
|
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
|
2019-12-14 13:34:30 +01:00
|
|
|
bool QskSkinnable::resetHint( QskAspect::Aspect aspect )
|
2019-03-19 17:36:12 +01:00
|
|
|
{
|
2019-12-14 16:40:18 +01:00
|
|
|
if ( !m_data->hintTable.hasHint( aspect ) )
|
|
|
|
return false;
|
2019-12-14 13:34:30 +01:00
|
|
|
|
2019-12-14 16:40:18 +01:00
|
|
|
/*
|
|
|
|
To be able to indicate, when the resolved value has changed
|
|
|
|
we retrieve the value before and after removing the hint from
|
|
|
|
the local table. An implementation with less lookups
|
|
|
|
should be possible, but as reset is a low frequently called
|
|
|
|
operation, we prefer to keep the implementation simple.
|
|
|
|
*/
|
2019-12-14 13:34:30 +01:00
|
|
|
|
2019-12-14 16:40:18 +01:00
|
|
|
auto a = aspect;
|
|
|
|
a.setSubControl( effectiveSubcontrol( a.subControl() ) );
|
|
|
|
a.setPlacement( effectivePlacement() );
|
|
|
|
|
|
|
|
if ( a.state() == QskAspect::NoState )
|
|
|
|
a = a | skinState();
|
2020-08-09 11:50:34 +02:00
|
|
|
|
2019-12-14 16:40:18 +01:00
|
|
|
const auto oldHint = storedHint( a );
|
|
|
|
|
|
|
|
m_data->hintTable.removeHint( aspect );
|
|
|
|
|
|
|
|
return oldHint != storedHint( a );
|
2019-03-19 17:36:12 +01:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QVariant QskSkinnable::effectiveHint(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
|
|
|
aspect.setSubControl( effectiveSubcontrol( aspect.subControl() ) );
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect.setPlacement( effectivePlacement() );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( aspect.isAnimator() )
|
2017-08-22 19:47:06 +02:00
|
|
|
return storedHint( aspect, status );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
const QVariant v = animatedValue( aspect, status );
|
|
|
|
if ( v.isValid() )
|
|
|
|
return v;
|
|
|
|
|
|
|
|
if ( aspect.state() == QskAspect::NoState )
|
|
|
|
aspect = aspect | skinState();
|
|
|
|
|
2017-08-22 19:47:06 +02:00
|
|
|
return storedHint( aspect, status );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskSkinHintStatus QskSkinnable::hintStatus( QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
QskSkinHintStatus status;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
( void ) effectiveHint( aspect, &status );
|
2017-07-21 18:21:34 +02:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVariant QskSkinnable::animatedValue(
|
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
|
|
|
QVariant v;
|
|
|
|
|
|
|
|
if ( aspect.state() == QskAspect::NoState )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The local animators were invented to be stateless
|
|
|
|
and we never have an aspect with a state here.
|
|
|
|
But that might change ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
v = m_data->animators.currentValue( aspect );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !v.isValid() )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( QskSkinTransition::isRunning() &&
|
|
|
|
!m_data->hintTable.hasHint( aspect ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Next we check for values from the skin. Those
|
|
|
|
animators are usually from global skin/color changes
|
|
|
|
and are state aware
|
|
|
|
*/
|
|
|
|
|
2019-02-03 12:48:43 +01:00
|
|
|
if ( const auto control = owningControl() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2019-02-03 12:48:43 +01:00
|
|
|
if ( aspect.state() == QskAspect::NoState )
|
|
|
|
aspect = aspect | skinState();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-04-23 11:34:34 +02:00
|
|
|
const auto a = aspect;
|
2019-04-19 17:04:36 +02:00
|
|
|
|
2019-02-03 12:48:43 +01:00
|
|
|
Q_FOREVER
|
|
|
|
{
|
2019-04-23 11:34:34 +02:00
|
|
|
v = QskSkinTransition::animatedHint( control->window(), aspect );
|
2019-04-19 17:04:36 +02:00
|
|
|
|
2019-04-23 11:34:34 +02:00
|
|
|
if ( !v.isValid() )
|
|
|
|
{
|
|
|
|
if ( const auto topState = aspect.topState() )
|
|
|
|
{
|
|
|
|
aspect.clearState( aspect.topState() );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( aspect.placement() )
|
|
|
|
{
|
|
|
|
// clear the placement bits and restart
|
|
|
|
aspect = a;
|
|
|
|
aspect.setPlacement( QskAspect::NoPlacement );
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-19 17:04:36 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 11:34:34 +02:00
|
|
|
break;
|
2019-02-03 12:48:43 +01:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( status && v.isValid() )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::Animator;
|
|
|
|
status->aspect = aspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2017-08-22 19:47:06 +02:00
|
|
|
const QVariant& QskSkinnable::storedHint(
|
2017-07-21 18:21:34 +02:00
|
|
|
QskAspect::Aspect aspect, QskSkinHintStatus* status ) const
|
|
|
|
{
|
2020-03-19 14:10:50 +01:00
|
|
|
const auto skin = effectiveSkin();
|
|
|
|
|
|
|
|
// clearing all state bits not being handled from the skin
|
|
|
|
aspect.clearState( ~skin->stateMask() );
|
2020-08-09 11:50:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAspect::Aspect resolvedAspect;
|
2017-08-22 20:50:55 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
const auto& localTable = m_data->hintTable;
|
|
|
|
if ( localTable.hasHints() )
|
|
|
|
{
|
|
|
|
QskAspect::Aspect a = aspect;
|
|
|
|
|
|
|
|
if ( !localTable.hasStates() )
|
|
|
|
{
|
|
|
|
// we don't need to clear the state bits stepwise
|
|
|
|
a.clearStates();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( const QVariant* value = localTable.resolvedHint( a, &resolvedAspect ) )
|
|
|
|
{
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::Skinnable;
|
|
|
|
status->aspect = resolvedAspect;
|
|
|
|
}
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// next we try the hints from the skin
|
|
|
|
|
2020-03-19 14:10:50 +01:00
|
|
|
const auto& skinTable = skin->hintTable();
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( skinTable.hasHints() )
|
|
|
|
{
|
|
|
|
QskAspect::Aspect a = aspect;
|
|
|
|
|
|
|
|
const QVariant* value = skinTable.resolvedHint( a, &resolvedAspect );
|
|
|
|
if ( value )
|
|
|
|
{
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::Skin;
|
|
|
|
status->aspect = resolvedAspect;
|
2018-03-22 11:22:13 +01:00
|
|
|
}
|
2017-10-17 17:34:00 +02:00
|
|
|
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( aspect.subControl() != QskAspect::Control )
|
|
|
|
{
|
2019-12-14 13:34:30 +01:00
|
|
|
// trying to resolve something from the skin default settings
|
2017-10-17 17:34:00 +02:00
|
|
|
|
|
|
|
aspect.setSubControl( QskAspect::Control );
|
|
|
|
aspect.clearStates();
|
|
|
|
|
|
|
|
value = skinTable.resolvedHint( aspect, &resolvedAspect );
|
|
|
|
if ( value )
|
|
|
|
{
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::Skin;
|
|
|
|
status->aspect = resolvedAspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( status )
|
|
|
|
{
|
|
|
|
status->source = QskSkinHintStatus::NoSource;
|
|
|
|
status->aspect = QskAspect::Aspect();
|
|
|
|
}
|
|
|
|
|
|
|
|
static QVariant hintInvalid;
|
|
|
|
return hintInvalid;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskAspect::State QskSkinnable::skinState() const
|
|
|
|
{
|
|
|
|
return m_data->skinState;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* QskSkinnable::skinStateAsPrintable() const
|
|
|
|
{
|
|
|
|
return skinStateAsPrintable( skinState() );
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* QskSkinnable::skinStateAsPrintable( QskAspect::State state ) const
|
|
|
|
{
|
|
|
|
QString tmp;
|
|
|
|
|
|
|
|
QDebug debug( &tmp );
|
|
|
|
qskDebugState( debug, metaObject(), state );
|
|
|
|
|
|
|
|
// we should find a better way
|
2018-08-03 08:15:28 +02:00
|
|
|
static QByteArray bytes[ 10 ];
|
2017-07-21 18:21:34 +02:00
|
|
|
static int counter = 0;
|
|
|
|
|
|
|
|
counter = ( counter + 1 ) % 10;
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
bytes[ counter ] = tmp.toUtf8();
|
|
|
|
return bytes[ counter ].constData();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2017-10-20 13:09:30 +02:00
|
|
|
static inline QMarginsF qskEffectivePadding( const QskSkinnable* skinnable,
|
2017-10-17 17:34:00 +02:00
|
|
|
QskAspect::Aspect aspect, const QSizeF& size, bool inner )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
using namespace QskAspect;
|
2017-10-20 13:09:30 +02:00
|
|
|
using namespace Qt;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-20 13:09:30 +02:00
|
|
|
const auto shape = skinnable->boxShapeHint( aspect | Shape ).toAbsolute( size );
|
|
|
|
const auto borderMetrics = skinnable->boxBorderMetricsHint( aspect | Border );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-20 13:09:30 +02:00
|
|
|
const qreal left = qMax( shape.radius( TopLeftCorner ).width(),
|
|
|
|
shape.radius( BottomLeftCorner ).width() );
|
|
|
|
|
|
|
|
const qreal top = qMax( shape.radius( TopLeftCorner ).height(),
|
|
|
|
shape.radius( TopRightCorner ).height() );
|
|
|
|
|
|
|
|
const qreal right = qMax( shape.radius( TopRightCorner ).width(),
|
|
|
|
shape.radius( BottomRightCorner ).width() );
|
|
|
|
|
|
|
|
const qreal bottom = qMax( shape.radius( Qt::BottomLeftCorner ).height(),
|
|
|
|
shape.radius( Qt::BottomRightCorner ).height() );
|
|
|
|
|
|
|
|
QMarginsF padding( left, top, right, bottom );
|
|
|
|
|
|
|
|
// half of the border goes to the inner side
|
|
|
|
const auto borderMargins = borderMetrics.toAbsolute( size ).widths() * 0.5;
|
|
|
|
|
|
|
|
if ( inner )
|
|
|
|
{
|
|
|
|
padding -= borderMargins;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// not correct, but to get things started. TODO ...
|
|
|
|
padding += borderMargins;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sin 45° ceiled : 0.70710678;
|
|
|
|
padding *= 1.0 - 0.70710678;
|
|
|
|
|
|
|
|
const QMarginsF paddingHint = skinnable->marginsHint( aspect | Padding );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
return QMarginsF(
|
2017-10-20 13:09:30 +02:00
|
|
|
qMax( padding.left(), paddingHint.left() ),
|
|
|
|
qMax( padding.top(), paddingHint.top() ),
|
|
|
|
qMax( padding.right(), paddingHint.right() ),
|
|
|
|
qMax( padding.bottom(), paddingHint.bottom() )
|
2020-08-09 11:50:34 +02:00
|
|
|
);
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-18 19:41:46 +02:00
|
|
|
QMarginsF QskSkinnable::innerPadding(
|
|
|
|
QskAspect::Aspect aspect, const QSizeF& outerBoxSize ) const
|
|
|
|
{
|
|
|
|
return qskEffectivePadding( this, aspect, outerBoxSize, true );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QSizeF QskSkinnable::innerBoxSize(
|
|
|
|
QskAspect::Aspect aspect, const QSizeF& outerBoxSize ) const
|
|
|
|
{
|
2017-10-20 13:09:30 +02:00
|
|
|
const QMarginsF m = qskEffectivePadding( this, aspect, outerBoxSize, true );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
return QSizeF( outerBoxSize.width() - m.left() - m.right(),
|
|
|
|
outerBoxSize.height() - m.top() - m.bottom() );
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskSkinnable::innerBox(
|
|
|
|
QskAspect::Aspect aspect, const QRectF& outerBox ) const
|
|
|
|
{
|
2017-10-20 13:09:30 +02:00
|
|
|
const QMarginsF m = qskEffectivePadding( this, aspect, outerBox.size(), true );
|
2017-07-21 18:21:34 +02:00
|
|
|
return outerBox.marginsRemoved( m );
|
|
|
|
}
|
|
|
|
|
|
|
|
QSizeF QskSkinnable::outerBoxSize(
|
|
|
|
QskAspect::Aspect aspect, const QSizeF& innerBoxSize ) const
|
|
|
|
{
|
2017-10-20 13:09:30 +02:00
|
|
|
const QMarginsF m = qskEffectivePadding( this, aspect, innerBoxSize, false );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
return QSizeF( innerBoxSize.width() + m.left() + m.right(),
|
|
|
|
innerBoxSize.height() + m.top() + m.bottom() );
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskSkinnable::outerBox(
|
|
|
|
QskAspect::Aspect aspect, const QRectF& innerBox ) const
|
|
|
|
{
|
2020-08-09 10:45:48 +02:00
|
|
|
const auto m = qskEffectivePadding( this, aspect, innerBox.size(), false );
|
2017-07-21 18:21:34 +02:00
|
|
|
return innerBox.marginsAdded( m );
|
|
|
|
}
|
|
|
|
|
2018-10-10 08:55:03 +02:00
|
|
|
bool QskSkinnable::isTransitionAccepted( QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
Q_UNUSED( aspect )
|
|
|
|
|
|
|
|
/*
|
|
|
|
Usually we only need smooth transitions, when state changes
|
|
|
|
happen while the skinnable is visible. There are few exceptions
|
|
|
|
like QskPopup::Closed, that is used to slide/fade in.
|
|
|
|
*/
|
|
|
|
if ( auto control = owningControl() )
|
|
|
|
return control->isInitiallyPainted();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskSkinnable::startTransition( QskAspect::Aspect aspect,
|
|
|
|
QskAnimationHint animationHint, QVariant from, QVariant to )
|
|
|
|
{
|
|
|
|
if ( animationHint.duration <= 0 || ( from == to ) )
|
|
|
|
return;
|
|
|
|
|
2020-08-09 10:45:48 +02:00
|
|
|
auto control = this->owningControl();
|
2018-10-10 08:55:03 +02:00
|
|
|
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
return;
|
|
|
|
|
2018-12-20 16:40:06 +01:00
|
|
|
/*
|
|
|
|
We might be invalid for one of the values, when an aspect
|
|
|
|
has not been defined for all states ( f.e. metrics are expected
|
|
|
|
to fallback to 0.0 ). In this case we create a default one.
|
|
|
|
*/
|
2017-09-05 12:48:58 +02:00
|
|
|
|
|
|
|
if ( !from.isValid() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-12-20 16:40:06 +01:00
|
|
|
from = QVariant( to.userType(), nullptr );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2017-09-05 12:48:58 +02:00
|
|
|
else if ( !to.isValid() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-12-20 16:40:06 +01:00
|
|
|
to = QVariant( from.userType(), nullptr );
|
2017-09-05 12:48:58 +02:00
|
|
|
}
|
2018-12-20 16:40:06 +01:00
|
|
|
else if ( from.userType() != to.userType() )
|
2017-09-05 12:48:58 +02:00
|
|
|
{
|
|
|
|
return;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
const auto skin = effectiveSkin();
|
|
|
|
|
|
|
|
from.setValue( skin->graphicFilter( from.toInt() ) );
|
|
|
|
to.setValue( skin->graphicFilter( to.toInt() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
aspect.clearStates();
|
|
|
|
aspect.setAnimator( false );
|
2017-10-17 17:34:00 +02:00
|
|
|
aspect.setPlacement( effectivePlacement() );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
#if DEBUG_ANIMATOR
|
2018-02-06 14:58:24 +01:00
|
|
|
qDebug() << aspect << animationHint.duration;
|
2017-10-17 17:34:00 +02:00
|
|
|
#endif
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
auto animator = m_data->animators.animator( aspect );
|
|
|
|
if ( animator && animator->isRunning() )
|
|
|
|
from = animator->currentValue();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
m_data->animators.start( control, aspect, animationHint, from, to );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-12-22 16:11:24 +01:00
|
|
|
void QskSkinnable::setSkinStateFlag( QskAspect::State stateFlag, bool on )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
const auto newState = on
|
2018-12-22 16:11:24 +01:00
|
|
|
? ( m_data->skinState | stateFlag )
|
|
|
|
: ( m_data->skinState & ~stateFlag );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-12-22 16:11:24 +01:00
|
|
|
setSkinState( newState );
|
|
|
|
}
|
|
|
|
|
2020-07-27 07:26:38 +02:00
|
|
|
void QskSkinnable::setSkinState( QskAspect::State newState, bool animated )
|
2018-12-22 16:11:24 +01:00
|
|
|
{
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( m_data->skinState == newState )
|
|
|
|
return;
|
|
|
|
|
2020-03-19 14:10:50 +01:00
|
|
|
auto control = owningControl();
|
2017-09-05 12:48:58 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
#if DEBUG_STATE
|
2018-03-22 11:22:13 +01:00
|
|
|
qDebug() << control->className() << ":"
|
2017-09-05 12:48:58 +02:00
|
|
|
<< skinStateAsPrintable( m_data->skinState ) << "->"
|
|
|
|
<< skinStateAsPrintable( newState );
|
|
|
|
#endif
|
|
|
|
|
2020-03-19 14:10:50 +01:00
|
|
|
const auto skin = effectiveSkin();
|
|
|
|
|
|
|
|
if ( skin )
|
|
|
|
{
|
|
|
|
const auto mask = skin->stateMask();
|
|
|
|
if ( ( newState & mask ) == ( m_data->skinState & mask ) )
|
|
|
|
{
|
|
|
|
// the modified bits are not handled by the skin
|
|
|
|
|
|
|
|
m_data->skinState = newState;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-27 07:26:38 +02:00
|
|
|
if ( control->window() && animated && isTransitionAccepted( QskAspect::Aspect() ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
const auto placement = effectivePlacement();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-30 12:06:19 +01:00
|
|
|
const auto subControls = control->subControls();
|
|
|
|
for ( const auto subControl : subControls )
|
2017-10-17 17:34:00 +02:00
|
|
|
{
|
|
|
|
using namespace QskAspect;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
Aspect aspect = subControl | placement;
|
2017-08-22 20:50:55 +02:00
|
|
|
|
2020-03-19 14:10:50 +01:00
|
|
|
const auto& skinTable = skin->hintTable();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
for ( int i = 0; i <= LastType; i++ )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
const auto type = static_cast< Type >( i );
|
|
|
|
|
2018-02-06 14:58:24 +01:00
|
|
|
const auto hint = effectiveAnimation( type, subControl, newState );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
if ( hint.duration > 0 )
|
2017-08-22 20:50:55 +02:00
|
|
|
{
|
2017-10-17 17:34:00 +02:00
|
|
|
/*
|
|
|
|
Starting an animator for all primitives,
|
|
|
|
that differ between the states
|
|
|
|
*/
|
2017-12-07 17:04:05 +01:00
|
|
|
for ( uint primitive = 0; primitive <= LastPrimitive; primitive++ )
|
2017-10-17 17:34:00 +02:00
|
|
|
{
|
|
|
|
aspect.setPrimitive( type, primitive );
|
|
|
|
|
|
|
|
Aspect a1 = aspect | m_data->skinState;
|
|
|
|
Aspect a2 = aspect | newState;
|
|
|
|
|
|
|
|
bool doTransition = true;
|
|
|
|
|
|
|
|
if ( !m_data->hintTable.hasStates() )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The hints are found by stripping the state bits one by
|
|
|
|
one until a lookup into the hint table is successful.
|
|
|
|
So for deciding whether two aspects lead to the same hint
|
|
|
|
we can stop as soon as the aspects have the same state bits.
|
|
|
|
This way we can reduce the number of lookups significantly
|
|
|
|
for skinnables with many state bits.
|
2018-03-22 11:22:13 +01:00
|
|
|
|
2017-10-17 17:34:00 +02:00
|
|
|
*/
|
|
|
|
doTransition = !qskCompareResolvedStates( a1, a2, skinTable );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( doTransition )
|
|
|
|
{
|
|
|
|
startTransition( aspect, hint,
|
|
|
|
storedHint( a1 ), storedHint( a2 ) );
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->skinState = newState;
|
2018-07-02 08:08:08 +02:00
|
|
|
|
|
|
|
if ( control->flags() & QQuickItem::ItemHasContents )
|
|
|
|
control->update();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskSkin* QskSkinnable::effectiveSkin() const
|
|
|
|
{
|
2020-03-19 14:10:50 +01:00
|
|
|
QskSkin* skin = nullptr;
|
|
|
|
|
|
|
|
if ( m_data->skinlet )
|
|
|
|
skin = m_data->skinlet->skin();
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
return skin ? skin : qskSetup->skin();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::updateNode( QSGNode* parentNode )
|
|
|
|
{
|
|
|
|
effectiveSkinlet()->updateNode( this, parentNode );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskAspect::Subcontrol QskSkinnable::effectiveSubcontrol(
|
|
|
|
QskAspect::Subcontrol subControl ) const
|
|
|
|
{
|
|
|
|
// derived classes might want to redirect a sub-control
|
|
|
|
return subControl;
|
|
|
|
}
|
|
|
|
|
|
|
|
QskControl* QskSkinnable::controlCast()
|
|
|
|
{
|
|
|
|
return qskIsControl( this )
|
|
|
|
? static_cast< QskControl* >( this ) : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QskControl* QskSkinnable::controlCast() const
|
|
|
|
{
|
|
|
|
return qskIsControl( this )
|
|
|
|
? static_cast< const QskControl* >( this ) : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::debug( QDebug debug, QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
qskDebugAspect( debug, metaObject(), aspect );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::debug( QDebug debug, QskAspect::State state ) const
|
|
|
|
{
|
|
|
|
qskDebugState( debug, metaObject(), state );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::debug( QskAspect::Aspect aspect ) const
|
|
|
|
{
|
|
|
|
qskDebugAspect( qDebug(), metaObject(), aspect );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSkinnable::debug( QskAspect::State state ) const
|
|
|
|
{
|
|
|
|
qskDebugState( qDebug(), metaObject(), state );
|
|
|
|
}
|
2020-09-28 09:04:25 +02:00
|
|
|
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
|
|
|
|
|
|
#include <qdebug.h>
|
|
|
|
|
|
|
|
QDebug operator<<( QDebug debug, const QskSkinHintStatus& status )
|
|
|
|
{
|
|
|
|
QDebugStateSaver saver( debug );
|
|
|
|
debug.nospace();
|
|
|
|
|
|
|
|
switch( status.source )
|
|
|
|
{
|
|
|
|
case QskSkinHintStatus::Skinnable:
|
|
|
|
debug << "Skinnable";
|
|
|
|
break;
|
|
|
|
case QskSkinHintStatus::Skin:
|
|
|
|
debug << "Skin";
|
|
|
|
break;
|
|
|
|
case QskSkinHintStatus::Animator:
|
|
|
|
debug << "Animator";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
debug << "None";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug << ": " << status.aspect;
|
|
|
|
|
|
|
|
return debug;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|