diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index 6c102f3c..7b5cadf6 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -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 ) diff --git a/src/controls/QskPopup.h b/src/controls/QskPopup.h index 0a3995b7..bacd88a7 100644 --- a/src/controls/QskPopup.h +++ b/src/controls/QskPopup.h @@ -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 diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 76b87e2a..52ff8d3d 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -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(); diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index 2588817e..9d8800f2 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -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; diff --git a/src/dialogs/QskDialogSubWindow.cpp b/src/dialogs/QskDialogSubWindow.cpp index ae771ef6..f89dff15 100644 --- a/src/dialogs/QskDialogSubWindow.cpp +++ b/src/dialogs/QskDialogSubWindow.cpp @@ -59,7 +59,7 @@ QskDialog::DialogCode QskDialogSubWindow::exec() mouseGrabber->ungrabMouse(); } - show(); + open(); QEventLoop eventLoop; diff --git a/src/dialogs/QskInputWindow.cpp b/src/dialogs/QskInputWindow.cpp index 1f7d7260..e66a12f6 100644 --- a/src/dialogs/QskInputWindow.cpp +++ b/src/dialogs/QskInputWindow.cpp @@ -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 ); diff --git a/src/inputpanel/QskInputContext.cpp b/src/inputpanel/QskInputContext.cpp index 12a94327..f153af64 100644 --- a/src/inputpanel/QskInputContext.cpp +++ b/src/inputpanel/QskInputContext.cpp @@ -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 ) );