From e4b9b8bd1bb734c8dd051d9c45043adf458aa22c Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 24 Oct 2023 11:39:32 +0200 Subject: [PATCH] Squashed commit of the following: commit c6aec22cbdbed31955e70ea73fc6863d9369ba22 Author: Uwe Rathmann Date: Tue Oct 24 11:34:55 2023 +0200 wip commit 473946633e150129a3ba67ea321bda23fcd0f5a7 Author: Uwe Rathmann Date: Tue Oct 24 11:28:23 2023 +0200 wip commit 368f6edac32457d6a060c2f2e8a722c0d15bf35c Author: Uwe Rathmann Date: Tue Oct 24 11:26:46 2023 +0200 wip commit eccffafec006d471cc691f3d10143219ec263c63 Author: Uwe Rathmann Date: Tue Oct 24 11:22:01 2023 +0200 wip commit c5ae5025d4ed0ea7184fb8b4fa97df7f3ca54c0e Author: Uwe Rathmann Date: Tue Oct 24 10:53:09 2023 +0200 wip commit 174b1d946067c5b5c0bf4a9982d7b3f7f717d263 Author: Uwe Rathmann Date: Tue Oct 24 10:51:54 2023 +0200 wip commit 3e89f17ea971adc19c89cca0eb38dc3e973a090e Author: Uwe Rathmann Date: Tue Oct 24 10:15:17 2023 +0200 wip commit 499a13ab74501c24f1e0effe56dfb9cf9ff9bf63 Author: Uwe Rathmann Date: Tue Oct 24 10:03:55 2023 +0200 wip commit 935ffc6d23dcd20a2c70898f28063fcdffea0795 Merge: 82f44d41 0cf60b41 Author: Uwe Rathmann Date: Mon Oct 23 10:49:32 2023 +0200 Merge branch 'drawer' into Overlay commit 0cf60b414cee9a26fa5a53f05938e69ab39294d7 Merge: da76eb6d 2013338d Author: Uwe Rathmann Date: Mon Oct 23 10:49:13 2023 +0200 Merge branch 'features/drawer' into drawer commit 82f44d41c03f8cedab8b1bf3f1c164dfa65200cf Author: Uwe Rathmann Date: Sun Oct 22 10:44:50 2023 +0200 wip commit 3b254ff4551d457e1581b5dbb85fae2dcf55e2cc Author: Uwe Rathmann Date: Sun Oct 22 10:43:15 2023 +0200 wip commit 81e2bd8b35fb4a361e67f8a56870d99ea05ffa4d Author: Uwe Rathmann Date: Sun Oct 22 09:50:07 2023 +0200 wip commit 916f5ca888f675bdded0b97b763de02c8c6c12ff Author: Uwe Rathmann Date: Fri Oct 20 17:08:53 2023 +0200 wip commit b3e8fd4d7cf4eab710b7781bb1e9dc730548d51b Author: Uwe Rathmann Date: Fri Oct 20 17:03:12 2023 +0200 wip commit da76eb6df5353029856084e6306db32cb2712b6b Author: Uwe Rathmann Date: Fri Oct 20 16:05:19 2023 +0200 wip commit 72fbd0b6fad4b33e09643e5a2c67833f6e2c4abd Author: Uwe Rathmann Date: Fri Oct 20 15:21:14 2023 +0200 wip commit 7f899d67d6c8f7f98212030ac0e83e86581cefff Author: Uwe Rathmann Date: Fri Oct 20 15:00:42 2023 +0200 wip commit 72872cad2a0749afe9c901775c18459ac4cd956a Author: Uwe Rathmann Date: Fri Oct 20 13:49:05 2023 +0200 wip commit f16572e29a5fe8851479e2be6e04ff2d991e9ebd Author: Uwe Rathmann Date: Fri Oct 20 13:40:47 2023 +0200 wip commit 4300a2cf225554f93cb492062f89af51dcc5df64 Author: Uwe Rathmann Date: Fri Oct 20 13:34:37 2023 +0200 wip commit c85150bcb98934b144e4453f12fbe37c4900049f Author: Uwe Rathmann Date: Fri Oct 20 13:32:44 2023 +0200 wip commit 00d069d134319b0c3fb6d192cba32c31640e161f Author: Uwe Rathmann Date: Fri Oct 20 13:21:45 2023 +0200 wip commit 36b4a203a3d761d4a3a5e17e1ba2deb7f9b37740 Author: Uwe Rathmann Date: Fri Oct 20 12:52:52 2023 +0200 wip commit e27fa8af6ddc233862ec431e2c129299e24f4ee0 Author: Uwe Rathmann Date: Fri Oct 20 11:12:42 2023 +0200 wip commit 579a45149a92ffef72c569c79456a3a2c753e37c Author: Uwe Rathmann Date: Fri Oct 20 10:57:51 2023 +0200 QskSlideIn added --- playground/drawer/main.cpp | 4 - skins/fluent2/QskFluent2Skin.cpp | 19 +-- skins/material3/QskMaterial3Skin.cpp | 10 +- skins/squiek/QskSquiekSkin.cpp | 12 +- src/CMakeLists.txt | 2 + src/controls/QskDrawer.cpp | 239 +++------------------------ src/controls/QskDrawer.h | 22 +-- src/controls/QskMenu.cpp | 1 - src/controls/QskMenuSkinlet.cpp | 4 +- src/controls/QskPopup.cpp | 86 +++++----- src/controls/QskPopup.h | 7 +- src/controls/QskPopupSkinlet.cpp | 38 ++++- src/controls/QskSlideIn.cpp | 236 ++++++++++++++++++++++++++ src/controls/QskSlideIn.h | 44 +++++ 14 files changed, 411 insertions(+), 313 deletions(-) create mode 100644 src/controls/QskSlideIn.cpp create mode 100644 src/controls/QskSlideIn.h diff --git a/playground/drawer/main.cpp b/playground/drawer/main.cpp index f838c03d..3002310b 100644 --- a/playground/drawer/main.cpp +++ b/playground/drawer/main.cpp @@ -23,10 +23,6 @@ namespace Drawer( Qt::Edge edge, QQuickItem* parent ) : QskDrawer( parent ) { -#if 1 - setAnimationHint( transitionAspect(), 1000 ); -#endif - setEdge( edge ); auto content = new QskControl( this ); diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index e1e114ff..bd89bcfe 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -620,12 +620,6 @@ void Editor::setupDialogButtonBoxColors( void Editor::setupDrawerMetrics() { - using Q = QskDrawer; - using A = QskAspect; - -#if 1 - setAnimation( Q::Panel | A::Metric | A::Position, 200 ); -#endif } void Editor::setupDrawerColors( QskAspect::Section, const QskFluent2Theme& ) @@ -744,7 +738,6 @@ void Editor::setupListViewColors( void Editor::setupMenuMetrics() { using Q = QskMenu; - using A = QskAspect; setPadding( Q::Panel, { 4, 6, 4, 6 } ); setBoxBorderMetrics( Q::Panel, 1 ); @@ -758,14 +751,6 @@ void Editor::setupMenuMetrics() setStrutSize( Q::Icon, 12, 12 ); setPadding( Q::Icon, { 8, 8, 0, 8 } ); - -#if 1 - setPosition( Q::Panel, 1 ); - setPosition( Q::Panel | QskPopup::Closed, 0 ); - - // copied from Mat3 - what are the correct values for Fluent2 ??? - setAnimation( Q::Panel | A::Metric, 150 ); -#endif } void Editor::setupMenuColors( @@ -876,10 +861,14 @@ void Editor::setupPageIndicatorColors( void Editor::setupPopup( const QskFluent2Theme& theme ) { using Q = QskPopup; + using A = QskAspect; const auto& pal = theme.palette; + setHint( Q::Overlay | A::Style, true ); setGradient( Q::Overlay, pal.background.overlay.defaultColor ); + + setAnimation( Q::Popup, 200 ); } void Editor::setupProgressBarMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index f5780f04..b30ebc69 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -331,6 +331,8 @@ void Editor::setupPopup() setHint( Q::Overlay | A::Style, true ); setGradient( Q::Overlay, stateLayerColor( m_pal.outline, 0.8 ) ); + + setAnimation( Q::Popup, qskDuration ); } void Editor::setupMenu() @@ -378,10 +380,6 @@ void Editor::setupMenu() setColor( Q::Text, m_pal.onSurface ); setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); - setPosition( Q::Panel, 1 ); - setPosition( Q::Panel | QskPopup::Closed, 0 ); - - setAnimation( Q::Panel | A::Metric, 150 ); setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic ); } @@ -808,10 +806,6 @@ void Editor::setupDialogButtonBox() void Editor::setupDrawer() { - using Q = QskDrawer; - using A = QskAspect; - - setAnimation( Q::Panel | A::Metric | A::Position, qskDuration ); } void Editor::setupSlider() diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index f92ba16a..f7dac2e0 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -409,7 +409,9 @@ void Editor::setupPopup() using Q = QskPopup; setHint( Q::Overlay | A::Style, true ); - setGradient( Q::Overlay, QColor( 220, 220, 220, 150 ) ); + setGradient( Q::Overlay, qRgba( 220, 220, 220, 150 ) ); + + setAnimation( Q::Popup, 200 ); } void Editor::setupMenu() @@ -446,10 +448,6 @@ void Editor::setupMenu() setGraphicRole( Q::Icon | Q::Disabled, DisabledSymbol ); setGraphicRole( Q::Icon | Q::Selected, CursorSymbol ); - setPosition( Q::Panel, 1 ); - setPosition( Q::Panel | QskPopup::Closed, 0 ); - - setAnimation( Q::Panel | A::Metric, 150 ); setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic ); } @@ -761,10 +759,6 @@ void Editor::setupDialogButtonBox() void Editor::setupDrawer() { - using A = QskAspect; - using Q = QskDrawer; - - setAnimation( Q::Panel | A::Metric | A::Position, qskDuration ); } void Editor::setupTabButton() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ce13c74..ee05894d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -210,6 +210,7 @@ list(APPEND HEADERS controls/QskQuickItem.h controls/QskRadioBox.h controls/QskRadioBoxSkinlet.h + controls/QskSlideIn.h controls/QskScrollArea.h controls/QskScrollBox.h controls/QskScrollView.h @@ -316,6 +317,7 @@ list(APPEND SOURCES controls/QskScrollBox.cpp controls/QskScrollView.cpp controls/QskScrollViewSkinlet.cpp + controls/QskSlideIn.cpp controls/QskRadioBox.cpp controls/QskRadioBoxSkinlet.cpp controls/QskSegmentedBar.cpp diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 3b965e36..c699e73f 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -5,7 +5,6 @@ #include "QskDrawer.h" #include "QskAspect.h" -#include "QskAnimationHint.h" #include "QskQuick.h" #include "QskEvent.h" @@ -15,24 +14,13 @@ #include #include -QSK_QT_PRIVATE_BEGIN -#include -#include -QSK_QT_PRIVATE_END - -/* - Only used for the sliding in animation. Do we want to - introduce a specific panel as background ??? - */ -QSK_SUBCONTROL( QskDrawer, Panel ) - static inline qreal qskDefaultDragMargin() { // a skin hint ??? return QGuiApplication::styleHints()->startDragDistance(); } -static void qskCatchMouseEvents( QQuickItem* item ) +static inline void qskCatchMouseEvents( QQuickItem* item ) { #if 1 // manipulating other items - do we really want to do this ? @@ -63,13 +51,12 @@ static bool qskCheckDirection( Qt::Edge edge, const QskPanGesture* gesture ) return false; } -static void qskLayoutDrawer( const QRectF& rect, QskDrawer* drawer ) +static inline QRectF qskAlignedToEdge( + const QRectF& rect, const QSizeF& size, Qt::Edge edge ) { - const auto size = qskSizeConstraint( drawer, Qt::PreferredSize ); - QRectF r( 0.0, 0.0, size.width(), size.height() ); - switch( drawer->edge() ) + switch( edge ) { case Qt::LeftEdge: { @@ -99,91 +86,7 @@ static void qskLayoutDrawer( const QRectF& rect, QskDrawer* drawer ) } } - drawer->setGeometry( r ); -} - -static inline QRectF qskSlidingRect( - const QSizeF& size, Qt::Edge edge, qreal ratio ) -{ - auto x = 0.0; - auto y = 0.0; - - ratio = 1.0 - ratio; - - switch( edge ) - { - case Qt::LeftEdge: - x = -ratio * size.width(); - break; - - case Qt::RightEdge: - x = ratio * size.width(); - break; - - case Qt::TopEdge: - y = -ratio * size.height(); - break; - - case Qt::BottomEdge: - y = ratio * size.height(); - break; - } - - return QRectF( x, y, size.width(), size.height() ); -} - -namespace -{ - class GeometryListener final : public QQuickItemChangeListener - { - public: - GeometryListener( QQuickItem* item, QQuickItem* adjustedItem ) - : m_item( item ) - , m_adjustedItem( adjustedItem ) - { - adjust(); - setEnabled( true ); - } - - ~GeometryListener() - { - setEnabled( false ); - } - - private: - void itemGeometryChanged( QQuickItem*, - QQuickGeometryChange, const QRectF& ) override - { - adjust(); - } - - private: - void adjust() - { -#if 0 - const auto pos = m_adjustedItem->mapFromItem( m_item, QPointF() ); - qskSetItemGeometry( m_adjustedItem, - pos.x(), pos.y(), m_item->width(), m_item->height() ); -#else - qskLayoutDrawer( QRectF( QPointF(), m_item->size() ), - qobject_cast< QskDrawer* >( m_adjustedItem ) ); -#endif - } - - void setEnabled( bool on ) - { - const auto changeTypes = QQuickItemPrivate::Geometry; - - auto d = QQuickItemPrivate::get( m_item ); - if ( on ) - d->addItemChangeListener( this, changeTypes ); - else - d->removeItemChangeListener( this, changeTypes ); - } - - QQuickItem* m_item; - QQuickItem* m_adjustedItem; - }; + return r; } namespace @@ -243,18 +146,8 @@ namespace class QskDrawer::PrivateData { public: - inline void resetListener( QskDrawer* drawer ) - { - delete listener; - listener = nullptr; - - if ( drawer->parentItem() && drawer->isVisible() ) - listener = new GeometryListener( drawer->parentItem(), drawer ); - } - Qt::Edge edge = Qt::LeftEdge; GestureRecognizer* gestureRecognizer = nullptr; - GeometryListener* listener = nullptr; qreal dragMargin = qskDefaultDragMargin(); }; @@ -269,25 +162,11 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) setAutoLayoutChildren( true ); setInteractive( true ); - - setPopupFlag( PopupFlag::CloseOnPressOutside, true ); - setTransitionAspect( Panel | QskAspect::Position | QskAspect::Metric ); - - /* - The drawer wants to be on top of the parent - not being - layouted into its layoutRect(). So we opt out and do - the layout updates manually. - */ - setPlacementPolicy( QskPlacementPolicy::Ignore ); - m_data->resetListener( this ); - - connect( this, &QskPopup::openChanged, this, &QskDrawer::setSliding ); - connect( this, &QskPopup::transitioningChanged, this, &QskDrawer::setIntermediate ); + setAdjustingToParentGeometry( true ); } QskDrawer::~QskDrawer() { - delete m_data->listener; } Qt::Edge QskDrawer::edge() const @@ -352,6 +231,24 @@ qreal QskDrawer::dragMargin() const return m_data->dragMargin; } +bool QskDrawer::event( QEvent* event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + if ( isAdjustingToParentGeometry() && parentItem() ) + { + auto r = qskItemRect( parentItem() ); + r = qskAlignedToEdge( r, sizeConstraint( Qt::PreferredSize ), edge() ); + + setGeometry( r ); + + return true; + } + } + + return Inherited::event( event ); +} + void QskDrawer::keyPressEvent( QKeyEvent* event ) { if ( isOpen() ) @@ -432,18 +329,6 @@ void QskDrawer::gestureEvent( QskGestureEvent* event ) Inherited::gestureEvent( event ); } -QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const -{ - qreal ratio; - - if ( isTransitioning() ) - ratio = metric( transitionAspect() ); - else - ratio = isOpen() ? 1.0 : 0.0; - - return qskSlidingRect( size, m_data->edge, ratio ); -} - void QskDrawer::itemChange( QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& value ) { @@ -456,85 +341,9 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, if ( parentItem() && isInteractive() ) qskCatchMouseEvents( parentItem() ); - m_data->resetListener( this ); - break; - } - case QQuickItem::ItemVisibleHasChanged: - { - m_data->resetListener( this ); break; } } } -void QskDrawer::setSliding( bool on ) -{ - const qreal from = on ? 0.0 : 1.0; - const qreal to = on ? 1.0 : 0.0; - - const auto aspect = transitionAspect(); - - auto hint = animationHint( aspect ); - hint.updateFlags = QskAnimationHint::UpdatePolish; - - startTransition( aspect, hint, from, to ); -} - -QRectF QskDrawer::clipRect() const -{ - if ( !isTransitioning() ) - return Inherited::clipRect(); - - /* - When fading we want to clip against the edge, where the drawer - slides in/out. However the size of the drawer is often smaller than the - one of the parent and we would clip the overlay node - and all content, that is located outside the drawer geometry. - - So we expand the clip rectangle to "unbounded" at the other edges. - - Note, that clipping against "rounded" rectangles can't be done - properly by overloading clipRect. We would have to manipulate the clip node - manually - like it is done in QskScrollArea. TODO .. - */ - constexpr qreal d = std::numeric_limits< short >::max(); - - QRectF r( -d, -d, 2.0 * d, 2.0 * d ); - - switch( m_data->edge ) - { - case Qt::LeftEdge: - r.setLeft( 0.0 ); - break; - - case Qt::RightEdge: - r.setRight( width() ); - break; - - case Qt::TopEdge: - r.setTop( 0.0 ); - break; - - case Qt::BottomEdge: - r.setBottom( height() ); - break; - } - - return r; -} - -void QskDrawer::setIntermediate( bool on ) -{ - setClip( on ); - Q_EMIT focusIndicatorRectChanged(); -} - -QRectF QskDrawer::focusIndicatorRect() const -{ - if ( isTransitioning() ) - return QRectF(); - - return Inherited::focusIndicatorRect(); -} - #include "moc_QskDrawer.cpp" diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index de02b435..c067e5ac 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -6,14 +6,13 @@ #ifndef QSK_DRAWER_H #define QSK_DRAWER_H -#include "QskPopup.h" -#include +#include "QskSlideIn.h" -class QSK_EXPORT QskDrawer : public QskPopup +class QSK_EXPORT QskDrawer : public QskSlideIn { Q_OBJECT - using Inherited = QskPopup; + using Inherited = QskSlideIn; Q_PROPERTY( Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged ) @@ -24,13 +23,11 @@ class QSK_EXPORT QskDrawer : public QskPopup WRITE setInteractive NOTIFY interactiveChanged ) public: - QSK_SUBCONTROLS( Panel ) - QskDrawer( QQuickItem* = nullptr ); ~QskDrawer() override; void setEdge( Qt::Edge ); - Qt::Edge edge() const; + Qt::Edge edge() const override final; void setInteractive( bool ); bool isInteractive() const; @@ -39,26 +36,19 @@ class QSK_EXPORT QskDrawer : public QskPopup void resetDragMargin(); qreal dragMargin() const; - QRectF layoutRectForSize( const QSizeF& ) const override; - - QRectF clipRect() const override; - QRectF focusIndicatorRect() const override; - Q_SIGNALS: void edgeChanged( Qt::Edge ); void dragMarginChanged( qreal ); void interactiveChanged( bool ); protected: + bool event( QEvent* ) override; + void itemChange( ItemChange, const ItemChangeData& ) override; void gestureEvent( QskGestureEvent* ) override; void keyPressEvent( QKeyEvent* ) override; private: - void setSliding( bool ); - - void setIntermediate( bool ); - class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 43ed0a1f..e4db363a 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -58,7 +58,6 @@ QskMenu::QskMenu( QQuickItem* parent ) , m_data( new PrivateData ) { setModal( true ); - setTransitionAspect( QskMenu::Panel | QskAspect::Position | QskAspect::Metric ); setPopupFlag( QskPopup::CloseOnPressOutside, true ); setPopupFlag( QskPopup::DeleteOnClose, true ); diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index 13be2b34..db16df1b 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -236,8 +236,8 @@ QSGNode* QskMenuSkinlet::updateSubNode( auto slideInNode = QskSGNode::ensureNode< QskSlideInNode >( node ); - const auto progress = popup->metric( popup->transitionAspect() ); - slideInNode->updateTranslation( rect, Qt::TopEdge, progress ); + slideInNode->updateTranslation( + rect, Qt::TopEdge, popup->transitioningFactor() ); auto contentsNode = updateContentsNode( popup, slideInNode->contentsNode() ); slideInNode->setContentsNode( contentsNode ); diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index 54cd4060..d57dbb64 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -19,9 +19,16 @@ QSK_QT_PRIVATE_BEGIN #include QSK_QT_PRIVATE_END +QSK_SUBCONTROL( QskPopup, Popup ) QSK_SUBCONTROL( QskPopup, Overlay ) + QSK_SYSTEM_STATE( QskPopup, Closed, QskAspect::FirstSystemState << 1 ) +static QskAspect qskTransitioningAspect() +{ + return QskPopup::Popup | QskAspect::Metric; +} + static void qskSetFocus( QQuickItem* item, bool on ) { if ( item->window() == nullptr ) @@ -76,6 +83,23 @@ static bool qskReplayMousePress() return false; } +static void qskStartTransition( QskPopup* popup, bool on ) +{ + const auto aspect = qskTransitioningAspect(); + + auto hint = popup->animationHint( aspect.subControl() ); + + if ( hint.isValid() ) + { + hint.updateFlags = QskAnimationHint::UpdatePolish | QskAnimationHint::UpdateNode; + + const qreal from = on ? 0.0 : 1.0; + const qreal to = on ? 1.0 : 0.0; + + popup->startTransition( aspect, hint, from, to ); + } +} + namespace { class InputGrabber final : public QskInputGrabber @@ -139,7 +163,6 @@ class QskPopup::PrivateData InputGrabber* inputGrabber = nullptr; uint priority = 0; - QskAspect transitionAspect; int flags : 4; bool isModal : 1; @@ -217,6 +240,8 @@ void QskPopup::setOpen( bool on ) else Q_EMIT closed(); + qskStartTransition( this, on ); + if ( isTransitioning() ) { Q_EMIT transitioningChanged( true ); @@ -240,7 +265,15 @@ bool QskPopup::isOpen() const bool QskPopup::isTransitioning() const { - return runningHintAnimator( m_data->transitionAspect ) != nullptr; + return runningHintAnimator( qskTransitioningAspect() ) != nullptr; +} + +qreal QskPopup::transitioningFactor() const +{ + if ( auto animator = runningHintAnimator( qskTransitioningAspect() ) ) + return animator->currentValue().value< qreal >(); + + return isOpen() ? 1.0 : 0.0; } QRectF QskPopup::overlayRect() const @@ -291,44 +324,23 @@ void QskPopup::updateInputGrabber() } } -QskAspect QskPopup::transitionAspect() const -{ - return m_data->transitionAspect; -} - -void QskPopup::setTransitionAspect( QskAspect aspect ) -{ - auto transitionAspect = aspect; - transitionAspect.clearStates(); // animated values are always stateless - - if ( transitionAspect == m_data->transitionAspect ) - return; - - if ( isTransitioning() ) - { - // stop the running animation TODO ... - } - - m_data->transitionAspect = transitionAspect; -} - bool QskPopup::isTransitionAccepted( QskAspect aspect ) const { - if ( isVisible() ) + if ( isVisible() && !isInitiallyPainted() ) { + /* + Usually we suppress transitions, when a control has never been + painted before as there is no valid starting point. Popups are + different as we want to have smooth fade/slide appearances. + */ if ( ( aspect.value() == 0 ) ) - { - return true; - } - - if ( aspect == m_data->transitionAspect ) return true; - if ( aspect.isColor() ) - { - if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) ) - return true; - } + if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Popup ) ) + return true; + + if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) ) + return true; } return Inherited::isTransitionAccepted( aspect ); @@ -472,10 +484,10 @@ bool QskPopup::event( QEvent* event ) } case QskEvent::Animator: { - const auto animtorEvent = static_cast< QskAnimatorEvent* >( event ); + const auto animatorEvent = static_cast< QskAnimatorEvent* >( event ); - if ( ( animtorEvent->state() == QskAnimatorEvent::Terminated ) - && ( animtorEvent->aspect() == m_data->transitionAspect ) ) + if ( ( animatorEvent->state() == QskAnimatorEvent::Terminated ) + && ( animatorEvent->aspect() == qskTransitioningAspect() ) ) { if ( !isOpen() ) { diff --git a/src/controls/QskPopup.h b/src/controls/QskPopup.h index f811a26d..7d427def 100644 --- a/src/controls/QskPopup.h +++ b/src/controls/QskPopup.h @@ -24,7 +24,7 @@ class QSK_EXPORT QskPopup : public QskControl using Inherited = QskControl; public: - QSK_SUBCONTROLS( Overlay ) + QSK_SUBCONTROLS( Popup, Overlay ) QSK_STATES( Closed ) enum PopupFlag @@ -56,11 +56,8 @@ class QSK_EXPORT QskPopup : public QskControl void setPriority( uint ); uint priority() const; - // transitions between open/closed states - QskAspect transitionAspect() const; - void setTransitionAspect( QskAspect ); - bool isTransitioning() const; + qreal transitioningFactor() const; bool isOpen() const; bool isClosed() const; diff --git a/src/controls/QskPopupSkinlet.cpp b/src/controls/QskPopupSkinlet.cpp index d3777785..04730b3b 100644 --- a/src/controls/QskPopupSkinlet.cpp +++ b/src/controls/QskPopupSkinlet.cpp @@ -5,6 +5,12 @@ #include "QskPopupSkinlet.h" #include "QskPopup.h" +#include "QskRgbValue.h" + +static inline QRgb qskInterpolatedRgb( QRgb rgb, qreal factor ) +{ + return QskRgb::toTransparent( rgb, qRound( factor * qAlpha( rgb ) ) ); +} QskPopupSkinlet::QskPopupSkinlet( QskSkin* skin ) : Inherited( skin ) @@ -28,13 +34,43 @@ QRectF QskPopupSkinlet::subControlRect( const QskSkinnable* skinnable, QSGNode* QskPopupSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { + const auto popup = static_cast< const QskPopup* >( skinnable ); + switch ( nodeRole ) { case OverlayRole: - return updateBoxNode( skinnable, node, QskPopup::Overlay ); + return updateOverlayNode( popup, node ); } return Inherited::updateSubNode( skinnable, nodeRole, node ); } +QSGNode* QskPopupSkinlet::updateOverlayNode( + const QskPopup* popup, QSGNode* node ) const +{ + using Q = QskPopup; + + const auto factor = popup->transitioningFactor(); + if ( factor <= 0.0 ) + return nullptr; + + const auto rect = popup->subControlRect( Q::Overlay ); + if ( rect.isEmpty() ) + return nullptr; + + auto gradient = popup->gradientHint( Q::Overlay ); + + if ( gradient.isVisible() && factor != 1.0 ) + { + auto stops = gradient.stops(); + + for ( auto& stop : stops ) + stop.setRgb( qskInterpolatedRgb( stop.rgb(), factor ) ); + + gradient.setStops( stops ); + } + + return updateBoxNode( popup, node, rect, gradient, QskPopup::Overlay ); +} + #include "moc_QskPopupSkinlet.cpp" diff --git a/src/controls/QskSlideIn.cpp b/src/controls/QskSlideIn.cpp new file mode 100644 index 00000000..9dc1c998 --- /dev/null +++ b/src/controls/QskSlideIn.cpp @@ -0,0 +1,236 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskSlideIn.h" +#include "QskAnimationHint.h" + +QSK_QT_PRIVATE_BEGIN +#include +#include +QSK_QT_PRIVATE_END + +namespace +{ + // Using an eventFilter for QskEvent::GeometryChange instead ??? + + class GeometryListener final : public QQuickItemChangeListener + { + public: + GeometryListener( QQuickItem* item, QQuickItem* adjustedItem ) + : m_item( item ) + , m_adjustedItem( adjustedItem ) + { + adjust(); + setEnabled( true ); + } + + ~GeometryListener() + { + setEnabled( false ); + } + + private: + void itemGeometryChanged( QQuickItem*, + QQuickGeometryChange, const QRectF& ) override + { + adjust(); + } + + private: + void adjust() + { + QEvent event( QEvent::PolishRequest ); + QCoreApplication::sendEvent( m_adjustedItem, &event ); + } + + void setEnabled( bool on ) + { + const auto changeTypes = QQuickItemPrivate::Geometry; + + auto d = QQuickItemPrivate::get( m_item ); + if ( on ) + d->addItemChangeListener( this, changeTypes ); + else + d->removeItemChangeListener( this, changeTypes ); + } + + QQuickItem* m_item; + QQuickItem* m_adjustedItem; + }; +} + +class QskSlideIn::PrivateData +{ + public: + inline void resetListener( QskSlideIn* slideIn ) + { + delete listener; + listener = nullptr; + + if ( adjustingToParentGeometry ) + { + if ( slideIn->parentItem() && slideIn->isVisible() ) + listener = new GeometryListener( slideIn->parentItem(), slideIn ); + } + } + + bool adjustingToParentGeometry = false; + GeometryListener* listener = nullptr; +}; + +QskSlideIn::QskSlideIn( QQuickItem* parentItem ) + : Inherited ( parentItem ) + , m_data( new PrivateData ) +{ + setPopupFlag( PopupFlag::CloseOnPressOutside, true ); + + /* + A slider wants to be on top of its parent - not being + layouted into its layoutRect(). So we opt out and do + the layout updates manually. + */ + setPlacementPolicy( QskPlacementPolicy::Ignore ); + + connect( this, &QskPopup::transitioningChanged, + this, &QskSlideIn::setIntermediate ); +} + +QskSlideIn::~QskSlideIn() +{ + delete m_data->listener; +} + +void QskSlideIn::setAdjustingToParentGeometry( bool on ) +{ + if ( on != m_data->adjustingToParentGeometry ) + { + m_data->adjustingToParentGeometry = on; + m_data->resetListener( this ); + } +} + +bool QskSlideIn::isAdjustingToParentGeometry() const +{ + return m_data->adjustingToParentGeometry; +} + +void QskSlideIn::itemChange( QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData& value ) +{ + Inherited::itemChange( change, value ); + + switch( static_cast< int >( change ) ) + { + case QQuickItem::ItemParentHasChanged: + case QQuickItem::ItemVisibleHasChanged: + { + m_data->resetListener( this ); + break; + } + } +} + +bool QskSlideIn::event( QEvent* event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + // isVisible ??? + if ( m_data->adjustingToParentGeometry && parentItem() ) + { + setSize( parentItem()->size() ); + return true; + } + } + + return Inherited::event( event ); +} + +void QskSlideIn::setIntermediate( bool on ) +{ + setClip( on ); + Q_EMIT focusIndicatorRectChanged(); +} + +QRectF QskSlideIn::focusIndicatorRect() const +{ + if ( isTransitioning() ) + return QRectF(); + + return Inherited::focusIndicatorRect(); +} + +QRectF QskSlideIn::clipRect() const +{ + if ( !isTransitioning() ) + return Inherited::clipRect(); + + /* + When sliding we want to clip against the edge, where the drawer + slides in/out. However the size of the slidein is often smaller than the + one of the parent and we would clip the overlay node + and all content, that is located outside the drawer geometry. + + So we expand the clip rectangle to "unbounded" at the other edges. + + Note, that clipping against "rounded" rectangles can't be done + properly by overloading clipRect. We would have to manipulate the clip node + manually - like it is done in QskScrollArea. TODO .. + */ + constexpr qreal d = std::numeric_limits< short >::max(); + + QRectF r( -d, -d, 2.0 * d, 2.0 * d ); + + switch( edge() ) + { + case Qt::LeftEdge: + r.setLeft( 0.0 ); + break; + + case Qt::RightEdge: + r.setRight( width() ); + break; + + case Qt::TopEdge: + r.setTop( 0.0 ); + break; + + case Qt::BottomEdge: + r.setBottom( height() ); + break; + } + + return r; +} + +QRectF QskSlideIn::layoutRectForSize( const QSizeF& size ) const +{ + const auto ratio = 1.0 - transitioningFactor(); + + auto x = 0.0; + auto y = 0.0; + + switch( edge() ) + { + case Qt::LeftEdge: + x = -ratio * size.width(); + break; + + case Qt::RightEdge: + x = ratio * size.width(); + break; + + case Qt::TopEdge: + y = -ratio * size.height(); + break; + + case Qt::BottomEdge: + y = ratio * size.height(); + break; + } + + return QRectF( x, y, size.width(), size.height() ); +} + +#include "moc_QskSlideIn.cpp" diff --git a/src/controls/QskSlideIn.h b/src/controls/QskSlideIn.h new file mode 100644 index 00000000..8727589f --- /dev/null +++ b/src/controls/QskSlideIn.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_SLIDE_IN_H +#define QSK_SLIDE_IN_H + +#include "QskPopup.h" +#include + +class QSK_EXPORT QskSlideIn : public QskPopup +{ + Q_OBJECT + + using Inherited = QskPopup; + + public: + ~QskSlideIn() override; + + virtual Qt::Edge edge() const = 0; + + QRectF focusIndicatorRect() const override; + QRectF clipRect() const override; + + void setAdjustingToParentGeometry( bool on ); + bool isAdjustingToParentGeometry() const; + + QRectF layoutRectForSize( const QSizeF& ) const override; + + protected: + QskSlideIn( QQuickItem* = nullptr ); + + bool event( QEvent* ) override; + void itemChange( ItemChange, const ItemChangeData& ) override; + + private: + void setIntermediate( bool ); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif