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_SUBCONTROL( QskPopup, Overlay )
QSK_STATE( QskPopup, Closed, QskAspect::FirstSystemState << 1 )
static void qskSetFocus( QQuickItem* item, bool on )
{
@ -101,7 +102,7 @@ class QskPopup::PrivateData
PrivateData()
: flags( 0 )
, isModal( false )
, isOpen( false )
, hasFaderEffect( true )
, autoGrabFocus( true )
, handoverFocus( true )
{
@ -110,10 +111,11 @@ class QskPopup::PrivateData
InputGrabber* inputGrabber = nullptr;
uint priority = 0;
QskAspect::Aspect faderAspect;
int flags : 4;
bool isModal : 1;
bool isOpen : 1;
int flags : 4;
bool isModal : 1;
bool hasFaderEffect : 1;
const bool autoGrabFocus : 1;
const bool handoverFocus : 1;
@ -123,6 +125,10 @@ QskPopup::QskPopup( QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData() )
{
// initially the popup is closed and invisible
Inherited::setVisible( false );
setSkinStateFlag( QskPopup::Closed );
// we need to stop event propagation
setAcceptedMouseButtons( Qt::AllButtons );
setWheelEnabled( true );
@ -134,6 +140,11 @@ QskPopup::QskPopup( QQuickItem* parent )
setTabFence( true );
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 );
}
@ -144,33 +155,61 @@ QskPopup::~QskPopup()
void QskPopup::open()
{
setFading( true );
setOpen( true );
}
void QskPopup::close()
{
const bool wasOpen = m_data->isOpen;
m_data->isOpen = false;
setFading( false );
if ( wasOpen )
{
Q_EMIT closed();
if ( testPopupFlag( DeleteOnClose ) )
deleteLater();
}
setOpen( false );
}
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
{
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
@ -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 )
{
if ( m_data->priority != priority )
@ -240,6 +328,20 @@ bool QskPopup::isModal() const
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 )
{
m_data->flags = flags;
@ -323,7 +425,7 @@ bool QskPopup::event( QEvent* event )
{
bool ok = Inherited::event( event );
switch ( event->type() )
switch ( static_cast< int >( event->type() ) )
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
@ -344,6 +446,26 @@ bool QskPopup::event( QEvent* event )
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:
{
/*
@ -418,8 +540,6 @@ QQuickItem* QskPopup::focusSuccessor() const
void QskPopup::aboutToShow()
{
m_data->isOpen = true;
if ( m_data->autoGrabFocus )
{
// 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 ( !value.boolValue )
{
updateInputGrabber();
updateInputGrabber();
if ( value.boolValue )
{
polish(); // so that aboutToShow is called. TODO ...
}
else
{
grabFocus( false );
if ( m_data->isOpen )
{
if ( testPopupFlag( CloseOnHide ) )
close();
}
}
}
else if ( change == QQuickItem::ItemParentHasChanged )

View File

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

View File

@ -692,6 +692,21 @@ QRectF QskSkinnable::outerBox(
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,
QskAnimationHint animationHint, QVariant from, QVariant to )
{
@ -699,7 +714,7 @@ void QskSkinnable::startTransition( QskAspect::Aspect aspect,
return;
QskControl* control = this->owningControl();
if ( control->window() == nullptr || !control->isInitiallyPainted() )
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
return;
// 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 );
#endif
if ( control->window() && control->isInitiallyPainted() )
if ( control->window() && isTransitionAccepted( QskAspect::Aspect() ) )
{
const auto placement = effectivePlacement();

View File

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

View File

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

View File

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

View File

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