qskinny/src/controls/QskHintAnimator.cpp

327 lines
8.0 KiB
C++
Raw Normal View History

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 "QskHintAnimator.h"
#include "QskAnimationHint.h"
#include "QskControl.h"
#include "QskEvent.h"
2018-07-19 14:10:48 +02:00
#include <qobject.h>
#include <qthread.h>
2017-07-21 18:21:34 +02:00
2018-08-03 08:15:28 +02:00
#include <algorithm>
2017-07-21 18:21:34 +02:00
#include <map>
#include <vector>
#define ALIGN_VALUES 0
#if ALIGN_VALUES
static inline qreal qskAligned05( qreal value )
{
// aligned to 0.5
return qRound( 2.0 * value ) / 2.0;
}
static inline QSizeF qskAligned05( const QSizeF& size )
{
return QSizeF( qskAligned05( size.width() ), qskAligned05( size.height() ) );
}
static inline QskMargins qskAligned05( const QskMargins& margins )
{
const qreal left = qskAligned05( margins.left() );
const qreal top = qskAligned05( margins.top() );
const qreal right = qskAligned05( margins.right() );
const qreal bottom = qskAligned05( margins.bottom() );
return QskMargins( left, top, right, bottom );
}
static inline QVariant qskAligned05( const QVariant& value )
2017-07-21 18:21:34 +02:00
{
2018-08-03 08:15:28 +02:00
if ( value.canConvert< QskBoxBorderMetrics >() )
{
auto metrics = value.value< QskBoxBorderMetrics >();
if ( metrics.sizeMode() == Qt::AbsoluteSize )
{
metrics.setWidths( qskAligned05( metrics.widths() ) );
return QVariant::fromValue( metrics );
}
}
else if ( value.canConvert< QskBoxShapeMetrics >() )
2017-07-21 18:21:34 +02:00
{
auto metrics = value.value< QskBoxShapeMetrics >();
if ( metrics.sizeMode() == Qt::AbsoluteSize )
2017-07-21 18:21:34 +02:00
{
for ( int i = Qt::TopLeftCorner; i <= Qt::BottomRightCorner; i++ )
{
2018-07-05 13:02:25 +02:00
const auto corner = static_cast< Qt::Corner >( i );
metrics.setRadius( corner, qskAligned05( metrics.radius( corner ) ) );
}
2018-07-05 13:02:25 +02:00
return QVariant::fromValue( metrics );
2017-07-21 18:21:34 +02:00
}
}
else if ( value.canConvert< QskMargins >() )
{
const auto margins = value.value< QskMargins >();
return QVariant::fromValue( qskAligned05( margins ) );
}
2017-07-21 18:21:34 +02:00
return value;
}
#endif
2018-07-05 13:02:25 +02:00
static inline bool qskCheckReceiverThread( const QObject* receiver )
{
/*
QskInputPanelSkinlet changes the skin state, what leads to
sending events from the wrong thread. Until we have fixed it
let's block sending the event to avoid running into assertions
in QCoreApplication::sendEvent
*/
2018-07-05 13:02:25 +02:00
const QThread* thread = receiver->thread();
if ( thread == nullptr )
return true;
return ( thread == QThread::currentThread() );
2018-07-05 13:02:25 +02:00
}
2017-07-21 18:21:34 +02:00
QskHintAnimator::QskHintAnimator()
{
}
QskHintAnimator::~QskHintAnimator()
{
}
2020-12-21 16:06:58 +01:00
void QskHintAnimator::setAspect( QskAspect aspect )
2017-07-21 18:21:34 +02:00
{
m_aspect = aspect;
}
void QskHintAnimator::setUpdateFlags( QskAnimationHint::UpdateFlags flags )
{
m_updateFlags = flags;
}
2017-07-21 18:21:34 +02:00
void QskHintAnimator::setControl( QskControl* control )
{
m_control = control;
}
void QskHintAnimator::advance( qreal progress )
{
const QVariant oldValue = currentValue();
Inherited::advance( progress );
#if ALIGN_VALUES
setCurrentValue( qskAligned05( currentValue() ) );
#endif
2017-07-21 18:21:34 +02:00
if ( m_control && ( currentValue() != oldValue ) )
{
if ( m_updateFlags == QskAnimationHint::UpdateAuto )
2017-07-21 18:21:34 +02:00
{
if ( m_aspect.isMetric() )
{
m_control->resetImplicitSize();
if ( !m_control->childItems().isEmpty() )
m_control->polish();
}
m_control->update();
2017-07-21 18:21:34 +02:00
}
else
{
if ( m_updateFlags & QskAnimationHint::UpdateSizeHint )
m_control->resetImplicitSize();
2017-07-21 18:21:34 +02:00
if ( m_updateFlags & QskAnimationHint::UpdatePolish )
m_control->polish();
if ( m_updateFlags & QskAnimationHint::UpdateNode )
m_control->update();
}
2017-07-21 18:21:34 +02:00
}
}
namespace
{
class AnimatorGuard final : public QObject
{
Q_OBJECT
2018-08-03 08:15:28 +02:00
public:
2017-07-21 18:21:34 +02:00
AnimatorGuard()
{
QskAnimator::addCleanupHandler( this,
2018-07-05 13:02:25 +02:00
SLOT(cleanup()), Qt::QueuedConnection );
2017-07-21 18:21:34 +02:00
}
void registerTable( QskHintAnimatorTable* table )
{
auto it = std::lower_bound( m_tables.begin(), m_tables.end(), table );
if ( it == m_tables.end() || *it != table )
m_tables.insert( it, table );
}
void unregisterTable( QskHintAnimatorTable* table )
{
auto it = std::lower_bound( m_tables.begin(), m_tables.end(), table );
if ( it != m_tables.end() && *it == table )
2017-07-21 18:21:34 +02:00
m_tables.erase( it );
}
2018-08-03 08:15:28 +02:00
private Q_SLOTS:
2017-07-21 18:21:34 +02:00
void cleanup()
{
2018-08-03 08:15:28 +02:00
for ( auto it = m_tables.begin(); it != m_tables.end(); )
2017-07-21 18:21:34 +02:00
{
if ( ( *it )->cleanup() )
it = m_tables.erase( it );
else
++it;
}
}
2018-08-03 08:15:28 +02:00
private:
2017-07-21 18:21:34 +02:00
// a vector as iteration is more important than insertion
std::vector< QskHintAnimatorTable* > m_tables;
};
Q_GLOBAL_STATIC( AnimatorGuard, qskAnimatorGuard )
}
class QskHintAnimatorTable::PrivateData
{
2018-08-03 08:15:28 +02:00
public:
2017-07-21 18:21:34 +02:00
// we won't have many entries, so we prefer less memory over
// using a hash table
2020-12-21 16:06:58 +01:00
std::map< QskAspect, QskHintAnimator > map;
2017-07-21 18:21:34 +02:00
};
2018-08-03 08:15:28 +02:00
QskHintAnimatorTable::QskHintAnimatorTable()
: m_data( nullptr )
2017-07-21 18:21:34 +02:00
{
}
QskHintAnimatorTable::~QskHintAnimatorTable()
{
qskAnimatorGuard->unregisterTable( this );
if ( m_data )
delete m_data;
}
void QskHintAnimatorTable::start( QskControl* control,
2020-12-21 16:06:58 +01:00
QskAspect aspect, QskAnimationHint animationHint,
2017-07-21 18:21:34 +02:00
const QVariant& from, const QVariant& to )
{
if ( m_data == nullptr )
{
m_data = new PrivateData();
qskAnimatorGuard->registerTable( this );
}
2018-12-20 14:38:01 +01:00
auto& animator = m_data->map[ aspect ];
2017-07-21 18:21:34 +02:00
animator.setAspect( aspect );
animator.setStartValue( from );
animator.setEndValue( to );
animator.setDuration( animationHint.duration );
animator.setEasingCurve( animationHint.type );
animator.setUpdateFlags( animationHint.updateFlags );
2017-07-21 18:21:34 +02:00
animator.setControl( control );
animator.setWindow( control->window() );
animator.start();
if ( qskCheckReceiverThread( control ) )
{
QskAnimatorEvent event( aspect, QskAnimatorEvent::Started );
QCoreApplication::sendEvent( control, &event );
}
2017-07-21 18:21:34 +02:00
}
2020-12-21 16:06:58 +01:00
const QskHintAnimator* QskHintAnimatorTable::animator( QskAspect aspect ) const
2017-07-21 18:21:34 +02:00
{
if ( m_data == nullptr )
return nullptr;
auto it = m_data->map.find( aspect );
if ( it == m_data->map.end() )
return nullptr;
return &( it->second );
}
2020-12-21 16:06:58 +01:00
QVariant QskHintAnimatorTable::currentValue( QskAspect aspect ) const
2017-07-21 18:21:34 +02:00
{
if ( m_data )
{
const auto it = m_data->map.find( aspect );
if ( it != m_data->map.cend() )
{
const auto& animator = it->second;
if ( animator.isRunning() )
return animator.currentValue();
}
}
return QVariant();
}
bool QskHintAnimatorTable::cleanup()
{
if ( m_data == nullptr )
return true;
2018-08-03 08:15:28 +02:00
for ( auto it = m_data->map.begin(); it != m_data->map.end(); )
2017-07-21 18:21:34 +02:00
{
// remove all terminated animators
if ( !it->second.isRunning() )
{
auto control = it->second.control();
auto aspect = it->first;
it = m_data->map.erase( it );
if ( control )
{
if ( qskCheckReceiverThread( control ) )
{
2020-03-13 18:11:31 +01:00
auto event = new QskAnimatorEvent( aspect, QskAnimatorEvent::Terminated );
QCoreApplication::postEvent( control, event );
}
2017-07-21 18:21:34 +02:00
}
}
else
{
++it;
}
}
if ( m_data->map.empty() )
{
delete m_data;
m_data = nullptr;
return true;
}
return false;
}
#include "QskHintAnimator.moc"