QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskStackBoxAnimator.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskStackBoxAnimator.h"
7#include "QskStackBox.h"
8#include "QskEvent.h"
9#include "QskQuick.h"
10#include "QskFunctions.h"
11
12QSK_QT_PRIVATE_BEGIN
13#include <private/qquickitem_p.h>
14QSK_QT_PRIVATE_END
15
16namespace
17{
18 class RotationTransform : public QQuickTransform
19 {
20 Q_OBJECT
21
22 public:
23 RotationTransform( Qt::Axis axis, qreal radians, QQuickItem* item )
24 : QQuickTransform( item )
25 , m_axis( axis )
26 , m_radians( radians )
27 {
28 prependToItem( item );
29 }
30
31 void setRadians( qreal radians )
32 {
33 if ( m_radians != radians )
34 {
35 m_radians = radians;
36 update();
37 }
38 }
39
40 void applyTo( QMatrix4x4* matrix) const override
41 {
42 if ( const auto item = qobject_cast< QQuickItem* >( parent() ) )
43 {
44 const auto dx = 0.5 * item->width();
45 const auto dy = 0.5 * item->height();
46
47 QTransform transform;
48 transform.translate( dx, dy );
49 transform.rotateRadians( m_radians, m_axis );
50 transform.translate( -dx, -dy );
51
52 *matrix *= transform;
53 }
54 }
55
56 private:
57 const Qt::Axis m_axis;
58 qreal m_radians;
59 };
60
61 class QuickTransform final : public QQuickTransform
62 {
63 Q_OBJECT
64
65 public:
66 QuickTransform( QQuickItem* item )
67 : QQuickTransform( item )
68 {
69 prependToItem( item );
70 }
71
72 void setTransform( const QTransform& transform )
73 {
74 if ( transform != m_transform )
75 {
76 m_transform = transform;
77 update();
78 }
79 }
80
81 void applyTo( QMatrix4x4* matrix ) const override
82 {
83 if ( const auto item = qobject_cast< QQuickItem* >( parent() ) )
84 *matrix *= m_transform;
85 }
86
87 private:
88 QTransform m_transform;
89 };
90
91 template< typename Transform >
92 Transform* qskFindTransform( const QQuickItem* item )
93 {
94 const auto& transforms = QQuickItemPrivate::get( item )->transforms;
95 for ( const auto& t : transforms )
96 {
97 if ( auto transform = qobject_cast< Transform* >( t ) )
98 return transform;
99 }
100
101 return nullptr;
102 }
103}
104
105QskStackBoxAnimator::QskStackBoxAnimator( QskStackBox* parent )
106 : QObject( parent )
107 , m_startIndex( -1 )
108 , m_endIndex( -1 )
109{
110}
111
112QskStackBoxAnimator::~QskStackBoxAnimator()
113{
114}
115
116void QskStackBoxAnimator::setStartIndex( int index )
117{
118 m_transientIndex = m_startIndex = index;
119}
120
121void QskStackBoxAnimator::setEndIndex( int index )
122{
123 m_endIndex = index;
124}
125
126int QskStackBoxAnimator::startIndex() const
127{
128 return m_startIndex;
129}
130
131int QskStackBoxAnimator::endIndex() const
132{
133 return m_endIndex;
134}
135
136QskStackBox* QskStackBoxAnimator::stackBox() const
137{
138 return static_cast< QskStackBox* >( parent() );
139}
140
141QQuickItem* QskStackBoxAnimator::itemAt( int index ) const
142{
143 return stackBox()->itemAtIndex(
144 ( index == 0 ) ? m_startIndex : m_endIndex );
145}
146
147qreal QskStackBoxAnimator::transientIndex() const
148{
149 return m_transientIndex;
150}
151
152void QskStackBoxAnimator::advance( qreal progress )
153{
154 qreal transientIndex;
155
156 if ( qFuzzyIsNull( progress ) )
157 {
158 transientIndex = m_startIndex;
159 }
160 else if ( qFuzzyCompare( progress, 1.0 ) )
161 {
162 transientIndex = m_endIndex;
163 }
164 else
165 {
166 const auto box = stackBox();
167
168 auto startIndex = m_startIndex;
169 auto endIndex = m_endIndex;
170
171 if ( startIndex == 0 )
172 {
173 if ( endIndex == box->itemCount() - 1 )
174 startIndex += box->itemCount();
175 }
176 else if ( startIndex == box->itemCount() - 1 )
177 {
178 if ( endIndex == 0 )
179 endIndex += box->itemCount();
180 }
181
182 transientIndex = startIndex + ( endIndex - startIndex ) * progress;
183 }
184
185 if ( !qskFuzzyCompare( m_transientIndex, transientIndex ) )
186 {
187 m_transientIndex = transientIndex;
188 advanceIndex( progress );
189
190 Q_EMIT stackBox()->transientIndexChanged( m_transientIndex );
191 }
192}
193
194QskStackBoxAnimator1::QskStackBoxAnimator1( QskStackBox* parent )
195 : QskStackBoxAnimator( parent )
196 , m_direction( Qsk::LeftToRight )
197 , m_isDirty( false )
198 , m_hasClip( false )
199{
200 // catching geometryChanges to know about resizing
201}
202
203QskStackBoxAnimator1::~QskStackBoxAnimator1()
204{
205}
206
207void QskStackBoxAnimator1::setDirection( Qsk::Direction direction )
208{
209 if ( m_direction != direction )
210 {
211 stop();
212 m_direction = direction;
213 }
214}
215
216Qsk::Direction QskStackBoxAnimator1::direction() const
217{
218 return m_direction;
219}
220
221void QskStackBoxAnimator1::setup()
222{
223 auto stackBox = this->stackBox();
224
225 m_hasClip = stackBox->clip();
226 if ( !m_hasClip )
227 stackBox->setClip( true );
228
229 stackBox->installEventFilter( this );
230 m_isDirty = true;
231}
232
233void QskStackBoxAnimator1::advanceIndex( qreal value )
234{
235 auto stackBox = this->stackBox();
236
237 const bool isHorizontal = ( m_direction == Qsk::LeftToRight )
238 || ( m_direction == Qsk::RightToLeft );
239
240 for ( int i = 0; i < 2; i++ )
241 {
242 if ( auto item = itemAt( i ) )
243 {
244 QRectF rect = qskItemGeometry( item );
245
246 if ( m_isDirty )
247 {
248 const int index = ( i == 0 ) ? startIndex() : endIndex();
249 rect = stackBox->geometryForItemAt( index );
250
251 m_itemOffset[ i ] = isHorizontal ? rect.x() : rect.y();
252 }
253
254 qreal x, y;
255
256 if ( isHorizontal )
257 {
258 qreal off = stackBox->width() * ( value - i );
259 if ( m_direction == Qsk::LeftToRight )
260 off = -off;
261
262 x = m_itemOffset[ i ] + off;
263 y = rect.y();
264 }
265 else
266 {
267 qreal off = stackBox->height() * ( value - i );
268 if ( m_direction == Qsk::BottomToTop )
269 off = -off;
270
271 x = rect.x();
272 y = m_itemOffset[ i ] + off;
273 }
274
275 qskSetItemGeometry( item, x, y, rect.width(), rect.height() );
276
277 if ( !item->isVisible() )
278 item->setVisible( true );
279 }
280 }
281
282 m_isDirty = false;
283}
284
285void QskStackBoxAnimator1::done()
286{
287 for ( int i = 0; i < 2; i++ )
288 {
289 if ( auto item = itemAt( i ) )
290 {
291 item->removeEventFilter( this );
292 item->setVisible( i == 1 );
293 }
294 }
295
296 if ( !m_hasClip )
297 stackBox()->setClip( false );
298}
299
300bool QskStackBoxAnimator1::eventFilter( QObject* object, QEvent* event )
301{
302 if ( !m_isDirty && object == stackBox() )
303 {
304 switch( static_cast< int >( event->type() ) )
305 {
306 case QskEvent::GeometryChange:
307 case QskEvent::ContentsRectChange:
308 case QskEvent::LayoutRequest:
309 {
310 m_isDirty = true;
311 break;
312 }
313 }
314 }
315
316 return QObject::eventFilter( object, event );
317}
318
319QskStackBoxAnimator2::QskStackBoxAnimator2( QskStackBox* parent )
320 : QskStackBoxAnimator( parent )
321 , m_orientation( Qt::Horizontal )
322 , m_inverted( false )
323{
324}
325
326QskStackBoxAnimator2::~QskStackBoxAnimator2()
327{
328}
329
330void QskStackBoxAnimator2::setOrientation( Qt::Orientation orientation )
331{
332 if ( m_orientation != orientation )
333 {
334 stop();
335 m_orientation = orientation;
336 }
337}
338
339Qt::Orientation QskStackBoxAnimator2::orientation() const
340{
341 return m_orientation;
342}
343
344void QskStackBoxAnimator2::setInverted( bool on )
345{
346 if ( m_inverted != on )
347 {
348 stop();
349 m_inverted = on;
350 }
351}
352
353bool QskStackBoxAnimator2::isInverted() const
354{
355 return m_inverted;
356}
357
358void QskStackBoxAnimator2::setup()
359{
360 const auto axis = ( m_orientation == Qt::Horizontal )
361 ? Qt::YAxis : Qt::XAxis;
362
363 if ( auto item = itemAt( 0 ) )
364 ( void ) new RotationTransform( axis, 0.0, item );
365
366 if ( auto item = itemAt( 1 ) )
367 ( void ) new RotationTransform( axis, M_PI_2, item );
368}
369
370void QskStackBoxAnimator2::advanceIndex( qreal value )
371{
372 auto radians = value * M_PI;
373
374 if ( radians < M_PI_2 && radians > -M_PI_2 )
375 {
376 if ( auto item = itemAt( 0 ) )
377 {
378 if ( !m_inverted )
379 radians = 2 * M_PI - radians;
380
381 auto rotation = qskFindTransform< RotationTransform >( item );
382 rotation->setRadians( radians );
383
384 item->setVisible( true );
385 }
386
387 if ( auto item = itemAt( 1 ) )
388 {
389 item->setVisible( false );
390 }
391 }
392 else
393 {
394 if ( auto item = itemAt( 0 ) )
395 {
396 item->setVisible( false );
397 }
398
399 if ( auto item = itemAt( 1 ) )
400 {
401 radians = radians - M_PI;
402 if ( !m_inverted )
403 radians = 2 * M_PI - radians;
404
405 auto rotation = qskFindTransform< RotationTransform >( item );
406 rotation->setRadians( radians );
407
408 item->setVisible( true );
409 }
410 }
411}
412
413void QskStackBoxAnimator2::done()
414{
415 for ( int i = 0; i < 2; i++ )
416 {
417 if ( auto item = itemAt( i ) )
418 {
419 delete qskFindTransform< RotationTransform >( item );
420 item->setVisible( i == 1 );
421 }
422 }
423}
424
425QskStackBoxAnimator3::QskStackBoxAnimator3( QskStackBox* parent )
426 : QskStackBoxAnimator( parent )
427{
428}
429
430QskStackBoxAnimator3::~QskStackBoxAnimator3()
431{
432}
433
434void QskStackBoxAnimator3::setup()
435{
436 if ( auto item = itemAt( 1 ) )
437 {
438 item->setOpacity( 0.0 );
439 item->setVisible( true );
440 }
441}
442
443void QskStackBoxAnimator3::advanceIndex( qreal value )
444{
445 if ( auto item1 = itemAt( 0 ) )
446 item1->setOpacity( 1.0 - value );
447
448 if ( auto item2 = itemAt( 1 ) )
449 item2->setOpacity( value );
450}
451
452void QskStackBoxAnimator3::done()
453{
454 for ( int i = 0; i < 2; i++ )
455 {
456 if ( auto item = itemAt( i ) )
457 {
458 item->setOpacity( 1.0 );
459 item->setVisible( i == 1 ); // not here !!
460 }
461 }
462}
463
464QskStackBoxAnimator4::QskStackBoxAnimator4( QskStackBox* parent )
465 : QskStackBoxAnimator( parent )
466 , m_orientation( Qt::Horizontal )
467 , m_inverted( false )
468{
469}
470
471QskStackBoxAnimator4::~QskStackBoxAnimator4()
472{
473}
474
475void QskStackBoxAnimator4::setOrientation( Qt::Orientation orientation )
476{
477 if ( m_orientation != orientation )
478 {
479 stop();
480 m_orientation = orientation;
481 }
482}
483
484Qt::Orientation QskStackBoxAnimator4::orientation() const
485{
486 return m_orientation;
487}
488
489void QskStackBoxAnimator4::setInverted( bool on )
490{
491 if ( m_inverted != on )
492 {
493 stop();
494 m_inverted = on;
495 }
496}
497
498bool QskStackBoxAnimator4::isInverted() const
499{
500 return m_inverted;
501}
502
503void QskStackBoxAnimator4::setup()
504{
505 if ( auto item = itemAt( 0 ) )
506 {
507 ( void ) new QuickTransform( item );
508 item->setVisible( true );
509 }
510
511 if ( auto item = itemAt( 1 ) )
512 {
513 ( void ) new QuickTransform( item );
514 item->setVisible( true );
515 }
516}
517
518void QskStackBoxAnimator4::advanceIndex( qreal value )
519{
520 auto item1 = itemAt( 1 );
521 auto item2 = itemAt( 0 );
522
523 if ( isInverted() )
524 std::swap( item1, item2 );
525
526 const qreal posEdge = isInverted() ? value : 1.0 - value;
527
528 if ( item1 )
529 {
530 const auto transform = transformation( item1, false, posEdge );
531
532 auto rotation = qskFindTransform< QuickTransform >( item1 );
533 rotation->setTransform( transform );
534 }
535
536 if ( item2 )
537 {
538 const auto transform = transformation( item2, true, posEdge );
539
540 auto rotation = qskFindTransform< QuickTransform >( item2 );
541 rotation->setTransform( transform );
542 }
543}
544
545QTransform QskStackBoxAnimator4::transformation(
546 const QQuickItem* item, bool first, qreal posEdge ) const
547{
548 /*
549 first: left or top item
550 posEdge: position of the edge in the range of [0-1]
551 ( left->right, top->bottom ).
552 */
553
554 const qreal radians = M_PI_2 * ( 1.0 - posEdge );
555
556 QTransform transform;
557
558 if( orientation() == Qt::Horizontal )
559 {
560 const qreal dx = posEdge * ( item->x() + item->width() );
561 const qreal dy = 0.5 * item->height();
562
563 if ( first )
564 {
565 transform.translate( -item->x() + dx, dy );
566 transform.rotateRadians( radians, Qt::YAxis );
567 transform.translate( -item->width(), -dy );
568 }
569 else
570 {
571 transform.translate( dx, dy );
572 transform.rotateRadians( radians - M_PI_2, Qt::YAxis );
573 transform.translate( 0.0, -dy );
574 }
575 }
576 else
577 {
578 const qreal dx = 0.5 * item->width();
579 const qreal dy = posEdge * ( item->y() + item->height() );
580
581 if ( first )
582 {
583 transform.translate( dx, -item->y() + dy );
584 transform.rotateRadians( radians, Qt::XAxis );
585 transform.translate( -dx, -item->height() );
586 }
587 else
588 {
589 transform.translate( dx, dy );
590 transform.rotateRadians( radians - M_PI_2, Qt::XAxis );
591 transform.translate( -dx, 0.0 );
592 }
593 }
594
595 return transform;
596}
597
598void QskStackBoxAnimator4::done()
599{
600 for ( int i = 0; i < 2; i++ )
601 {
602 if ( auto item = itemAt( i ) )
603 {
604 delete qskFindTransform< QuickTransform >( item );
605 item->setVisible( i == 1 );
606 }
607 }
608}
609
610#include "moc_QskStackBoxAnimator.cpp"
611#include "QskStackBoxAnimator.moc"
Global definitions.
Direction
This enum type specifies a horizontal ot vertical direction.
@ LeftToRight
@ RightToLeft
@ BottomToTop