QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSkinHintTable.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSkinHintTable.h"
7#include "QskAnimationHint.h"
8
9#include <limits>
10
11const QVariant QskSkinHintTable::invalidHint;
12
13inline const QVariant* qskResolvedHint( QskAspect aspect,
14 const QHash< QskAspect, QVariant >& hints, QskAspect* resolvedAspect )
15{
16 auto a = aspect;
17
18 Q_FOREVER
19 {
20 auto it = hints.constFind( aspect );
21 if ( it != hints.constEnd() )
22 {
23 if ( resolvedAspect )
24 *resolvedAspect = aspect;
25
26 return &it.value();
27 }
28
29#if 1
30 /*
31 We intend to remove the obscure mechanism of resolving a hint
32 by dropping the state bits ony by one in the future. Instead we
33 will have methods in QskSkinHintTableEditor, that allow
34 to set combinations of states in one call.
35 */
36 if ( const auto topState = aspect.topState() )
37 {
38 aspect.clearState( topState );
39 continue;
40 }
41#else
42 if ( aspect.hasState() )
43 {
44 aspect.clearStates();
45 continue;
46 }
47#endif
48
49 if ( aspect.variation() )
50 {
51 // clear the variation bits and restart
52 aspect = a;
54
55 continue;
56 }
57
58 if ( aspect.section() != QskAspect::Body )
59 {
60 // try to resolve from QskAspect::Body
61
62 a.setSection( QskAspect::Body );
63 aspect = a;
64
65 continue;
66 }
67
68 return nullptr;
69 }
70}
71
72QskSkinHintTable::QskSkinHintTable()
73{
74}
75
76QskSkinHintTable::QskSkinHintTable( const QskSkinHintTable& other )
77 : m_animatorCount( other.m_animatorCount )
78 , m_states( other.m_states )
79{
80 if ( other.m_hints )
81 {
82 /*
83 A previous implementation was using STL containers - however:
84
85 according to https://tessil.github.io/2016/08/29/benchmark-hopscotch-map.html
86 QHash does slightly faster lookups than std::unordered_map in the category
87 "Random full reads: execution time (integers)", that is the most relevant one
88 in our use case.
89
90 Considering, that the "copy on write" strategy of QHash makes the implementation
91 of copy/assignment operators much easier ( needed in QskSkinTransition ) we prefer
92 using the Qt container over the STL counterparts.
93 */
94 m_hints = new QHash< QskAspect, QVariant >( *other.m_hints );
95 }
96}
97
98QskSkinHintTable::~QskSkinHintTable()
99{
100 delete m_hints;
101}
102
103QskSkinHintTable& QskSkinHintTable::operator=( const QskSkinHintTable& other )
104{
105 m_animatorCount = ( other.m_animatorCount );
106 m_states = other.m_states;
107
108 delete m_hints;
109 m_hints = nullptr;
110
111 if ( other.m_hints )
112 m_hints = new QHash< QskAspect, QVariant >( *other.m_hints );
113
114 return *this;
115}
116
117const QHash< QskAspect, QVariant >& QskSkinHintTable::hints() const
118{
119 if ( m_hints )
120 return *m_hints;
121
122 static QHash< QskAspect, QVariant > dummyHints;
123 return dummyHints;
124}
125
126#define QSK_ASSERT_COUNTER( x ) Q_ASSERT( x < std::numeric_limits< decltype( x ) >::max() )
127
128bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint )
129{
130 if ( m_hints == nullptr )
131 m_hints = new QHash< QskAspect, QVariant >();
132
133 auto it = m_hints->find( aspect );
134 if ( it == m_hints->end() )
135 {
136 m_hints->insert( aspect, skinHint );
137
138 if ( aspect.isAnimator() )
139 {
140 m_animatorCount++;
141 QSK_ASSERT_COUNTER( m_animatorCount );
142 }
143
144 m_states |= aspect.states();
145
146 return true;
147 }
148
149 if ( it.value() != skinHint )
150 {
151 it.value() = skinHint;
152 return true;
153 }
154
155 return false;
156}
157
158#undef QSK_ASSERT_COUNTER
159
160bool QskSkinHintTable::removeHint( QskAspect aspect )
161{
162 if ( m_hints == nullptr )
163 return false;
164
165 const bool erased = m_hints->remove( aspect );
166
167 if ( erased )
168 {
169 if ( aspect.isAnimator() )
170 m_animatorCount--;
171
172 // how to clear m_states ? TODO ...
173
174 if ( m_hints->empty() )
175 {
176 delete m_hints;
177 m_hints = nullptr;
178 }
179 }
180
181 return erased;
182}
183
184QVariant QskSkinHintTable::takeHint( QskAspect aspect )
185{
186 if ( m_hints )
187 {
188 auto it = m_hints->find( aspect );
189 if ( it != m_hints->end() )
190 {
191 const auto value = it.value();
192 m_hints->erase( it );
193
194 if ( aspect.isAnimator() )
195 m_animatorCount--;
196
197 // how to clear m_states ? TODO ...
198
199 if ( m_hints->empty() )
200 {
201 delete m_hints;
202 m_hints = nullptr;
203 }
204
205 return value;
206 }
207 }
208
209 return QVariant();
210}
211
212void QskSkinHintTable::clear()
213{
214 delete m_hints;
215 m_hints = nullptr;
216
217 m_animatorCount = 0;
218 m_states = QskAspect::NoState;
219}
220
221const QVariant* QskSkinHintTable::resolvedHint(
222 QskAspect aspect, QskAspect* resolvedAspect ) const
223{
224 if ( m_hints != nullptr )
225 return qskResolvedHint( aspect & m_states, *m_hints, resolvedAspect );
226
227 return nullptr;
228}
229
230QskAspect QskSkinHintTable::resolvedAspect( QskAspect aspect ) const
231{
232 QskAspect a;
233
234 if ( m_hints != nullptr )
235 qskResolvedHint( aspect & m_states, *m_hints, &a );
236
237 return a;
238}
239
240QskAspect QskSkinHintTable::resolvedAnimator(
241 QskAspect aspect, QskAnimationHint& hint ) const
242{
243 if ( m_hints && m_animatorCount > 0 )
244 {
245 aspect &= m_states;
246
247 Q_FOREVER
248 {
249 auto it = m_hints->find( aspect );
250 if ( it != m_hints->cend() )
251 {
252 hint = it.value().value< QskAnimationHint >();
253 return aspect;
254 }
255
256 if ( const auto topState = aspect.topState() )
257 aspect.clearState( topState );
258 else
259 break;
260 }
261 }
262
263 return QskAspect();
264}
265
266QskAnimationHint QskSkinHintTable::animation( QskAspect aspect ) const
267{
268 aspect.setAnimator( true );
269 return hint< QskAnimationHint >( aspect );
270}
271
272bool QskSkinHintTable::setAnimation(
273 QskAspect aspect, QskAnimationHint animation )
274{
275 aspect.setAnimator( true );
276 return setHint( aspect, animation );
277}
278
279bool QskSkinHintTable::isResolutionMatching(
280 QskAspect aspect1, QskAspect aspect2 ) const
281{
282 // remove states we do not have early
283 aspect1 &= m_states;
284 aspect2 &= m_states;
285
286 if ( aspect1 == aspect2 )
287 return true;
288
289 if ( aspect1.trunk() != aspect2.trunk() )
290 return false;
291
292 auto a1 = aspect1;
293 auto a2 = aspect2;
294
295 Q_FOREVER
296 {
297 const auto state1 = aspect1.topState();
298 const auto state2 = aspect2.topState();
299
300 if ( state1 > state2 )
301 {
302 if ( hasHint( aspect1 ) )
303 return false;
304
305 aspect1.clearState( state1 );
306 continue;
307 }
308
309 if ( state2 > state1 )
310 {
311 if ( hasHint( aspect2 ) )
312 return false;
313
314 aspect2.clearState( state2 );
315 continue;
316 }
317
318 if ( aspect1 == aspect2 )
319 {
320 if ( hasHint( aspect1 ) )
321 return true;
322
323 if ( state1 == 0 )
324 {
325 if ( aspect1.variation() == QskAspect::NoVariation )
326 {
327 if ( aspect1.section() == QskAspect::Body )
328 return true;
329
330 // clear the section bits and restart with the initial state
331
332 a1.setSection( QskAspect::Body );
333 a2.setSection( QskAspect::Body );
334
335 aspect1 = a1;
336 aspect2 = a2;
337
338 }
339 else
340 {
341 // clear the variation bits and restart with the initial state
342 aspect1 = a1;
344
345 aspect2 = a2;
347 }
348
349 continue;
350 }
351 }
352 else
353 {
354 if ( hasHint( aspect1 ) || hasHint( aspect2 ) )
355 return false;
356 }
357
358 aspect1.clearState( state1 );
359 aspect2.clearState( state2 );
360 }
361}
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
State topState() const noexcept
void clearStates(States=AllStates) noexcept
Definition QskAspect.h:492
constexpr bool isAnimator() const noexcept
Definition QskAspect.h:407
@ NoVariation
Definition QskAspect.h:83
constexpr quint64 value() const noexcept
Definition QskAspect.h:402
constexpr QskAspect trunk() const noexcept
Definition QskAspect.h:396
constexpr Variation variation() const noexcept
Definition QskAspect.h:525
void setAnimator(bool on) noexcept
Definition QskAspect.h:412
constexpr States states() const noexcept
Definition QskAspect.h:467
void setVariation(Variation) noexcept
Definition QskAspect.h:530
void clearState(State) noexcept
Definition QskAspect.h:487