diff --git a/playground/drawer/main.cpp b/playground/drawer/main.cpp index be688b60..e5261f24 100644 --- a/playground/drawer/main.cpp +++ b/playground/drawer/main.cpp @@ -24,33 +24,34 @@ namespace : QskDrawer( parent ) { #if 1 - setAnimationHint( Panel | QskAspect::Position, 1000 ); + setAnimationHint( faderAspect(), 1000 ); #endif setEdge( edge ); setOverlay( true ); auto content = new QskControl( this ); + content->setObjectName( "Content" ); switch( edge ) { case Qt::LeftEdge: content->setBackgroundColor( QskRgb::Tomato ); - setFixedWidth( 100 ); + content->setFixedWidth( 100 ); break; case Qt::RightEdge: - setFixedWidth( 200 ); + content->setFixedWidth( 200 ); content->setBackgroundColor( QskRgb::Orchid ); break; case Qt::TopEdge: - setFixedHeight( 100 ); - content->setBackgroundColor( QskRgb::Wheat ); + content->setFixedHeight( 100 ); + content->setBackgroundColor( QskRgb::Chartreuse ); break; case Qt::BottomEdge: - setFixedHeight( 200 ); + content->setFixedHeight( 200 ); content->setBackgroundColor( QskRgb::Wheat ); break; } @@ -65,16 +66,26 @@ namespace { setBackgroundColor( QskRgb::LightSteelBlue ); - setMargins( 50 ); + setMargins( 10 ); setAutoLayoutChildren( true ); - (void) new QskPushButton( this ); - for ( int i = 0; i < 4; i++ ) { const auto edge = static_cast< Qt::Edge >( 1 << i ); m_drawers[i] = new Drawer( edge, this ); + + auto dragMargin = 30; // the default setting is pretty small + if ( edge == Qt::TopEdge ) + { + // to check if dragging works above the button + dragMargin = 120; + } + + m_drawers[i]->setDragMargin( dragMargin ); } + + auto button = new QskPushButton( "Push Me", this ); + button->setPreferredHeight( 100 ); } private: @@ -87,7 +98,6 @@ namespace MainBox( QQuickItem* parent = nullptr ) : QskControl( parent ) { - setBackgroundColor( QskRgb::LemonChiffon ); setMargins( 40 ); setAutoLayoutChildren( true ); diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 1b90f732..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() @@ -735,6 +729,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 +743,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( diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 957c1ba2..9ea1c054 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 ); @@ -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/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/QskDrawer.cpp b/src/controls/QskDrawer.cpp index aa5853ce..11f7bdbd 100644 --- a/src/controls/QskDrawer.cpp +++ b/src/controls/QskDrawer.cpp @@ -12,48 +12,88 @@ #include "QskPanGestureRecognizer.h" #include "QskGesture.h" +#include +#include + QSK_QT_PRIVATE_BEGIN #include #include QSK_QT_PRIVATE_END -// we need a skinlet to draw the panel TODO ... +/* + Only used for the sliding in animation. Do we want to + introduce a specific panel as background ??? + */ QSK_SUBCONTROL( QskDrawer, Panel ) -static QRectF qskDrawerRect( const QRectF& rect, - Qt::Edge edge, qreal pos, const QSizeF& size ) +static void qskCatchMouseEvents( QQuickItem* item ) { - QRectF r( 0.0, 0.0, size.width(), size.height() ); +#if 1 + // manipulating other items - do we really want to do this ? + item->setAcceptedMouseButtons( Qt::LeftButton ); + item->setFiltersChildMouseEvents( true ); +#endif +} - const auto progress = pos = 1.0 - pos; +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.moveRight( rect.left() + progress * size.width() ); + r.setHeight( rect.height() ); + r.moveRight( rect.left() + size.width() ); break; } case Qt::RightEdge: { - r.moveLeft( rect.right() - progress * size.width() ); + r.setHeight( rect.height() ); + r.moveLeft( rect.right() - size.width() ); break; } case Qt::TopEdge: { - r.moveBottom( rect.top() + progress * size.height() ); + r.setWidth( rect.width() ); + r.moveBottom( rect.top() + size.height() ); break; } case Qt::BottomEdge: { - r.moveTop( rect.bottom() - progress * size.height() ); + r.setWidth( rect.width() ); + r.moveTop( rect.bottom() - size.height() ); break; } } - return r; + drawer->setGeometry( r ); } namespace @@ -61,10 +101,11 @@ namespace class GeometryListener final : public QQuickItemChangeListener { public: - GeometryListener( QskDrawer* drawer ) - : m_drawer( drawer ) - , m_parent( drawer->parentItem() ) + GeometryListener( QQuickItem* item, QQuickItem* adjustedItem ) + : m_item( item ) + , m_adjustedItem( adjustedItem ) { + adjust(); setEnabled( true ); } @@ -74,31 +115,43 @@ namespace } private: - void itemGeometryChanged( QQuickItem*, + void itemGeometryChanged( QQuickItem*, QQuickGeometryChange, const QRectF& ) override { - m_drawer->polish(); + adjust(); } private: - void setEnabled( bool on ) + void adjust() { - if ( m_parent ) - { - const auto changeTypes = QQuickItemPrivate::Geometry; - - auto d = QQuickItemPrivate::get( m_parent ); - if ( on ) - d->addItemChangeListener( this, changeTypes ); - else - d->removeItemChangeListener( this, changeTypes ); - } +#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 } - QskDrawer* m_drawer = nullptr; - QQuickItem* m_parent = nullptr; - }; + 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; @@ -112,33 +165,36 @@ namespace } protected: - QRectF gestureRect() const override + bool isAcceptedPos( const QPointF& pos ) const override { - auto drawer = qobject_cast< QskDrawer* >( parent() ); + auto drawer = qobject_cast< const QskDrawer* >( targetItem() ); + + const auto dragMargin = drawer->dragMargin(); + if ( dragMargin <= 0.0 ) + return false; - const auto dist = 50; auto rect = qskItemRect( watchedItem() ); switch( drawer->edge() ) { case Qt::LeftEdge: - rect.setRight( rect.left() + dist ); + rect.setRight( rect.left() + dragMargin ); break; case Qt::RightEdge: - rect.setLeft( rect.right() - dist ); + rect.setLeft( rect.right() - dragMargin ); break; case Qt::TopEdge: - rect.setBottom( rect.top() + dist ); + rect.setBottom( rect.top() + dragMargin ); break; case Qt::BottomEdge: - rect.setTop( rect.bottom() - dist ); + rect.setTop( rect.bottom() - dragMargin ); break; } - return rect; + return rect.contains( pos ); } }; } @@ -147,23 +203,26 @@ class QskDrawer::PrivateData { public: 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 - setAutoLayoutChildren( true ); + setPolishOnResize( true ); setPopupFlag( PopupFlag::CloseOnPressOutside, true ); setFaderAspect( Panel | QskAspect::Position | QskAspect::Metric ); - connect( this, &QskDrawer::closed, - this, [this]() { startFading( false ); } ); - /* The drawer wants to be on top of the parent - not being layouted into its layoutRect(). So we opt out and do @@ -172,17 +231,22 @@ QskDrawer::QskDrawer( QQuickItem* parentItem ) setPlacementPolicy( QskPlacementPolicy::Ignore ); if ( parentItem ) { - /* - QskPopup has an internal QskInputGrabber, that does something - very similar. Maybe we can make use of it ... TODO - */ - m_data->listener = new GeometryListener( this ); + m_data->listener = new GeometryListener( parentItem, this ); + qskCatchMouseEvents( parentItem ); } - (void) new GestureRecognizer( this ); -#if 1 - parentItem->setAcceptedMouseButtons( Qt::LeftButton ); -#endif + m_data->gestureRecognizer = new GestureRecognizer( 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 ); } QskDrawer::~QskDrawer() @@ -205,13 +269,36 @@ void QskDrawer::setEdge( Qt::Edge edge ) edgeChanged( edge ); } +void QskDrawer::setDragMargin( qreal margin ) +{ + margin = std::max( margin, 0.0 ); + + 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 ) - open(); + { + if ( qskCheckDirection( m_data->edge, gesture ) ) + open(); + } return; } @@ -219,30 +306,89 @@ void QskDrawer::gestureEvent( QskGestureEvent* event ) Inherited::gestureEvent( event ); } -QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const +QSizeF QskDrawer::layoutSizeHint( + Qt::SizeHint which, const QSizeF& constraint ) const { - return Inherited::layoutRectForSize( size ); + 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 targetRect = qskItemRect( parentItem() ); - const auto size = qskConstrainedItemSize( this, targetRect.size() ); + auto dx = 0.0; + auto dy = 0.0; - const auto rect = qskDrawerRect( targetRect, - m_data->edge, metric( faderAspect() ), size ); + if ( isFading() ) + { + const auto f = metric( faderAspect() ); - qskSetItemGeometry( this, rect ); - Inherited::updateLayout(); -} + switch( m_data->edge ) + { + case Qt::LeftEdge: + dx = -f * width(); + break; -void QskDrawer::aboutToShow() -{ - startFading( true ); - Inherited::aboutToShow(); + 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 ); + } + } } void QskDrawer::itemChange( QQuickItem::ItemChange change, @@ -250,23 +396,39 @@ void QskDrawer::itemChange( QQuickItem::ItemChange change, { Inherited::itemChange( change, value ); - if ( change == QQuickItem::ItemParentHasChanged ) + switch( static_cast< int >( change ) ) { - delete m_data->listener; - m_data->listener = nullptr; + case QQuickItem::ItemParentHasChanged: + { + if ( parentItem() ) + qskCatchMouseEvents( parentItem() ); - if ( parentItem() ) - m_data->listener = new GeometryListener( this ); + 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::startFading( bool open ) +void QskDrawer::setFading( bool on ) { - const auto from = open ? 1.0 : 0.0; - const auto to = open ? 0.0 : 1.0; + const qreal from = on ? 1.0 : 0.0; + const qreal to = on ? 0.0 : 1.0; - const auto hint = animationHint( Panel | QskAspect::Position ); - startTransition( faderAspect(), hint, from, to ); + const auto aspect = faderAspect(); + + auto hint = animationHint( aspect ); + hint.updateFlags = QskAnimationHint::UpdatePolish; + + startTransition( aspect, hint, from, to ); } #include "moc_QskDrawer.cpp" diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index ad1ef93f..2c8d68e7 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -17,6 +17,9 @@ 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 ) @@ -26,20 +29,23 @@ class QSK_EXPORT QskDrawer : public QskPopup void setEdge( Qt::Edge ); Qt::Edge edge() const; - QRectF layoutRectForSize( const QSizeF& ) const override; + void setDragMargin( qreal ); + qreal dragMargin() const; + 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 startFading( bool ); + void setFading( bool ); class PrivateData; std::unique_ptr< PrivateData > m_data; 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 c0dd4b08..c4ac00dc 100644 --- a/src/controls/QskGestureRecognizer.h +++ b/src/controls/QskGestureRecognizer.h @@ -73,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/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 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..d3777785 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; @@ -125,54 +28,13 @@ 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 ); - - 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/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; } }; } 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; } 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