From b73513eb1f499dec8f9b18cb6827aa25fd1b5a91 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 9 Oct 2023 10:32:03 +0200 Subject: [PATCH 01/28] using styleHints for the minimal pan distance --- src/controls/QskPanGestureRecognizer.cpp | 5 +++-- src/controls/QskSkinManager.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controls/QskPanGestureRecognizer.cpp b/src/controls/QskPanGestureRecognizer.cpp index f7db9f5a..c103730d 100644 --- a/src/controls/QskPanGestureRecognizer.cpp +++ b/src/controls/QskPanGestureRecognizer.cpp @@ -7,10 +7,11 @@ #include "QskEvent.h" #include "QskGesture.h" -#include #include #include #include +#include +#include static inline bool qskIsInOrientation( const QPointF& from, const QPointF& to, Qt::Orientations orientations ) @@ -146,7 +147,7 @@ class QskPanGestureRecognizer::PrivateData public: Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical; - int minDistance = 15; + int minDistance = QGuiApplication::styleHints()->startDragDistance() + 5; quint64 timestampVelocity = 0.0; // timestamp of the last mouse event qreal angle = 0.0; diff --git a/src/controls/QskSkinManager.cpp b/src/controls/QskSkinManager.cpp index 218d2747..33864821 100644 --- a/src/controls/QskSkinManager.cpp +++ b/src/controls/QskSkinManager.cpp @@ -121,7 +121,7 @@ namespace scheme = Qt::ColorScheme::Unknown; } - const auto systemScheme = qGuiApp->styleHints()->colorScheme(); + const auto systemScheme = QGuiApplication::styleHints()->colorScheme(); if( scheme == systemScheme ) { From 2e980f087d3101b89526f336de581c2a3ae4fc7a Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 9 Oct 2023 10:37:07 +0200 Subject: [PATCH 02/28] QskGestureRecognizer::targetItem introduced --- src/controls/QskGestureRecognizer.cpp | 13 ++++++++++++- src/controls/QskGestureRecognizer.h | 7 ++++++- src/controls/QskPanGestureRecognizer.cpp | 15 ++++++++++----- src/controls/QskPanGestureRecognizer.h | 3 ++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/controls/QskGestureRecognizer.cpp b/src/controls/QskGestureRecognizer.cpp index 07a44f2d..b383730c 100644 --- a/src/controls/QskGestureRecognizer.cpp +++ b/src/controls/QskGestureRecognizer.cpp @@ -68,7 +68,8 @@ class QskGestureRecognizer::PrivateData return watchedItem->acceptedMouseButtons(); } - QQuickItem* watchedItem = nullptr; + QPointer< QQuickItem > watchedItem = nullptr; + QPointer< QQuickItem > targetItem = nullptr; QVector< QMouseEvent* > pendingEvents; @@ -121,6 +122,16 @@ QQuickItem* QskGestureRecognizer::watchedItem() const return m_data->watchedItem; } +void QskGestureRecognizer::setTargetItem( QQuickItem* item ) +{ + m_data->targetItem = item; +} + +QQuickItem* QskGestureRecognizer::targetItem() const +{ + return m_data->targetItem; +} + void QskGestureRecognizer::setAcceptedMouseButtons( Qt::MouseButtons buttons ) { m_data->buttons = buttons; diff --git a/src/controls/QskGestureRecognizer.h b/src/controls/QskGestureRecognizer.h index 012aa839..b34cd012 100644 --- a/src/controls/QskGestureRecognizer.h +++ b/src/controls/QskGestureRecognizer.h @@ -43,11 +43,16 @@ class QSK_EXPORT QskGestureRecognizer : public QObject QskGestureRecognizer( QObject* parent = nullptr ); ~QskGestureRecognizer() override; - bool eventFilter( QObject* object, QEvent* event) override; + bool eventFilter( QObject*, QEvent* ) override; + // the item where the gesture happens void setWatchedItem( QQuickItem* ); QQuickItem* watchedItem() const; + // the item processing the gesture events + void setTargetItem( QQuickItem* ); + QQuickItem* targetItem() const; + // Qt::NoButton means: all buttons accepted void setAcceptedMouseButtons( Qt::MouseButtons ); Qt::MouseButtons acceptedMouseButtons() const; diff --git a/src/controls/QskPanGestureRecognizer.cpp b/src/controls/QskPanGestureRecognizer.cpp index c103730d..859b8d91 100644 --- a/src/controls/QskPanGestureRecognizer.cpp +++ b/src/controls/QskPanGestureRecognizer.cpp @@ -61,9 +61,14 @@ static inline qreal qskAngle( } static void qskSendPanGestureEvent( - QQuickItem* item, QskGesture::State state, qreal velocity, qreal angle, - const QPointF& origin, const QPointF& lastPosition, const QPointF& position ) + QskGestureRecognizer* recognizer, QskGesture::State state, + qreal velocity, qreal angle, const QPointF& origin, + const QPointF& lastPosition, const QPointF& position ) { + auto item = recognizer->targetItem(); + if ( item == nullptr ) + item = recognizer->watchedItem(); + auto gesture = std::make_shared< QskPanGesture >(); gesture->setState( state ); @@ -244,12 +249,12 @@ void QskPanGestureRecognizer::processMove( const QPointF& pos, quint64 timestamp if ( started ) { - qskSendPanGestureEvent( watchedItem(), QskGesture::Started, + qskSendPanGestureEvent( this, QskGesture::Started, velocity, m_data->angle, m_data->origin, m_data->origin, m_data->pos ); } else { - qskSendPanGestureEvent( watchedItem(), QskGesture::Updated, + qskSendPanGestureEvent( this, QskGesture::Updated, velocity, m_data->angle, m_data->origin, oldPos, m_data->pos ); } } @@ -262,7 +267,7 @@ void QskPanGestureRecognizer::processRelease( const QPointF&, quint64 timestamp const ulong elapsedTotal = timestamp - timestampStarted(); const qreal velocity = m_data->velocityTracker.velocity( elapsedTotal ); - qskSendPanGestureEvent( watchedItem(), QskGesture::Finished, + qskSendPanGestureEvent( this, QskGesture::Finished, velocity, m_data->angle, m_data->origin, m_data->pos, m_data->pos ); } } diff --git a/src/controls/QskPanGestureRecognizer.h b/src/controls/QskPanGestureRecognizer.h index 26a72576..ba3bb121 100644 --- a/src/controls/QskPanGestureRecognizer.h +++ b/src/controls/QskPanGestureRecognizer.h @@ -23,11 +23,12 @@ class QSK_EXPORT QskPanGestureRecognizer : public QskGestureRecognizer void setOrientations( Qt::Orientations ); Qt::Orientations orientations() const; - private: + protected: void processPress( const QPointF&, quint64 timestamp, bool isFinal ) override; void processMove( const QPointF&, quint64 timestamp ) override; void processRelease( const QPointF&, quint64 timestamp ) override; + private: class PrivateData; std::unique_ptr< PrivateData > m_data; }; From 208865ba8d15e1216f053f06945c19ea37295eb4 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 11 Oct 2023 09:18:09 +0200 Subject: [PATCH 03/28] QskGestureRecognizer improvements --- examples/gallery/main.cpp | 12 ++++++++---- src/controls/QskGestureRecognizer.cpp | 9 +++------ src/controls/QskGestureRecognizer.h | 3 ++- src/controls/QskScrollBox.cpp | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index b074c299..f0916df0 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -229,19 +229,17 @@ namespace } { - auto drawer = new Drawer( parentItem() ); - drawer->setEdge( Qt::RightEdge ); - auto burger = new QskPushButton( "≡", this ); burger->setEmphasis( QskPushButton::LowEmphasis ); connect( burger, &QskPushButton::clicked, - drawer, &QskPopup::open ); + this, &Header::drawerRequested ); } } Q_SIGNALS: void enabledToggled( bool ); + void drawerRequested(); }; class MainView : public QskMainView @@ -264,6 +262,12 @@ namespace connect( header, &Header::enabledToggled, tabView, &TabView::setPagesEnabled ); + auto drawer = new Drawer( this ); + drawer->setEdge( Qt::RightEdge ); + + connect( header, &Header::drawerRequested, + drawer, &QskPopup::open ); + setHeader( header ); setBody( tabView ); } diff --git a/src/controls/QskGestureRecognizer.cpp b/src/controls/QskGestureRecognizer.cpp index b383730c..099307ab 100644 --- a/src/controls/QskGestureRecognizer.cpp +++ b/src/controls/QskGestureRecognizer.cpp @@ -142,12 +142,9 @@ Qt::MouseButtons QskGestureRecognizer::acceptedMouseButtons() const return m_data->buttons; } -QRectF QskGestureRecognizer::gestureRect() const +bool QskGestureRecognizer::isAcceptedPos( const QPointF& pos ) const { - if ( m_data->watchedItem ) - return qskItemRect( m_data->watchedItem ); - - return QRectF( 0.0, 0.0, -1.0, -1.0 ); + return m_data->watchedItem && m_data->watchedItem->contains( pos ); } void QskGestureRecognizer::setRejectOnTimeout( bool on ) @@ -313,7 +310,7 @@ bool QskGestureRecognizer::processMouseEvent( if ( event->type() == QEvent::MouseButtonPress ) { - if ( !gestureRect().contains( pos ) ) + if ( !isAcceptedPos( pos ) ) return false; if ( m_data->state != Idle ) diff --git a/src/controls/QskGestureRecognizer.h b/src/controls/QskGestureRecognizer.h index b34cd012..c4ac00dc 100644 --- a/src/controls/QskGestureRecognizer.h +++ b/src/controls/QskGestureRecognizer.h @@ -22,6 +22,7 @@ class QSK_EXPORT QskGestureRecognizer : public QObject Q_PROPERTY( State state READ state NOTIFY stateChanged ) Q_PROPERTY( QQuickItem* watchedItem READ watchedItem WRITE setWatchedItem ) + Q_PROPERTY( QQuickItem* targetItem READ targetItem WRITE setTargetItem ) Q_PROPERTY( Qt::MouseButtons acceptedMouseButtons READ acceptedMouseButtons WRITE setAcceptedMouseButtons ) @@ -72,7 +73,7 @@ class QSK_EXPORT QskGestureRecognizer : public QObject State state() const; - virtual QRectF gestureRect() const; + virtual bool isAcceptedPos( const QPointF& ) const; Q_SIGNALS: void stateChanged( State from, State to ); diff --git a/src/controls/QskScrollBox.cpp b/src/controls/QskScrollBox.cpp index a2304842..a975baed 100644 --- a/src/controls/QskScrollBox.cpp +++ b/src/controls/QskScrollBox.cpp @@ -136,15 +136,15 @@ namespace setOrientations( Qt::Horizontal | Qt::Vertical ); } - QRectF gestureRect() const override + bool isAcceptedPos( const QPointF& pos ) const override { if ( auto scrollBox = qobject_cast< const QskScrollBox* >( watchedItem() ) ) { if ( qskIsScrollable( scrollBox, orientations() ) ) - return scrollBox->viewContentsRect(); + return scrollBox->viewContentsRect().contains( pos ); } - return QRectF( 0.0, 0.0, -1.0, -1.0 ); // empty + return false; } }; } From 65dea17cfde5d81abf3618b7b56aac604fc5c4d0 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 15 Oct 2023 15:31:51 +0200 Subject: [PATCH 04/28] smooth sliding of menus --- skins/fluent2/QskFluent2Skin.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 1b90f732..7b56147c 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -735,6 +735,7 @@ void Editor::setupListViewColors( void Editor::setupMenuMetrics() { using Q = QskMenu; + using A = QskAspect; setPadding( Q::Panel, { 4, 6, 4, 6 } ); setBoxBorderMetrics( Q::Panel, 1 ); @@ -748,6 +749,14 @@ void Editor::setupMenuMetrics() setStrutSize( Q::Icon, 12, 12 ); setPadding( Q::Icon, { 8, 8, 0, 8 } ); + +#if 1 + setPosition( Q::Panel, 0 ); + setPosition( Q::Panel | QskPopup::Closed, 1 ); + + // copied from Mat3 - what are the correct values for Fluent2 ??? + setAnimation( Q::Panel | A::Metric, 150 ); +#endif } void Editor::setupMenuColors( From 9069c8e39084764096bd8e9d41b7ceb158bf43a1 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 15 Oct 2023 15:33:32 +0200 Subject: [PATCH 05/28] position is a progress value [0-1] - nothing in dp --- skins/material3/QskMaterial3Skin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 957c1ba2..6653e202 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -379,7 +379,7 @@ void Editor::setupMenu() setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); setPosition( Q::Panel, 0 ); - setPosition( Q::Panel | QskPopup::Closed, 1_dp ); + setPosition( Q::Panel | QskPopup::Closed, 1 ); setAnimation( Q::Panel | A::Metric, 150 ); setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic ); From 56446c26db87c27fc03b2ea9bb64fed41c2fb958 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 15 Oct 2023 15:43:29 +0200 Subject: [PATCH 06/28] missing enum initialization fixed --- src/controls/QskListViewSkinlet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controls/QskListViewSkinlet.h b/src/controls/QskListViewSkinlet.h index e26f4cb1..c16cc637 100644 --- a/src/controls/QskListViewSkinlet.h +++ b/src/controls/QskListViewSkinlet.h @@ -24,7 +24,7 @@ class QSK_EXPORT QskListViewSkinlet : public QskScrollViewSkinlet public: enum NodeRole { - TextRole, + TextRole = Inherited::RoleCount, GraphicRole, RoleCount From 3ecd8f337ee89bac5b18e726befbf81215fd45b5 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 15 Oct 2023 15:44:09 +0200 Subject: [PATCH 07/28] code moved from QskPopupSkinlet to QskSlideInNode --- src/CMakeLists.txt | 2 + src/controls/QskMenuSkinlet.cpp | 42 ++++++++- src/controls/QskMenuSkinlet.h | 9 +- src/controls/QskPopupSkinlet.cpp | 138 +---------------------------- src/controls/QskPopupSkinlet.h | 5 -- src/nodes/QskSlideInNode.cpp | 146 +++++++++++++++++++++++++++++++ src/nodes/QskSlideInNode.h | 33 +++++++ 7 files changed, 228 insertions(+), 147 deletions(-) create mode 100644 src/nodes/QskSlideInNode.cpp create mode 100644 src/nodes/QskSlideInNode.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 237d1577..7ce13c74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -115,6 +115,7 @@ list(APPEND HEADERS nodes/QskRichTextRenderer.h nodes/QskScaleRenderer.h nodes/QskSGNode.h + nodes/QskSlideInNode.h nodes/QskStrokeNode.h nodes/QskStippledLineRenderer.h nodes/QskShapeNode.h @@ -146,6 +147,7 @@ list(APPEND SOURCES nodes/QskRichTextRenderer.cpp nodes/QskScaleRenderer.cpp nodes/QskSGNode.cpp + nodes/QskSlideInNode.cpp nodes/QskStrokeNode.cpp nodes/QskStippledLineRenderer.cpp nodes/QskShapeNode.cpp diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index 0629b137..d75323a9 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -9,12 +9,14 @@ #include "QskGraphic.h" #include "QskColorFilter.h" #include "QskTextOptions.h" -#include "QskSGNode.h" #include "QskFunctions.h" #include "QskMargins.h" #include "QskFunctions.h" #include "QskLabelData.h" +#include "QskSGNode.h" +#include "QskSlideInNode.h" + #include #include @@ -209,11 +211,44 @@ QskMenuSkinlet::QskMenuSkinlet( QskSkin* skin ) : Inherited( skin ) , m_data( new PrivateData() ) { - appendNodeRoles( { PanelRole } ); + appendNodeRoles( { ContentsRole, PanelRole } ); } QskMenuSkinlet::~QskMenuSkinlet() = default; +QSGNode* QskMenuSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + switch ( nodeRole ) + { + case ContentsRole: + { + /* + QskSlideInNode works for controls made of nodes - not for + containers of other quick items. TODO ... + */ + + const auto popup = static_cast< const QskPopup* >( skinnable ); + + auto rect = popup->contentsRect(); + if ( rect.isEmpty() ) + return nullptr; + + auto slideInNode = QskSGNode::ensureNode< QskSlideInNode >( node ); + + const auto progress = popup->metric( popup->faderAspect() ); + slideInNode->updateTranslation( rect, Qt::TopEdge, progress ); + + auto contentsNode = updateContentsNode( popup, slideInNode->contentsNode() ); + slideInNode->setContentsNode( contentsNode ); + + return slideInNode; + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + QRectF QskMenuSkinlet::cursorRect( const QskSkinnable* skinnable, const QRectF& contentsRect, int index ) const { @@ -407,7 +442,8 @@ QskAspect::States QskMenuSkinlet::sampleStates( } } - const auto cursorPos = menu->effectiveSkinHint( Q::Segment | Q::Hovered | A::Metric | A::Position ).toPointF(); + const auto cursorPos = menu->effectiveSkinHint( + Q::Segment | Q::Hovered | A::Metric | A::Position ).toPointF(); if( !cursorPos.isNull() && menu->indexAtPosition( cursorPos ) == index ) { diff --git a/src/controls/QskMenuSkinlet.h b/src/controls/QskMenuSkinlet.h index 4ebe5284..34a3c0b5 100644 --- a/src/controls/QskMenuSkinlet.h +++ b/src/controls/QskMenuSkinlet.h @@ -20,7 +20,9 @@ class QSK_EXPORT QskMenuSkinlet : public QskPopupSkinlet public: enum NodeRole { - PanelRole = QskPopupSkinlet::RoleCount, + ContentsRole = Inherited::RoleCount, + PanelRole, + RoleCount }; @@ -48,7 +50,10 @@ class QSK_EXPORT QskMenuSkinlet : public QskPopupSkinlet Qt::SizeHint, const QSizeF& ) const override; protected: - QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const override; + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const; QSGNode* updateMenuNode( const QskSkinnable*, QSGNode* ) const; QSGNode* updateSampleNode( const QskSkinnable*, diff --git a/src/controls/QskPopupSkinlet.cpp b/src/controls/QskPopupSkinlet.cpp index 70cf300e..07dbb53c 100644 --- a/src/controls/QskPopupSkinlet.cpp +++ b/src/controls/QskPopupSkinlet.cpp @@ -5,108 +5,11 @@ #include "QskPopupSkinlet.h" #include "QskPopup.h" -#include "QskSGNode.h" - -#include -#include -#include - -namespace -{ - class RootNode : public QSGNode - { - public: - ~RootNode() override - { - delete m_clipNode; - delete m_transformNode; - delete m_contentsNode; - } - - void setClipRect( const QRectF& rect ) - { - if ( m_clipNode == nullptr ) - { - m_clipNode = new QSGClipNode(); - m_clipNode->setFlag( QSGNode::OwnedByParent, false ); - m_clipNode->setIsRectangular( true ); - } - - m_clipNode->setClipRect( rect ); - } - - void resetClip() - { - delete m_clipNode; - m_clipNode = nullptr; - } - - void setTranslation( qreal dx, qreal dy ) - { - if ( dx != 0.0 || dy != 0.0 ) - { - if ( m_transformNode == nullptr ) - { - m_transformNode = new QSGTransformNode(); - m_transformNode->setFlag( QSGNode::OwnedByParent, false ); - } - - QTransform transform; - transform.translate( dx, dy ); - - m_transformNode->setMatrix( transform ); - } - else - { - delete m_transformNode; - m_transformNode = nullptr; - } - } - - void setContentsNode( QSGNode* contentsNode ) - { - if ( m_contentsNode != contentsNode ) - { - if ( contentsNode ) - contentsNode->setFlag( QSGNode::OwnedByParent, false ); - - delete m_contentsNode; - m_contentsNode = contentsNode; - } - } - - void rearrangeNodes() - { - const std::initializer_list< QSGNode* > nodes = - { m_clipNode, m_transformNode, m_contentsNode }; - - QSGNode* parentNode = this; - for ( auto node : nodes ) - { - if ( node ) - { - QskSGNode::setParentNode( node, parentNode ); - parentNode = node; - } - } - } - - inline QSGNode* contentsNode() - { - return m_contentsNode; - } - - private: - QSGClipNode* m_clipNode = nullptr; - QSGTransformNode* m_transformNode = nullptr; - QSGNode* m_contentsNode = nullptr; - }; -} QskPopupSkinlet::QskPopupSkinlet( QskSkin* skin ) : Inherited( skin ) { - appendNodeRoles( { OverlayRole, ContentsRole } ); + appendNodeRoles( { OverlayRole } ); } QskPopupSkinlet::~QskPopupSkinlet() = default; @@ -131,48 +34,9 @@ QSGNode* QskPopupSkinlet::updateSubNode( { case OverlayRole: return updateBoxNode( skinnable, node, QskPopup::Overlay ); - - case ContentsRole: - return updateExtraNode( popup, node ); } return Inherited::updateSubNode( skinnable, nodeRole, node ); } -QSGNode* QskPopupSkinlet::updateExtraNode( const QskPopup* popup, QSGNode* node ) const -{ - auto cr = popup->contentsRect(); - if ( cr.isEmpty() ) - return nullptr; - - auto rootNode = QskSGNode::ensureNode< RootNode >( node ); - - const auto faderProgress = popup->metric( popup->faderAspect() ); - if ( faderProgress > 0.0 && faderProgress <= 1.0 ) - { - auto clipRect = QRectF( popup->mapFromScene( QPointF() ), popup->window()->size() ); - clipRect.setTop( cr.top() ); - - rootNode->setClipRect( clipRect ); - } - else - { - rootNode->resetClip(); - } - - rootNode->setTranslation( 0.0, -faderProgress * cr.height() ); - - auto contentsNode = updateContentsNode( popup, rootNode->contentsNode() ); - rootNode->setContentsNode( contentsNode ); - - rootNode->rearrangeNodes(); - - return rootNode; -} - -QSGNode* QskPopupSkinlet::updateContentsNode( const QskPopup*, QSGNode* ) const -{ - return nullptr; -} - #include "moc_QskPopupSkinlet.cpp" diff --git a/src/controls/QskPopupSkinlet.h b/src/controls/QskPopupSkinlet.h index 076f86f9..ecd3a758 100644 --- a/src/controls/QskPopupSkinlet.h +++ b/src/controls/QskPopupSkinlet.h @@ -20,8 +20,6 @@ class QSK_EXPORT QskPopupSkinlet : public QskSkinlet enum NodeRole { OverlayRole, - ContentsRole, - RoleCount }; @@ -35,10 +33,7 @@ class QSK_EXPORT QskPopupSkinlet : public QskSkinlet QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; - virtual QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const; - private: - QSGNode* updateExtraNode( const QskPopup*, QSGNode* ) const; QSGNode* updateOverlayNode( const QskPopup*, QSGNode* ) const; }; diff --git a/src/nodes/QskSlideInNode.cpp b/src/nodes/QskSlideInNode.cpp new file mode 100644 index 00000000..6c77b366 --- /dev/null +++ b/src/nodes/QskSlideInNode.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskSlideInNode.h" +#include "QskSGNode.h" +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +class QskSlideInNodePrivate final : public QSGNodePrivate +{ + public: + ~QskSlideInNodePrivate() + { + delete clipNode; + delete transformNode; + delete contentsNode; + } + + void reparentContentNode( QskSlideInNode* node ) + { + if ( contentsNode ) + { + QSGNode* parentNode = transformNode; + + if ( parentNode == nullptr ) + parentNode = clipNode; + + if ( parentNode == nullptr ) + parentNode = node; + + QskSGNode::setParentNode( contentsNode, parentNode ); + } + } + + QSGClipNode* clipNode = nullptr; + QSGTransformNode* transformNode = nullptr; + QSGNode* contentsNode = nullptr; +}; + +QskSlideInNode::QskSlideInNode() + : QSGNode( *new QskSlideInNodePrivate, QSGNode::BasicNodeType ) +{ +} + +QskSlideInNode::~QskSlideInNode() = default; + +void QskSlideInNode::updateTranslation( const QRectF& rect, + Qt::Edge edge, qreal progress ) +{ + Q_UNUSED( edge ); // TODO ... + + Q_D( QskSlideInNode ); + + { + // clipping + + if ( progress > 0.0 && progress <= 1.0 ) + { + if ( d->clipNode == nullptr ) + { + d->clipNode = new QSGClipNode(); + d->clipNode->setFlag( QSGNode::OwnedByParent, false ); + d->clipNode->setIsRectangular( true ); + } + + d->clipNode->setClipRect( rect ); + + } + else + { + delete d->clipNode; + d->clipNode = nullptr; + } + + if ( d->clipNode ) + QskSGNode::setParentNode( d->clipNode, this ); + } + + { + // translation + + qreal dx = 0.0; + qreal dy = -progress* rect.height(); + + if ( dx != 0.0 || dy != 0.0 ) + { + if ( d->transformNode == nullptr ) + { + d->transformNode = new QSGTransformNode(); + d->transformNode->setFlag( QSGNode::OwnedByParent, false ); + } + + QTransform transform; + transform.translate( dx, dy ); + + d->transformNode->setMatrix( transform ); + } + else + { + delete d->transformNode; + d->transformNode = nullptr; + } + + if ( d->transformNode ) + { + QSGNode* parentNode = d->clipNode; + if ( parentNode == nullptr ) + parentNode = this; + + QskSGNode::setParentNode( d->transformNode, parentNode ); + } + } + + d->reparentContentNode( this ); +} + +void QskSlideInNode::setContentsNode( QSGNode* node ) +{ + Q_D( QskSlideInNode ); + + if ( d->contentsNode == node ) + return; + + if ( node ) + node->setFlag( QSGNode::OwnedByParent, false ); + + delete d->contentsNode; + d->contentsNode = node; + + d->reparentContentNode( this ); +} + +QSGNode* QskSlideInNode::contentsNode() +{ + return d_func()->contentsNode; +} + +const QSGNode* QskSlideInNode::contentsNode() const +{ + return d_func()->contentsNode; +} diff --git a/src/nodes/QskSlideInNode.h b/src/nodes/QskSlideInNode.h new file mode 100644 index 00000000..532663f2 --- /dev/null +++ b/src/nodes/QskSlideInNode.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_SLIDE_IN_NODE_H +#define QSK_SLIDE_IN_NODE_H + +#include "QskGlobal.h" + +#include +#include + +class QskSlideInNodePrivate; + +class QSK_EXPORT QskSlideInNode : public QSGNode +{ + public: + QskSlideInNode(); + ~QskSlideInNode() override; + + void updateTranslation( const QRectF&, Qt::Edge, qreal progress ); + + void setContentsNode( QSGNode* ); + + QSGNode* contentsNode(); + const QSGNode* contentsNode() const; + + private: + Q_DECLARE_PRIVATE( QskSlideInNode ) +}; + +#endif From 7cf88e7b4515908e0cf601f4ae6314f7725a59ab Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 15 Oct 2023 15:53:27 +0200 Subject: [PATCH 08/28] compiler warning fixed --- src/controls/QskPopupSkinlet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controls/QskPopupSkinlet.cpp b/src/controls/QskPopupSkinlet.cpp index 07dbb53c..d3777785 100644 --- a/src/controls/QskPopupSkinlet.cpp +++ b/src/controls/QskPopupSkinlet.cpp @@ -28,8 +28,6 @@ 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: From 3b5e6aa0fd9c7390863aa0041de2c0cd96988716 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 16 Oct 2023 19:19:38 +0200 Subject: [PATCH 09/28] always accept transitions for items without QQuickItem::ItemHasContents --- src/controls/QskSkinnable.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index fa97a45f..03b9139c 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -1259,13 +1259,19 @@ bool QskSkinnable::isTransitionAccepted( QskAspect 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 = qskControlCast( owningItem() ) ) - return control->isInitiallyPainted(); + { + /* + 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 ( control->flags() & QQuickItem::ItemHasContents ) + return control->isInitiallyPainted(); + + return true; + } return false; } From 5f31eb1e785152dee44b0be6a6d2c3f50111c784 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 17 Oct 2023 12:14:42 +0200 Subject: [PATCH 10/28] QskDrawer reimplemented, can be open by a swipe gesture now. more work to do --- examples/gallery/main.cpp | 5 +- skins/fluent2/QskFluent2Skin.cpp | 12 +- skins/material3/QskMaterial3Skin.cpp | 7 +- skins/squiek/QskSquiekSkin.cpp | 9 +- src/controls/QskDrawer.cpp | 435 ++++++++++++++++++++++----- src/controls/QskDrawer.h | 23 +- 6 files changed, 396 insertions(+), 95 deletions(-) diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index f0916df0..611c620f 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -62,7 +62,8 @@ namespace Drawer( QQuickItem* parent = nullptr ) : QskDrawer( parent ) { - auto box = new QskLinearBox( Qt::Vertical ); + auto box = new QskLinearBox( Qt::Vertical, this ); + box->setSection( QskAspect::Header ); box->setPanel( true ); box->setPaddingHint( QskBox::Panel, 20 ); @@ -75,8 +76,6 @@ namespace auto btn = new QskPushButton( "Close", box ); connect( btn, &QskPushButton::clicked, this, &QskDrawer::close ); - - setContent( box ); } }; diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 7b56147c..30829413 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -606,21 +606,15 @@ void Editor::setupDialogButtonBoxColors( void Editor::setupDrawerMetrics() { using Q = QskDrawer; - - setPadding( Q::Panel, 5 ); - setHint( Q::Overlay | QskAspect::Style, false ); + using A = QskAspect; #if 1 - setAnimation( Q::Panel | QskAspect::Position, 200 ); + setAnimation( Q::Panel | A::Metric | A::Position, 200 ); #endif } -void Editor::setupDrawerColors( - QskAspect::Section section, const QskFluent2Theme& theme ) +void Editor::setupDrawerColors( QskAspect::Section, const QskFluent2Theme& ) { - using Q = QskDrawer; - - setGradient( Q::Panel | section, theme.palette.background.solid.base ); } void Editor::setupFocusIndicatorMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 6653e202..9ea1c054 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -809,12 +809,9 @@ void Editor::setupDialogButtonBox() void Editor::setupDrawer() { using Q = QskDrawer; + using A = QskAspect; - setPadding( Q::Panel, 5_dp ); - setGradient( Q::Panel, m_pal.background ); - setHint( Q::Overlay | QskAspect::Style, false ); - - setAnimation( Q::Panel | QskAspect::Position, qskDuration ); + setAnimation( Q::Panel | A::Metric | A::Position, qskDuration ); } void Editor::setupSlider() diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index d116d9db..c5ea53ab 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -759,13 +759,12 @@ void Editor::setupDialogButtonBox() setBoxShape( Q::Panel, 2 ); } -void Editor::setupDrawer() { +void Editor::setupDrawer() +{ + using A = QskAspect; using Q = QskDrawer; - setPadding( Q::Panel, 5 ); - setGradient( Q::Panel, m_pal.darker125 ); - setAnimation( Q::Panel | QskAspect::Position, qskDuration ); - setHint( Q::Overlay | QskAspect::Style, false ); + setAnimation( Q::Panel | A::Metric | A::Position, qskDuration ); } void Editor::setupTabButton() diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 905a5987..fe422e92 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -1,48 +1,258 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + #include "QskDrawer.h" #include "QskAspect.h" -#include "QskControl.h" +#include "QskAnimationHint.h" +#include "QskQuick.h" +#include "QskEvent.h" -#include -#include -#include -#include +#include "QskPanGestureRecognizer.h" +#include "QskGesture.h" +#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 ) -QSK_SUBCONTROL( QskDrawer, Overlay ) + +static void qskCatchMouseEvents( QQuickItem* item ) +{ +#if 1 + // manipulating other items - do we really want to do this ? + item->setAcceptedMouseButtons( Qt::LeftButton ); + item->setFiltersChildMouseEvents( true ); +#endif +} + +static bool qskCheckDirection( Qt::Edge edge, const QskPanGesture* gesture ) +{ + const auto degrees = gesture->angle(); + + switch( edge ) + { + case Qt::LeftEdge: + return ( degrees < 90.0 ) || ( degrees ) > 270.0; + + case Qt::RightEdge: + return ( degrees > 90.0 ) && ( degrees < 270.0 ); + + case Qt::TopEdge: + return degrees > 180.0; + + case Qt::BottomEdge: + return degrees < 180.0; + } + + return false; +} + +static void qskLayoutDrawer( const QRectF& rect, QskDrawer* drawer ) +{ + const auto size = qskSizeConstraint( drawer, Qt::PreferredSize ); + + QRectF r( 0.0, 0.0, size.width(), size.height() ); + + switch( drawer->edge() ) + { + case Qt::LeftEdge: + { + r.setHeight( rect.height() ); + r.moveRight( rect.left() + size.width() ); + break; + } + case Qt::RightEdge: + { + r.setHeight( rect.height() ); + r.moveLeft( rect.right() - size.width() ); + break; + } + + case Qt::TopEdge: + { + r.setWidth( rect.width() ); + r.moveBottom( rect.top() + size.height() ); + break; + } + + case Qt::BottomEdge: + { + r.setWidth( rect.width() ); + r.moveTop( rect.bottom() - size.height() ); + break; + } + } + + drawer->setGeometry( r ); +} + +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; + }; +} + +namespace +{ + class GestureRecognizer : public QskPanGestureRecognizer + { + using Inherited = QskPanGestureRecognizer; + + public: + GestureRecognizer( QskDrawer* drawer ) + : QskPanGestureRecognizer( drawer ) + { + setWatchedItem( drawer->parentItem() ); + setTargetItem( drawer ); + } + + protected: + bool isAcceptedPos( const QPointF& pos ) const override + { + auto drawer = qobject_cast< const QskDrawer* >( targetItem() ); + + const auto dragMargin = drawer->dragMargin(); + if ( dragMargin <= 0.0 ) + return false; + + auto rect = qskItemRect( watchedItem() ); + + switch( drawer->edge() ) + { + case Qt::LeftEdge: + rect.setRight( rect.left() + dragMargin ); + break; + + case Qt::RightEdge: + rect.setLeft( rect.right() - dragMargin ); + break; + + case Qt::TopEdge: + rect.setBottom( rect.top() + dragMargin ); + break; + + case Qt::BottomEdge: + rect.setTop( rect.bottom() - dragMargin ); + break; + } + + return rect.contains( pos ); + } + }; +} class QskDrawer::PrivateData { public: - QskControl* content = nullptr; - QskBox* contentBox = nullptr; Qt::Edge edge = Qt::LeftEdge; + GestureRecognizer* gestureRecognizer = nullptr; + GeometryListener* listener = nullptr; + + // a skin hint ??? + qreal dragMargin = QGuiApplication::styleHints()->startDragDistance(); }; QskDrawer::QskDrawer( QQuickItem* parentItem ) : Inherited ( parentItem ) , m_data( new PrivateData ) { +#if 1 setZ( 1 ); +#endif + + setOverlay( true ); + setPolishOnResize( true ); setPopupFlag( PopupFlag::CloseOnPressOutside, true ); + setFaderAspect( Panel | QskAspect::Position | QskAspect::Metric ); - m_data->contentBox = new QskBox(this); - m_data->contentBox->setSubcontrolProxy( QskBox::Panel, Panel ); - m_data->contentBox->setPanel( true ); + /* + 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 ); + if ( parentItem ) + { + m_data->listener = new GeometryListener( parentItem, this ); + qskCatchMouseEvents( parentItem ); + } - setSubcontrolProxy( Inherited::Overlay, Overlay ); + m_data->gestureRecognizer = new GestureRecognizer( this ); - setFaderAspect( Panel | QskAspect::Metric ); - connect(this, &QskDrawer::closed, this, [this]() { - startTransition( Panel | QskAspect::Metric, - animationHint( Panel | QskAspect::Position ), - 0.0, 1.0 ); - }); + connect( this, &QskPopup::openChanged, this, &QskDrawer::setFading ); + + /* + When the content of the parentItem does not fit we will have + a difference between fading and normal state. To overcome this problem + we need to expand the rectangle of the QQuickDefaultClipNode manually to + the window borders: TODO ... + */ + connect( this, &QskPopup::fadingChanged, parentItem, &QQuickItem::setClip ); } QskDrawer::~QskDrawer() { + delete m_data->listener; } Qt::Edge QskDrawer::edge() const @@ -60,79 +270,166 @@ void QskDrawer::setEdge( Qt::Edge edge ) edgeChanged( edge ); } -void QskDrawer::setContent( QskControl* content ) +void QskDrawer::setDragMargin( qreal margin ) { - content->setParentItem( m_data->contentBox ); - if ( content->parent() == nullptr ) - content->setParent( m_data->contentBox ); + margin = std::max( margin, 0.0 ); - m_data->content = content; + if ( margin != m_data->dragMargin ) + { + m_data->dragMargin = margin; + Q_EMIT dragMarginChanged( margin ); + } +} + +qreal QskDrawer::dragMargin() const +{ + return m_data->dragMargin; +} + +void QskDrawer::gestureEvent( QskGestureEvent* event ) +{ + if ( event->gesture()->type() == QskGesture::Pan ) + { + /* + For the moment we treat the gesture like a swipe gesture + without dragging the drawer when moving the mouse. TODO ... + */ + const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() ); + if ( gesture->state() == QskGesture::Finished ) + { + if ( qskCheckDirection( m_data->edge, gesture ) ) + open(); + } + + return; + } + + Inherited::gestureEvent( event ); +} + +QSizeF QskDrawer::layoutSizeHint( + Qt::SizeHint which, const QSizeF& constraint ) const +{ + if ( which == Qt::MaximumSize ) + return QSizeF(); + + qreal w = -1.0; + qreal h = -1.0; + + const auto children = childItems(); + + for ( const auto child : children ) + { + if ( !qskIsVisibleToLayout( child ) ) + continue; + + const auto policy = qskSizePolicy( child ); + + if ( constraint.width() >= 0.0 && policy.isConstrained( Qt::Vertical ) ) + { + const auto hint = qskSizeConstraint( child, which, constraint ); + h = qMax( h, hint.height() ); + } + else if ( constraint.height() >= 0.0 && policy.isConstrained( Qt::Horizontal ) ) + { + const auto hint = qskSizeConstraint( child, which, constraint ); + w = qMax( w, hint.width() ); + } + else + { + const auto hint = qskSizeConstraint( child, which ); + + w = qMax( w, hint.width() ); + h = qMax( h, hint.height() ); + } + } + + return QSizeF( w, h ); } void QskDrawer::updateLayout() { - if ( !isOpen() && !isFading() ) + if ( !( isOpen() || isFading() ) || size().isEmpty() ) return; - const auto padding = paddingHint( Panel ); + auto dx = 0.0; + auto dy = 0.0; - auto contentSize = m_data->content->preferredSize(); - contentSize = contentSize.grownBy( padding ); - - const auto parentSize = parentItem()->size(); - - switch( m_data->edge ) + if ( isFading() ) { - case Qt::Edge::LeftEdge: + const auto f = metric( faderAspect() ); + + switch( m_data->edge ) { - qreal x = metric( faderAspect() ) * contentSize.width() * -1.0; + case Qt::LeftEdge: + dx = -f * width(); + break; - qskSetItemGeometry( m_data->contentBox, - x, 0, contentSize.width(), parentSize.height() ); - break; - } - case Qt::Edge::RightEdge: - { - qreal x = ( metric( faderAspect() ) * contentSize.width() ) - + parentSize.width() - contentSize.width(); + case Qt::RightEdge: + dx = f * width(); + break; - qskSetItemGeometry( m_data->contentBox, - x, 0, contentSize.width(), parentSize.height() ); - break; - } + case Qt::TopEdge: + dy = -f * height(); + break; - case Qt::Edge::TopEdge: - { - qreal y = metric( faderAspect() ) * contentSize.height(); - - qskSetItemGeometry( m_data->contentBox, - 0, -y, parentSize.width(), contentSize.height() ); - break; - } - - case Qt::Edge::BottomEdge: - { - qreal y = metric( faderAspect() ) * contentSize.height() + parentSize.height() - - contentSize.height(); - - qskSetItemGeometry( m_data->contentBox, - 0, y, parentSize.width(), contentSize.height() ); - break; + case Qt::BottomEdge: + dy = f * height(); + break; } } - m_data->content->setGeometry( QPointF( padding.left(), padding.top() ), - m_data->contentBox->size().shrunkBy( padding ) ); + const QRectF layoutRect( dx, dy, width(), height() ); - Inherited::updateLayout(); + const auto children = childItems(); + for ( auto child : children ) + { + if ( qskIsAdjustableByLayout( child ) ) + { + const auto r = qskConstrainedItemRect( child, layoutRect ); + qskSetItemGeometry( child, r ); + } + } } -void QskDrawer::aboutToShow() +void QskDrawer::itemChange( QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData& value ) { - startTransition( Panel | QskAspect::Metric, - animationHint( Panel | QskAspect::Position ), 1.0, 0.0 ); + Inherited::itemChange( change, value ); - Inherited::aboutToShow(); + switch( static_cast< int >( change ) ) + { + case QQuickItem::ItemParentHasChanged: + { + if ( parentItem() ) + qskCatchMouseEvents( parentItem() ); + + Q_FALLTHROUGH(); + } + case QQuickItem::ItemVisibleHasChanged: + { + delete m_data->listener; + m_data->listener = nullptr; + + if ( parentItem() && isVisible() ) + m_data->listener = new GeometryListener( parentItem(), this ); + + break; + } + } +} + +void QskDrawer::setFading( bool on ) +{ + const qreal from = on ? 1.0 : 0.0; + const qreal to = on ? 0.0 : 1.0; + + const auto aspect = faderAspect(); + + auto hint = animationHint( aspect ); + hint.updateFlags = QskAnimationHint::UpdatePolish | QskAnimationHint::UpdateNode; + + startTransition( aspect, hint, from, to ); } #include "moc_QskDrawer.cpp" diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index 442f95e7..2c8d68e7 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -1,3 +1,8 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + #ifndef QSK_DRAWER_H #define QSK_DRAWER_H @@ -12,8 +17,11 @@ class QSK_EXPORT QskDrawer : public QskPopup Q_PROPERTY( Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged ) + Q_PROPERTY( qreal dragMargin READ dragMargin + WRITE setDragMargin NOTIFY dragMarginChanged ) + public: - QSK_SUBCONTROLS( Panel, Overlay ) + QSK_SUBCONTROLS( Panel ) QskDrawer( QQuickItem* = nullptr ); ~QskDrawer() override; @@ -21,17 +29,24 @@ class QSK_EXPORT QskDrawer : public QskPopup void setEdge( Qt::Edge ); Qt::Edge edge() const; - void updateLayout() override; + void setDragMargin( qreal ); + qreal dragMargin() const; - void setContent( QskControl* ); + void updateLayout() override; Q_SIGNALS: void edgeChanged( Qt::Edge ); + void dragMarginChanged( qreal ); protected: - void aboutToShow() override; + void itemChange( ItemChange, const ItemChangeData& ) override; + + QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override; + void gestureEvent( QskGestureEvent* ) override; private: + void setFading( bool ); + class PrivateData; std::unique_ptr< PrivateData > m_data; }; From b31f94225d14e2c2a6781202ee3cc132a6c8c70a Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 17 Oct 2023 14:34:20 +0200 Subject: [PATCH 11/28] ItemChildRemovedChange handling added --- src/controls/QskControl.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/controls/QskControl.cpp b/src/controls/QskControl.cpp index a293882b..9b3d5113 100644 --- a/src/controls/QskControl.cpp +++ b/src/controls/QskControl.cpp @@ -915,10 +915,16 @@ void QskControl::itemChange( QQuickItem::ItemChange change, break; } case QQuickItem::ItemChildAddedChange: + case QQuickItem::ItemChildRemovedChange: { - if ( autoLayoutChildren() && qskIsAdjustableByLayout( value.item ) ) - polish(); + if ( autoLayoutChildren() ) + { + if ( qskIsVisibleToLayout( value.item ) ) + resetImplicitSize(); + if ( qskIsAdjustableByLayout( value.item ) ) + polish(); + } break; } case QQuickItem::ItemActiveFocusHasChanged: From d85d0a033811caa338bcd497be44b5d90473788d Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 17 Oct 2023 14:35:21 +0200 Subject: [PATCH 12/28] QskPopup::toggle added --- src/controls/QskPopup.cpp | 5 +++++ src/controls/QskPopup.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index c3292083..50714836 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -196,6 +196,11 @@ void QskPopup::close() setOpen( false ); } +void QskPopup::toggle() +{ + setOpen( !isOpen() ); +} + void QskPopup::setOpen( bool on ) { if ( on == isOpen() ) diff --git a/src/controls/QskPopup.h b/src/controls/QskPopup.h index 8beaed80..b2c99c7d 100644 --- a/src/controls/QskPopup.h +++ b/src/controls/QskPopup.h @@ -72,6 +72,8 @@ class QSK_EXPORT QskPopup : public QskControl public Q_SLOTS: void open(); void close(); + void toggle(); + void setOpen( bool ); Q_SIGNALS: From d0cd48f8c25873ad9c400b720005dd5e97596f7b Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 17 Oct 2023 14:36:44 +0200 Subject: [PATCH 13/28] QskDrawer improvements --- examples/gallery/main.cpp | 4 ++-- src/controls/QskDrawer.cpp | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index 611c620f..cf2e19c5 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -261,11 +261,11 @@ namespace connect( header, &Header::enabledToggled, tabView, &TabView::setPagesEnabled ); - auto drawer = new Drawer( this ); + auto drawer = new Drawer( tabView ); drawer->setEdge( Qt::RightEdge ); connect( header, &Header::drawerRequested, - drawer, &QskPopup::open ); + drawer, &QskPopup::toggle ); setHeader( header ); setBody( tabView ); diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index fe422e92..c29c1c12 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -218,7 +218,6 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) setZ( 1 ); #endif - setOverlay( true ); setPolishOnResize( true ); setPopupFlag( PopupFlag::CloseOnPressOutside, true ); @@ -399,6 +398,17 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, switch( static_cast< int >( change ) ) { + case QQuickItem::ItemChildAddedChange: + case QQuickItem::ItemChildRemovedChange: + { + if ( qskIsVisibleToLayout( value.item ) ) + resetImplicitSize(); + + if ( qskIsAdjustableByLayout( value.item ) ) + polish(); + + break; + } case QQuickItem::ItemParentHasChanged: { if ( parentItem() ) @@ -427,7 +437,7 @@ void QskDrawer::setFading( bool on ) const auto aspect = faderAspect(); auto hint = animationHint( aspect ); - hint.updateFlags = QskAnimationHint::UpdatePolish | QskAnimationHint::UpdateNode; + hint.updateFlags = QskAnimationHint::UpdatePolish; startTransition( aspect, hint, from, to ); } From 142995504b8a81ad16aef278d4560ab01ae46d61 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 17 Oct 2023 15:53:30 +0200 Subject: [PATCH 14/28] QskDrawer using autoLayoutChildren --- src/controls/QskDrawer.cpp | 126 +++++++++++-------------------------- src/controls/QskDrawer.h | 4 +- 2 files changed, 36 insertions(+), 94 deletions(-) diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index c29c1c12..23fc4ad8 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -96,6 +96,34 @@ 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; + + 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 @@ -218,7 +246,7 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) setZ( 1 ); #endif - setPolishOnResize( true ); + setAutoLayoutChildren( true ); setPopupFlag( PopupFlag::CloseOnPressOutside, true ); setFaderAspect( Panel | QskAspect::Position | QskAspect::Metric ); @@ -306,89 +334,16 @@ void QskDrawer::gestureEvent( QskGestureEvent* event ) Inherited::gestureEvent( event ); } -QSizeF QskDrawer::layoutSizeHint( - Qt::SizeHint which, const QSizeF& constraint ) const +QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const { - if ( which == Qt::MaximumSize ) - return QSizeF(); - - qreal w = -1.0; - qreal h = -1.0; - - const auto children = childItems(); - - for ( const auto child : children ) - { - if ( !qskIsVisibleToLayout( child ) ) - continue; - - const auto policy = qskSizePolicy( child ); - - if ( constraint.width() >= 0.0 && policy.isConstrained( Qt::Vertical ) ) - { - const auto hint = qskSizeConstraint( child, which, constraint ); - h = qMax( h, hint.height() ); - } - else if ( constraint.height() >= 0.0 && policy.isConstrained( Qt::Horizontal ) ) - { - const auto hint = qskSizeConstraint( child, which, constraint ); - w = qMax( w, hint.width() ); - } - else - { - const auto hint = qskSizeConstraint( child, which ); - - w = qMax( w, hint.width() ); - h = qMax( h, hint.height() ); - } - } - - return QSizeF( w, h ); -} - -void QskDrawer::updateLayout() -{ - if ( !( isOpen() || isFading() ) || size().isEmpty() ) - return; - - auto dx = 0.0; - auto dy = 0.0; + qreal ratio; if ( isFading() ) - { - const auto f = metric( faderAspect() ); + ratio = metric( faderAspect() ); + else + ratio = isOpen() ? 0.0 : 1.0; - switch( m_data->edge ) - { - case Qt::LeftEdge: - dx = -f * width(); - break; - - case Qt::RightEdge: - dx = f * width(); - break; - - case Qt::TopEdge: - dy = -f * height(); - break; - - case Qt::BottomEdge: - dy = f * height(); - break; - } - } - - const QRectF layoutRect( dx, dy, width(), height() ); - - const auto children = childItems(); - for ( auto child : children ) - { - if ( qskIsAdjustableByLayout( child ) ) - { - const auto r = qskConstrainedItemRect( child, layoutRect ); - qskSetItemGeometry( child, r ); - } - } + return qskSlidingRect( size, m_data->edge, ratio ); } void QskDrawer::itemChange( QQuickItem::ItemChange change, @@ -398,17 +353,6 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, switch( static_cast< int >( change ) ) { - case QQuickItem::ItemChildAddedChange: - case QQuickItem::ItemChildRemovedChange: - { - if ( qskIsVisibleToLayout( value.item ) ) - resetImplicitSize(); - - if ( qskIsAdjustableByLayout( value.item ) ) - polish(); - - break; - } case QQuickItem::ItemParentHasChanged: { if ( parentItem() ) diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index 2c8d68e7..a82e57b3 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -32,7 +32,7 @@ class QSK_EXPORT QskDrawer : public QskPopup void setDragMargin( qreal ); qreal dragMargin() const; - void updateLayout() override; + QRectF layoutRectForSize( const QSizeF& ) const override; Q_SIGNALS: void edgeChanged( Qt::Edge ); @@ -40,8 +40,6 @@ class QSK_EXPORT QskDrawer : public QskPopup protected: void itemChange( ItemChange, const ItemChangeData& ) override; - - QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override; void gestureEvent( QskGestureEvent* ) override; private: From a791867a3eff742bcb8e9b89dde3ab1a04d485fe Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 17 Oct 2023 17:05:57 +0200 Subject: [PATCH 15/28] QskDrawer::interactive, gesture handling for closing --- src/controls/QskDrawer.cpp | 91 +++++++++++++++++++++++++++----------- src/controls/QskDrawer.h | 10 ++++- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 23fc4ad8..c24a4d2d 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -26,6 +26,12 @@ QSK_QT_PRIVATE_END */ QSK_SUBCONTROL( QskDrawer, Panel ) +static inline qreal qskDefaultDragMargin() +{ + // a skin hint ??? + return QGuiApplication::styleHints()->startDragDistance(); +} + static void qskCatchMouseEvents( QQuickItem* item ) { #if 1 @@ -196,30 +202,35 @@ namespace bool isAcceptedPos( const QPointF& pos ) const override { auto drawer = qobject_cast< const QskDrawer* >( targetItem() ); - - const auto dragMargin = drawer->dragMargin(); - if ( dragMargin <= 0.0 ) + if ( drawer->isFading() ) return false; auto rect = qskItemRect( watchedItem() ); - switch( drawer->edge() ) + if ( !drawer->isOpen() ) { - case Qt::LeftEdge: - rect.setRight( rect.left() + dragMargin ); - break; + const auto dragMargin = drawer->dragMargin(); + if ( dragMargin <= 0.0 ) + return false; - case Qt::RightEdge: - rect.setLeft( rect.right() - dragMargin ); - break; + switch( drawer->edge() ) + { + case Qt::LeftEdge: + rect.setRight( rect.left() + dragMargin ); + break; - case Qt::TopEdge: - rect.setBottom( rect.top() + dragMargin ); - break; + case Qt::RightEdge: + rect.setLeft( rect.right() - dragMargin ); + break; - case Qt::BottomEdge: - rect.setTop( rect.bottom() - dragMargin ); - break; + case Qt::TopEdge: + rect.setBottom( rect.top() + dragMargin ); + break; + + case Qt::BottomEdge: + rect.setTop( rect.bottom() - dragMargin ); + break; + } } return rect.contains( pos ); @@ -234,8 +245,7 @@ class QskDrawer::PrivateData GestureRecognizer* gestureRecognizer = nullptr; GeometryListener* listener = nullptr; - // a skin hint ??? - qreal dragMargin = QGuiApplication::styleHints()->startDragDistance(); + qreal dragMargin = qskDefaultDragMargin(); }; QskDrawer::QskDrawer( QQuickItem* parentItem ) @@ -247,6 +257,7 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) #endif setAutoLayoutChildren( true ); + setInteractive( true ); setPopupFlag( PopupFlag::CloseOnPressOutside, true ); setFaderAspect( Panel | QskAspect::Position | QskAspect::Metric ); @@ -258,13 +269,7 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) */ setPlacementPolicy( QskPlacementPolicy::Ignore ); if ( parentItem ) - { m_data->listener = new GeometryListener( parentItem, this ); - qskCatchMouseEvents( parentItem ); - } - - m_data->gestureRecognizer = new GestureRecognizer( this ); - connect( this, &QskPopup::openChanged, this, &QskDrawer::setFading ); @@ -297,6 +302,32 @@ void QskDrawer::setEdge( Qt::Edge edge ) edgeChanged( edge ); } +void QskDrawer::setInteractive( bool on ) +{ + if ( on == isInteractive() ) + return; + + if ( on ) + { + m_data->gestureRecognizer = new GestureRecognizer( this ); + if ( parentItem() ) + qskCatchMouseEvents( parentItem() ); + } + else + { + // how to revert qskCatchMouseEvents properly ??? + delete m_data->gestureRecognizer; + m_data->gestureRecognizer = nullptr; + } + + Q_EMIT interactiveChanged( on ); +} + +bool QskDrawer::isInteractive() const +{ + return m_data->gestureRecognizer != nullptr; +} + void QskDrawer::setDragMargin( qreal margin ) { margin = std::max( margin, 0.0 ); @@ -308,6 +339,11 @@ void QskDrawer::setDragMargin( qreal margin ) } } +void QskDrawer::resetDragMargin() +{ + setDragMargin( qskDefaultDragMargin() ); +} + qreal QskDrawer::dragMargin() const { return m_data->dragMargin; @@ -324,8 +360,9 @@ void QskDrawer::gestureEvent( QskGestureEvent* event ) const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() ); if ( gesture->state() == QskGesture::Finished ) { - if ( qskCheckDirection( m_data->edge, gesture ) ) - open(); + const auto forwards = qskCheckDirection( m_data->edge, gesture ); + if ( forwards != isOpen() ) + setOpen( forwards ); } return; @@ -355,7 +392,7 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, { case QQuickItem::ItemParentHasChanged: { - if ( parentItem() ) + if ( parentItem() && isInteractive() ) qskCatchMouseEvents( parentItem() ); Q_FALLTHROUGH(); diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index a82e57b3..6627b9fc 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -18,7 +18,10 @@ class QSK_EXPORT QskDrawer : public QskPopup Q_PROPERTY( Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged ) Q_PROPERTY( qreal dragMargin READ dragMargin - WRITE setDragMargin NOTIFY dragMarginChanged ) + WRITE setDragMargin RESET resetDragMargin NOTIFY dragMarginChanged ) + + Q_PROPERTY( bool interactive READ isInteractive + WRITE setInteractive NOTIFY interactiveChanged ) public: QSK_SUBCONTROLS( Panel ) @@ -29,7 +32,11 @@ class QSK_EXPORT QskDrawer : public QskPopup void setEdge( Qt::Edge ); Qt::Edge edge() const; + void setInteractive( bool ); + bool isInteractive() const; + void setDragMargin( qreal ); + void resetDragMargin(); qreal dragMargin() const; QRectF layoutRectForSize( const QSizeF& ) const override; @@ -37,6 +44,7 @@ class QSK_EXPORT QskDrawer : public QskPopup Q_SIGNALS: void edgeChanged( Qt::Edge ); void dragMarginChanged( qreal ); + void interactiveChanged( bool ); protected: void itemChange( ItemChange, const ItemChangeData& ) override; From 25461aaaf63db61e5c690083a99a805bd72ee30e Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 18 Oct 2023 15:05:17 +0200 Subject: [PATCH 16/28] QskDrawer improvements --- src/controls/QskDrawer.cpp | 146 ++++++++++++++++++++++++++++++++----- src/controls/QskDrawer.h | 7 ++ 2 files changed, 136 insertions(+), 17 deletions(-) diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index c24a4d2d..55a8d33f 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -241,6 +241,15 @@ 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; @@ -268,18 +277,10 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) the layout updates manually. */ setPlacementPolicy( QskPlacementPolicy::Ignore ); - if ( parentItem ) - m_data->listener = new GeometryListener( parentItem, this ); + m_data->resetListener( this ); connect( this, &QskPopup::openChanged, this, &QskDrawer::setFading ); - - /* - When the content of the parentItem does not fit we will have - a difference between fading and normal state. To overcome this problem - we need to expand the rectangle of the QQuickDefaultClipNode manually to - the window borders: TODO ... - */ - connect( this, &QskPopup::fadingChanged, parentItem, &QQuickItem::setClip ); + connect( this, &QskPopup::fadingChanged, this, &QskDrawer::setIntermediate ); } QskDrawer::~QskDrawer() @@ -349,6 +350,64 @@ qreal QskDrawer::dragMargin() const return m_data->dragMargin; } +void QskDrawer::keyPressEvent( QKeyEvent* event ) +{ + if ( isOpen() ) + { + bool doClose = false; + + const auto key = event->key(); + + switch( key ) + { + case Qt::Key_Escape: + case Qt::Key_Cancel: + { + doClose = true; + break; + } + +#if 0 + /* + Do we want to have this - and what about opening with + the same keys ??? + */ + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + { + switch( m_data->edge ) + { + case Qt::TopEdge: + doClose = ( key == Qt::Key_Up ); + break; + case Qt::BottomEdge: + doClose = ( key == Qt::Key_Down ); + break; + case Qt::LeftEdge: + doClose = ( key == Qt::Key_Left ); + break; + case Qt::RightEdge: + doClose = ( key == Qt::Key_Right ); + break; + } + + break; + } +#endif + } + + if ( doClose ) + { + close(); + return; + } + } + + Inherited::keyPressEvent( event ); +} + void QskDrawer::gestureEvent( QskGestureEvent* event ) { if ( event->gesture()->type() == QskGesture::Pan ) @@ -395,16 +454,12 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, if ( parentItem() && isInteractive() ) qskCatchMouseEvents( parentItem() ); - Q_FALLTHROUGH(); + m_data->resetListener( this ); + break; } case QQuickItem::ItemVisibleHasChanged: { - delete m_data->listener; - m_data->listener = nullptr; - - if ( parentItem() && isVisible() ) - m_data->listener = new GeometryListener( parentItem(), this ); - + m_data->resetListener( this ); break; } } @@ -423,4 +478,61 @@ void QskDrawer::setFading( bool on ) startTransition( aspect, hint, from, to ); } +QRectF QskDrawer::clipRect() const +{ + if ( !isFading() ) + 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 ( isFading() ) + return QRectF(); + + return Inherited::focusIndicatorRect(); +} + #include "moc_QskDrawer.cpp" diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index 6627b9fc..d7e66d0c 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -41,6 +41,9 @@ class QSK_EXPORT QskDrawer : public QskPopup QRectF layoutRectForSize( const QSizeF& ) const override; + QRectF clipRect() const override; + QRectF focusIndicatorRect() const override; + Q_SIGNALS: void edgeChanged( Qt::Edge ); void dragMarginChanged( qreal ); @@ -49,9 +52,13 @@ class QSK_EXPORT QskDrawer : public QskPopup protected: void itemChange( ItemChange, const ItemChangeData& ) override; void gestureEvent( QskGestureEvent* ) override; + void keyPressEvent( QKeyEvent* ) override; private: void setFading( bool ); + void setFadingClip( bool ); + + void setIntermediate( bool ); class PrivateData; std::unique_ptr< PrivateData > m_data; From e56360480fed61dbf3419ef122c9b8235f81ed45 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 18 Oct 2023 15:07:29 +0200 Subject: [PATCH 17/28] using 0/1 for closed/open ( instead of 1/0 ) --- skins/fluent2/QskFluent2Skin.cpp | 4 ++-- skins/material3/QskMaterial3Skin.cpp | 4 ++-- skins/squiek/QskSquiekSkin.cpp | 4 ++-- src/controls/QskDrawer.cpp | 8 +++++--- src/nodes/QskSlideInNode.cpp | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 30829413..963e22a3 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -745,8 +745,8 @@ void Editor::setupMenuMetrics() setPadding( Q::Icon, { 8, 8, 0, 8 } ); #if 1 - setPosition( Q::Panel, 0 ); - setPosition( Q::Panel | QskPopup::Closed, 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 ); diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 9ea1c054..f5780f04 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -378,8 +378,8 @@ void Editor::setupMenu() setColor( Q::Text, m_pal.onSurface ); setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); - setPosition( Q::Panel, 0 ); - setPosition( Q::Panel | QskPopup::Closed, 1 ); + 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 ); diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index c5ea53ab..f92ba16a 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -446,8 +446,8 @@ void Editor::setupMenu() setGraphicRole( Q::Icon | Q::Disabled, DisabledSymbol ); setGraphicRole( Q::Icon | Q::Selected, CursorSymbol ); - setPosition( Q::Panel, 0 ); - setPosition( Q::Panel | QskPopup::Closed, 1 ); + 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 ); diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 55a8d33f..717a30e5 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -108,6 +108,8 @@ static inline QRectF qskSlidingRect( auto x = 0.0; auto y = 0.0; + ratio = 1.0 - ratio; + switch( edge ) { case Qt::LeftEdge: @@ -437,7 +439,7 @@ QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const if ( isFading() ) ratio = metric( faderAspect() ); else - ratio = isOpen() ? 0.0 : 1.0; + ratio = isOpen() ? 1.0 : 0.0; return qskSlidingRect( size, m_data->edge, ratio ); } @@ -467,8 +469,8 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, void QskDrawer::setFading( bool on ) { - const qreal from = on ? 1.0 : 0.0; - const qreal to = on ? 0.0 : 1.0; + const qreal from = on ? 0.0 : 1.0; + const qreal to = on ? 1.0 : 0.0; const auto aspect = faderAspect(); diff --git a/src/nodes/QskSlideInNode.cpp b/src/nodes/QskSlideInNode.cpp index 6c77b366..24590845 100644 --- a/src/nodes/QskSlideInNode.cpp +++ b/src/nodes/QskSlideInNode.cpp @@ -59,7 +59,7 @@ void QskSlideInNode::updateTranslation( const QRectF& rect, { // clipping - if ( progress > 0.0 && progress <= 1.0 ) + if ( progress >= 0.0 && progress < 1.0 ) { if ( d->clipNode == nullptr ) { @@ -85,7 +85,7 @@ void QskSlideInNode::updateTranslation( const QRectF& rect, // translation qreal dx = 0.0; - qreal dy = -progress* rect.height(); + qreal dy = ( progress - 1.0 ) * rect.height(); if ( dx != 0.0 || dy != 0.0 ) { From 4cbd0d62a5c079a791da3102a7c545d75618c9d8 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 18 Oct 2023 15:10:55 +0200 Subject: [PATCH 18/28] respect QskPopup::hasFaderEffect --- src/controls/QskDrawer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 717a30e5..614271d2 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -469,6 +469,9 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, void QskDrawer::setFading( bool on ) { + if ( !hasFaderEffect() ) + return; + const qreal from = on ? 0.0 : 1.0; const qreal to = on ? 1.0 : 0.0; From b3c7eb7412fe3dfb96ceba172c7395ca2e13ac7b Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 19 Oct 2023 09:47:42 +0200 Subject: [PATCH 19/28] QskPopup::fading renamed to QskPopup::transitioning --- src/controls/QskDrawer.cpp | 23 ++++++------- src/controls/QskDrawer.h | 3 +- src/controls/QskMenu.cpp | 8 ++--- src/controls/QskMenuSkinlet.cpp | 2 +- src/controls/QskPopup.cpp | 58 ++++++++++++--------------------- src/controls/QskPopup.h | 26 ++++++++------- 6 files changed, 51 insertions(+), 69 deletions(-) diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 614271d2..3b965e36 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -204,7 +204,7 @@ namespace bool isAcceptedPos( const QPointF& pos ) const override { auto drawer = qobject_cast< const QskDrawer* >( targetItem() ); - if ( drawer->isFading() ) + if ( drawer->isTransitioning() ) return false; auto rect = qskItemRect( watchedItem() ); @@ -271,7 +271,7 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) setInteractive( true ); setPopupFlag( PopupFlag::CloseOnPressOutside, true ); - setFaderAspect( Panel | QskAspect::Position | QskAspect::Metric ); + setTransitionAspect( Panel | QskAspect::Position | QskAspect::Metric ); /* The drawer wants to be on top of the parent - not being @@ -281,8 +281,8 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) setPlacementPolicy( QskPlacementPolicy::Ignore ); m_data->resetListener( this ); - connect( this, &QskPopup::openChanged, this, &QskDrawer::setFading ); - connect( this, &QskPopup::fadingChanged, this, &QskDrawer::setIntermediate ); + connect( this, &QskPopup::openChanged, this, &QskDrawer::setSliding ); + connect( this, &QskPopup::transitioningChanged, this, &QskDrawer::setIntermediate ); } QskDrawer::~QskDrawer() @@ -436,8 +436,8 @@ QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const { qreal ratio; - if ( isFading() ) - ratio = metric( faderAspect() ); + if ( isTransitioning() ) + ratio = metric( transitionAspect() ); else ratio = isOpen() ? 1.0 : 0.0; @@ -467,15 +467,12 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, } } -void QskDrawer::setFading( bool on ) +void QskDrawer::setSliding( bool on ) { - if ( !hasFaderEffect() ) - return; - const qreal from = on ? 0.0 : 1.0; const qreal to = on ? 1.0 : 0.0; - const auto aspect = faderAspect(); + const auto aspect = transitionAspect(); auto hint = animationHint( aspect ); hint.updateFlags = QskAnimationHint::UpdatePolish; @@ -485,7 +482,7 @@ void QskDrawer::setFading( bool on ) QRectF QskDrawer::clipRect() const { - if ( !isFading() ) + if ( !isTransitioning() ) return Inherited::clipRect(); /* @@ -534,7 +531,7 @@ void QskDrawer::setIntermediate( bool on ) QRectF QskDrawer::focusIndicatorRect() const { - if ( isFading() ) + if ( isTransitioning() ) return QRectF(); return Inherited::focusIndicatorRect(); diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index d7e66d0c..de02b435 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -55,8 +55,7 @@ class QSK_EXPORT QskDrawer : public QskPopup void keyPressEvent( QKeyEvent* ) override; private: - void setFading( bool ); - void setFadingClip( bool ); + void setSliding( bool ); void setIntermediate( bool ); diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 7390e317..43ed0a1f 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -58,7 +58,7 @@ QskMenu::QskMenu( QQuickItem* parent ) , m_data( new PrivateData ) { setModal( true ); - setFaderAspect( QskMenu::Panel | QskAspect::Position | QskAspect::Metric ); + setTransitionAspect( QskMenu::Panel | QskAspect::Position | QskAspect::Metric ); setPopupFlag( QskPopup::CloseOnPressOutside, true ); setPopupFlag( QskPopup::DeleteOnClose, true ); @@ -67,8 +67,8 @@ QskMenu::QskMenu( QQuickItem* parent ) initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - // we hide the focus indicator while fading - connect( this, &QskMenu::fadingChanged, this, + // we hide the focus indicator while sliding + connect( this, &QskMenu::transitioningChanged, this, &QskControl::focusIndicatorRectChanged ); connect( this, &QskMenu::opened, this, @@ -452,7 +452,7 @@ void QskMenu::aboutToShow() QRectF QskMenu::focusIndicatorRect() const { - if ( isFading() ) + if ( isTransitioning() ) return QRectF(); if( currentIndex() >= 0 ) diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index d75323a9..13be2b34 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -236,7 +236,7 @@ QSGNode* QskMenuSkinlet::updateSubNode( auto slideInNode = QskSGNode::ensureNode< QskSlideInNode >( node ); - const auto progress = popup->metric( popup->faderAspect() ); + const auto progress = popup->metric( popup->transitionAspect() ); slideInNode->updateTranslation( rect, Qt::TopEdge, progress ); auto contentsNode = updateContentsNode( popup, slideInNode->contentsNode() ); diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index 50714836..bef48146 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -130,7 +130,6 @@ class QskPopup::PrivateData PrivateData() : flags( 0 ) , isModal( false ) - , hasFaderEffect( true ) , autoGrabFocus( true ) , handoverFocus( true ) { @@ -139,11 +138,10 @@ class QskPopup::PrivateData InputGrabber* inputGrabber = nullptr; uint priority = 0; - QskAspect faderAspect; + QskAspect transitionAspect; int flags : 4; bool isModal : 1; - bool hasFaderEffect : 1; const bool autoGrabFocus : 1; const bool handoverFocus : 1; @@ -218,9 +216,9 @@ void QskPopup::setOpen( bool on ) else Q_EMIT closed(); - if ( isFading() ) + if ( isTransitioning() ) { - Q_EMIT fadingChanged( true ); + Q_EMIT transitioningChanged( true ); } else { @@ -239,13 +237,13 @@ bool QskPopup::isOpen() const return !hasSkinState( QskPopup::Closed ); } -bool QskPopup::isFading() const +bool QskPopup::isTransitioning() const { - if ( m_data->faderAspect.value() == 0 ) + if ( m_data->transitionAspect.value() == 0 ) return false; QskSkinHintStatus status; - (void) effectiveSkinHint( m_data->faderAspect, &status ); + (void) effectiveSkinHint( m_data->transitionAspect, &status ); return status.source == QskSkinHintStatus::Animator; } @@ -298,37 +296,37 @@ void QskPopup::updateInputGrabber() } } -QskAspect QskPopup::faderAspect() const +QskAspect QskPopup::transitionAspect() const { - return m_data->faderAspect; + return m_data->transitionAspect; } -void QskPopup::setFaderAspect( QskAspect aspect ) +void QskPopup::setTransitionAspect( QskAspect aspect ) { - auto faderAspect = aspect; - faderAspect.clearStates(); // animated values are always stateless + auto transitionAspect = aspect; + transitionAspect.clearStates(); // animated values are always stateless - if ( faderAspect == m_data->faderAspect ) + if ( transitionAspect == m_data->transitionAspect ) return; - if ( isFading() ) + if ( isTransitioning() ) { // stop the running animation TODO ... } - m_data->faderAspect = faderAspect; + m_data->transitionAspect = transitionAspect; } bool QskPopup::isTransitionAccepted( QskAspect aspect ) const { - if ( isVisible() && m_data->hasFaderEffect ) + if ( isVisible() ) { if ( ( aspect.value() == 0 ) ) { return true; } - if ( aspect == m_data->faderAspect ) + if ( aspect == m_data->transitionAspect ) return true; if ( aspect.isColor() ) @@ -371,20 +369,6 @@ 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 ) { const auto newFlags = static_cast< int >( flags ); @@ -496,7 +480,7 @@ bool QskPopup::event( QEvent* event ) const auto animtorEvent = static_cast< QskAnimatorEvent* >( event ); if ( ( animtorEvent->state() == QskAnimatorEvent::Terminated ) - && ( animtorEvent->aspect() == m_data->faderAspect ) ) + && ( animtorEvent->aspect() == m_data->transitionAspect ) ) { if ( !isOpen() ) { @@ -506,7 +490,7 @@ bool QskPopup::event( QEvent* event ) deleteLater(); } - Q_EMIT fadingChanged( false ); + Q_EMIT transitioningChanged( false ); } break; @@ -643,7 +627,7 @@ int QskPopup::execPopup() */ connect( popup, &QObject::destroyed, this, &EventLoop::reject ); - connect( popup, &QskPopup::fadingChanged, this, &EventLoop::maybeQuit ); + connect( popup, &QskPopup::transitioningChanged, this, &EventLoop::maybeQuit ); connect( popup, &QskPopup::openChanged, this, &EventLoop::maybeQuit ); } @@ -658,7 +642,7 @@ int QskPopup::execPopup() { if ( auto popup = qobject_cast< const QskPopup* >( parent() ) ) { - if ( popup->isOpen() || popup->isFading() ) + if ( popup->isOpen() || popup->isTransitioning() ) return; } @@ -666,7 +650,7 @@ int QskPopup::execPopup() } }; - if ( isOpen() || isFading() ) + if ( isOpen() || isTransitioning() ) { qWarning() << "QskPopup::exec: popup is already opened"; return -1; diff --git a/src/controls/QskPopup.h b/src/controls/QskPopup.h index b2c99c7d..f811a26d 100644 --- a/src/controls/QskPopup.h +++ b/src/controls/QskPopup.h @@ -14,13 +14,11 @@ class QSK_EXPORT QskPopup : public QskControl Q_PROPERTY( bool open READ isOpen WRITE setOpen NOTIFY openChanged ) Q_PROPERTY( bool modal READ isModal WRITE setModal NOTIFY modalChanged ) + Q_PROPERTY( bool transitioning READ isTransitioning NOTIFY transitioningChanged ) Q_PROPERTY( bool overlay READ hasOverlay WRITE setOverlay RESET resetOverlay 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; @@ -58,16 +56,16 @@ class QSK_EXPORT QskPopup : public QskControl void setPriority( uint ); uint priority() const; - void setFaderEffect( bool ); - bool hasFaderEffect() const; + // transitions between open/closed states + QskAspect transitionAspect() const; + void setTransitionAspect( QskAspect ); - QskAspect faderAspect() const; - void setFaderAspect( QskAspect ); - - virtual QRectF overlayRect() const; + bool isTransitioning() const; bool isOpen() const; - bool isFading() const; + bool isClosed() const; + + virtual QRectF overlayRect() const; public Q_SLOTS: void open(); @@ -80,12 +78,11 @@ class QSK_EXPORT QskPopup : public QskControl void opened(); void closed(); void openChanged( bool ); - void fadingChanged( bool ); + void transitioningChanged( bool ); void modalChanged( bool ); void overlayChanged( bool ); void priorityChanged( uint ); - void faderEffectChanged( bool ); protected: void aboutToShow() override; @@ -115,6 +112,11 @@ class QSK_EXPORT QskPopup : public QskControl std::unique_ptr< PrivateData > m_data; }; +inline bool QskPopup::isClosed() const +{ + return !isOpen(); +} + Q_DECLARE_OPERATORS_FOR_FLAGS( QskPopup::PopupFlags ) #endif From a0f5bbf54a1bc118ad94fe1bfc0422724edf87b3 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 22 Oct 2023 10:51:29 +0200 Subject: [PATCH 20/28] more defensive updates for non-color animations --- src/controls/QskHintAnimator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controls/QskHintAnimator.cpp b/src/controls/QskHintAnimator.cpp index db6e7253..d9dc82f0 100644 --- a/src/controls/QskHintAnimator.cpp +++ b/src/controls/QskHintAnimator.cpp @@ -141,7 +141,7 @@ void QskHintAnimator::advance( qreal progress ) { if ( m_updateFlags == QskAnimationHint::UpdateAuto ) { - if ( m_aspect.isMetric() ) + if ( !m_aspect.isColor() ) { m_control->resetImplicitSize(); From d4ad863b7af3c19510d49368a7de73773cfe6f51 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 22 Oct 2023 10:52:28 +0200 Subject: [PATCH 21/28] sending geometry events to the root item, to support event filtering --- src/controls/QskWindow.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/controls/QskWindow.cpp b/src/controls/QskWindow.cpp index a0b7a3b5..f46c6da5 100644 --- a/src/controls/QskWindow.cpp +++ b/src/controls/QskWindow.cpp @@ -424,8 +424,18 @@ void QskWindow::exposeEvent( QExposeEvent* event ) void QskWindow::resizeEvent( QResizeEvent* event ) { + auto rootItem = contentItem(); + + const auto oldRect = qskItemGeometry( rootItem ); Inherited::resizeEvent( event ); + const auto newRect = qskItemGeometry( rootItem ); + if ( newRect != oldRect ) + { + QskGeometryChangeEvent event( newRect, oldRect ); + QCoreApplication::sendEvent( rootItem, &event ); + } + if ( isExposed() ) layoutItems(); } From cec07e78ffc35238436e90d5aa4e801087337666 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 22 Oct 2023 10:58:07 +0200 Subject: [PATCH 22/28] QskSkinnable::runnigHintAnimator added --- src/controls/QskPopup.cpp | 9 ++------- src/controls/QskSkinnable.cpp | 17 +++++++++++++++++ src/controls/QskSkinnable.h | 3 +++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index bef48146..54cd4060 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -10,6 +10,7 @@ #include "QskWindow.h" #include "QskEvent.h" #include "QskPlatform.h" +#include "QskHintAnimator.h" #include @@ -239,13 +240,7 @@ bool QskPopup::isOpen() const bool QskPopup::isTransitioning() const { - if ( m_data->transitionAspect.value() == 0 ) - return false; - - QskSkinHintStatus status; - (void) effectiveSkinHint( m_data->transitionAspect, &status ); - - return status.source == QskSkinHintStatus::Animator; + return runningHintAnimator( m_data->transitionAspect ) != nullptr; } QRectF QskPopup::overlayRect() const diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 03b9139c..e82f51b3 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -988,6 +988,23 @@ bool QskSkinnable::moveSkinHint( QskAspect aspect, const QVariant& value ) return moveSkinHint( aspect, effectiveSkinHint( aspect ), value ); } +const QskHintAnimator* QskSkinnable::runningHintAnimator( + QskAspect aspect, int index ) const +{ + const auto& animators = m_data->animators; + + if ( animators.isEmpty() ) + return nullptr; + + aspect = qskAnimatorAspect( aspect ); + + auto animator = animators.animator( aspect, index ); + if ( animator == nullptr && index >= 0 ) + animator = animators.animator( aspect, -1 ); + + return animator; +} + QVariant QskSkinnable::animatedHint( QskAspect aspect, QskSkinHintStatus* status ) const { diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index 51d2f1a7..94886960 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -25,6 +25,7 @@ class QQuickItem; class QskArcMetrics; class QskControl; class QskAnimationHint; +class QskHintAnimator; class QskColorFilter; class QskBoxShapeMetrics; class QskBoxBorderMetrics; @@ -262,6 +263,8 @@ class QSK_EXPORT QskSkinnable bool startHintTransitions( const QVector< QskAspect::Subcontrol >&, QskAspect::States, QskAspect::States, int index = -1 ); + const QskHintAnimator* runningHintAnimator( QskAspect, int index = -1 ) const; + protected: virtual void updateNode( QSGNode* ); virtual bool isTransitionAccepted( QskAspect ) const; From 9de8663f93fa2259725d269527e15549e63b1058 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 23 Oct 2023 10:16:31 +0200 Subject: [PATCH 23/28] Don't flatten border colors to a color without alpha value #345 --- skins/fluent2/QskFluent2Skin.cpp | 96 +++++++++++++++++++------------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 963e22a3..e1e114ff 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -4,7 +4,7 @@ *****************************************************************************/ /* - Definitions ( where possible ) taken from + Definitions ( where possible ) taken from https://www.figma.com/file/NAWMapFlXnoOb86Q2H5GKr/Windows-UI-(Community) */ @@ -35,35 +35,18 @@ */ /* - The palette is made of a specific configurable colors and - predefined semitransparent shades of gray. Both need to - be resolved to opaque colors with the base colors of the sections. + The palette is made of a couple of configurable base/accent colors and a + predefined set of semitransparent shades of gray. These grays are used + to create darkened/lightend colors from the configurable colors that can + be used for borders etc. - Resolving the colors can be done in 2 ways: + However borders need to be darkened for light base colors and lightened for + dark ones. So we actually have 2 different sets of grays for light/dark + colors schemes. - - render time - - This actually means, that we do not create opaque colors and - create the scene graph nodes with semitransparent colors. - - - definition time - - We create opaque colors for the base colors of the sections - and set them as skin hints. - - Resolving at render time sounds like the right solution as we - background colors set in application code will just work. - - Unfortunately we have 2 different sets of grays for light/dark - base colors and when applications are setting a light color, where a - dark color ( or v.v ) is expected we might end up with unacceptable - results: ( white on light or black on dark ). - - So there are pros and cons and we do not have a final opinion - about waht to do. For the moment we implement resolving at definition - time as an option to be able to play with both solutions. + The main advantage of this concept is, that a working color schemes + can be defined by setting the accent/base colors only. */ - #include "QskFluent2Skin.h" #include "QskFluent2Theme.h" @@ -139,9 +122,30 @@ namespace return qRgba( value, value, value, qRound( opacity * 255 ) ); } + /* + When application code is manipulating base colors manually the colors of + the borders will be lightened/darkened from it like expected - as long + as the application color matches the color scheme. Otherwise we end + up with lightened borders on light backgrounds or v.v. + + To avoid this problem we could resolve the grays with the background + colors of the sections at definition time. This solves the problem with + applications using backgrounds from the "wrong" color scheme, but requires + more work for customizing controls hen using the "correct" scheme + ( -> border colors need to be set as well ). + + When enabling QSK_RESOLVE_COLORS a code path is enabled that is intended + to play with resolving the grays at definition time. But I'm not decided + if it is worth to make a feature from it. TODO ... + */ + +#define QSK_RESOLVE_COLORS 0 + +#if QSK_RESOLVE_COLORS + inline constexpr QRgb rgbFlattened( QRgb foreground, QRgb background ) { - //Q_ASSERT( qAlpha( background ) == 255 ); + Q_ASSERT( qAlpha( background ) == 255 ); const auto r2 = qAlpha( foreground ) / 255.0; const auto r1 = 1.0 - r2; @@ -160,15 +164,25 @@ namespace or without resolving the foreground alpha value */ -#if 0 return rgbFlattened( foreground, background ); -#else - //Q_ASSERT( qAlpha( background ) == 255 ); + } + +#else // !QSK_RESOLVE_COLORS + + inline QRgb rgbFlattened( QRgb foreground, QRgb background ) + { + const auto alpha = qAlpha( foreground ) / 255.0; + return QskRgb::interpolated( background, foreground, alpha ); + } + + inline constexpr QRgb rgbSolid( QRgb foreground, QRgb background ) + { Q_UNUSED( background ); return foreground; -#endif } +#endif + class Editor : private QskSkinHintTableEditor { public: @@ -521,7 +535,8 @@ void Editor::setupComboBoxColors( const auto& pal = theme.palette; - for ( const auto state : { QskAspect::NoState, Q::Hovered, Q::Focused, Q::Pressed, Q::Disabled } ) + for ( const auto state : + { QskAspect::NoState, Q::Hovered, Q::Focused, Q::Pressed, Q::Disabled } ) { QRgb panelColor, borderColor1, borderColor2, textColor; @@ -693,7 +708,7 @@ void Editor::setupListViewColors( { if ( state1 == Q::Hovered ) cellColor = pal.fillColor.subtle.tertiary; - else + else cellColor = pal.fillColor.subtle.secondary; } @@ -793,7 +808,8 @@ void Editor::setupMenuColors( setColor( Q::Text | Q::Selected | Q::Pressed, pal.fillColor.text.secondary ); setGraphicRole( Q::Icon, QskFluent2Skin::GraphicRoleFillColorTextPrimary ); - setGraphicRole( Q::Icon | Q::Selected | Q::Pressed, QskFluent2Skin::GraphicRoleFillColorTextSecondary ); + setGraphicRole( Q::Icon | Q::Selected | Q::Pressed, + QskFluent2Skin::GraphicRoleFillColorTextSecondary ); } void Editor::setupPageIndicatorMetrics() @@ -811,7 +827,7 @@ void Editor::setupPageIndicatorMetrics() /* Pressed/Hovered are not yet implemented. - Sizes would be: + Sizes would be: - Q::Pressed : 3 - Q::Pressed | Q::Selected : 5 @@ -1048,7 +1064,7 @@ void Editor::setupRadioBoxMetrics() However the colors of the inner side of the border are not solid for the selected states and we use a dummy indicator to get this done. - How to solve this in a better way, TODO ... + How to solve this in a better way, TODO ... */ setBoxShape( Q::CheckIndicator, 100, Qt::RelativeSize ); @@ -1098,7 +1114,7 @@ void Editor::setupRadioBoxColors( if ( states & Q::Disabled ) textColor = pal.fillColor.text.disabled; - QRgb panelBorderColor; + QRgb panelBorderColor; if ( states & ( Q::Disabled | Q::Pressed ) ) panelBorderColor = pal.strokeColor.controlStrong.disabled; else @@ -1411,7 +1427,7 @@ void Editor::setupSliderColors( setBoxBorderGradient( Q::Handle, pal.elevation.circle.border, handleColor ); } - for ( auto state : { A::NoState , Q::Pressed , Q::Disabled } ) + for ( auto state : { A::NoState, Q::Pressed, Q::Disabled } ) { QRgb grooveColor, fillColor, rippleColor; @@ -1571,7 +1587,7 @@ void Editor::setupTabButtonColors( using Q = QskTabButton; const auto& pal = theme.palette; - for ( const auto state : { QskAspect::NoState, + for ( const auto state : { QskAspect::NoState, Q::Checked, Q::Hovered, Q::Pressed, Q::Disabled } ) { QRgb panelColor, textColor; From 346aff98d18e6ad437e48565fd1cc74153bf529e Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 23 Oct 2023 18:32:08 +0200 Subject: [PATCH 24/28] using QQuickItemPrivate::inDestructor for Qt >= 6.5 instead of the componentComplete hack done in ~QskQuickItem --- src/controls/QskMainView.cpp | 2 +- src/controls/QskPopup.cpp | 2 +- src/controls/QskQuick.cpp | 15 +++++++++++++++ src/controls/QskQuick.h | 1 + src/controls/QskQuickItem.cpp | 5 +++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controls/QskMainView.cpp b/src/controls/QskMainView.cpp index ced25d56..cad52776 100644 --- a/src/controls/QskMainView.cpp +++ b/src/controls/QskMainView.cpp @@ -103,7 +103,7 @@ void QskMainView::focusInEvent( QFocusEvent* event ) { if ( auto focusItem = nextItemInFocusChain( true ) ) { - if ( qskIsItemComplete( focusItem ) + if ( !qskIsItemInDestructor( focusItem ) && qskIsAncestorOf( this, focusItem ) ) { focusItem->setFocus( true ); diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index 54cd4060..3d680049 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -527,7 +527,7 @@ void QskPopup::focusInEvent( QFocusEvent* event ) if ( auto focusItem = nextItemInFocusChain( true ) ) { - if ( qskIsItemComplete( focusItem ) + if ( !qskIsItemInDestructor( focusItem ) && qskIsAncestorOf( this, focusItem ) ) { focusItem->setFocus( true ); diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 436f6f88..43967d29 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -47,6 +47,21 @@ bool qskIsItemComplete( const QQuickItem* item ) return QQuickItemPrivate::get( item )->componentComplete; } +bool qskIsItemInDestructor( const QQuickItem* item ) +{ + auto d = QQuickItemPrivate::get( item ); + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 ) + return d->inDestructor; +#else + /* + QskQuickItem sets componentComplete to false in its destructor, + but for other items we will will return the wrong information + */ + return !d->componentComplete; +#endif +} + bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child ) { if ( item == nullptr || child == nullptr ) diff --git a/src/controls/QskQuick.h b/src/controls/QskQuick.h index 4141483e..458edf4f 100644 --- a/src/controls/QskQuick.h +++ b/src/controls/QskQuick.h @@ -24,6 +24,7 @@ template< typename T > class QList; of QQuickItem. */ +QSK_EXPORT bool qskIsItemInDestructor( const QQuickItem* ); QSK_EXPORT bool qskIsItemComplete( const QQuickItem* ); QSK_EXPORT bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child ); QSK_EXPORT bool qskIsTabFence( const QQuickItem* ); diff --git a/src/controls/QskQuickItem.cpp b/src/controls/QskQuickItem.cpp index 41be05a5..76d786ac 100644 --- a/src/controls/QskQuickItem.cpp +++ b/src/controls/QskQuickItem.cpp @@ -180,6 +180,11 @@ QskQuickItem::~QskQuickItem() We set componentComplete to false, so that operations that are triggered by detaching the item from its parent can be aware of the about-to-delete state. + + Note, that since Qt >= 6.5 this information is stored + in QQuickItemPrivate::inDestructor. + + s.a: qskIsItemInDestructor */ d_func()->componentComplete = false; From 83df3ad8da18b5eb0e51e3679603910d581f7029 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 25 Oct 2023 10:07:38 +0200 Subject: [PATCH 25/28] QskPopup key handling added --- src/controls/QskDrawer.cpp | 60 +----------------------------- src/controls/QskDrawer.h | 1 - src/controls/QskMenu.cpp | 20 ++++------ src/controls/QskPopup.cpp | 17 +++++++-- src/controls/QskPopup.h | 1 + src/dialogs/QskDialogSubWindow.cpp | 11 ++---- 6 files changed, 27 insertions(+), 83 deletions(-) diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 3b965e36..4793cdab 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -32,7 +32,7 @@ static inline qreal qskDefaultDragMargin() 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 ? @@ -352,64 +352,6 @@ qreal QskDrawer::dragMargin() const return m_data->dragMargin; } -void QskDrawer::keyPressEvent( QKeyEvent* event ) -{ - if ( isOpen() ) - { - bool doClose = false; - - const auto key = event->key(); - - switch( key ) - { - case Qt::Key_Escape: - case Qt::Key_Cancel: - { - doClose = true; - break; - } - -#if 0 - /* - Do we want to have this - and what about opening with - the same keys ??? - */ - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Left: - case Qt::Key_Right: - { - switch( m_data->edge ) - { - case Qt::TopEdge: - doClose = ( key == Qt::Key_Up ); - break; - case Qt::BottomEdge: - doClose = ( key == Qt::Key_Down ); - break; - case Qt::LeftEdge: - doClose = ( key == Qt::Key_Left ); - break; - case Qt::RightEdge: - doClose = ( key == Qt::Key_Right ); - break; - } - - break; - } -#endif - } - - if ( doClose ) - { - close(); - return; - } - } - - Inherited::keyPressEvent( event ); -} - void QskDrawer::gestureEvent( QskGestureEvent* event ) { if ( event->gesture()->type() == QskGesture::Pan ) diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index de02b435..f8aada41 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -52,7 +52,6 @@ class QSK_EXPORT QskDrawer : public QskPopup protected: void itemChange( ItemChange, const ItemChangeData& ) override; void gestureEvent( QskGestureEvent* ) override; - void keyPressEvent( QKeyEvent* ) override; private: void setSliding( bool ); diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 43ed0a1f..ba86d66c 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -268,20 +268,18 @@ void QskMenu::keyPressEvent( QKeyEvent* event ) if( m_data->currentIndex < 0 ) return; - int key = event->key(); - - switch( key ) + switch( event->key() ) { case Qt::Key_Up: { traverse( -1 ); - break; + return; } case Qt::Key_Down: { traverse( 1 ); - break; + return; } case Qt::Key_Select: @@ -293,19 +291,17 @@ void QskMenu::keyPressEvent( QKeyEvent* event ) return; } - case Qt::Key_Escape: - case Qt::Key_Cancel: - { - close(); - return; - } - default: { if ( const int steps = qskFocusChainIncrement( event ) ) + { traverse( steps ); + return; + } } } + + return Inherited::keyPressEvent( event ); } void QskMenu::keyReleaseEvent( QKeyEvent* ) diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index 3d680049..d993cc51 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -472,10 +472,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() == m_data->transitionAspect ) ) { if ( !isOpen() ) { @@ -507,6 +507,17 @@ bool QskPopup::event( QEvent* event ) return ok; } +void QskPopup::keyPressEvent( QKeyEvent* event ) +{ + if ( qskIsStandardKeyInput( event, QKeySequence::Cancel ) ) + { + close(); + return; + } + + return Inherited::keyPressEvent( event ); +} + void QskPopup::focusInEvent( QFocusEvent* event ) { Inherited::focusInEvent( event ); diff --git a/src/controls/QskPopup.h b/src/controls/QskPopup.h index f811a26d..ff1201b2 100644 --- a/src/controls/QskPopup.h +++ b/src/controls/QskPopup.h @@ -91,6 +91,7 @@ class QSK_EXPORT QskPopup : public QskControl bool event( QEvent* ) override; void focusInEvent( QFocusEvent* ) override; void focusOutEvent( QFocusEvent* ) override; + void keyPressEvent( QKeyEvent* ) override; void windowChangeEvent( QskWindowChangeEvent* ) override; void itemChange( QQuickItem::ItemChange, diff --git a/src/dialogs/QskDialogSubWindow.cpp b/src/dialogs/QskDialogSubWindow.cpp index 69e84ffb..5643fec7 100644 --- a/src/dialogs/QskDialogSubWindow.cpp +++ b/src/dialogs/QskDialogSubWindow.cpp @@ -278,15 +278,10 @@ void QskDialogSubWindow::keyPressEvent( QKeyEvent* event ) { auto button = m_data->buttonBox->defaultButton(); if ( button && button->isEnabled() ) + { button->click(); - } - - if ( qskIsStandardKeyInput( event, QKeySequence::Cancel ) ) - { - // using shortcuts instead ??? - - reject(); - return; + return; + } } Inherited::keyPressEvent( event ); From b5fb6d80ad64ad6f7f0c9f40a1d866025a63770e Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 30 Oct 2023 17:08:01 +0100 Subject: [PATCH 26/28] the itemNode is always a QSGTransformNode --- src/controls/QskQuick.cpp | 2 +- src/controls/QskQuick.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 43967d29..6a8f045f 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -371,7 +371,7 @@ QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* item ) return QList< QQuickItem* >(); } -const QSGNode* qskItemNode( const QQuickItem* item ) +const QSGTransformNode* qskItemNode( const QQuickItem* item ) { if ( item == nullptr ) return nullptr; diff --git a/src/controls/QskQuick.h b/src/controls/QskQuick.h index 458edf4f..6b2ae2df 100644 --- a/src/controls/QskQuick.h +++ b/src/controls/QskQuick.h @@ -16,6 +16,7 @@ class QskSizePolicy; class QQuickItem; class QSGNode; +class QSGTransformNode; class QRectF; template< typename T > class QList; @@ -67,7 +68,7 @@ QSK_EXPORT QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* ); QSK_EXPORT void qskUpdateInputMethod( const QQuickItem*, Qt::InputMethodQueries ); QSK_EXPORT void qskInputMethodSetVisible( const QQuickItem*, bool ); -QSK_EXPORT const QSGNode* qskItemNode( const QQuickItem* ); +QSK_EXPORT const QSGTransformNode* qskItemNode( const QQuickItem* ); QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* ); QSK_EXPORT void qskItemUpdateRecursive( QQuickItem* ); From 3b4f1677095af67a063c6c5c595ad176cdee0427 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 30 Oct 2023 17:08:39 +0100 Subject: [PATCH 27/28] always sending ( ot posting ) animator events. Otherwise we might be one updateNode cycle too late --- src/controls/QskHintAnimator.cpp | 48 +++++++++++++++----------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/controls/QskHintAnimator.cpp b/src/controls/QskHintAnimator.cpp index d9dc82f0..cec53ccd 100644 --- a/src/controls/QskHintAnimator.cpp +++ b/src/controls/QskHintAnimator.cpp @@ -76,20 +76,28 @@ static inline QVariant qskAligned05( const QVariant& value ) #endif -static inline bool qskCheckReceiverThread( const QObject* receiver ) +static inline void qskSendAnimatorEvent( + const QskAspect aspect, int index, bool on, 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 - */ + const auto state = on ? QskAnimatorEvent::Started : QskAnimatorEvent::Terminated; - const QThread* thread = receiver->thread(); - if ( thread == nullptr ) - return true; - - return ( thread == QThread::currentThread() ); + const auto thread = receiver->thread(); + if ( thread && ( thread != QThread::currentThread() ) ) + { + /* + QskInputPanelSkinlet changes the skin state, what leads to + sending events from the wrong thread. We can't use + QCoreApplication::sendEvent then, TODO ... + */ + + auto event = new QskAnimatorEvent( aspect, index, state ); + QCoreApplication::postEvent( receiver, event ); + } + else + { + QskAnimatorEvent event( aspect, index, state ); + QCoreApplication::sendEvent( receiver, &event ); + } } QskHintAnimator::QskHintAnimator() noexcept @@ -338,11 +346,7 @@ void QskHintAnimatorTable::start( QskControl* control, animator->start(); - if ( qskCheckReceiverThread( control ) ) - { - QskAnimatorEvent event( aspect, index, QskAnimatorEvent::Started ); - QCoreApplication::sendEvent( control, &event ); - } + qskSendAnimatorEvent( aspect, index, true, control ); } const QskHintAnimator* QskHintAnimatorTable::animator( QskAspect aspect, int index ) const @@ -390,15 +394,7 @@ bool QskHintAnimatorTable::cleanup() it = animators.erase( it ); if ( control ) - { - if ( qskCheckReceiverThread( control ) ) - { - auto event = new QskAnimatorEvent( - aspect, index, QskAnimatorEvent::Terminated ); - - QCoreApplication::postEvent( control, event ); - } - } + qskSendAnimatorEvent( aspect, index, false, control ); } else { From 1ab236de9fc76de6576ae662dd3e9a31583073cd Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 3 Nov 2023 18:13:24 +0100 Subject: [PATCH 28/28] better fading support for popups, being used in QskDrawer/QskMenu/QskSubWindow --- skins/fluent2/QskFluent2Skin.cpp | 23 +-- skins/material3/QskMaterial3Skin.cpp | 10 +- skins/squiek/QskSquiekSkin.cpp | 16 +- src/CMakeLists.txt | 4 +- src/controls/QskDrawer.cpp | 259 +++++++++++++-------------- src/controls/QskDrawer.h | 18 +- src/controls/QskDrawerSkinlet.cpp | 45 +++++ src/controls/QskDrawerSkinlet.h | 40 +++++ src/controls/QskMenu.cpp | 70 +++++++- src/controls/QskMenu.h | 8 +- src/controls/QskMenuSkinlet.cpp | 16 +- src/controls/QskPopup.cpp | 94 +++++----- src/controls/QskPopup.h | 14 +- src/controls/QskPopupSkinlet.cpp | 38 +++- src/controls/QskSkin.cpp | 4 + src/controls/QskSubWindow.cpp | 11 ++ src/controls/QskSubWindow.h | 3 + src/controls/QskSubWindowSkinlet.cpp | 12 ++ src/dialogs/QskDialog.cpp | 27 +-- src/nodes/QskSlideInNode.cpp | 146 --------------- src/nodes/QskSlideInNode.h | 33 ---- 21 files changed, 454 insertions(+), 437 deletions(-) create mode 100644 src/controls/QskDrawerSkinlet.cpp create mode 100644 src/controls/QskDrawerSkinlet.h delete mode 100644 src/nodes/QskSlideInNode.cpp delete mode 100644 src/nodes/QskSlideInNode.h diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index e1e114ff..4f5de05d 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -623,13 +623,14 @@ void Editor::setupDrawerMetrics() using Q = QskDrawer; using A = QskAspect; -#if 1 - setAnimation( Q::Panel | A::Metric | A::Position, 200 ); -#endif + setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic ); } -void Editor::setupDrawerColors( QskAspect::Section, const QskFluent2Theme& ) +void Editor::setupDrawerColors( + QskAspect::Section section, const QskFluent2Theme& theme ) { + setGradient( QskDrawer::Panel | section, + theme.palette.background.solid.base ); } void Editor::setupFocusIndicatorMetrics() @@ -759,13 +760,7 @@ 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 + setAnimation( Q::Panel | A::Position, 100 ); } void Editor::setupMenuColors( @@ -876,9 +871,11 @@ 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 ); } @@ -1885,6 +1882,8 @@ void Editor::setupSwitchButtonColors( void Editor::setupSubWindow( const QskFluent2Theme& theme ) { using Q = QskSubWindow; + using A = QskAspect; + const auto& pal = theme.palette; setPadding( Q::Panel, { 0, 31, 0, 0 } ); @@ -1902,6 +1901,8 @@ void Editor::setupSubWindow( const QskFluent2Theme& theme ) setColor( Q::TitleBarText, pal.fillColor.text.primary ); setAlignment( Q::TitleBarText, Qt::AlignLeft ); setTextOptions( Q::TitleBarText, Qt::ElideRight, QskTextOptions::NoWrap ); + + setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic ); } void Editor::setupVirtualKeyboardMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index f5780f04..8443dbf5 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -378,11 +378,9 @@ 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 ); + + setAnimation( Q::Panel | A::Position, 75 ); } void Editor::setupTextLabel() @@ -811,7 +809,8 @@ void Editor::setupDrawer() using Q = QskDrawer; using A = QskAspect; - setAnimation( Q::Panel | A::Metric | A::Position, qskDuration ); + setGradient( Q::Panel, m_pal.background ); + setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic ); } void Editor::setupSlider() @@ -1261,6 +1260,7 @@ void Editor::setupSubWindow() for ( auto subControl : { Q::Panel, Q::TitleBarPanel, Q::TitleBarText } ) setAnimation( subControl | A::Color, qskDuration ); + setAnimation( Q::Panel | A::Position, qskDuration, QEasingCurve::OutCubic ); } QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme ) diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index f92ba16a..19530b32 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -409,7 +409,7 @@ 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 ) ); } void Editor::setupMenu() @@ -446,11 +446,8 @@ 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 ); + setAnimation( Q::Panel | A::Position, 100 ); } void Editor::setupTextLabel() @@ -761,10 +758,11 @@ void Editor::setupDialogButtonBox() void Editor::setupDrawer() { - using A = QskAspect; using Q = QskDrawer; + using A = QskAspect; - setAnimation( Q::Panel | A::Metric | A::Position, qskDuration ); + setPanel( Q::Panel, Plain ); + setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic ); } void Editor::setupTabButton() @@ -1126,8 +1124,12 @@ void Editor::setupSubWindow() setAlignment( Q::TitleBarText, Qt::AlignLeft | Qt::AlignVCenter ); +#if 1 for ( auto subControl : { Q::Panel, Q::TitleBarPanel, Q::TitleBarText } ) setAnimation( subControl | A::Color, qskDuration ); +#endif + + setAnimation( Q::Panel | A::Position, qskDuration, QEasingCurve::OutCubic ); } void Editor::setupSpinBox() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ce13c74..a2f36047 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -115,7 +115,6 @@ list(APPEND HEADERS nodes/QskRichTextRenderer.h nodes/QskScaleRenderer.h nodes/QskSGNode.h - nodes/QskSlideInNode.h nodes/QskStrokeNode.h nodes/QskStippledLineRenderer.h nodes/QskShapeNode.h @@ -147,7 +146,6 @@ list(APPEND SOURCES nodes/QskRichTextRenderer.cpp nodes/QskScaleRenderer.cpp nodes/QskSGNode.cpp - nodes/QskSlideInNode.cpp nodes/QskStrokeNode.cpp nodes/QskStippledLineRenderer.cpp nodes/QskShapeNode.cpp @@ -178,6 +176,7 @@ list(APPEND HEADERS controls/QskComboBoxSkinlet.h controls/QskControl.h controls/QskDrawer.h + controls/QskDrawerSkinlet.h controls/QskEvent.h controls/QskFlickAnimator.h controls/QskFocusIndicator.h @@ -281,6 +280,7 @@ list(APPEND SOURCES controls/QskControlPrivate.cpp controls/QskDirtyItemFilter.cpp controls/QskDrawer.cpp + controls/QskDrawerSkinlet.cpp controls/QskEvent.cpp controls/QskFlickAnimator.cpp controls/QskFocusIndicator.cpp diff --git a/src/controls/QskDrawer.cpp b/src/controls/QskDrawer.cpp index 4793cdab..87890a8d 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" @@ -20,10 +19,6 @@ QSK_QT_PRIVATE_BEGIN #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() @@ -63,77 +58,60 @@ 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& r, const QSizeF& sz, Qt::Edge edge ) { - const auto size = qskSizeConstraint( drawer, Qt::PreferredSize ); + switch( edge ) + { + case Qt::LeftEdge: + return QRectF( r.left(), r.top(), sz.width(), r.height() ); - QRectF r( 0.0, 0.0, size.width(), size.height() ); + case Qt::RightEdge: + return QRectF( r.right() - sz.width(), r.top(), sz.width(), r.height() ); + + case Qt::TopEdge: + return QRectF( r.left(), r.top(), r.width(), sz.height() ); + + case Qt::BottomEdge: + return QRectF( r.left(), r.bottom() - sz.height(), r.width(), sz.height() ); + } + + return QRectF(); +} + +static QPointF qskDrawerTranslation( const QskDrawer* drawer, const QSizeF& size ) +{ + const auto ratio = 1.0 - drawer->fadingFactor(); + + auto dx = 0.0; + auto dy = 0.0; switch( drawer->edge() ) { case Qt::LeftEdge: - { - r.setHeight( rect.height() ); - r.moveRight( rect.left() + size.width() ); - break; - } - case Qt::RightEdge: - { - r.setHeight( rect.height() ); - r.moveLeft( rect.right() - size.width() ); - break; - } - - case Qt::TopEdge: - { - r.setWidth( rect.width() ); - r.moveBottom( rect.top() + size.height() ); - break; - } - - case Qt::BottomEdge: - { - r.setWidth( rect.width() ); - r.moveTop( rect.bottom() - size.height() ); - break; - } - } - - 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(); + dx = -ratio * size.width(); break; case Qt::RightEdge: - x = ratio * size.width(); + dx = ratio * size.width(); break; case Qt::TopEdge: - y = -ratio * size.height(); + dy = -ratio * size.height(); break; case Qt::BottomEdge: - y = ratio * size.height(); + dy = ratio * size.height(); break; } - return QRectF( x, y, size.width(), size.height() ); + return QPointF( dx, dy ); } namespace { + // Using an eventFilter for QskEvent::GeometryChange instead ??? + class GeometryListener final : public QQuickItemChangeListener { public: @@ -160,14 +138,7 @@ namespace 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 + m_adjustedItem->polish(); } void setEnabled( bool on ) @@ -184,10 +155,7 @@ namespace QQuickItem* m_item; QQuickItem* m_adjustedItem; }; -} -namespace -{ class GestureRecognizer : public QskPanGestureRecognizer { using Inherited = QskPanGestureRecognizer; @@ -204,7 +172,7 @@ namespace bool isAcceptedPos( const QPointF& pos ) const override { auto drawer = qobject_cast< const QskDrawer* >( targetItem() ); - if ( drawer->isTransitioning() ) + if ( drawer->isFading() ) return false; auto rect = qskItemRect( watchedItem() ); @@ -243,6 +211,11 @@ namespace class QskDrawer::PrivateData { public: + PrivateData( Qt::Edge edge ) + : edge( edge ) + { + } + inline void resetListener( QskDrawer* drawer ) { delete listener; @@ -252,37 +225,39 @@ class QskDrawer::PrivateData listener = new GeometryListener( drawer->parentItem(), drawer ); } - Qt::Edge edge = Qt::LeftEdge; - GestureRecognizer* gestureRecognizer = nullptr; GeometryListener* listener = nullptr; + GestureRecognizer* gestureRecognizer = nullptr; qreal dragMargin = qskDefaultDragMargin(); + Qt::Edge edge; }; QskDrawer::QskDrawer( QQuickItem* parentItem ) + : QskDrawer( Qt::LeftEdge, parentItem ) +{ +} + +QskDrawer::QskDrawer( Qt::Edge edge, QQuickItem* parentItem ) : Inherited ( parentItem ) - , m_data( new PrivateData ) + , m_data( new PrivateData( edge ) ) { #if 1 setZ( 1 ); #endif - 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 + A drawer 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 ); - m_data->resetListener( this ); - connect( this, &QskPopup::openChanged, this, &QskDrawer::setSliding ); - connect( this, &QskPopup::transitioningChanged, this, &QskDrawer::setIntermediate ); + setAutoLayoutChildren( true ); + setInteractive( true ); + + connect( this, &QskPopup::fadingChanged, this, &QQuickItem::setClip ); } QskDrawer::~QskDrawer() @@ -374,18 +349,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 ) { @@ -409,74 +372,96 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, } } -void QskDrawer::setSliding( bool on ) +void QskDrawer::updateResources() { - const qreal from = on ? 0.0 : 1.0; - const qreal to = on ? 1.0 : 0.0; + Inherited::updateResources(); - const auto aspect = transitionAspect(); + /* + Adjusting the geometry to the parent needs to be done before + the layouting of the children ( -> autoLayoutChildren ) is done. + So we are using this hook even if it is not about resources: TODO ... + */ + if ( const auto item = parentItem() ) + { + auto r = qskItemRect( item ); + r = qskAlignedToEdge( r, sizeConstraint( Qt::PreferredSize ), edge() ); - auto hint = animationHint( aspect ); - hint.updateFlags = QskAnimationHint::UpdatePolish; + r.translate( qskDrawerTranslation( this, r.size() ) ); + setGeometry( r ); + } +} - startTransition( aspect, hint, from, to ); +void QskDrawer::updateNode( QSGNode* node ) +{ + if ( isFading() && clip() ) + { + if ( auto clipNode = QQuickItemPrivate::get( this )->clipNode() ) + { + /* + The clipRect is changing while fading. Couldn't + find a way how to trigger updates - maybe be enabling/disabling + the clip. So we do the updates manually. TODO ... + */ + const auto r = clipRect(); + if ( r != clipNode->rect() ) + { + clipNode->setRect( r ); + clipNode->update(); + } + } + } + + Inherited::updateNode( node ); } 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 ) + if ( isFading() && parentItem() ) { - case Qt::LeftEdge: - r.setLeft( 0.0 ); - break; + /* + We might not fit into our parent and our children not + into our rect. So we want to have a clip against the + edge, where the drawer slides in/out only. + Otherwise we would have unwanted effects, when clipping gets + disabled once the transition is over. + */ + constexpr qreal d = 1e6; - case Qt::RightEdge: - r.setRight( width() ); - break; + QRectF r( -d, -d, 2.0 * d, 2.0 * d ); - case Qt::TopEdge: - r.setTop( 0.0 ); - break; + switch( edge() ) + { + case Qt::LeftEdge: + r.setLeft( -x() ); + break; - case Qt::BottomEdge: - r.setBottom( height() ); - break; + case Qt::RightEdge: + r.setRight( parentItem()->width() - x() ); + break; + + case Qt::TopEdge: + r.setTop( -y() ); + break; + + case Qt::BottomEdge: + r.setBottom( parentItem()->height() - y() ); + break; + } + + return r; } - return r; + return Inherited::clipRect(); } -void QskDrawer::setIntermediate( bool on ) +QskAspect QskDrawer::fadingAspect() const { - setClip( on ); - Q_EMIT focusIndicatorRectChanged(); + return QskDrawer::Panel | QskAspect::Position; } -QRectF QskDrawer::focusIndicatorRect() const +QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const { - if ( isTransitioning() ) - return QRectF(); - - return Inherited::focusIndicatorRect(); + return subControlContentsRect( size, Panel ); } #include "moc_QskDrawer.cpp" diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index f8aada41..b6c852f2 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -7,7 +7,6 @@ #define QSK_DRAWER_H #include "QskPopup.h" -#include class QSK_EXPORT QskDrawer : public QskPopup { @@ -27,6 +26,8 @@ class QSK_EXPORT QskDrawer : public QskPopup QSK_SUBCONTROLS( Panel ) QskDrawer( QQuickItem* = nullptr ); + QskDrawer( Qt::Edge, QQuickItem* = nullptr ); + ~QskDrawer() override; void setEdge( Qt::Edge ); @@ -39,10 +40,10 @@ 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; + QskAspect fadingAspect() const override; + + QRectF layoutRectForSize( const QSizeF& ) const override; Q_SIGNALS: void edgeChanged( Qt::Edge ); @@ -50,14 +51,13 @@ class QSK_EXPORT QskDrawer : public QskPopup void interactiveChanged( bool ); protected: - void itemChange( ItemChange, const ItemChangeData& ) override; void gestureEvent( QskGestureEvent* ) override; + void itemChange( ItemChange, const ItemChangeData& ) override; + + void updateResources() override; + void updateNode( QSGNode* ) override; private: - void setSliding( bool ); - - void setIntermediate( bool ); - class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/controls/QskDrawerSkinlet.cpp b/src/controls/QskDrawerSkinlet.cpp new file mode 100644 index 00000000..e10f5923 --- /dev/null +++ b/src/controls/QskDrawerSkinlet.cpp @@ -0,0 +1,45 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskDrawerSkinlet.h" +#include "QskDrawer.h" + +QskDrawerSkinlet::QskDrawerSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + appendNodeRoles( { PanelRole } ); +} + +QskDrawerSkinlet::~QskDrawerSkinlet() = default; + +QRectF QskDrawerSkinlet::subControlRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + if ( subControl == QskDrawer::Panel ) + return contentsRect; + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSGNode* QskDrawerSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + if ( nodeRole == PanelRole ) + return updateBoxNode( skinnable, node, QskDrawer::Panel ); + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskDrawerSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& constraint ) const +{ + if ( which == Qt::PreferredSize ) + return skinnable->strutSizeHint( QskDrawer::Panel ); + + return Inherited::sizeHint( skinnable, which, constraint ); +} + +#include "moc_QskDrawerSkinlet.cpp" diff --git a/src/controls/QskDrawerSkinlet.h b/src/controls/QskDrawerSkinlet.h new file mode 100644 index 00000000..b6d1c771 --- /dev/null +++ b/src/controls/QskDrawerSkinlet.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_DRAWER_SKINLET_H +#define QSK_DRAWER_SKINLET_H + +#include "QskPopupSkinlet.h" + +class QSK_EXPORT QskDrawerSkinlet : public QskPopupSkinlet +{ + Q_GADGET + + using Inherited = QskPopupSkinlet; + + public: + enum NodeRole + { + ContentsRole = Inherited::RoleCount, + PanelRole, + + RoleCount + }; + + Q_INVOKABLE QskDrawerSkinlet( QskSkin* = nullptr ); + ~QskDrawerSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; +}; + +#endif diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index ba86d66c..e3af5707 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -18,7 +18,10 @@ #include #include -QSK_SUBCONTROL( QskMenu, Overlay ) +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + QSK_SUBCONTROL( QskMenu, Panel ) QSK_SUBCONTROL( QskMenu, Segment ) QSK_SUBCONTROL( QskMenu, Cursor ) @@ -58,20 +61,23 @@ 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 ); + setPlacementPolicy( QskPlacementPolicy::Ignore ); setSubcontrolProxy( Inherited::Overlay, Overlay ); initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); // we hide the focus indicator while sliding - connect( this, &QskMenu::transitioningChanged, this, - &QskControl::focusIndicatorRectChanged ); + connect( this, &QskPopup::fadingChanged, + this, &QskControl::focusIndicatorRectChanged ); - connect( this, &QskMenu::opened, this, + connect( this, &QskPopup::fadingChanged, + this, &QQuickItem::setClip ); + + connect( this, &QskPopup::opened, this, [this]() { m_data->triggeredIndex = -1; } ); setAcceptHoverEvents( true ); @@ -81,6 +87,17 @@ QskMenu::~QskMenu() { } +QRectF QskMenu::clipRect() const +{ + if ( isFading() ) + { + constexpr qreal d = 1e6; + return QRectF( -d, m_data->origin.y() - y(), 2.0 * d, d ); + } + + return Inherited::clipRect(); +} + #if 1 // has no effect as we do not offer submenus yet. TODO ... @@ -263,6 +280,40 @@ QString QskMenu::triggeredText() const return optionAt( m_data->triggeredIndex ).text(); } +void QskMenu::updateResources() +{ + qreal dy = 0.0; + if ( isFading() ) + dy = ( 1.0 - fadingFactor() ) * height(); + + setPosition( m_data->origin.x(), m_data->origin.y() - dy ); + + Inherited::updateResources(); +} + +void QskMenu::updateNode( QSGNode* node ) +{ + if ( isFading() && clip() ) + { + if ( auto clipNode = QQuickItemPrivate::get( this )->clipNode() ) + { + /* + The clipRect is changing while fading. Couldn't + find a way how to trigger updates - maybe be enabling/disabling + the clip. So we do the updates manually. TODO ... + */ + const auto r = clipRect(); + if ( r != clipNode->rect() ) + { + clipNode->setRect( r ); + clipNode->update(); + } + } + } + + Inherited::updateNode( node ); +} + void QskMenu::keyPressEvent( QKeyEvent* event ) { if( m_data->currentIndex < 0 ) @@ -435,7 +486,7 @@ void QskMenu::mouseReleaseEvent( QMouseEvent* event ) void QskMenu::aboutToShow() { - setGeometry( QRectF( m_data->origin, sizeConstraint() ) ); + setSize( sizeConstraint() ); if ( m_data->currentIndex < 0 ) { @@ -448,7 +499,7 @@ void QskMenu::aboutToShow() QRectF QskMenu::focusIndicatorRect() const { - if ( isTransitioning() ) + if ( isFading() ) return QRectF(); if( currentIndex() >= 0 ) @@ -492,6 +543,11 @@ void QskMenu::trigger( int index ) } } +QskAspect QskMenu::fadingAspect() const +{ + return QskMenu::Panel | QskAspect::Position; +} + int QskMenu::exec() { (void) execPopup(); diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h index bc4cb2ca..b0042455 100644 --- a/src/controls/QskMenu.h +++ b/src/controls/QskMenu.h @@ -37,7 +37,7 @@ class QSK_EXPORT QskMenu : public QskPopup using Inherited = QskPopup; public: - QSK_SUBCONTROLS( Overlay, Panel, Segment, Cursor, Text, Icon, Separator ) + QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Icon, Separator ) QSK_STATES( Selected, Pressed ) QskMenu( QQuickItem* parentItem = nullptr ); @@ -81,6 +81,9 @@ class QSK_EXPORT QskMenu : public QskPopup bool isPressed() const; + QRectF clipRect() const override; + QskAspect fadingAspect() const override; + Q_INVOKABLE int exec(); Q_SIGNALS: @@ -115,6 +118,9 @@ class QSK_EXPORT QskMenu : public QskPopup void aboutToShow() override; void trigger( int ); + void updateResources() override; + void updateNode( QSGNode* ) override; + private: void traverse( int steps ); diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index 13be2b34..dae18d89 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -15,7 +15,6 @@ #include "QskLabelData.h" #include "QskSGNode.h" -#include "QskSlideInNode.h" #include #include @@ -223,26 +222,13 @@ QSGNode* QskMenuSkinlet::updateSubNode( { case ContentsRole: { - /* - QskSlideInNode works for controls made of nodes - not for - containers of other quick items. TODO ... - */ - const auto popup = static_cast< const QskPopup* >( skinnable ); auto rect = popup->contentsRect(); if ( rect.isEmpty() ) return nullptr; - auto slideInNode = QskSGNode::ensureNode< QskSlideInNode >( node ); - - const auto progress = popup->metric( popup->transitionAspect() ); - slideInNode->updateTranslation( rect, Qt::TopEdge, progress ); - - auto contentsNode = updateContentsNode( popup, slideInNode->contentsNode() ); - slideInNode->setContentsNode( contentsNode ); - - return slideInNode; + return updateContentsNode( popup, node ); } } diff --git a/src/controls/QskPopup.cpp b/src/controls/QskPopup.cpp index d993cc51..45e73c91 100644 --- a/src/controls/QskPopup.cpp +++ b/src/controls/QskPopup.cpp @@ -76,6 +76,23 @@ static bool qskReplayMousePress() return false; } +static void qskStartFading( QskPopup* popup, bool on ) +{ + const auto aspect = popup->fadingAspect(); + + auto hint = popup->animationHint( aspect ); + + 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 +156,6 @@ class QskPopup::PrivateData InputGrabber* inputGrabber = nullptr; uint priority = 0; - QskAspect transitionAspect; int flags : 4; bool isModal : 1; @@ -217,9 +233,11 @@ void QskPopup::setOpen( bool on ) else Q_EMIT closed(); - if ( isTransitioning() ) + qskStartFading( this, on ); + + if ( isFading() ) { - Q_EMIT transitioningChanged( true ); + Q_EMIT fadingChanged( true ); } else { @@ -238,9 +256,22 @@ bool QskPopup::isOpen() const return !hasSkinState( QskPopup::Closed ); } -bool QskPopup::isTransitioning() const +QskAspect QskPopup::fadingAspect() const { - return runningHintAnimator( m_data->transitionAspect ) != nullptr; + return QskAspect(); +} + +bool QskPopup::isFading() const +{ + return runningHintAnimator( fadingAspect() ) != nullptr; +} + +qreal QskPopup::fadingFactor() const +{ + if ( auto animator = runningHintAnimator( fadingAspect() ) ) + return animator->currentValue().value< qreal >(); + + return isOpen() ? 1.0 : 0.0; } QRectF QskPopup::overlayRect() const @@ -291,44 +322,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( fadingAspect().subControl() ) ) + return true; + + if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) ) + return true; } return Inherited::isTransitionAccepted( aspect ); @@ -475,7 +485,7 @@ bool QskPopup::event( QEvent* event ) const auto animatorEvent = static_cast< QskAnimatorEvent* >( event ); if ( ( animatorEvent->state() == QskAnimatorEvent::Terminated ) - && ( animatorEvent->aspect() == m_data->transitionAspect ) ) + && ( animatorEvent->aspect() == fadingAspect() ) ) { if ( !isOpen() ) { @@ -485,7 +495,7 @@ bool QskPopup::event( QEvent* event ) deleteLater(); } - Q_EMIT transitioningChanged( false ); + Q_EMIT fadingChanged( false ); } break; @@ -633,7 +643,7 @@ int QskPopup::execPopup() */ connect( popup, &QObject::destroyed, this, &EventLoop::reject ); - connect( popup, &QskPopup::transitioningChanged, this, &EventLoop::maybeQuit ); + connect( popup, &QskPopup::fadingChanged, this, &EventLoop::maybeQuit ); connect( popup, &QskPopup::openChanged, this, &EventLoop::maybeQuit ); } @@ -648,7 +658,7 @@ int QskPopup::execPopup() { if ( auto popup = qobject_cast< const QskPopup* >( parent() ) ) { - if ( popup->isOpen() || popup->isTransitioning() ) + if ( popup->isOpen() || popup->isFading() ) return; } @@ -656,7 +666,7 @@ int QskPopup::execPopup() } }; - if ( isOpen() || isTransitioning() ) + if ( isOpen() || isFading() ) { qWarning() << "QskPopup::exec: popup is already opened"; return -1; diff --git a/src/controls/QskPopup.h b/src/controls/QskPopup.h index ff1201b2..caf66346 100644 --- a/src/controls/QskPopup.h +++ b/src/controls/QskPopup.h @@ -14,7 +14,7 @@ class QSK_EXPORT QskPopup : public QskControl Q_PROPERTY( bool open READ isOpen WRITE setOpen NOTIFY openChanged ) Q_PROPERTY( bool modal READ isModal WRITE setModal NOTIFY modalChanged ) - Q_PROPERTY( bool transitioning READ isTransitioning NOTIFY transitioningChanged ) + Q_PROPERTY( bool fading READ isFading NOTIFY fadingChanged ) Q_PROPERTY( bool overlay READ hasOverlay WRITE setOverlay RESET resetOverlay NOTIFY overlayChanged ) @@ -56,15 +56,13 @@ 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; - bool isOpen() const; bool isClosed() const; + bool isFading() const; + qreal fadingFactor() const; + virtual QskAspect fadingAspect() const; + virtual QRectF overlayRect() const; public Q_SLOTS: @@ -78,7 +76,7 @@ class QSK_EXPORT QskPopup : public QskControl void opened(); void closed(); void openChanged( bool ); - void transitioningChanged( bool ); + void fadingChanged( bool ); void modalChanged( bool ); void overlayChanged( bool ); diff --git a/src/controls/QskPopupSkinlet.cpp b/src/controls/QskPopupSkinlet.cpp index d3777785..b989dc1e 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->fadingFactor(); + 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/QskSkin.cpp b/src/controls/QskSkin.cpp index a24a212f..a791eb05 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -32,6 +32,9 @@ #include "QskComboBox.h" #include "QskComboBoxSkinlet.h" +#include "QskDrawer.h" +#include "QskDrawerSkinlet.h" + #include "QskFocusIndicator.h" #include "QskFocusIndicatorSkinlet.h" @@ -160,6 +163,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskBox, QskBoxSkinlet >(); declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >(); declareSkinlet< QskComboBox, QskComboBoxSkinlet >(); + declareSkinlet< QskDrawer, QskDrawerSkinlet >(); declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >(); declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >(); declareSkinlet< QskListView, QskListViewSkinlet >(); diff --git a/src/controls/QskSubWindow.cpp b/src/controls/QskSubWindow.cpp index c278a889..229b4000 100644 --- a/src/controls/QskSubWindow.cpp +++ b/src/controls/QskSubWindow.cpp @@ -253,4 +253,15 @@ void QskSubWindow::itemChange( QQuickItem::ItemChange change, } } +void QskSubWindow::updateResources() +{ + setOpacity( fadingFactor() ); + Inherited::updateResources(); +} + +QskAspect QskSubWindow::fadingAspect() const +{ + return QskSubWindow::Panel | QskAspect::Position; +} + #include "moc_QskSubWindow.cpp" diff --git a/src/controls/QskSubWindow.h b/src/controls/QskSubWindow.h index 47d08e5f..42afd11b 100644 --- a/src/controls/QskSubWindow.h +++ b/src/controls/QskSubWindow.h @@ -83,6 +83,7 @@ class QSK_EXPORT QskSubWindow : public QskPopup QRectF titleBarRect() const; QRectF layoutRectForSize( const QSizeF& ) const override; + QskAspect fadingAspect() const override; Q_SIGNALS: void decorationsChanged( Decorations ); @@ -95,6 +96,8 @@ class QSK_EXPORT QskSubWindow : public QskPopup bool event( QEvent* ) override; void updateLayout() override; + void updateResources() override; + QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override; void itemChange( QQuickItem::ItemChange, diff --git a/src/controls/QskSubWindowSkinlet.cpp b/src/controls/QskSubWindowSkinlet.cpp index beea972b..84e9a571 100644 --- a/src/controls/QskSubWindowSkinlet.cpp +++ b/src/controls/QskSubWindowSkinlet.cpp @@ -94,6 +94,18 @@ QSGNode* QskSubWindowSkinlet::updateSubNode( return nullptr; } + case OverlayRole: + { + /* + Overloading QskPopupSkinlet: as the opacity of the subwindow already + depends on the fadingFactor we do not want the additional opacity + adjustments for the overlay node. + Maybe we should have a flag that indicates if the popup does + opacity or geometry transitions, when fading TODO ... + */ + updateBoxNode( subWindow, node, Q::Overlay ); + break; + } } return Inherited::updateSubNode( skinnable, nodeRole, node ); diff --git a/src/dialogs/QskDialog.cpp b/src/dialogs/QskDialog.cpp index cb1b5da7..d75b5b4e 100644 --- a/src/dialogs/QskDialog.cpp +++ b/src/dialogs/QskDialog.cpp @@ -77,6 +77,7 @@ static void qskSetupSubWindow( const QString& title, QskDialog::Actions actions, QskDialog::Action defaultAction, QskDialogSubWindow* subWindow ) { + subWindow->setPopupFlag( QskPopup::DeleteOnClose ); subWindow->setModal( true ); subWindow->setWindowTitle( title ); subWindow->setDialogActions( actions ); @@ -128,14 +129,14 @@ static QskDialog::Action qskMessageSubWindow( const QString& text, int symbolType, QskDialog::Actions actions, QskDialog::Action defaultAction ) { - QskMessageSubWindow subWindow( window->contentItem() ); - subWindow.setSymbolType( symbolType ); - subWindow.setText( text ); + auto subWindow = new QskMessageSubWindow( window->contentItem() ); + subWindow->setSymbolType( symbolType ); + subWindow->setText( text ); - qskSetupSubWindow( title, actions, defaultAction, &subWindow ); - ( void ) subWindow.exec(); + qskSetupSubWindow( title, actions, defaultAction, subWindow ); + ( void ) subWindow->exec(); - auto clickedAction = subWindow.clickedAction(); + auto clickedAction = subWindow->clickedAction(); if ( clickedAction == QskDialog::NoAction ) { // dialog might have been closed by the window menu @@ -172,16 +173,16 @@ static QString qskSelectSubWindow( QskDialog::Actions actions, QskDialog::Action defaultAction, const QStringList& entries, int selectedRow ) { - QskSelectionSubWindow subWindow( window->contentItem() ); - subWindow.setInfoText( text ); - subWindow.setEntries( entries ); - subWindow.setSelectedRow( selectedRow ); + auto subWindow = new QskSelectionSubWindow( window->contentItem() ); + subWindow->setInfoText( text ); + subWindow->setEntries( entries ); + subWindow->setSelectedRow( selectedRow ); QString selectedEntry; - qskSetupSubWindow( title, actions, defaultAction, &subWindow ); - if ( subWindow.exec() == QskDialog::Accepted ) - selectedEntry = subWindow.selectedEntry(); + qskSetupSubWindow( title, actions, defaultAction, subWindow ); + if ( subWindow->exec() == QskDialog::Accepted ) + selectedEntry = subWindow->selectedEntry(); return selectedEntry; } diff --git a/src/nodes/QskSlideInNode.cpp b/src/nodes/QskSlideInNode.cpp deleted file mode 100644 index 24590845..00000000 --- a/src/nodes/QskSlideInNode.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "QskSlideInNode.h" -#include "QskSGNode.h" -#include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END - -class QskSlideInNodePrivate final : public QSGNodePrivate -{ - public: - ~QskSlideInNodePrivate() - { - delete clipNode; - delete transformNode; - delete contentsNode; - } - - void reparentContentNode( QskSlideInNode* node ) - { - if ( contentsNode ) - { - QSGNode* parentNode = transformNode; - - if ( parentNode == nullptr ) - parentNode = clipNode; - - if ( parentNode == nullptr ) - parentNode = node; - - QskSGNode::setParentNode( contentsNode, parentNode ); - } - } - - QSGClipNode* clipNode = nullptr; - QSGTransformNode* transformNode = nullptr; - QSGNode* contentsNode = nullptr; -}; - -QskSlideInNode::QskSlideInNode() - : QSGNode( *new QskSlideInNodePrivate, QSGNode::BasicNodeType ) -{ -} - -QskSlideInNode::~QskSlideInNode() = default; - -void QskSlideInNode::updateTranslation( const QRectF& rect, - Qt::Edge edge, qreal progress ) -{ - Q_UNUSED( edge ); // TODO ... - - Q_D( QskSlideInNode ); - - { - // clipping - - if ( progress >= 0.0 && progress < 1.0 ) - { - if ( d->clipNode == nullptr ) - { - d->clipNode = new QSGClipNode(); - d->clipNode->setFlag( QSGNode::OwnedByParent, false ); - d->clipNode->setIsRectangular( true ); - } - - d->clipNode->setClipRect( rect ); - - } - else - { - delete d->clipNode; - d->clipNode = nullptr; - } - - if ( d->clipNode ) - QskSGNode::setParentNode( d->clipNode, this ); - } - - { - // translation - - qreal dx = 0.0; - qreal dy = ( progress - 1.0 ) * rect.height(); - - if ( dx != 0.0 || dy != 0.0 ) - { - if ( d->transformNode == nullptr ) - { - d->transformNode = new QSGTransformNode(); - d->transformNode->setFlag( QSGNode::OwnedByParent, false ); - } - - QTransform transform; - transform.translate( dx, dy ); - - d->transformNode->setMatrix( transform ); - } - else - { - delete d->transformNode; - d->transformNode = nullptr; - } - - if ( d->transformNode ) - { - QSGNode* parentNode = d->clipNode; - if ( parentNode == nullptr ) - parentNode = this; - - QskSGNode::setParentNode( d->transformNode, parentNode ); - } - } - - d->reparentContentNode( this ); -} - -void QskSlideInNode::setContentsNode( QSGNode* node ) -{ - Q_D( QskSlideInNode ); - - if ( d->contentsNode == node ) - return; - - if ( node ) - node->setFlag( QSGNode::OwnedByParent, false ); - - delete d->contentsNode; - d->contentsNode = node; - - d->reparentContentNode( this ); -} - -QSGNode* QskSlideInNode::contentsNode() -{ - return d_func()->contentsNode; -} - -const QSGNode* QskSlideInNode::contentsNode() const -{ - return d_func()->contentsNode; -} diff --git a/src/nodes/QskSlideInNode.h b/src/nodes/QskSlideInNode.h deleted file mode 100644 index 532663f2..00000000 --- a/src/nodes/QskSlideInNode.h +++ /dev/null @@ -1,33 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_SLIDE_IN_NODE_H -#define QSK_SLIDE_IN_NODE_H - -#include "QskGlobal.h" - -#include -#include - -class QskSlideInNodePrivate; - -class QSK_EXPORT QskSlideInNode : public QSGNode -{ - public: - QskSlideInNode(); - ~QskSlideInNode() override; - - void updateTranslation( const QRectF&, Qt::Edge, qreal progress ); - - void setContentsNode( QSGNode* ); - - QSGNode* contentsNode(); - const QSGNode* contentsNode() const; - - private: - Q_DECLARE_PRIVATE( QskSlideInNode ) -}; - -#endif