QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskVariantAnimator.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskVariantAnimator.h"
7#include "QskArcMetrics.h"
8#include "QskBoxBorderColors.h"
9#include "QskBoxBorderMetrics.h"
10#include "QskBoxShapeMetrics.h"
11#include "QskShadowMetrics.h"
12#include "QskStippleMetrics.h"
13#include "QskGraduationMetrics.h"
14#include "QskColorFilter.h"
15#include "QskGradient.h"
16#include "QskMargins.h"
17#include "QskIntervalF.h"
18#include "QskTextColors.h"
19
20// Even if we don't use the standard Qt animation system we
21// use its registry of interpolators: why adding our own ...
22
23#include <qvariantanimation.h>
24
25QSK_QT_PRIVATE_BEGIN
26
27#if QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 )
28 #ifndef emit
29 #define emit
30 #include <private/qabstractanimation_p.h>
31 #undef emit
32 #endif
33#endif
34
35#include <private/qvariantanimation_p.h>
36
37QSK_QT_PRIVATE_END
38
39#if 1
40static void qskRegisterInterpolator()
41{
42 qRegisterAnimationInterpolator< QskColorFilter >( QskColorFilter::interpolate );
43 qRegisterAnimationInterpolator< QskIntervalF >( QskIntervalF::interpolate );
44 qRegisterAnimationInterpolator< QskMargins >( QskMargins::interpolate );
45 qRegisterAnimationInterpolator< QskGradient >( QskGradient::interpolate );
46 qRegisterAnimationInterpolator< QskBoxShapeMetrics >( QskBoxShapeMetrics::interpolate );
47 qRegisterAnimationInterpolator< QskBoxBorderMetrics >( QskBoxBorderMetrics::interpolate );
48 qRegisterAnimationInterpolator< QskBoxBorderColors >( QskBoxBorderColors::interpolate );
49 qRegisterAnimationInterpolator< QskTextColors >( QskTextColors::interpolate );
50 qRegisterAnimationInterpolator< QskShadowMetrics >( QskShadowMetrics::interpolate );
51 qRegisterAnimationInterpolator< QskStippleMetrics >( QskStippleMetrics::interpolate );
52 qRegisterAnimationInterpolator< QskArcMetrics >( QskArcMetrics::interpolate );
53 qRegisterAnimationInterpolator< QskGraduationMetrics >( QskGraduationMetrics::interpolate );
54}
55
56Q_CONSTRUCTOR_FUNCTION( qskRegisterInterpolator )
57#endif
58
59#if defined( Q_CC_CLANG )
60 #if __has_feature( address_sanitizer )
61 #define QSK_DECL_INSANE __attribute__( ( no_sanitize( "undefined" ) ) )
62 #endif
63#endif
64
65#if !defined( QSK_DECL_INSANE )
66 #define QSK_DECL_INSANE
67#endif
68
69QSK_DECL_INSANE static inline QVariant qskInterpolate(
70 void ( *interpolator )(), const QVariant& from, const QVariant& to, qreal progress )
71{
72#if 1
73 /*
74 how to get rid of the reported runtime error from the clang sanitizer,
75 when calling F( const T&, ... ) as G( const void* ... ); TODO ...
76 */
77#endif
78
79 auto f = reinterpret_cast< QVariantAnimation::Interpolator >( interpolator );
80 return f( from.constData(), to.constData(), progress );
81}
82
83#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
84
85using QskMetaType = int;
86static inline QskMetaType qskMetaType( const QVariant& v ) { return v.userType(); }
87
88#else
89
90using QskMetaType = QMetaType;
91static inline QskMetaType qskMetaType( const QVariant& v ) { return v.metaType(); }
92
93#endif
94
95static inline QVariant qskDefaultVariant( QskMetaType type )
96{
97 return QVariant( type, nullptr );
98}
99
100static inline QVariant qskConvertedVariant( const QVariant& from, QskMetaType type )
101{
102 auto v = from;
103 v.convert( type );
104
105 return v;
106}
107
108QskVariantAnimator::QskVariantAnimator()
109 : m_interpolator( nullptr )
110{
111}
112
113QskVariantAnimator::~QskVariantAnimator()
114{
115}
116
117void QskVariantAnimator::setStartValue( const QVariant& value )
118{
119 stop();
120 m_startValue = value;
121}
122
123void QskVariantAnimator::setEndValue( const QVariant& value )
124{
125 stop();
126 m_endValue = value;
127}
128
129void QskVariantAnimator::setCurrentValue( const QVariant& value )
130{
131 m_currentValue = value;
132}
133
134bool QskVariantAnimator::convertValues( QVariant& v1, QVariant& v2 )
135{
136 if ( !v1.isValid() && !v2.isValid() )
137 return false;
138
139 const auto type1 = qskMetaType( v1 );
140 const auto type2 = qskMetaType( v2 );
141
142 if ( !v1.isValid() )
143 {
144 v1 = qskDefaultVariant( type2 );
145 return true;
146 }
147
148 if ( !v2.isValid() )
149 {
150 v2 = qskDefaultVariant( type1 );
151 return true;
152 }
153
154 if ( type1 != type2 )
155 {
156 if ( v1.canConvert( type2 ) )
157 {
158 v1.convert( type2 );
159 return true;
160 }
161
162 if ( v2.canConvert( type1 ) )
163 {
164 v2.convert( type1 );
165 return true;
166 }
167
168 return false;
169 }
170
171 return true;
172}
173
174void QskVariantAnimator::setup()
175{
176 m_interpolator = nullptr;
177
178 if ( convertValues( m_startValue, m_endValue ) )
179 {
180 if ( m_startValue != m_endValue )
181 {
182 const auto id = m_startValue.userType();
183
184 // all what has been registered by qRegisterAnimationInterpolator
185 m_interpolator = reinterpret_cast< void ( * )() >(
186 QVariantAnimationPrivate::getInterpolator( id ) );
187 }
188 }
189
190 m_currentValue = m_interpolator ? m_startValue : m_endValue;
191}
192
193void QskVariantAnimator::advance( qreal progress )
194{
195 if ( m_interpolator )
196 {
197 if ( qFuzzyCompare( progress, 1.0 ) )
198 progress = 1.0;
199
200 Q_ASSERT( qskMetaType( m_startValue ) == qskMetaType( m_endValue ) );
201
202 m_currentValue = qskInterpolate( m_interpolator,
203 m_startValue, m_endValue, progress );
204 }
205}
206
207void QskVariantAnimator::done()
208{
209 m_interpolator = nullptr;
210}
211
212bool QskVariantAnimator::maybeInterpolate(
213 const QVariant& value1, const QVariant& value2, bool acceptIdentity )
214{
215 if ( !value1.isValid() && !value2.isValid() )
216 return false;
217
218 if ( acceptIdentity )
219 {
220 if ( value1.isValid() && value2.isValid() )
221 {
222 const auto type1 = qskMetaType( value1 );
223 const auto type2 = qskMetaType( value2 );
224
225 if ( type1 != type2 )
226 return value1.canConvert( type2 ) || value2.canConvert( type1 );
227 }
228
229 return true;
230 }
231 else
232 {
233 const auto type1 = qskMetaType( value1 );
234 const auto type2 = qskMetaType( value2 );
235
236 if ( !value1.isValid() )
237 return value2 != qskDefaultVariant( type2 );
238
239 if ( !value2.isValid() )
240 return value1 != qskDefaultVariant( type1 );
241
242 if ( type1 != type2 )
243 {
244 if ( value1.canConvert( type2 ) )
245 return value2 != qskConvertedVariant( value1, type2 );
246
247 if ( value2.canConvert( type1 ) )
248 return value1 != qskConvertedVariant( value2, type1 );
249
250 return false;
251 }
252
253 return value1 != value2;
254 }
255}