working on fade/slide in/out effects for popups

This commit is contained in:
Uwe Rathmann 2018-10-10 08:55:03 +02:00
parent 1ddb7aee6b
commit f1ac0cc13d
7 changed files with 205 additions and 42 deletions

View File

@ -15,6 +15,7 @@ QSK_QT_PRIVATE_BEGIN
QSK_QT_PRIVATE_END QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskPopup, Overlay ) QSK_SUBCONTROL( QskPopup, Overlay )
QSK_STATE( QskPopup, Closed, QskAspect::FirstSystemState << 1 )
static void qskSetFocus( QQuickItem* item, bool on ) static void qskSetFocus( QQuickItem* item, bool on )
{ {
@ -101,7 +102,7 @@ class QskPopup::PrivateData
PrivateData() PrivateData()
: flags( 0 ) : flags( 0 )
, isModal( false ) , isModal( false )
, isOpen( false ) , hasFaderEffect( true )
, autoGrabFocus( true ) , autoGrabFocus( true )
, handoverFocus( true ) , handoverFocus( true )
{ {
@ -110,10 +111,11 @@ class QskPopup::PrivateData
InputGrabber* inputGrabber = nullptr; InputGrabber* inputGrabber = nullptr;
uint priority = 0; uint priority = 0;
QskAspect::Aspect faderAspect;
int flags : 4; int flags : 4;
bool isModal : 1; bool isModal : 1;
bool isOpen : 1; bool hasFaderEffect : 1;
const bool autoGrabFocus : 1; const bool autoGrabFocus : 1;
const bool handoverFocus : 1; const bool handoverFocus : 1;
@ -123,6 +125,10 @@ QskPopup::QskPopup( QQuickItem* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData() ) , m_data( new PrivateData() )
{ {
// initially the popup is closed and invisible
Inherited::setVisible( false );
setSkinStateFlag( QskPopup::Closed );
// we need to stop event propagation // we need to stop event propagation
setAcceptedMouseButtons( Qt::AllButtons ); setAcceptedMouseButtons( Qt::AllButtons );
setWheelEnabled( true ); setWheelEnabled( true );
@ -134,6 +140,11 @@ QskPopup::QskPopup( QQuickItem* parent )
setTabFence( true ); setTabFence( true );
setFocusPolicy( Qt::StrongFocus ); setFocusPolicy( Qt::StrongFocus );
/*
sending a notification to the window, that can
be used to register popups for some sort of
popup/window management
*/
qskSendPopupEvent( window(), this, true ); qskSendPopupEvent( window(), this, true );
} }
@ -144,33 +155,61 @@ QskPopup::~QskPopup()
void QskPopup::open() void QskPopup::open()
{ {
setFading( true ); setOpen( true );
} }
void QskPopup::close() void QskPopup::close()
{ {
const bool wasOpen = m_data->isOpen; setOpen( false );
m_data->isOpen = false;
setFading( false );
if ( wasOpen )
{
Q_EMIT closed();
if ( testPopupFlag( DeleteOnClose ) )
deleteLater();
}
} }
void QskPopup::setFading( bool on ) void QskPopup::setOpen( bool on )
{ {
setVisible( on ); if ( on == isOpen() )
return;
if ( on )
QskControl::setVisible( on );
setSkinStateFlag( QskPopup::Closed, !on );
Q_EMIT openChanged( on );
if ( on )
Q_EMIT opened();
else
Q_EMIT closed();
if ( isFading() )
{
Q_EMIT fadingChanged( true );
}
else
{
if ( !on )
{
Inherited::setVisible( false );
if ( testPopupFlag( QskPopup::DeleteOnClose ) )
deleteLater();
}
}
} }
bool QskPopup::isOpen() const bool QskPopup::isOpen() const
{ {
return m_data->isOpen; return !( skinState() & QskPopup::Closed );
}
bool QskPopup::isFading() const
{
if ( m_data->faderAspect.value() == 0 )
return false;
QskSkinHintStatus status;
(void) effectiveHint( m_data->faderAspect, &status );
return status.source == QskSkinHintStatus::Animator;
} }
QRectF QskPopup::overlayRect() const QRectF QskPopup::overlayRect() const
@ -210,6 +249,55 @@ void QskPopup::updateInputGrabber()
} }
} }
QskAspect::Aspect QskPopup::faderAspect() const
{
return m_data->faderAspect;
}
void QskPopup::setFaderAspect( QskAspect::Aspect aspect )
{
auto faderAspect = aspect;
faderAspect.clearStates(); // animated values are always stateless
if ( faderAspect == m_data->faderAspect )
return;
if ( isFading() )
{
// stop the running animation TODO ...
}
m_data->faderAspect = faderAspect;
}
bool QskPopup::isTransitionAccepted( QskAspect::Aspect aspect ) const
{
if ( isVisible() && m_data->hasFaderEffect )
{
if ( ( aspect.value() == 0 ) )
{
/*
QskAspect::Aspect() is an early check that is used
to find out if more detailed checking of aspects
is necessary.
*/
return true;
}
if ( aspect == m_data->faderAspect )
return true;
if ( aspect.type() == QskAspect::Color )
{
if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) )
return true;
}
}
return Inherited::isTransitionAccepted( aspect );
}
void QskPopup::setPriority( uint priority ) void QskPopup::setPriority( uint priority )
{ {
if ( m_data->priority != priority ) if ( m_data->priority != priority )
@ -240,6 +328,20 @@ bool QskPopup::isModal() const
return m_data->isModal; return m_data->isModal;
} }
void QskPopup::setFaderEffect( bool on )
{
if ( on != m_data->hasFaderEffect )
{
m_data->hasFaderEffect = on;
Q_EMIT faderEffectChanged( on );
}
}
bool QskPopup::hasFaderEffect() const
{
return m_data->hasFaderEffect;
}
void QskPopup::setPopupFlags( PopupFlags flags ) void QskPopup::setPopupFlags( PopupFlags flags )
{ {
m_data->flags = flags; m_data->flags = flags;
@ -323,7 +425,7 @@ bool QskPopup::event( QEvent* event )
{ {
bool ok = Inherited::event( event ); bool ok = Inherited::event( event );
switch ( event->type() ) switch ( static_cast< int >( event->type() ) )
{ {
case QEvent::KeyPress: case QEvent::KeyPress:
case QEvent::KeyRelease: case QEvent::KeyRelease:
@ -344,6 +446,26 @@ bool QskPopup::event( QEvent* event )
break; break;
} }
case QskEvent::Animator:
{
const auto animtorEvent = static_cast< QskAnimatorEvent* >( event );
if ( ( animtorEvent->state() == QskAnimatorEvent::Terminated )
&& ( animtorEvent->aspect() == m_data->faderAspect ) )
{
if ( !isOpen() )
{
Inherited::setVisible( false );
if ( testPopupFlag( QskPopup::DeleteOnClose ) )
deleteLater();
}
Q_EMIT fadingChanged( false );
}
break;
}
default: default:
{ {
/* /*
@ -418,8 +540,6 @@ QQuickItem* QskPopup::focusSuccessor() const
void QskPopup::aboutToShow() void QskPopup::aboutToShow()
{ {
m_data->isOpen = true;
if ( m_data->autoGrabFocus ) if ( m_data->autoGrabFocus )
{ {
// What to do, when we are hidden below another popup ?? // What to do, when we are hidden below another popup ??
@ -436,16 +556,15 @@ void QskPopup::itemChange( QQuickItem::ItemChange change,
if ( change == QQuickItem::ItemVisibleHasChanged ) if ( change == QQuickItem::ItemVisibleHasChanged )
{ {
if ( !value.boolValue ) updateInputGrabber();
{
updateInputGrabber();
if ( value.boolValue )
{
polish(); // so that aboutToShow is called. TODO ...
}
else
{
grabFocus( false ); grabFocus( false );
if ( m_data->isOpen )
{
if ( testPopupFlag( CloseOnHide ) )
close();
}
} }
} }
else if ( change == QQuickItem::ItemParentHasChanged ) else if ( change == QQuickItem::ItemParentHasChanged )

View File

@ -12,20 +12,25 @@ class QSK_EXPORT QskPopup : public QskControl
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY( bool open READ isOpen WRITE setOpen NOTIFY openChanged )
Q_PROPERTY( bool modal READ isModal WRITE setModal NOTIFY modalChanged ) Q_PROPERTY( bool modal READ isModal WRITE setModal NOTIFY modalChanged )
Q_PROPERTY( bool overlay READ hasOverlay WRITE setOverlay NOTIFY overlayChanged ) Q_PROPERTY( bool overlay READ hasOverlay WRITE setOverlay NOTIFY overlayChanged )
Q_PROPERTY( bool faderEffect READ hasFaderEffect
WRITE setFaderEffect NOTIFY faderEffectChanged )
Q_PROPERTY( uint priority READ priority WRITE setPriority NOTIFY priorityChanged ) Q_PROPERTY( uint priority READ priority WRITE setPriority NOTIFY priorityChanged )
using Inherited = QskControl; using Inherited = QskControl;
public: public:
QSK_SUBCONTROLS( Overlay ) QSK_SUBCONTROLS( Overlay )
QSK_STATES( Closed )
enum PopupFlag enum PopupFlag
{ {
CloseOnHide = 1 << 0, DeleteOnClose = 1 << 0,
DeleteOnClose = 1 << 1, CloseOnPressOutside = 1 << 1
CloseOnPressOutside = 1 << 2
}; };
Q_ENUM( PopupFlag ) Q_ENUM( PopupFlag )
@ -46,27 +51,39 @@ class QSK_EXPORT QskPopup : public QskControl
void setOverlay( bool on = true ); void setOverlay( bool on = true );
bool hasOverlay() const; bool hasOverlay() const;
// allows for stacking according to priorities // allows for stacking orders based on priorities
void setPriority( uint ); void setPriority( uint );
uint priority() const; uint priority() const;
void setFaderEffect( bool );
bool hasFaderEffect() const;
QskAspect::Aspect faderAspect() const;
void setFaderAspect( QskAspect::Aspect );
virtual QRectF overlayRect() const; virtual QRectF overlayRect() const;
bool isOpen() const; bool isOpen() const;
bool isFading() const;
public Q_SLOTS: public Q_SLOTS:
void open(); void open();
void close(); void close();
void setOpen( bool );
Q_SIGNALS: Q_SIGNALS:
void opened();
void closed(); void closed();
void openChanged( bool );
void fadingChanged( bool );
void modalChanged( bool ); void modalChanged( bool );
void overlayChanged( bool ); void overlayChanged( bool );
void priorityChanged( uint ); void priorityChanged( uint );
void faderEffectChanged( bool );
protected: protected:
void aboutToShow() override; void aboutToShow() override;
virtual void setFading( bool on );
bool event( QEvent* ) override; bool event( QEvent* ) override;
void focusInEvent( QFocusEvent* ) override; void focusInEvent( QFocusEvent* ) override;
@ -77,14 +94,21 @@ class QSK_EXPORT QskPopup : public QskControl
const QQuickItem::ItemChangeData& ) override; const QQuickItem::ItemChangeData& ) override;
virtual QQuickItem* focusSuccessor() const; virtual QQuickItem* focusSuccessor() const;
bool isTransitionAccepted( QskAspect::Aspect ) const override;
void grabFocus( bool ); void grabFocus( bool );
private: private:
void show() = delete;
void hide() = delete;
void setVisible( bool ) = delete;
void updateInputGrabber(); void updateInputGrabber();
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS( QskPopup::PopupFlags )
#endif #endif

View File

@ -692,6 +692,21 @@ QRectF QskSkinnable::outerBox(
return innerBox.marginsAdded( m ); return innerBox.marginsAdded( m );
} }
bool QskSkinnable::isTransitionAccepted( QskAspect::Aspect aspect ) const
{
Q_UNUSED( aspect )
/*
Usually we only need smooth transitions, when state changes
happen while the skinnable is visible. There are few exceptions
like QskPopup::Closed, that is used to slide/fade in.
*/
if ( auto control = owningControl() )
return control->isInitiallyPainted();
return false;
}
void QskSkinnable::startTransition( QskAspect::Aspect aspect, void QskSkinnable::startTransition( QskAspect::Aspect aspect,
QskAnimationHint animationHint, QVariant from, QVariant to ) QskAnimationHint animationHint, QVariant from, QVariant to )
{ {
@ -699,7 +714,7 @@ void QskSkinnable::startTransition( QskAspect::Aspect aspect,
return; return;
QskControl* control = this->owningControl(); QskControl* control = this->owningControl();
if ( control->window() == nullptr || !control->isInitiallyPainted() ) if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
return; return;
// We might be invalid for one of the values, when an aspect // We might be invalid for one of the values, when an aspect
@ -759,7 +774,7 @@ void QskSkinnable::setSkinStateFlag( QskAspect::State state, bool on )
<< skinStateAsPrintable( newState ); << skinStateAsPrintable( newState );
#endif #endif
if ( control->window() && control->isInitiallyPainted() ) if ( control->window() && isTransitionAccepted( QskAspect::Aspect() ) )
{ {
const auto placement = effectivePlacement(); const auto placement = effectivePlacement();

View File

@ -156,6 +156,7 @@ class QSK_EXPORT QskSkinnable
protected: protected:
void setSkinStateFlag( QskAspect::State, bool = true ); void setSkinStateFlag( QskAspect::State, bool = true );
virtual void updateNode( QSGNode* ); virtual void updateNode( QSGNode* );
virtual bool isTransitionAccepted( QskAspect::Aspect ) const;
QskSkinHintTable& hintTable(); QskSkinHintTable& hintTable();
const QskSkinHintTable& hintTable() const; const QskSkinHintTable& hintTable() const;

View File

@ -59,7 +59,7 @@ QskDialog::DialogCode QskDialogSubWindow::exec()
mouseGrabber->ungrabMouse(); mouseGrabber->ungrabMouse();
} }
show(); open();
QEventLoop eventLoop; QEventLoop eventLoop;

View File

@ -34,9 +34,14 @@ void QskInputWindow::setSubWindow( QskInputSubWindow* subWindow )
if ( m_subWindow ) if ( m_subWindow )
{ {
#if 1
// we shoudn't have a subwindow here TODO ...
m_subWindow->setModal( false ); m_subWindow->setModal( false );
m_subWindow->setDecorated( false ); m_subWindow->setDecorated( false );
m_subWindow->setOverlay( false ); m_subWindow->setOverlay( false );
m_subWindow->setFaderEffect( false );
m_subWindow->open();
#endif
addItem( m_subWindow ); addItem( m_subWindow );

View File

@ -397,11 +397,10 @@ void QskInputContext::showPanel( const QQuickItem* item )
popup->setParentItem( item->window()->contentItem() ); popup->setParentItem( item->window()->contentItem() );
popup->setParent( this ); popup->setParent( this );
#if 1
popup->setVisible( true );
#endif
channel->popup = popup; channel->popup = popup;
popup->open();
} }
panel->attachInputItem( const_cast< QQuickItem* >( item ) ); panel->attachInputItem( const_cast< QQuickItem* >( item ) );