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