From 64bc724f6f3a76887f223cba7d5c7c51b3532366 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 19 Jun 2023 11:22:34 +0200 Subject: [PATCH 01/21] Add QskSwipeView Resolves #107 --- examples/layouts/CMakeLists.txt | 1 + examples/layouts/SwipeViewPage.cpp | 61 ++++++++++++++++ examples/layouts/SwipeViewPage.h | 14 ++++ examples/layouts/main.cpp | 2 + src/CMakeLists.txt | 2 + src/controls/QskSwipeView.cpp | 112 +++++++++++++++++++++++++++++ src/controls/QskSwipeView.h | 38 ++++++++++ 7 files changed, 230 insertions(+) create mode 100644 examples/layouts/SwipeViewPage.cpp create mode 100644 examples/layouts/SwipeViewPage.h create mode 100644 src/controls/QskSwipeView.cpp create mode 100644 src/controls/QskSwipeView.h diff --git a/examples/layouts/CMakeLists.txt b/examples/layouts/CMakeLists.txt index e8d8b0bd..a4b57bc3 100644 --- a/examples/layouts/CMakeLists.txt +++ b/examples/layouts/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES LinearLayoutPage.h LinearLayoutPage.cpp DynamicConstraintsPage.h DynamicConstraintsPage.cpp StackLayoutPage.h StackLayoutPage.cpp + SwipeViewPage.h SwipeViewPage.cpp main.cpp ) qt_add_resources(SOURCES layouts.qrc) diff --git a/examples/layouts/SwipeViewPage.cpp b/examples/layouts/SwipeViewPage.cpp new file mode 100644 index 00000000..012e1951 --- /dev/null +++ b/examples/layouts/SwipeViewPage.cpp @@ -0,0 +1,61 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "SwipeViewPage.h" +#include "TestRectangle.h" + +#include +#include + +namespace +{ +class SwipeView : public QskSwipeView + { + Q_OBJECT + + public: + SwipeView( QQuickItem* parent = nullptr ) + : QskSwipeView( parent ) + { + setObjectName( "SwipeView" ); + + setBackgroundColor( Qt::white ); + setDefaultAlignment( Qt::AlignCenter ); + + addRectangle( "Gold" ); + addRectangle( "SeaGreen" ); + addRectangle( "SlateBlue" ); + addRectangle( "Peru" ); + + for ( int i = 0; i < itemCount(); i += 2 ) + { + if ( auto control = qskControlCast( itemAtIndex( i ) ) ) + control->setFixedSize( 200, 200 ); + } + } + private: + void addRectangle( const char* colorName ) + { + auto rect = new TestRectangle( colorName ); + rect->setText( QString::number( itemCount() + 1 ) ); + addItem( rect ); + } + + }; +} + +SwipeViewPage::SwipeViewPage( QQuickItem* parent ) + : QskLinearBox( Qt::Vertical, parent ) +{ + setObjectName( "SwipeViewPage" ); + + setMargins( 10 ); + setBackgroundColor( QskRgb::LightSteelBlue ); + + auto swipeView = new SwipeView(); + addItem( swipeView ); +} + +#include "SwipeViewPage.moc" diff --git a/examples/layouts/SwipeViewPage.h b/examples/layouts/SwipeViewPage.h new file mode 100644 index 00000000..02ce94dd --- /dev/null +++ b/examples/layouts/SwipeViewPage.h @@ -0,0 +1,14 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class SwipeViewPage : public QskLinearBox +{ + public: + SwipeViewPage( QQuickItem* parent = nullptr ); +}; diff --git a/examples/layouts/main.cpp b/examples/layouts/main.cpp index e14cf38f..2797a959 100644 --- a/examples/layouts/main.cpp +++ b/examples/layouts/main.cpp @@ -8,6 +8,7 @@ #include "LinearLayoutPage.h" #include "GridLayoutPage.h" #include "StackLayoutPage.h" +#include "SwipeViewPage.h" #include "TestRectangle.h" #include @@ -43,6 +44,7 @@ int main( int argc, char* argv[] ) tabView->addTab( "Linear Layout", new LinearLayoutPage() ); tabView->addTab( "Dynamic\nConstraints", new DynamicConstraintsPage() ); tabView->addTab( "Stack Layout", new StackLayoutPage() ); + tabView->addTab( "Swipe View", new SwipeViewPage() ); tabView->setCurrentIndex( 0 ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07b1b761..28a22229 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -236,6 +236,7 @@ list(APPEND HEADERS controls/QskSubWindowSkinlet.h controls/QskSwitchButton.h controls/QskSwitchButtonSkinlet.h + controls/QskSwipeView.h controls/QskTabBar.h controls/QskTabButton.h controls/QskTabButtonSkinlet.h @@ -334,6 +335,7 @@ list(APPEND SOURCES controls/QskSubWindowSkinlet.cpp controls/QskSwitchButton.cpp controls/QskSwitchButtonSkinlet.cpp + controls/QskSwipeView.cpp controls/QskTabBar.cpp controls/QskTabButton.cpp controls/QskTabButtonSkinlet.cpp diff --git a/src/controls/QskSwipeView.cpp b/src/controls/QskSwipeView.cpp new file mode 100644 index 00000000..97cb574e --- /dev/null +++ b/src/controls/QskSwipeView.cpp @@ -0,0 +1,112 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskSwipeView.h" + +#include "QskEvent.h" +#include "QskGesture.h" +#include "QskPanGestureRecognizer.h" +#include "QskStackBoxAnimator.h" + +class QskSwipeView::PrivateData +{ + public: + QskPanGestureRecognizer panRecognizer; + int panRecognizerTimeout = 100; + int duration = 500; +}; + +QSK_SUBCONTROL( QskSwipeView, Panel ) + +QskSwipeView::QskSwipeView( QQuickItem* parent ) + : Inherited( parent ) + , m_data( new PrivateData() ) +{ + setSubcontrolProxy( QskBox::Panel, Panel ); + + setFiltersChildMouseEvents( true ); + + setAcceptedMouseButtons( Qt::LeftButton ); + + m_data->panRecognizer.setWatchedItem( this ); + m_data->panRecognizer.setOrientations( Qt::Horizontal ); + m_data->panRecognizer.setMinDistance( 50 ); +} + +QskSwipeView::~QskSwipeView() +{ +} + + +int QskSwipeView::duration() const +{ + return m_data->duration; +} + +void QskSwipeView::setDuration( int duration ) +{ + m_data->duration = duration; +} + + +bool QskSwipeView::gestureFilter( QQuickItem* item, QEvent* event ) +{ + // see QskScrollBox.cpp + + auto& recognizer = m_data->panRecognizer; + + if ( event->type() == QEvent::MouseButtonPress ) + { + if ( ( item != this ) && ( recognizer.timeout() < 0 ) ) + { + const auto mouseEvent = static_cast< QMouseEvent* >( event ); + + if ( recognizer.hasProcessedBefore( mouseEvent ) ) + return false; + } + + recognizer.setTimeout( ( item == this ) ? -1 : m_data->panRecognizerTimeout ); + } + + return m_data->panRecognizer.processEvent( item, event ); +} + +void QskSwipeView::gestureEvent( QskGestureEvent* event ) +{ + if( event->gesture()->type() == QskGesture::Pan && event->gesture()->state() == QskGesture::Started ) + { + const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() ); + + auto animator = dynamic_cast< QskStackBoxAnimator1* >( this->animator() ); + + if ( animator == nullptr ) + { + animator = new QskStackBoxAnimator1( this ); + animator->setOrientation( Qt::Horizontal ); + } + + animator->setDuration( m_data->duration ); + setAnimator( animator ); + + const auto direction = ( ( gesture->angle() < 90.0 ) || ( gesture->angle() > 270.0 ) ) + ? Qsk::LeftToRight : Qsk::RightToLeft; + + auto newIndex = ( direction == Qsk::LeftToRight ) ? currentIndex() - 1 : currentIndex() + 1; + + if( newIndex < 0 ) + newIndex += itemCount(); + + newIndex %= itemCount(); + + setCurrentIndex( newIndex ); + } + else + { + Inherited::gestureEvent( event ); + } + +} + +#include "moc_QskSwipeView.cpp" diff --git a/src/controls/QskSwipeView.h b/src/controls/QskSwipeView.h new file mode 100644 index 00000000..cbcc9c0e --- /dev/null +++ b/src/controls/QskSwipeView.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_SWIPE_VIEW_H +#define QSK_SWIPE_VIEW_H + +#include "QskStackBox.h" + +class QskTabBar; +class QskTabButton; + +class QSK_EXPORT QskSwipeView : public QskStackBox +{ + Q_OBJECT + + typedef QskStackBox Inherited; + + public: + QSK_SUBCONTROLS( Panel ) + + QskSwipeView( QQuickItem* parent = nullptr ); + ~QskSwipeView() override; + + int duration() const; + void setDuration( int ); + + protected: + bool gestureFilter( QQuickItem*, QEvent* ) override final; + void gestureEvent( QskGestureEvent* ) override final; + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif From 849411ea97ccb53fcfeac5a3d95cfd46448f5927 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 31 Jul 2023 15:06:41 +0200 Subject: [PATCH 02/21] mouse press handling fixed --- examples/thumbnails/main.cpp | 11 ++++++ src/controls/QskScrollBox.cpp | 70 ++++++++++++++++++++++------------- src/controls/QskScrollBox.h | 2 + 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/examples/thumbnails/main.cpp b/examples/thumbnails/main.cpp index e0a7c7e3..ff1f092c 100644 --- a/examples/thumbnails/main.cpp +++ b/examples/thumbnails/main.cpp @@ -79,6 +79,17 @@ class Thumbnail : public QskPushButton setStrutSizeHint( QskPushButton::Icon, -1, -1 ); } +#if 0 + void mousePressEvent( QMouseEvent* event ) override + { + /* + rgnore events: to check if the pae gesture recoognizer of the scroll + area becomes active without timeout ( see QskScrollBox::mousePressEvent ) + */ + event->setAccepted( false ); + } +#endif + private: QskGraphic thumbnailGraphic( const QColor& color, int shape, const QSizeF& size ) const diff --git a/src/controls/QskScrollBox.cpp b/src/controls/QskScrollBox.cpp index c889f02a..8bd84942 100644 --- a/src/controls/QskScrollBox.cpp +++ b/src/controls/QskScrollBox.cpp @@ -362,6 +362,25 @@ void QskScrollBox::geometryChangeEvent( QskGeometryChangeEvent* event ) Inherited::geometryChangeEvent( event ); } +void QskScrollBox::mousePressEvent( QMouseEvent* event ) +{ + auto& recognizer = m_data->panRecognizer; + if ( recognizer.hasProcessedBefore( event ) ) + { + if ( m_data->panRecognizerTimeout != 0 ) + { + recognizer.abort(); + recognizer.setTimeout( -1 ); + + recognizer.processEvent( this, event, false ); + } + + return; + } + + return Inherited::mousePressEvent( event ); +} + void QskScrollBox::gestureEvent( QskGestureEvent* event ) { if ( event->gesture()->type() == QskGesture::Pan ) @@ -458,38 +477,39 @@ bool QskScrollBox::gestureFilter( QQuickItem* item, QEvent* event ) return false; } - /* - This code is a bit tricky as the filter is called in different situations: - - a) The press was on a child of the view - b) The press was on the view - - In case of b) things are simple and we can let the recognizer - decide without timeout if it is was a gesture or not. - - In case of a) we give the recognizer some time to decide - usually - based on the distances of the following mouse events. If no decision - could be made the recognizer aborts and replays the mouse events, so - that the children can process them. - - But if a child does not accept a mouse event it will be sent to - its parent. So we might finally receive the reposted events, but then - we can proceed as in b). - */ auto& recognizer = m_data->panRecognizer; + recognizer.setTimeout( m_data->panRecognizerTimeout ); if ( event->type() == QEvent::MouseButtonPress ) { - if ( ( item != this ) && ( recognizer.timeout() < 0 ) ) + /* + This code is a bit tricky as the filter is called in different situations: + + a) The press was on a child of the view + b) The press was on the view + + In case of b) things are simple and we can let the recognizer + decide without timeout if it is was a gesture or not. + + In case of a) we give the recognizer some time to decide - usually + based on the distances of the following mouse events. If no decision + could be made the recognizer aborts and replays the mouse events, so + that the children can process them. + + But if a child does not accept the mouse event it will be sent to + its parent, finally ending up here for a second time. + */ + + const auto mouseEvent = static_cast< QMouseEvent* >( event ); + if ( recognizer.hasProcessedBefore( mouseEvent ) ) { - const auto mouseEvent = static_cast< QMouseEvent* >( event ); - - if ( recognizer.hasProcessedBefore( mouseEvent ) ) - return false; + /* + Note that the recognizer will be restarted without timout if the + event ends up in in mousePressEvent ( = nobody else was interested ) + */ + return false; } - - recognizer.setTimeout( ( item == this ) ? -1 : m_data->panRecognizerTimeout ); } return recognizer.processEvent( item, event ); diff --git a/src/controls/QskScrollBox.h b/src/controls/QskScrollBox.h index a6334afd..46bc9604 100644 --- a/src/controls/QskScrollBox.h +++ b/src/controls/QskScrollBox.h @@ -64,6 +64,8 @@ class QSK_EXPORT QskScrollBox : public QskControl protected: void geometryChangeEvent( QskGeometryChangeEvent* ) override; void windowChangeEvent( QskWindowChangeEvent* ) override; + + void mousePressEvent( QMouseEvent* ) override; void gestureEvent( QskGestureEvent* ) override; #ifndef QT_NO_WHEELEVENT From 946bac677da08c703929bc93df9b82a11123b512 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 31 Jul 2023 17:16:41 +0200 Subject: [PATCH 03/21] Hovered/Pressed states for QskListView cells added --- skins/fluent2/QskFluent2Skin.cpp | 7 +- skins/material3/QskMaterial3Skin.cpp | 5 +- skins/squiek/QskSquiekSkin.cpp | 13 ++-- src/controls/QskListView.cpp | 102 ++++++++++++++++++++++++++- src/controls/QskListView.h | 6 ++ src/controls/QskListViewSkinlet.cpp | 7 +- 6 files changed, 127 insertions(+), 13 deletions(-) diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 3a47da77..2c438d03 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -612,8 +612,10 @@ void Editor::setupFocusIndicatorColors( void Editor::setupListViewMetrics() { using Q = QskListView; + using A = QskAspect; - setBoxBorderMetrics( Q::Cell | Q::Selected, { 3, 0, 0, 0 } ); + for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) + setBoxBorderMetrics( Q::Cell | state | Q::Selected, { 3, 0, 0, 0 } ); #if 1 // taken from M3 - what are the actual values, TODO ... setPadding( Q::Cell, { 16, 12, 16, 12 } ); @@ -693,6 +695,9 @@ void Editor::setupListViewColors( setColor( text, textColor ); } } + + setAnimation( Q::Cell | A::Color, 100 ); + setAnimation( Q::Text | A::Color, 100 ); } void Editor::setupMenuMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index ccdb2e21..c13187e4 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -1150,6 +1150,7 @@ void Editor::setupScrollView() void Editor::setupListView() { + using A = QskAspect; using Q = QskListView; setStrutSize( Q::Cell, { -1, 56 } ); @@ -1157,7 +1158,9 @@ void Editor::setupListView() setBoxBorderColors( Q::Cell, m_pal.outline ); setGradient( Q::Cell, m_pal.surface ); - setGradient( Q::Cell | Q::Selected, m_pal.primary12 ); + + for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) + setGradient( Q::Cell | state | Q::Selected, m_pal.primary12 ); setColor( Q::Text, m_pal.onSurface ); } diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 65fa848d..bdd40e47 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -1057,12 +1057,17 @@ void Editor::setupListView() // alternating row colors setColor( Q::Cell | A::Lower, Qt::white ); - setColor( Q::Cell | Q::Selected | A::Lower, m_pal.highlighted ); - setColor( Q::Cell | A::Upper, m_pal.contrasted ); - setColor( Q::Cell | Q::Selected | A::Upper, m_pal.highlighted ); - setColor( Q::Text | Q::Selected, m_pal.highlightedText ); + for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) + { + const auto aspect = Q::Cell | state | Q::Selected; + + setColor( aspect | A::Lower, m_pal.highlighted ); + setColor( aspect | A::Upper, m_pal.highlighted ); + + setColor( Q::Text | state | Q::Selected, m_pal.highlightedText ); + } } void Editor::setupSubWindow() diff --git a/src/controls/QskListView.cpp b/src/controls/QskListView.cpp index 08e5cf44..dd97c20a 100644 --- a/src/controls/QskListView.cpp +++ b/src/controls/QskListView.cpp @@ -46,6 +46,48 @@ class QskListView::PrivateData { } + void setRowState( QskListView* listView, int row, QskAspect::State state ) + { + using Q = QskListView; + + auto& storedRow = ( state == Q::Hovered ) + ? hoveredRow : ( ( state == Q::Pressed ) ? pressedRow : selectedRow ); + + if ( row == storedRow ) + return; + + if ( storedRow >= 0 ) + { + const auto states = listView->rowStates( storedRow ); + startTransitions( listView, storedRow, states, states & ~state ); + } + + if ( row >= 0 ) + { + const auto states = listView->rowStates( row ); + startTransitions( listView, row, states, states | state ); + } + + storedRow = row; + } + + private: + inline void startTransitions( QskListView* listView, int row, + QskAspect::States oldStates, QskAspect::States newStates ) + { + /* + working implementation can be found in + https://github.com/uwerat/qskinny/tree/features/listview + */ + + Q_UNUSED( row ); + Q_UNUSED( oldStates ); + Q_UNUSED( newStates ); + + listView->update(); + } + + public: /* Currently we only support single selection. We can't navigate the current item ( = focus ) without changing the selection. @@ -55,6 +97,8 @@ class QskListView::PrivateData bool preferredWidthFromColumns : 1; SelectionMode selectionMode : 4; + int hoveredRow = -1; + int pressedRow = -1; int selectedRow = -1; }; @@ -131,7 +175,8 @@ void QskListView::setSelectedRow( int row ) if ( row != m_data->selectedRow ) { - m_data->selectedRow = row; + m_data->setRowState( this, row, Selected ); + Q_EMIT selectedRowChanged( row ); Q_EMIT focusIndicatorRectChanged(); @@ -280,6 +325,7 @@ void QskListView::mousePressEvent( QMouseEvent* event ) const int row = qskRowAt( this, qskMousePosition( event ) ); if ( row >= 0 ) { + m_data->setRowState( this, row, Pressed ); setSelectedRow( row ); return; } @@ -290,9 +336,44 @@ void QskListView::mousePressEvent( QMouseEvent* event ) void QskListView::mouseReleaseEvent( QMouseEvent* event ) { + m_data->setRowState( this, -1, Pressed ); Inherited::mouseReleaseEvent( event ); } +void QskListView::mouseUngrabEvent() +{ + m_data->setRowState( this, -1, Pressed ); + Inherited::mouseUngrabEvent(); +} + +void QskListView::hoverEnterEvent( QHoverEvent* event ) +{ + if ( m_data->selectionMode != NoSelection ) + { + const int row = qskRowAt( this, qskHoverPosition( event ) ); + m_data->setRowState( this, row, Hovered ); + } + + Inherited::hoverEnterEvent( event ); +} + +void QskListView::hoverMoveEvent( QHoverEvent* event ) +{ + if ( m_data->selectionMode != NoSelection ) + { + const int row = qskRowAt( this, qskHoverPosition( event ) ); + m_data->setRowState( this, row, Hovered ); + } + + Inherited::hoverMoveEvent( event ); +} + +void QskListView::hoverLeaveEvent( QHoverEvent* event ) +{ + m_data->setRowState( this, -1, Hovered ); + Inherited::hoverLeaveEvent( event ); +} + void QskListView::changeEvent( QEvent* event ) { if ( event->type() == QEvent::StyleChange ) @@ -301,6 +382,25 @@ void QskListView::changeEvent( QEvent* event ) Inherited::changeEvent( event ); } +QskAspect::States QskListView::rowStates( int row ) const +{ + auto states = skinStates(); + + if ( row >= 0 ) + { + if ( row == m_data->selectedRow ) + states |= Selected; + + if ( row == m_data->hoveredRow ) + states |= Hovered; + + if ( row == m_data->pressedRow ) + states |= Pressed; + } + + return states; +} + #ifndef QT_NO_WHEELEVENT static qreal qskAlignedToRows( const qreal y0, qreal dy, diff --git a/src/controls/QskListView.h b/src/controls/QskListView.h index 13fe8055..8023cfcf 100644 --- a/src/controls/QskListView.h +++ b/src/controls/QskListView.h @@ -54,6 +54,7 @@ class QSK_EXPORT QskListView : public QskScrollView QskTextOptions textOptions() const; Q_INVOKABLE int selectedRow() const; + QskAspect::States rowStates( int ) const; virtual int rowCount() const = 0; virtual int columnCount() const = 0; @@ -83,6 +84,11 @@ class QSK_EXPORT QskListView : public QskScrollView void mousePressEvent( QMouseEvent* ) override; void mouseReleaseEvent( QMouseEvent* ) override; + void mouseUngrabEvent() override; + + void hoverEnterEvent( QHoverEvent* ) override; + void hoverMoveEvent( QHoverEvent* ) override; + void hoverLeaveEvent( QHoverEvent* ) override; #ifndef QT_NO_WHEELEVENT virtual QPointF scrollOffset( const QWheelEvent* ) const override; diff --git a/src/controls/QskListViewSkinlet.cpp b/src/controls/QskListViewSkinlet.cpp index 91d8f226..5dc2fa5c 100644 --- a/src/controls/QskListViewSkinlet.cpp +++ b/src/controls/QskListViewSkinlet.cpp @@ -470,12 +470,7 @@ QskAspect::States QskListViewSkinlet::sampleStates( const QskSkinnable* skinnabl if ( subControl == Q::Cell || subControl == Q::Text || subControl == Q::Graphic ) { const auto listView = static_cast< const QskListView* >( skinnable ); - - auto states = listView->skinStates(); - if ( index == listView->selectedRow() ) - states |= Q::Selected; - - return states; + return listView->rowStates( index ); } return Inherited::sampleStates( skinnable, subControl, index ); From 95a8809979c7efebc055b0d69e2a0dd15756f229 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 2 Aug 2023 09:01:06 +0200 Subject: [PATCH 04/21] ListView: alternating row colors removed. Adding sample variations bits for animations makes the code too messy for the value of this feature. --- skins/squiek/QskSquiekSkin.cpp | 11 ++--------- src/controls/QskListViewSkinlet.cpp | 6 +----- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index bdd40e47..3e3bbff3 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -1054,18 +1054,11 @@ void Editor::setupListView() setPadding( Q::Cell, QskMargins( 4, 8 ) ); setColor( Q::Text, m_pal.themeForeground ); - - // alternating row colors - setColor( Q::Cell | A::Lower, Qt::white ); - setColor( Q::Cell | A::Upper, m_pal.contrasted ); + setColor( Q::Cell, Qt::white ); for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) { - const auto aspect = Q::Cell | state | Q::Selected; - - setColor( aspect | A::Lower, m_pal.highlighted ); - setColor( aspect | A::Upper, m_pal.highlighted ); - + setColor( Q::Cell | state | Q::Selected, m_pal.highlighted ); setColor( Q::Text | state | Q::Selected, m_pal.highlightedText ); } } diff --git a/src/controls/QskListViewSkinlet.cpp b/src/controls/QskListViewSkinlet.cpp index 5dc2fa5c..33de9cba 100644 --- a/src/controls/QskListViewSkinlet.cpp +++ b/src/controls/QskListViewSkinlet.cpp @@ -196,7 +196,6 @@ void QskListViewSkinlet::updateBackgroundNodes( const QskListView* listView, QSGNode* backgroundNode ) const { using Q = QskListView; - using A = QskAspect; auto listViewNode = static_cast< const ListViewNode* >( backgroundNode->parent() ); @@ -209,10 +208,7 @@ void QskListViewSkinlet::updateBackgroundNodes( const auto rect = sampleRect( listView, listView->contentsRect(), Q::Cell, row ); - const QskAspect aspect = Q::Cell | ( ( row % 2 ) ? A::Upper : A::Lower ); - const auto boxHints = listView->boxHints( aspect ); - - auto newNode = updateBoxNode( listView, rowNode, rect, boxHints ); + auto newNode = updateBoxNode( listView, rowNode, rect, Q::Cell ); if ( newNode ) { if ( newNode->parent() != backgroundNode ) From 97628151b7d1bf63e488758d3b03b955150752df Mon Sep 17 00:00:00 2001 From: Rick Vogel <111582062+rick-vogel@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:11:19 +0200 Subject: [PATCH 05/21] Remove QSK_EXPORT to prevent inconsitent dll linkage warning --- playground/charts/ChartSample.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/charts/ChartSample.h b/playground/charts/ChartSample.h index 0f1c9f1d..710128bd 100644 --- a/playground/charts/ChartSample.h +++ b/playground/charts/ChartSample.h @@ -105,6 +105,6 @@ inline void ChartSample::setSample( #ifndef QT_NO_DEBUG_STREAM class QDebug; - QSK_EXPORT QDebug operator<<( QDebug, const ChartSample& ); + QDebug operator<<( QDebug, const ChartSample& ); #endif From b014cc9958a38f9e833167983efe1a7d0ca47abc Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 4 Aug 2023 15:33:49 +0200 Subject: [PATCH 06/21] list views: style states for M3 --- skins/material3/QskMaterial3Skin.cpp | 35 +++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index c13187e4..9f0a36de 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -1150,19 +1150,42 @@ void Editor::setupScrollView() void Editor::setupListView() { - using A = QskAspect; using Q = QskListView; - setStrutSize( Q::Cell, { -1, 56 } ); - setPadding( Q::Cell, { 16_dp, 12_dp, 16_dp, 12_dp } ); + setStrutSize( Q::Cell, { -1, 56_dp } ); + setPadding( Q::Cell, { 16_dp, 8_dp, 24_dp, 8_dp } ); setBoxBorderColors( Q::Cell, m_pal.outline ); - setGradient( Q::Cell, m_pal.surface ); - for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) - setGradient( Q::Cell | state | Q::Selected, m_pal.primary12 ); + + setGradient( Q::Cell, m_pal.surface ); + setGradient( Q::Cell | Q::Disabled, m_pal.surface ); + + const auto hoveredColor = flattenedColor( m_pal.onSurface, + m_pal.surface, m_pal.hoverOpacity ); + setGradient( Q::Cell | Q::Hovered, hoveredColor ); + + const auto pressedColor = flattenedColor( m_pal.onSurface, + m_pal.primary12, m_pal.pressedOpacity ); + setGradient( Q::Cell | Q::Pressed, pressedColor ); + + setGradient( Q::Cell | Q::Selected, m_pal.primary12 ); + + const auto selectedHoveredColor = flattenedColor( m_pal.onSurface, + m_pal.primary12, m_pal.focusOpacity ); + setGradient( Q::Cell | Q::Selected | Q::Hovered, selectedHoveredColor ); + + const auto selectedFocusedColor = flattenedColor( m_pal.onSurface, + m_pal.primary12, m_pal.focusOpacity ); + setGradient( Q::Cell | Q::Selected | Q::Focused, selectedFocusedColor ); + + setGradient( Q::Cell | Q::Selected | Q::Disabled, m_pal.surfaceVariant ); + + + setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); setColor( Q::Text, m_pal.onSurface ); + setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); } void Editor::setupSubWindow() From 1a6a9f5941fc9a8e6f32e825fc8030f71caf6d72 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 7 Aug 2023 08:35:37 +0200 Subject: [PATCH 07/21] mat3 skin prepared for smooth list view updates --- skins/fluent2/QskFluent2Skin.cpp | 2 +- skins/material3/QskMaterial3Skin.cpp | 74 +++++++++++++++++++--------- src/controls/QskListView.cpp | 3 +- src/controls/QskSkinnable.cpp | 20 ++++---- 4 files changed, 65 insertions(+), 34 deletions(-) diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 2c438d03..85227355 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -639,7 +639,7 @@ void Editor::setupListViewColors( textColor = pal.fillColor.text.disabled; indicatorColor = pal.fillColor.accent.disabled; } - if ( state1 == Q::Pressed ) + else if ( state1 == Q::Pressed ) { textColor = pal.fillColor.text.secondary; indicatorColor = pal.fillColor.accent.defaultColor; diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 9f0a36de..564b0521 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -1151,41 +1151,71 @@ void Editor::setupScrollView() void Editor::setupListView() { using Q = QskListView; + using A = QskAspect; setStrutSize( Q::Cell, { -1, 56_dp } ); setPadding( Q::Cell, { 16_dp, 8_dp, 24_dp, 8_dp } ); setBoxBorderColors( Q::Cell, m_pal.outline ); + for ( const auto state1 : { A::NoState, Q::Hovered, Q::Focused, Q::Pressed, Q::Disabled } ) + { + for ( const auto state2 : { A::NoState, Q::Selected } ) + { + QRgb cellColor; - setGradient( Q::Cell, m_pal.surface ); - setGradient( Q::Cell | Q::Disabled, m_pal.surface ); - - const auto hoveredColor = flattenedColor( m_pal.onSurface, - m_pal.surface, m_pal.hoverOpacity ); - setGradient( Q::Cell | Q::Hovered, hoveredColor ); - - const auto pressedColor = flattenedColor( m_pal.onSurface, - m_pal.primary12, m_pal.pressedOpacity ); - setGradient( Q::Cell | Q::Pressed, pressedColor ); - - setGradient( Q::Cell | Q::Selected, m_pal.primary12 ); - - const auto selectedHoveredColor = flattenedColor( m_pal.onSurface, - m_pal.primary12, m_pal.focusOpacity ); - setGradient( Q::Cell | Q::Selected | Q::Hovered, selectedHoveredColor ); - - const auto selectedFocusedColor = flattenedColor( m_pal.onSurface, - m_pal.primary12, m_pal.focusOpacity ); - setGradient( Q::Cell | Q::Selected | Q::Focused, selectedFocusedColor ); - - setGradient( Q::Cell | Q::Selected | Q::Disabled, m_pal.surfaceVariant ); + if ( state2 == A::NoState ) + { + if ( state1 == Q::Hovered ) + { + cellColor = flattenedColor( m_pal.onSurface, + m_pal.surface, m_pal.hoverOpacity ); + } + else if ( state1 == Q::Pressed ) + { + cellColor = flattenedColor( m_pal.onSurface, + m_pal.primary12, m_pal.pressedOpacity ); + } + else + { + cellColor = m_pal.surface; + } + } + else + { + if ( state1 == Q::Hovered ) + { + cellColor = flattenedColor( m_pal.onSurface, + m_pal.primary12, m_pal.focusOpacity ); + } + else if ( state1 == Q::Focused ) + { + cellColor = flattenedColor( m_pal.onSurface, + m_pal.primary12, m_pal.focusOpacity ); + } + else if ( state1 == Q::Disabled ) + { + cellColor = m_pal.surfaceVariant; + } + else + { + cellColor = m_pal.primary12; + } + } + setGradient( Q::Cell | state1 | state2, cellColor ); + } + } setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); setColor( Q::Text, m_pal.onSurface ); setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); + +#if 1 + setAnimation( Q::Cell | A::Color, 100 ); + setAnimation( Q::Text | A::Color, 100 ); +#endif } void Editor::setupSubWindow() diff --git a/src/controls/QskListView.cpp b/src/controls/QskListView.cpp index dd97c20a..0218cccb 100644 --- a/src/controls/QskListView.cpp +++ b/src/controls/QskListView.cpp @@ -69,6 +69,7 @@ class QskListView::PrivateData } storedRow = row; + listView->update(); } private: @@ -83,8 +84,6 @@ class QskListView::PrivateData Q_UNUSED( row ); Q_UNUSED( oldStates ); Q_UNUSED( newStates ); - - listView->update(); } public: diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 70b238a0..52430f34 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -122,6 +122,16 @@ static inline T qskColor( const QskSkinnable* skinnable, aspect | QskAspect::Color, status ).value< T >(); } +static inline constexpr QskAspect qskAnimatorAspect( const QskAspect aspect ) +{ + /* + We do not need the extra bits that would slow down resolving + the effective aspect in animatedHint. + */ + + return aspect.type() | aspect.subControl() | aspect.primitive(); +} + static inline void qskTriggerUpdates( QskAspect aspect, QQuickItem* item ) { /* @@ -1296,15 +1306,7 @@ void QskSkinnable::startHintTransition( QskAspect aspect, int index, v2.setValue( skin->graphicFilter( v2.toInt() ) ); } - /* - We do not need the extra bits that would slow down resolving - the effective aspect in animatedHint. - */ - - aspect.clearStates(); - aspect.setSection( QskAspect::Body ); - aspect.setVariation( QskAspect::NoVariation ); - aspect.setAnimator( false ); + aspect = qskAnimatorAspect( aspect ); #if DEBUG_ANIMATOR qDebug() << aspect << animationHint.duration; From 8c4e1c822c789a6788d7ca17b8d896faa9cf9804 Mon Sep 17 00:00:00 2001 From: Rick Vogel <111582062+rick-vogel@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:41:34 +0200 Subject: [PATCH 08/21] Update .clang-format https://clang.llvm.org/docs/ClangFormatStyleOptions.html#usecrlf --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 93317fa2..def913dd 100644 --- a/.clang-format +++ b/.clang-format @@ -108,6 +108,6 @@ SpacesInSquareBrackets: true Standard: Cpp11 TabWidth: 4 UseTab: Never -UseCRLF: Always +UseCRLF: True ... From 2d5db67e7952508670a1e3b4cee38ba8ccff78ab Mon Sep 17 00:00:00 2001 From: Rick Vogel <111582062+rick-vogel@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:48:17 +0200 Subject: [PATCH 09/21] fix lower case --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index def913dd..49dd75f5 100644 --- a/.clang-format +++ b/.clang-format @@ -108,6 +108,6 @@ SpacesInSquareBrackets: true Standard: Cpp11 TabWidth: 4 UseTab: Never -UseCRLF: True +UseCRLF: true ... From ec61c11ab64c8701f5f3693d8a985c83238c8758 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 25 Jul 2023 08:56:21 +0200 Subject: [PATCH 10/21] progress bars: Introduce progress ring and refactor Most of the code between rings and bars can be shared with a common superclass QskProgressIndicator. Resolves #98 --- .../gallery/progressbar/ProgressBarPage.cpp | 89 +++++- .../gallery/progressbar/ProgressBarPage.h | 4 + examples/iotdashboard/Skin.cpp | 1 - examples/iotdashboard/StoragePage.cpp | 4 - qmlexport/QskQml.cpp | 2 + skins/fluent2/QskFluent2Skin.cpp | 43 ++- skins/material3/QskMaterial3Skin.cpp | 28 +- skins/material3/QskMaterial3Skin.h | 2 + skins/squiek/QskSquiekSkin.cpp | 29 +- src/CMakeLists.txt | 16 +- src/controls/QskProgressBar.cpp | 235 +--------------- src/controls/QskProgressBar.h | 62 +---- src/controls/QskProgressBarSkinlet.cpp | 113 ++++---- src/controls/QskProgressBarSkinlet.h | 19 +- src/controls/QskProgressIndicator.cpp | 255 ++++++++++++++++++ src/controls/QskProgressIndicator.h | 83 ++++++ src/controls/QskProgressIndicatorSkinlet.cpp | 40 +++ src/controls/QskProgressIndicatorSkinlet.h | 37 +++ src/controls/QskProgressRing.cpp | 76 ++++++ src/controls/QskProgressRing.h | 50 ++++ src/controls/QskProgressRingSkinlet.cpp | 125 +++++++++ src/controls/QskProgressRingSkinlet.h | 34 +++ src/controls/QskSkin.cpp | 4 + 23 files changed, 967 insertions(+), 384 deletions(-) create mode 100644 src/controls/QskProgressIndicator.cpp create mode 100644 src/controls/QskProgressIndicator.h create mode 100644 src/controls/QskProgressIndicatorSkinlet.cpp create mode 100644 src/controls/QskProgressIndicatorSkinlet.h create mode 100644 src/controls/QskProgressRing.cpp create mode 100644 src/controls/QskProgressRing.h create mode 100644 src/controls/QskProgressRingSkinlet.cpp create mode 100644 src/controls/QskProgressRingSkinlet.h diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index fe1faee4..8655343b 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -4,13 +4,18 @@ *****************************************************************************/ #include "ProgressBarPage.h" -#include + +#include #include #include #include #include +#include +#include #include +#include + namespace { class ProgressBar : public QskProgressBar @@ -35,9 +40,32 @@ namespace colors += hctColor.toned( 45 ).rgb(); colors += hctColor.toned( 30 ).rgb(); - setBarGradient( qskBuildGradientStops( colors, true ) ); + setFillGradient( qskBuildGradientStops( colors, true ) ); } }; + + class DeterminateIndicatorsAnimator : public QskAnimator + { + public: + DeterminateIndicatorsAnimator( const QVector< QskProgressIndicator* >& indicators ) + : QskAnimator() + , m_indicators( indicators ) + { + setAutoRepeat( true ); + setDuration( 3000 ); + } + + void advance( qreal value ) override + { + for( auto* indicator : m_indicators ) + { + indicator->setValueAsRatio( value ); + } + } + + private: + const QVector< QskProgressIndicator* > m_indicators; + }; } ProgressBarPage::ProgressBarPage( QQuickItem* parent ) @@ -53,10 +81,7 @@ void ProgressBarPage::populate() hBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); hBox->setSpacing( 20 ); - { - auto bar = new ProgressBar( hBox ); - bar->setValue( 35 ); - } + QVector< QskProgressIndicator* > determinateIndicators; { auto bar = new ProgressBar( hBox ); @@ -77,6 +102,11 @@ void ProgressBarPage::populate() bar->setValue( 25 ); } + { + auto bar = new ProgressBar( hBox ); + determinateIndicators.append( bar ); + } + { auto bar = new ProgressBar( hBox ); bar->setIndeterminate( true ); @@ -106,8 +136,55 @@ void ProgressBarPage::populate() bar->setValue( 10 ); } + { + auto bar = new ProgressBar( vBox ); + determinateIndicators.append( bar ); + } + { auto bar = new ProgressBar( vBox ); bar->setIndeterminate( true ); } + + const auto sizes = { QskProgressRing::SmallSize, QskProgressRing::NormalSize, + QskProgressRing::LargeSize }; + + auto determinateRingsHBox = new QskLinearBox( Qt::Horizontal, vBox ); + + auto indeterminateRingsHBox = new QskLinearBox( Qt::Horizontal, vBox ); + + for( const auto size : sizes ) + { + for( const auto indeterminate : { true, false } ) + { + auto* ring = new QskProgressRing( determinateRingsHBox ); + ring->setSize( size ); + + QQuickItem* parentItem; + + if( indeterminate ) + { + parentItem = indeterminateRingsHBox; + ring->setIndeterminate( true ); + } + else + { + parentItem = determinateRingsHBox; + determinateIndicators.append( ring ); + } + + ring->setParent( parentItem ); + ring->setParentItem( parentItem ); + } + } + + connect( this, &QskQuickItem::windowChanged, this, [this, determinateIndicators]( QQuickWindow* window ) + { + if( window ) + { + m_determinateIndicatorsAnimator.reset( new DeterminateIndicatorsAnimator( determinateIndicators ) ); + m_determinateIndicatorsAnimator->setWindow( window ); + m_determinateIndicatorsAnimator->start(); + } + } ); } diff --git a/examples/gallery/progressbar/ProgressBarPage.h b/examples/gallery/progressbar/ProgressBarPage.h index 0d6fdec4..7f18c15a 100644 --- a/examples/gallery/progressbar/ProgressBarPage.h +++ b/examples/gallery/progressbar/ProgressBarPage.h @@ -7,6 +7,8 @@ #include "Page.h" +class QskAnimator; + class ProgressBarPage : public Page { public: @@ -14,4 +16,6 @@ class ProgressBarPage : public Page private: void populate(); + + std::unique_ptr< QskAnimator > m_determinateIndicatorsAnimator; }; diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index 56fb9135..f369c58f 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/examples/iotdashboard/StoragePage.cpp b/examples/iotdashboard/StoragePage.cpp index da2ca929..35261843 100644 --- a/examples/iotdashboard/StoragePage.cpp +++ b/examples/iotdashboard/StoragePage.cpp @@ -5,9 +5,6 @@ #include "StoragePage.h" #include "Box.h" -#include "CircularProgressBar.h" -#include "Diagram.h" -#include "EnergyMeter.h" #include "StorageBar.h" #include "StorageMeter.h" #include @@ -17,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/qmlexport/QskQml.cpp b/qmlexport/QskQml.cpp index b2019d2d..d15be6a7 100644 --- a/qmlexport/QskQml.cpp +++ b/qmlexport/QskQml.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -220,6 +221,7 @@ void QskQml::registerTypes() registerObject< QskFocusIndicator >(); registerObject< QskSeparator >(); registerObject< QskProgressBar >(); + registerObject< QskProgressRing >(); registerObject< QskPushButton >(); registerObject< QskScrollView >(); registerObject< QskScrollArea >(); diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 85227355..7261baaa 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -212,6 +213,9 @@ namespace void setupProgressBarMetrics(); void setupProgressBarColors( QskAspect::Section, const QskFluent2Theme& ); + void setupProgressRingMetrics(); + void setupProgressRingColors( QskAspect::Section, const QskFluent2Theme& ); + void setupPushButtonMetrics(); void setupPushButtonColors( QskAspect::Section, const QskFluent2Theme& ); @@ -291,6 +295,7 @@ void Editor::setupMetrics() setupMenuMetrics(); setupPageIndicatorMetrics(); setupProgressBarMetrics(); + setupProgressRingMetrics(); setupPushButtonMetrics(); setupRadioBoxMetrics(); setupScrollViewMetrics(); @@ -327,6 +332,7 @@ void Editor::setupColors( QskAspect::Section section, const QskFluent2Theme& the setupMenuColors( section, theme ); setupPageIndicatorColors( section, theme ); setupProgressBarColors( section, theme ); + setupProgressRingColors( section, theme ); setupPushButtonColors( section, theme ); setupRadioBoxColors( section, theme ); setupScrollViewColors( section, theme ); @@ -675,7 +681,7 @@ void Editor::setupListViewColors( const auto text = Q::Text | section | state1 | state2; setGradient( cell, cellColor ); - + { /* We are using a section of the left border to display a @@ -839,8 +845,8 @@ void Editor::setupProgressBarMetrics() setMetric( Q::Groove | A::Size, 1 ); setBoxShape( Q::Groove, 100, Qt::RelativeSize ); - setMetric( Q::Bar | A::Size, 3 ); - setBoxShape( Q::Bar, 100, Qt::RelativeSize ); + setMetric( Q::Fill | A::Size, 3 ); + setBoxShape( Q::Fill, 100, Qt::RelativeSize ); } void Editor::setupProgressBarColors( @@ -851,7 +857,36 @@ void Editor::setupProgressBarColors( const auto& pal = theme.palette; setGradient( Q::Groove | section, pal.strokeColor.controlStrong.defaultColor ); - setGradient( Q::Bar | section, pal.fillColor.accent.defaultColor ); + setGradient( Q::Fill | section, pal.fillColor.accent.defaultColor ); +} + +void Editor::setupProgressRingMetrics() +{ + using Q = QskProgressRing; + using A = QskAspect; + + static constexpr QskAspect::Variation SmallSize = A::Small; + static constexpr QskAspect::Variation NormalSize = A::NoVariation; + static constexpr QskAspect::Variation LargeSize = A::Large; + + setStrutSize( Q::Fill | SmallSize, { 16, 16 } ); + setStrutSize( Q::Fill | NormalSize, { 32, 32 } ); + setStrutSize( Q::Fill | LargeSize, { 64, 64 } ); + + const auto startAngle = 90, spanAngle = -360; + setArcMetrics( Q::Fill | SmallSize, startAngle, spanAngle, 1.5 ); + setArcMetrics( Q::Fill | NormalSize, startAngle, spanAngle, 3 ); + setArcMetrics( Q::Fill | LargeSize, startAngle, spanAngle, 6 ); +} + +void Editor::setupProgressRingColors( + QskAspect::Section section, const QskFluent2Theme& theme ) +{ + using Q = QskProgressRing; + + const auto& pal = theme.palette; + + setGradient( Q::Fill | section, pal.fillColor.accent.defaultColor ); } void Editor::setupPushButtonMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 564b0521..f9537a55 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,7 @@ namespace void setupPageIndicator(); void setupPopup(); void setupProgressBar(); + void setupProgressRing(); void setupRadioBox(); void setupPushButton(); void setupScrollView(); @@ -161,6 +163,7 @@ void Editor::setup() setupPageIndicator(); setupPopup(); setupProgressBar(); + setupProgressRing(); setupPushButton(); setupRadioBox(); setupScrollView(); @@ -431,9 +434,9 @@ void Editor::setupProgressBar() using A = QskAspect; using Q = QskProgressBar; - auto size = 5_dp; + auto size = 4_dp; - for ( auto subControl : { Q::Groove, Q::Bar } ) + for ( auto subControl : { Q::Groove, Q::Fill } ) { setMetric( subControl | A::Size, size ); setPadding( subControl, 0 ); @@ -443,12 +446,21 @@ void Editor::setupProgressBar() } setMetric( Q::Groove | A::Size, size ); - setGradient( Q::Groove, m_pal.primaryContainer ); + setGradient( Q::Groove, m_pal.surfaceContainerHighest ); setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 ); - setGradient( Q::Bar, m_pal.primary ); - setGradient( Q::Bar | Q::Disabled, m_pal.onSurface38 ); + setGradient( Q::Fill, m_pal.primary ); + setGradient( Q::Fill | Q::Disabled, m_pal.onSurface38 ); +} + +void Editor::setupProgressRing() +{ + using Q = QskProgressRing; + + setStrutSize( Q::Fill, { 48_dp, 48_dp } ); + setGradient( Q::Fill, m_pal.primary ); + setArcMetrics( Q::Fill, 90, -360, 4_dp ); } void Editor::setupRadioBox() @@ -538,7 +550,7 @@ void Editor::setupSegmentedBar() setGradient( Q::Segment | Q::Selected | Q::Hovered, flattenedColor( m_pal.onSurface, m_pal.secondaryContainer, m_pal.hoverOpacity ) ); - + setGradient( Q::Segment | Q::Selected | Q::Focused, flattenedColor( m_pal.onSurface, m_pal.secondaryContainer, m_pal.focusOpacity ) ); @@ -1302,6 +1314,8 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, outline = m_palettes[ NeutralVariant ].toned( 50 ).rgb(); outlineVariant = m_palettes[ NeutralVariant ].toned( 80 ).rgb(); + surfaceContainerHighest = m_palettes[ NeutralVariant ].toned( 90 ).rgb(); + shadow = m_palettes[ Neutral ].toned( 0 ).rgb(); } else if ( colorScheme == QskSkin::DarkScheme ) @@ -1336,6 +1350,8 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, outline = m_palettes[ NeutralVariant ].toned( 60 ).rgb(); outlineVariant = m_palettes[ NeutralVariant ].toned( 30 ).rgb(); + surfaceContainerHighest = m_palettes[ NeutralVariant ].toned( 22 ).rgb(); + shadow = m_palettes[ Neutral ].toned( 0 ).rgb(); } diff --git a/skins/material3/QskMaterial3Skin.h b/skins/material3/QskMaterial3Skin.h index ce36e9bd..2dbdcbc9 100644 --- a/skins/material3/QskMaterial3Skin.h +++ b/skins/material3/QskMaterial3Skin.h @@ -79,6 +79,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme QRgb outline; QRgb outlineVariant; + QRgb surfaceContainerHighest; + QRgb shadow; QskShadowMetrics elevation0; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 3e3bbff3..d116d9db 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,7 @@ namespace void setupPageIndicator(); void setupPopup(); void setupProgressBar(); + void setupProgressRing(); void setupPushButton(); void setupRadioBox(); void setupScrollView(); @@ -287,6 +289,7 @@ void Editor::setup() setupPageIndicator(); setupPopup(); setupProgressBar(); + setupProgressRing(); setupPushButton(); setupRadioBox(); setupScrollView(); @@ -521,15 +524,35 @@ void Editor::setupProgressBar() using A = QskAspect; using Q = QskProgressBar; - for ( auto subControl : { Q::Groove, Q::Bar } ) + for ( auto subControl : { Q::Groove, Q::Fill } ) { setMetric( subControl | A::Size, 6 ); setPadding( subControl, 0 ); setBoxShape( subControl, 4 ); } - setGradient( Q::Groove, m_pal.darker200 ); - setGradient( Q::Bar, m_pal.highlighted ); + setGradient( Q::Groove, m_pal.lighter110 ); + setGradient( Q::Fill, m_pal.highlighted ); +} + +void Editor::setupProgressRing() +{ + using A = QskAspect; + using Q = QskProgressRing; + + for ( auto subControl : { Q::Groove, Q::Fill } ) + { + setMetric( subControl | A::Size, 6 ); + setPadding( subControl, 0 ); + setBoxShape( subControl, 4 ); + } + + setArcMetrics( Q::Groove, 90, -360, 6 ); + setGradient( Q::Groove, m_pal.lighter110 ); + + setStrutSize( Q::Fill, { 60, 60 } ); + setGradient( Q::Fill, m_pal.highlighted ); + setArcMetrics( Q::Fill, 90, -360, 6 ); } void Editor::setupFocusIndicator() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07b1b761..f244a2bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -196,10 +196,14 @@ list(APPEND HEADERS controls/QskPanGestureRecognizer.h controls/QskPopup.h controls/QskPopupSkinlet.h - controls/QskPushButton.h - controls/QskPushButtonSkinlet.h controls/QskProgressBar.h controls/QskProgressBarSkinlet.h + controls/QskProgressIndicator.h + controls/QskProgressIndicatorSkinlet.h + controls/QskProgressRing.h + controls/QskProgressRingSkinlet.h + controls/QskPushButton.h + controls/QskPushButtonSkinlet.h controls/QskQuick.h controls/QskQuickItem.h controls/QskRadioBox.h @@ -294,10 +298,14 @@ list(APPEND SOURCES controls/QskPanGestureRecognizer.cpp controls/QskPopup.cpp controls/QskPopupSkinlet.cpp - controls/QskPushButton.cpp - controls/QskPushButtonSkinlet.cpp controls/QskProgressBar.cpp controls/QskProgressBarSkinlet.cpp + controls/QskProgressIndicator.cpp + controls/QskProgressIndicatorSkinlet.cpp + controls/QskProgressRing.cpp + controls/QskProgressRingSkinlet.cpp + controls/QskPushButton.cpp + controls/QskPushButtonSkinlet.cpp controls/QskQuick.cpp controls/QskQuickItem.cpp controls/QskQuickItemPrivate.cpp diff --git a/src/controls/QskProgressBar.cpp b/src/controls/QskProgressBar.cpp index 7a496b2d..92a964ce 100644 --- a/src/controls/QskProgressBar.cpp +++ b/src/controls/QskProgressBar.cpp @@ -6,91 +6,30 @@ #include "QskProgressBar.h" #include "QskIntervalF.h" -#include "QskFunctions.h" -#include "QskAnimator.h" -#include "QskAspect.h" QSK_SUBCONTROL( QskProgressBar, Groove ) -QSK_SUBCONTROL( QskProgressBar, Bar ) - -namespace -{ - class PositionAnimator : public QskAnimator - { - public: - PositionAnimator( QskProgressBar* progressBar ) - : m_progressBar( progressBar ) - { - setAutoRepeat( true ); - setDuration( 1300 ); - - setWindow( progressBar->window() ); - } - - void advance( qreal value ) override - { - if ( m_progressBar->setPositionHint( QskProgressBar::Bar, value ) ) - m_progressBar->update(); - } - - private: - QskProgressBar* m_progressBar; - }; -} +QSK_SUBCONTROL( QskProgressBar, Fill ) class QskProgressBar::PrivateData { public: - void updateIndeterminateAnimator( QskProgressBar* progressBar ) - { - if ( !isIndeterminate ) - { - delete animator; - animator = nullptr; - - return; - } - - if ( progressBar->window() && progressBar->isVisible() ) - { - if ( animator == nullptr ) - animator = new PositionAnimator( progressBar ); - - animator->start(); - } - else - { - if ( animator ) - animator->stop(); - } - } - - PositionAnimator* animator = nullptr; - - qreal value = 0.0; - qreal origin = 0.0; - - bool hasOrigin = false; - bool isIndeterminate = false; - Qt::Orientation orientation; }; QskProgressBar::QskProgressBar( Qt::Orientation orientation, qreal min, qreal max, QQuickItem* parent ) - : QskBoundedControl( min, max, parent ) + : Inherited( min, max, parent ) , m_data( new PrivateData ) { m_data->orientation = orientation; - m_data->value = minimum(); if ( orientation == Qt::Horizontal ) initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed ); else initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::MinimumExpanding ); - connect( this, &QskBoundedControl::boundariesChanged, - this, &QskProgressBar::adjustValue ); + setSubcontrolProxy( Inherited::Groove, Groove ); + setSubcontrolProxy( Inherited::Fill, Fill ); } QskProgressBar::QskProgressBar( Qt::Orientation orientation, QQuickItem* parent ) @@ -115,7 +54,12 @@ QskProgressBar::QskProgressBar( QQuickItem* parent ) QskProgressBar::~QskProgressBar() { - delete m_data->animator; +} + +QskAspect::Variation QskProgressBar::effectiveVariation() const +{ + // so you can define different hints depending on the orientation + return static_cast< QskAspect::Variation >( m_data->orientation ); } Qt::Orientation QskProgressBar::orientation() const @@ -137,163 +81,4 @@ void QskProgressBar::setOrientation( Qt::Orientation orientation ) } } -bool QskProgressBar::isIndeterminate() const -{ - return m_data->isIndeterminate; -} - -void QskProgressBar::setIndeterminate( bool on ) -{ - if ( on == m_data->isIndeterminate ) - return; - - m_data->isIndeterminate = on; - m_data->updateIndeterminateAnimator( this ); - - update(); - Q_EMIT indeterminateChanged( on ); -} - -QskAspect::Variation QskProgressBar::effectiveVariation() const -{ - // so you can define different hints depending on the orientation - return static_cast< QskAspect::Variation >( m_data->orientation ); -} - -void QskProgressBar::setBarGradient( const QskGradient& gradient ) -{ - setGradientHint( Bar, gradient ); -} - -void QskProgressBar::resetBarGradient() -{ - resetColor( Bar ); -} - -QskGradient QskProgressBar::barGradient() const -{ - return gradientHint( QskProgressBar::Bar ); -} - -void QskProgressBar::setExtent( qreal extent ) -{ - if ( extent < 0.0 ) - extent = 0.0; - - if ( setMetric( Groove | QskAspect::Size, extent ) ) - Q_EMIT extentChanged( extent ); -} - -void QskProgressBar::resetExtent() -{ - if ( resetMetric( Groove | QskAspect::Size ) ) - Q_EMIT extentChanged( extent() ); -} - -qreal QskProgressBar::extent() const -{ - auto grooveSize = metric( Groove | QskAspect::Size ); - auto barSize = metric( Bar | QskAspect::Size ); - return qMax( grooveSize, barSize ); -} - -void QskProgressBar::setOrigin( qreal origin ) -{ - if ( isComponentComplete() ) - origin = boundedValue( origin ); - - if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) ) - { - m_data->hasOrigin = true; - m_data->origin = origin; - - update(); - Q_EMIT originChanged( origin ); - } -} - -void QskProgressBar::resetOrigin() -{ - if ( m_data->hasOrigin ) - { - m_data->hasOrigin = false; - - update(); - Q_EMIT originChanged( origin() ); - } -} - -qreal QskProgressBar::origin() const -{ - if ( m_data->hasOrigin ) - { - return boundedValue( m_data->origin ); - } - - return minimum(); -} - -void QskProgressBar::setValue( qreal value ) -{ - if ( isComponentComplete() ) - value = boundedValue( value ); - - setValueInternal( value ); -} - -qreal QskProgressBar::value() const -{ - return m_data->value; -} - -void QskProgressBar::setValueAsRatio( qreal ratio ) -{ - ratio = qBound( 0.0, ratio, 1.0 ); - setValue( minimum() + ratio * boundaryLength() ); -} - -qreal QskProgressBar::valueAsRatio() const -{ - return valueAsRatio( m_data->value ); -} - -void QskProgressBar::componentComplete() -{ - Inherited::componentComplete(); - adjustValue(); -} - -void QskProgressBar::adjustValue() -{ - if ( isComponentComplete() ) - setValueInternal( boundedValue( m_data->value ) ); -} - -void QskProgressBar::setValueInternal( qreal value ) -{ - if ( !qskFuzzyCompare( value, m_data->value ) ) - { - m_data->value = value; - Q_EMIT valueChanged( value ); - - update(); - } -} - -void QskProgressBar::itemChange( QQuickItem::ItemChange change, - const QQuickItem::ItemChangeData& value ) -{ - switch( static_cast< int >( change ) ) - { - case QQuickItem::ItemVisibleHasChanged: - case QQuickItem::ItemSceneChange: - { - m_data->updateIndeterminateAnimator( this ); - break; - } - } - - Inherited::itemChange( change, value ); -} - #include "moc_QskProgressBar.cpp" diff --git a/src/controls/QskProgressBar.h b/src/controls/QskProgressBar.h index f41dc387..f1f1b613 100644 --- a/src/controls/QskProgressBar.h +++ b/src/controls/QskProgressBar.h @@ -6,34 +6,19 @@ #ifndef QSK_PROGRESS_BAR_H #define QSK_PROGRESS_BAR_H -#include "QskBoundedControl.h" +#include "QskProgressIndicator.h" -class QskIntervalF; - -class QSK_EXPORT QskProgressBar : public QskBoundedControl +class QSK_EXPORT QskProgressBar : public QskProgressIndicator { Q_OBJECT Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged ) - Q_PROPERTY( qreal extent READ extent - WRITE setExtent RESET resetExtent NOTIFY extentChanged ) - - Q_PROPERTY( bool indeterminate READ isIndeterminate - WRITE setIndeterminate NOTIFY indeterminateChanged ) - - Q_PROPERTY( qreal origin READ origin - WRITE setOrigin RESET resetOrigin NOTIFY originChanged ) - - Q_PROPERTY( qreal value READ value WRITE setValue NOTIFY valueChanged ) - Q_PROPERTY( qreal valueAsRatio READ valueAsRatio - WRITE setValueAsRatio NOTIFY valueChanged ) - - using Inherited = QskBoundedControl; + using Inherited = QskProgressIndicator; public: - QSK_SUBCONTROLS( Groove, Bar ) + QSK_SUBCONTROLS( Groove, Fill ) QskProgressBar( Qt::Orientation, QQuickItem* parent = nullptr ); QskProgressBar( Qt::Orientation, qreal min, qreal max, QQuickItem* parent = nullptr ); @@ -43,50 +28,15 @@ class QSK_EXPORT QskProgressBar : public QskBoundedControl ~QskProgressBar() override; + QskAspect::Variation effectiveVariation() const override; + Qt::Orientation orientation() const; void setOrientation( Qt::Orientation orientation ); - bool isIndeterminate() const; - void setIndeterminate( bool on = true ); - - QskAspect::Variation effectiveVariation() const override; - - void setBarGradient( const QskGradient& ); - void resetBarGradient(); - QskGradient barGradient() const; - - void setExtent( qreal ); - void resetExtent(); - qreal extent() const; - - void resetOrigin(); - qreal origin() const; - - qreal value() const; - qreal valueAsRatio() const; // [0.0, 1.0] - using QskBoundedControl::valueAsRatio; - - public Q_SLOTS: - void setValue( qreal ); - void setValueAsRatio( qreal ); - void setOrigin( qreal ); - Q_SIGNALS: void orientationChanged( Qt::Orientation ); - void extentChanged( qreal ); - void indeterminateChanged( bool ); - void valueChanged( qreal ); - void originChanged( qreal ); - - protected: - void componentComplete() override; - void itemChange( ItemChange, const ItemChangeData& ) override; private: - void setValueInternal( qreal value ); - void adjustBoundaries( bool increasing ); - void adjustValue(); - class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/controls/QskProgressBarSkinlet.cpp b/src/controls/QskProgressBarSkinlet.cpp index 91de022a..92be1c09 100644 --- a/src/controls/QskProgressBarSkinlet.cpp +++ b/src/controls/QskProgressBarSkinlet.cpp @@ -7,51 +7,54 @@ #include "QskProgressBar.h" #include "QskIntervalF.h" #include "QskBoxBorderMetrics.h" -#include "QskGradientDirection.h" #include #include -static inline QskIntervalF qskBarInterval( const QskProgressBar* bar ) +using Q = QskProgressBar; + +namespace { - qreal pos1, pos2; - - if ( bar->isIndeterminate() ) + QskIntervalF qskFillInterval( const Q* bar ) { - const auto pos = bar->positionHint( QskProgressBar::Bar ); + qreal pos1, pos2; - static const QEasingCurve curve( QEasingCurve::InOutCubic ); - - const qreal off = 0.15; - - pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) ); - pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) ); - } - else - { - pos1 = bar->valueAsRatio( bar->origin() ); - pos2 = bar->valueAsRatio( bar->value() ); - } - - if( bar->orientation() == Qt::Horizontal ) - { - if ( bar->layoutMirroring() ) + if ( bar->isIndeterminate() ) { - pos1 = 1.0 - pos1; - pos2 = 1.0 - pos2; + const auto pos = bar->positionHint( QskProgressIndicator::Fill ); + + static const QEasingCurve curve( QEasingCurve::InOutCubic ); + + const qreal off = 0.15; + + pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) ); + pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) ); } + else + { + pos1 = bar->valueAsRatio( bar->origin() ); + pos2 = bar->valueAsRatio( bar->value() ); + } + + if( bar->orientation() == Qt::Horizontal ) + { + if ( bar->layoutMirroring() ) + { + pos1 = 1.0 - pos1; + pos2 = 1.0 - pos2; + } + } + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + return QskIntervalF( pos1, pos2 ); } - - if ( pos1 > pos2 ) - std::swap( pos1, pos2 ); - - return QskIntervalF( pos1, pos2 ); } QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin ) - : QskSkinlet( skin ) + : Inherited( skin ) { - setNodeRoles( { GrooveRole, BarRole } ); } QskProgressBarSkinlet::~QskProgressBarSkinlet() @@ -62,7 +65,6 @@ QRectF QskProgressBarSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - using Q = QskProgressBar; const auto bar = static_cast< const Q* >( skinnable ); if( subControl == Q::Groove ) @@ -84,7 +86,7 @@ QRectF QskProgressBarSkinlet::subControlRect( return rect; } - if( subControl == Q::Bar ) + if( subControl == Q::Fill ) { return barRect( bar ); } @@ -92,30 +94,18 @@ QRectF QskProgressBarSkinlet::subControlRect( return Inherited::subControlRect( skinnable, contentsRect, subControl ); } -QSGNode* QskProgressBarSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +QSGNode* QskProgressBarSkinlet::updateGrooveNode( + const QskProgressIndicator* indicator, QSGNode* node ) const { - switch( nodeRole ) - { - case GrooveRole: - { - return updateBoxNode( skinnable, node, QskProgressBar::Groove ); - } - - case BarRole: - { - const auto bar = static_cast< const QskProgressBar* >( skinnable ); - return updateBarNode( bar, node ); - } - } - - return Inherited::updateSubNode( skinnable, nodeRole, node ); + return updateBoxNode( indicator, node, Q::Groove ); } -QSGNode* QskProgressBarSkinlet::updateBarNode( - const QskProgressBar* bar, QSGNode* node ) const +QSGNode* QskProgressBarSkinlet::updateFillNode( + const QskProgressIndicator* indicator, QSGNode* node ) const { - const auto subControl = QskProgressBar::Bar; + const auto bar = static_cast< const Q* >( indicator ); + + const auto subControl = Q::Fill; const auto rect = bar->subControlRect( subControl ); if ( rect.isEmpty() ) @@ -139,26 +129,27 @@ QSGNode* QskProgressBarSkinlet::updateBarNode( not supporting this yet. TODO ... */ - const auto intv = qskBarInterval( bar ); + const auto intv = qskFillInterval( bar ); const auto stops = qskExtractedGradientStops( gradient.stops(), intv.lowerBound(), intv.upperBound() ); gradient.setStops( stops ); - gradient.setLinearDirection( bar->orientation() ); + + gradient.setLinearDirection( static_cast< Qt::Orientation >( bar->orientation() ) ); + if ( bar->orientation() == Qt::Vertical || bar->layoutMirroring() ) gradient.reverse(); } - return updateBoxNode( bar, node, rect, gradient, subControl ); + return updateBoxNode( indicator, node, rect, gradient, subControl ); } -QRectF QskProgressBarSkinlet::barRect( const QskProgressBar* bar ) const +QRectF QskProgressBarSkinlet::barRect( const Q* bar ) const { - using Q = QskProgressBar; const auto subControl = Q::Groove; - const auto barSize = bar->metric( Q::Bar | QskAspect::Size ); + const auto barSize = bar->metric( Q::Fill | QskAspect::Size ); auto rect = bar->subControlRect( subControl ); if ( bar->orientation() == Qt::Horizontal ) @@ -179,7 +170,7 @@ QRectF QskProgressBarSkinlet::barRect( const QskProgressBar* bar ) const rect = rect.marginsRemoved( m ); - const auto intv = qskBarInterval( bar ); + const auto intv = qskFillInterval( bar ); if( bar->orientation() == Qt::Horizontal ) { @@ -205,7 +196,7 @@ QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable, if ( which != Qt::PreferredSize ) return QSizeF(); - const auto bar = static_cast< const QskProgressBar* >( skinnable ); + const auto bar = static_cast< const Q* >( skinnable ); const auto extent = bar->extent(); diff --git a/src/controls/QskProgressBarSkinlet.h b/src/controls/QskProgressBarSkinlet.h index f6af6114..a3f36cd1 100644 --- a/src/controls/QskProgressBarSkinlet.h +++ b/src/controls/QskProgressBarSkinlet.h @@ -6,25 +6,17 @@ #ifndef QSK_PROGRESS_BAR_SKINLET_H #define QSK_PROGRESS_BAR_SKINLET_H -#include "QskSkinlet.h" +#include "QskProgressIndicatorSkinlet.h" class QskProgressBar; -class QSK_EXPORT QskProgressBarSkinlet : public QskSkinlet +class QSK_EXPORT QskProgressBarSkinlet : public QskProgressIndicatorSkinlet { Q_GADGET - using Inherited = QskSkinlet; + using Inherited = QskProgressIndicatorSkinlet; public: - enum NodeRole - { - GrooveRole, - BarRole, - - RoleCount - }; - Q_INVOKABLE QskProgressBarSkinlet( QskSkin* = nullptr ); ~QskProgressBarSkinlet() override; @@ -35,11 +27,10 @@ class QSK_EXPORT QskProgressBarSkinlet : public QskSkinlet Qt::SizeHint, const QSizeF& ) const override; protected: - QSGNode* updateSubNode( const QskSkinnable*, - quint8 nodeRole, QSGNode* ) const override; + QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const override; + QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; private: - QSGNode* updateBarNode( const QskProgressBar*, QSGNode* ) const; QRectF barRect( const QskProgressBar* ) const; }; diff --git a/src/controls/QskProgressIndicator.cpp b/src/controls/QskProgressIndicator.cpp new file mode 100644 index 00000000..9cf220e4 --- /dev/null +++ b/src/controls/QskProgressIndicator.cpp @@ -0,0 +1,255 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressIndicator.h" + +#include "QskIntervalF.h" +#include "QskFunctions.h" +#include "QskAnimator.h" +#include "QskAspect.h" + +QSK_SUBCONTROL( QskProgressIndicator, Groove ) +QSK_SUBCONTROL( QskProgressIndicator, Fill ) + +namespace +{ + class PositionAnimator : public QskAnimator + { + public: + PositionAnimator( QskProgressIndicator* indicator ) + : m_indicator( indicator ) + { + setAutoRepeat( true ); + setDuration( 1300 ); + + setWindow( indicator->window() ); + } + + void advance( qreal value ) override + { + if ( m_indicator->setPositionHint( QskProgressIndicator::Fill, value ) ) + m_indicator->update(); + } + + private: + QskProgressIndicator* m_indicator; + }; +} + +class QskProgressIndicator::PrivateData +{ + public: + void updateIndeterminateAnimator( QskProgressIndicator* indicator ) + { + if ( !isIndeterminate ) + { + delete animator; + animator = nullptr; + + return; + } + + if ( indicator->window() && indicator->isVisible() ) + { + if ( animator == nullptr ) + animator = new PositionAnimator( indicator ); + + animator->start(); + } + else + { + if ( animator ) + animator->stop(); + } + } + + PositionAnimator* animator = nullptr; + + qreal value = 0.0; + qreal origin = 0.0; + + bool hasOrigin = false; + bool isIndeterminate = false; +}; + +QskProgressIndicator::QskProgressIndicator( qreal min, qreal max, QQuickItem* parent ) + : QskBoundedControl( min, max, parent ) + , m_data( new PrivateData ) +{ + m_data->value = minimum(); + + connect( this, &QskBoundedControl::boundariesChanged, + this, &QskProgressIndicator::adjustValue ); +} + +QskProgressIndicator::QskProgressIndicator( QQuickItem* parent ) + : QskProgressIndicator( 0.0, 100.0, parent ) +{ +} + +QskProgressIndicator::QskProgressIndicator( const QskIntervalF& boundaries, QQuickItem* parent ) + : QskProgressIndicator( boundaries.lowerBound(), boundaries.upperBound(), parent ) +{ +} + +QskProgressIndicator::~QskProgressIndicator() +{ + delete m_data->animator; +} + +bool QskProgressIndicator::isIndeterminate() const +{ + return m_data->isIndeterminate; +} + +void QskProgressIndicator::setIndeterminate( bool on ) +{ + if ( on == m_data->isIndeterminate ) + return; + + m_data->isIndeterminate = on; + m_data->updateIndeterminateAnimator( this ); + + update(); + Q_EMIT indeterminateChanged( on ); +} + +void QskProgressIndicator::setFillGradient( const QskGradient& gradient ) +{ + setGradientHint( Fill, gradient ); +} + +void QskProgressIndicator::resetFillGradient() +{ + resetColor( Fill ); +} + +QskGradient QskProgressIndicator::fillGradient() const +{ + return gradientHint( Fill ); +} + +void QskProgressIndicator::setExtent( qreal extent ) +{ + if ( extent < 0.0 ) + extent = 0.0; + + if ( setMetric( Groove | QskAspect::Size, extent ) ) + Q_EMIT extentChanged( extent ); +} + +void QskProgressIndicator::resetExtent() +{ + if ( resetMetric( Groove | QskAspect::Size ) ) + Q_EMIT extentChanged( extent() ); +} + +qreal QskProgressIndicator::extent() const +{ + auto grooveSize = metric( Groove | QskAspect::Size ); + auto fillSize = metric( Fill | QskAspect::Size ); + return qMax( grooveSize, fillSize ); +} + +void QskProgressIndicator::setOrigin( qreal origin ) +{ + if ( isComponentComplete() ) + origin = boundedValue( origin ); + + if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) ) + { + m_data->hasOrigin = true; + m_data->origin = origin; + + update(); + Q_EMIT originChanged( origin ); + } +} + +void QskProgressIndicator::resetOrigin() +{ + if ( m_data->hasOrigin ) + { + m_data->hasOrigin = false; + + update(); + Q_EMIT originChanged( origin() ); + } +} + +qreal QskProgressIndicator::origin() const +{ + if ( m_data->hasOrigin ) + { + return boundedValue( m_data->origin ); + } + + return minimum(); +} + +void QskProgressIndicator::setValue( qreal value ) +{ + if ( isComponentComplete() ) + value = boundedValue( value ); + + setValueInternal( value ); +} + +qreal QskProgressIndicator::value() const +{ + return m_data->value; +} + +void QskProgressIndicator::setValueAsRatio( qreal ratio ) +{ + ratio = qBound( 0.0, ratio, 1.0 ); + setValue( minimum() + ratio * boundaryLength() ); +} + +qreal QskProgressIndicator::valueAsRatio() const +{ + return valueAsRatio( m_data->value ); +} + +void QskProgressIndicator::componentComplete() +{ + Inherited::componentComplete(); + adjustValue(); +} + +void QskProgressIndicator::adjustValue() +{ + if ( isComponentComplete() ) + setValueInternal( boundedValue( m_data->value ) ); +} + +void QskProgressIndicator::setValueInternal( qreal value ) +{ + if ( !qskFuzzyCompare( value, m_data->value ) ) + { + m_data->value = value; + Q_EMIT valueChanged( value ); + + update(); + } +} + +void QskProgressIndicator::itemChange( QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData& value ) +{ + switch( static_cast< int >( change ) ) + { + case QQuickItem::ItemVisibleHasChanged: + case QQuickItem::ItemSceneChange: + { + m_data->updateIndeterminateAnimator( this ); + break; + } + } + + Inherited::itemChange( change, value ); +} + +#include "moc_QskProgressIndicator.cpp" diff --git a/src/controls/QskProgressIndicator.h b/src/controls/QskProgressIndicator.h new file mode 100644 index 00000000..77d49002 --- /dev/null +++ b/src/controls/QskProgressIndicator.h @@ -0,0 +1,83 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_INDICATOR_H +#define QSK_PROGRESS_INDICATOR_H + +#include "QskBoundedControl.h" + +class QskIntervalF; + +class QSK_EXPORT QskProgressIndicator : public QskBoundedControl +{ + Q_OBJECT + + Q_PROPERTY( qreal extent READ extent + WRITE setExtent RESET resetExtent NOTIFY extentChanged ) + + Q_PROPERTY( bool indeterminate READ isIndeterminate + WRITE setIndeterminate NOTIFY indeterminateChanged ) + + Q_PROPERTY( qreal origin READ origin + WRITE setOrigin RESET resetOrigin NOTIFY originChanged ) + + Q_PROPERTY( qreal value READ value WRITE setValue NOTIFY valueChanged ) + Q_PROPERTY( qreal valueAsRatio READ valueAsRatio + WRITE setValueAsRatio NOTIFY valueChanged ) + + using Inherited = QskBoundedControl; + + public: + QSK_SUBCONTROLS( Groove, Fill ) + + QskProgressIndicator( QQuickItem* parent = nullptr ); + QskProgressIndicator( qreal min, qreal max, QQuickItem* parent = nullptr ); + QskProgressIndicator( const QskIntervalF&, QQuickItem* parent = nullptr ); + + ~QskProgressIndicator() override; + + bool isIndeterminate() const; + void setIndeterminate( bool on = true ); + + void setFillGradient( const QskGradient& ); + void resetFillGradient(); + QskGradient fillGradient() const; + + void setExtent( qreal ); + void resetExtent(); + qreal extent() const; + + void resetOrigin(); + qreal origin() const; + + qreal value() const; + qreal valueAsRatio() const; // [0.0, 1.0] + using QskBoundedControl::valueAsRatio; + + public Q_SLOTS: + void setValue( qreal ); + void setValueAsRatio( qreal ); + void setOrigin( qreal ); + + Q_SIGNALS: + void extentChanged( qreal ); + void indeterminateChanged( bool ); + void valueChanged( qreal ); + void originChanged( qreal ); + + protected: + void componentComplete() override; + void itemChange( ItemChange, const ItemChangeData& ) override; + + private: + void setValueInternal( qreal value ); + void adjustBoundaries( bool increasing ); + void adjustValue(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskProgressIndicatorSkinlet.cpp b/src/controls/QskProgressIndicatorSkinlet.cpp new file mode 100644 index 00000000..f107409c --- /dev/null +++ b/src/controls/QskProgressIndicatorSkinlet.cpp @@ -0,0 +1,40 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressIndicatorSkinlet.h" +#include "QskProgressIndicator.h" + +QskProgressIndicatorSkinlet::QskProgressIndicatorSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + setNodeRoles( { GrooveRole, FillRole } ); +} + +QskProgressIndicatorSkinlet::~QskProgressIndicatorSkinlet() +{ +} + +QSGNode* QskProgressIndicatorSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto indicator = static_cast< const QskProgressIndicator* >( skinnable ); + + switch( nodeRole ) + { + case GrooveRole: + { + return updateGrooveNode( indicator, node ); + } + + case FillRole: + { + return updateFillNode( indicator, node ); + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +#include "moc_QskProgressIndicatorSkinlet.cpp" diff --git a/src/controls/QskProgressIndicatorSkinlet.h b/src/controls/QskProgressIndicatorSkinlet.h new file mode 100644 index 00000000..effc2b13 --- /dev/null +++ b/src/controls/QskProgressIndicatorSkinlet.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_INDICATOR_SKINLET_H +#define QSK_PROGRESS_INDICATOR_SKINLET_H + +#include "QskSkinlet.h" + +class QskProgressIndicator; + +class QSK_EXPORT QskProgressIndicatorSkinlet : public QskSkinlet +{ + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + GrooveRole, + FillRole, + + RoleCount + }; + + QskProgressIndicatorSkinlet( QskSkin* = nullptr ); + ~QskProgressIndicatorSkinlet() override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + virtual QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const = 0; + virtual QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const = 0; +}; + +#endif diff --git a/src/controls/QskProgressRing.cpp b/src/controls/QskProgressRing.cpp new file mode 100644 index 00000000..9734ccab --- /dev/null +++ b/src/controls/QskProgressRing.cpp @@ -0,0 +1,76 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressRing.h" + +#include "QskIntervalF.h" + +QSK_SUBCONTROL( QskProgressRing, Groove ) +QSK_SUBCONTROL( QskProgressRing, Fill ) + +class QskProgressRing::PrivateData +{ + public: + int size : 2; +}; + +QskProgressRing::QskProgressRing( qreal min, qreal max, QQuickItem* parent ) + : Inherited( min, max, parent ) + , m_data( new PrivateData ) +{ + m_data->size = NormalSize; + + setSubcontrolProxy( Inherited::Groove, Groove ); + setSubcontrolProxy( Inherited::Fill, Fill ); +} + +QskProgressRing::QskProgressRing( QQuickItem* parent ) + : QskProgressRing( 0.0, 100.0, parent ) +{ +} + +QskProgressRing::QskProgressRing( const QskIntervalF& boundaries, QQuickItem* parent ) + : QskProgressRing( boundaries.lowerBound(), boundaries.upperBound(), parent ) +{ +} + +QskProgressRing::~QskProgressRing() +{ +} + +QskAspect::Variation QskProgressRing::effectiveVariation() const +{ + switch( size() ) + { + case SmallSize: + return QskAspect::Small; + + case LargeSize: + return QskAspect::Large; + + default: + return QskAspect::NoVariation; + } +} + +void QskProgressRing::setSize( Size size ) +{ + if ( size != m_data->size ) + { + m_data->size = size; + + resetImplicitSize(); + update(); + + Q_EMIT sizeChanged( size ); + } +} + +QskProgressRing::Size QskProgressRing::size() const +{ + return static_cast< Size >( m_data->size ); +} + +#include "moc_QskProgressRing.cpp" diff --git a/src/controls/QskProgressRing.h b/src/controls/QskProgressRing.h new file mode 100644 index 00000000..e4ce68e2 --- /dev/null +++ b/src/controls/QskProgressRing.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_RING_H +#define QSK_PROGRESS_RING_H + +#include "QskProgressIndicator.h" + +class QSK_EXPORT QskProgressRing : public QskProgressIndicator +{ + Q_OBJECT + + Q_PROPERTY( Size size READ size + WRITE setSize NOTIFY sizeChanged ) + + using Inherited = QskProgressIndicator; + + public: + QSK_SUBCONTROLS( Groove, Fill ) + + enum Size + { + SmallSize = -1, + NormalSize = 0, + LargeSize = 1 + }; + Q_ENUM( Size ) + + QskProgressRing( QQuickItem* parent = nullptr ); + QskProgressRing( qreal min, qreal max, QQuickItem* parent = nullptr ); + QskProgressRing( const QskIntervalF&, QQuickItem* parent = nullptr ); + + ~QskProgressRing() override; + + QskAspect::Variation effectiveVariation() const override; + + void setSize( Size ); + Size size() const; + + Q_SIGNALS: + void sizeChanged( Size ); + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskProgressRingSkinlet.cpp b/src/controls/QskProgressRingSkinlet.cpp new file mode 100644 index 00000000..fa5c7ca5 --- /dev/null +++ b/src/controls/QskProgressRingSkinlet.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressRingSkinlet.h" +#include "QskArcMetrics.h" +#include "QskProgressRing.h" +#include "QskIntervalF.h" + +using Q = QskProgressRing; + +namespace +{ + QskIntervalF qskFillInterval( const Q* ring ) + { + qreal pos1, pos2; + + if ( ring->isIndeterminate() ) + { + const auto pos = ring->positionHint( QskProgressIndicator::Fill ); + + pos1 = pos2 = pos; + } + else + { + pos1 = ring->valueAsRatio( ring->origin() ); + pos2 = ring->valueAsRatio( ring->value() ); + } + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + return QskIntervalF( pos1, pos2 ); + } +} + +QskProgressRingSkinlet::QskProgressRingSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ +} + +QskProgressRingSkinlet::~QskProgressRingSkinlet() +{ +} + +QRectF QskProgressRingSkinlet::subControlRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + const auto ring = static_cast< const Q* >( skinnable ); + + if( subControl == Q::Groove || subControl == Q::Fill ) + { + auto rect = contentsRect; + const auto size = ring->strutSizeHint( Q::Fill ); + + if( ring->layoutMirroring() ) + { + rect.setLeft( rect.right() - size.width() ); + } + else + { + rect.setWidth( size.width() ); + } + + rect.setTop( rect.top() + 0.5 * ( rect.height() - size.height() ) ); + rect.setHeight( size.height() ); + + return rect; + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSGNode* QskProgressRingSkinlet::updateGrooveNode( + const QskProgressIndicator* indicator, QSGNode* node ) const +{ + return updateArcNode( indicator, node, Q::Groove ); +} + +QSGNode* QskProgressRingSkinlet::updateFillNode( + const QskProgressIndicator* indicator, QSGNode* node ) const +{ + const auto ring = static_cast< const Q* >( indicator ); + + const auto subControl = Q::Fill; + + const auto rect = ring->subControlRect( subControl ); + if ( rect.isEmpty() ) + return nullptr; + + auto gradient = ring->gradientHint( subControl ); + if ( !gradient.isVisible() ) + return nullptr; + + if ( ( gradient.type() == QskGradient::Stops ) && !gradient.isMonochrome() ) + { + const auto center = rect.center(); + const auto arcMetrics = ring->arcMetricsHint( Q::Fill ); + gradient.setConicDirection( center.x(), center.y(), arcMetrics.startAngle(), arcMetrics.spanAngle() ); + } + + const auto interval = qskFillInterval( ring ); + const auto arcMetrics = ring->arcMetricsHint( subControl ); + + const auto startAngle = arcMetrics.startAngle() + interval.lowerBound() * arcMetrics.spanAngle(); + const auto spanAngle = interval.upperBound() * arcMetrics.spanAngle(); + + return updateArcNode( ring, node, rect, gradient, startAngle, spanAngle, subControl ); +} + +QSizeF QskProgressRingSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto ring = static_cast< const Q* >( skinnable ); + + const auto r = ring->strutSizeHint( Q::Fill ); + return r; +} + +#include "moc_QskProgressRingSkinlet.cpp" diff --git a/src/controls/QskProgressRingSkinlet.h b/src/controls/QskProgressRingSkinlet.h new file mode 100644 index 00000000..009640ee --- /dev/null +++ b/src/controls/QskProgressRingSkinlet.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_RING_SKINLET_H +#define QSK_PROGRESS_RING_SKINLET_H + +#include "QskProgressIndicatorSkinlet.h" + +class QskProgressRing; + +class QSK_EXPORT QskProgressRingSkinlet : public QskProgressIndicatorSkinlet +{ + Q_GADGET + + using Inherited = QskProgressIndicatorSkinlet; + + public: + Q_INVOKABLE QskProgressRingSkinlet( QskSkin* = nullptr ); + ~QskProgressRingSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + protected: + QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const override; + QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; +}; + +#endif diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index c3f66a71..a24a212f 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -53,6 +53,9 @@ #include "QskProgressBar.h" #include "QskProgressBarSkinlet.h" +#include "QskProgressRing.h" +#include "QskProgressRingSkinlet.h" + #include "QskRadioBox.h" #include "QskRadioBoxSkinlet.h" @@ -178,6 +181,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskTextLabel, QskTextLabelSkinlet >(); declareSkinlet< QskTextInput, QskTextInputSkinlet >(); declareSkinlet< QskProgressBar, QskProgressBarSkinlet >(); + declareSkinlet< QskProgressRing, QskProgressRingSkinlet >(); declareSkinlet< QskRadioBox, QskRadioBoxSkinlet >(); const QFont font = QGuiApplication::font(); From 85642665d44e39a27b54584fa6147eae274ef3de Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 9 Aug 2023 10:37:20 +0200 Subject: [PATCH 11/21] being more const aware --- examples/iotdashboard/MainItem.cpp | 4 ++-- examples/iotdashboard/MainItem.h | 2 +- src/controls/QskControl.cpp | 2 +- src/controls/QskControl.h | 2 +- src/controls/QskControlPrivate.cpp | 2 +- src/controls/QskControlPrivate.h | 2 +- src/controls/QskGestureRecognizer.cpp | 10 +++++----- src/controls/QskGestureRecognizer.h | 2 +- src/controls/QskScrollBox.cpp | 4 ++-- src/controls/QskScrollBox.h | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/iotdashboard/MainItem.cpp b/examples/iotdashboard/MainItem.cpp index 36a17243..cf84cd67 100644 --- a/examples/iotdashboard/MainItem.cpp +++ b/examples/iotdashboard/MainItem.cpp @@ -345,13 +345,13 @@ bool MainItem::eventFilter( QObject* object, QEvent* event ) } } -bool MainItem::gestureFilter( QQuickItem* item, QEvent* event ) +bool MainItem::gestureFilter( const QQuickItem* item, const QEvent* event ) { auto& recognizer = m_panRecognizer; if( event->type() == QEvent::MouseButtonPress ) { - const auto mouseEvent = static_cast< QMouseEvent* >( event ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); if( ( item != this ) || ( recognizer.timeout() < 0 ) ) { diff --git a/examples/iotdashboard/MainItem.h b/examples/iotdashboard/MainItem.h index 9d7ef47d..44e7e478 100644 --- a/examples/iotdashboard/MainItem.h +++ b/examples/iotdashboard/MainItem.h @@ -74,7 +74,7 @@ class MainItem : public QskControl protected: bool eventFilter(QObject* obj, QEvent* event) override final; - bool gestureFilter( QQuickItem*, QEvent* ) override final; + bool gestureFilter( const QQuickItem*, const QEvent* ) override final; void gestureEvent( QskGestureEvent* ) override final; private: diff --git a/src/controls/QskControl.cpp b/src/controls/QskControl.cpp index 86c863e3..0ea473d0 100644 --- a/src/controls/QskControl.cpp +++ b/src/controls/QskControl.cpp @@ -809,7 +809,7 @@ bool QskControl::childMouseEventFilter( QQuickItem* item, QEvent* event ) return d_func()->maybeGesture( item, event ); } -bool QskControl::gestureFilter( QQuickItem*, QEvent* ) +bool QskControl::gestureFilter( const QQuickItem*, const QEvent* ) { return false; } diff --git a/src/controls/QskControl.h b/src/controls/QskControl.h index e9a4bb7a..dd8ff9ed 100644 --- a/src/controls/QskControl.h +++ b/src/controls/QskControl.h @@ -196,7 +196,7 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable void hoverLeaveEvent( QHoverEvent* ) override; bool childMouseEventFilter( QQuickItem*, QEvent* ) override; - virtual bool gestureFilter( QQuickItem*, QEvent* ); + virtual bool gestureFilter( const QQuickItem*, const QEvent* ); void itemChange( ItemChange, const ItemChangeData& ) override; void geometryChange( const QRectF&, const QRectF& ) override; diff --git a/src/controls/QskControlPrivate.cpp b/src/controls/QskControlPrivate.cpp index 8a24102e..88e4747d 100644 --- a/src/controls/QskControlPrivate.cpp +++ b/src/controls/QskControlPrivate.cpp @@ -309,7 +309,7 @@ QSizeF QskControlPrivate::explicitSizeHint( Qt::SizeHint whichHint ) const return QSizeF(); } -bool QskControlPrivate::maybeGesture( QQuickItem* child, QEvent* event ) +bool QskControlPrivate::maybeGesture( const QQuickItem* child, const QEvent* event ) { Q_Q( QskControl ); diff --git a/src/controls/QskControlPrivate.h b/src/controls/QskControlPrivate.h index dadcc37d..06815304 100644 --- a/src/controls/QskControlPrivate.h +++ b/src/controls/QskControlPrivate.h @@ -36,7 +36,7 @@ class QskControlPrivate : public QskQuickItemPrivate void implicitSizeChanged() override final; void layoutConstraintChanged() override final; - bool maybeGesture( QQuickItem*, QEvent* ); + bool maybeGesture( const QQuickItem*, const QEvent* ); QskPlacementPolicy::Policy placementPolicy( bool visible ) const noexcept; void setPlacementPolicy( bool visible, QskPlacementPolicy::Policy ); diff --git a/src/controls/QskGestureRecognizer.cpp b/src/controls/QskGestureRecognizer.cpp index fea738a1..d5262fa7 100644 --- a/src/controls/QskGestureRecognizer.cpp +++ b/src/controls/QskGestureRecognizer.cpp @@ -295,7 +295,7 @@ QskGestureRecognizer::State QskGestureRecognizer::state() const } bool QskGestureRecognizer::processEvent( - QQuickItem* item, QEvent* event, bool blockReplayedEvents ) + const QQuickItem* item, const QEvent* event, bool blockReplayedEvents ) { if ( m_data->isReplayingEvents && blockReplayedEvents ) { @@ -331,7 +331,7 @@ bool QskGestureRecognizer::processEvent( if ( buttons == Qt::NoButton ) buttons = watchedItem->acceptedMouseButtons(); - auto mouseEvent = static_cast< QMouseEvent* >( event ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); if ( !( buttons & mouseEvent->button() ) ) return false; @@ -383,7 +383,7 @@ bool QskGestureRecognizer::processEvent( { case QEvent::MouseButtonPress: { - auto mouseEvent = static_cast< QMouseEvent* >( event ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); m_data->pendingEvents += qskClonedMouseEvent( mouseEvent ); pressEvent( mouseEvent ); @@ -392,7 +392,7 @@ bool QskGestureRecognizer::processEvent( case QEvent::MouseMove: { - auto mouseEvent = static_cast< QMouseEvent* >( event ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); m_data->pendingEvents += qskClonedMouseEvent( mouseEvent ); moveEvent( mouseEvent ); @@ -401,7 +401,7 @@ bool QskGestureRecognizer::processEvent( case QEvent::MouseButtonRelease: { - auto mouseEvent = static_cast< QMouseEvent* >( event ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); m_data->pendingEvents += qskClonedMouseEvent( mouseEvent ); if ( m_data->state == Pending ) diff --git a/src/controls/QskGestureRecognizer.h b/src/controls/QskGestureRecognizer.h index 41ad6509..7ed710be 100644 --- a/src/controls/QskGestureRecognizer.h +++ b/src/controls/QskGestureRecognizer.h @@ -41,7 +41,7 @@ class QSK_EXPORT QskGestureRecognizer // timestamp, when the Idle state had been left ulong timestamp() const; - bool processEvent( QQuickItem*, QEvent*, bool blockReplayedEvents = true ); + bool processEvent( const QQuickItem*, const QEvent*, bool blockReplayedEvents = true ); void reject(); void accept(); diff --git a/src/controls/QskScrollBox.cpp b/src/controls/QskScrollBox.cpp index 8bd84942..842a2dd5 100644 --- a/src/controls/QskScrollBox.cpp +++ b/src/controls/QskScrollBox.cpp @@ -446,7 +446,7 @@ void QskScrollBox::wheelEvent( QWheelEvent* event ) #endif -bool QskScrollBox::gestureFilter( QQuickItem* item, QEvent* event ) +bool QskScrollBox::gestureFilter( const QQuickItem* item, const QEvent* event ) { if ( event->type() == QEvent::MouseButtonPress ) { @@ -501,7 +501,7 @@ bool QskScrollBox::gestureFilter( QQuickItem* item, QEvent* event ) its parent, finally ending up here for a second time. */ - const auto mouseEvent = static_cast< QMouseEvent* >( event ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); if ( recognizer.hasProcessedBefore( mouseEvent ) ) { /* diff --git a/src/controls/QskScrollBox.h b/src/controls/QskScrollBox.h index 46bc9604..889a1809 100644 --- a/src/controls/QskScrollBox.h +++ b/src/controls/QskScrollBox.h @@ -73,7 +73,7 @@ class QSK_EXPORT QskScrollBox : public QskControl virtual QPointF scrollOffset( const QWheelEvent* ) const; #endif - bool gestureFilter( QQuickItem*, QEvent* ) override; + bool gestureFilter( const QQuickItem*, const QEvent* ) override; void setScrollableSize( const QSizeF& ); private: From 3912fc3a9127ba4a43b4ef49b69031cadbb6739c Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 9 Aug 2023 10:40:01 +0200 Subject: [PATCH 12/21] build warnings fixed --- src/controls/QskListView.cpp | 1 + src/controls/QskProgressIndicatorSkinlet.cpp | 2 -- src/controls/QskProgressIndicatorSkinlet.h | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controls/QskListView.cpp b/src/controls/QskListView.cpp index 0218cccb..e1bae2f7 100644 --- a/src/controls/QskListView.cpp +++ b/src/controls/QskListView.cpp @@ -81,6 +81,7 @@ class QskListView::PrivateData https://github.com/uwerat/qskinny/tree/features/listview */ + Q_UNUSED( listView ); Q_UNUSED( row ); Q_UNUSED( oldStates ); Q_UNUSED( newStates ); diff --git a/src/controls/QskProgressIndicatorSkinlet.cpp b/src/controls/QskProgressIndicatorSkinlet.cpp index f107409c..04d98efb 100644 --- a/src/controls/QskProgressIndicatorSkinlet.cpp +++ b/src/controls/QskProgressIndicatorSkinlet.cpp @@ -36,5 +36,3 @@ QSGNode* QskProgressIndicatorSkinlet::updateSubNode( return Inherited::updateSubNode( skinnable, nodeRole, node ); } - -#include "moc_QskProgressIndicatorSkinlet.cpp" diff --git a/src/controls/QskProgressIndicatorSkinlet.h b/src/controls/QskProgressIndicatorSkinlet.h index effc2b13..b99a53da 100644 --- a/src/controls/QskProgressIndicatorSkinlet.h +++ b/src/controls/QskProgressIndicatorSkinlet.h @@ -23,10 +23,11 @@ class QSK_EXPORT QskProgressIndicatorSkinlet : public QskSkinlet RoleCount }; - QskProgressIndicatorSkinlet( QskSkin* = nullptr ); ~QskProgressIndicatorSkinlet() override; protected: + QskProgressIndicatorSkinlet( QskSkin* = nullptr ); + QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; From c8316906d2ac67c333d76d56302ec052f0d6ab66 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 10 Aug 2023 13:36:01 +0200 Subject: [PATCH 13/21] QskDrawer setting for Fluent2 --- skins/fluent2/QskFluent2Skin.cpp | 26 ++++++++++++++++++++++++++ skins/material3/QskMaterial3Skin.cpp | 3 ++- src/controls/QskDrawer.h | 6 +++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 7261baaa..1b90f732 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,9 @@ namespace void setupDialogButtonBoxMetrics(); void setupDialogButtonBoxColors( QskAspect::Section, const QskFluent2Theme& ); + void setupDrawerMetrics(); + void setupDrawerColors( QskAspect::Section, const QskFluent2Theme& ); + void setupFocusIndicatorMetrics(); void setupFocusIndicatorColors( QskAspect::Section, const QskFluent2Theme& ); @@ -289,6 +293,7 @@ void Editor::setupMetrics() setupCheckBoxMetrics(); setupComboBoxMetrics(); setupDialogButtonBoxMetrics(); + setupDrawerMetrics(); setupFocusIndicatorMetrics(); setupGraphicLabelMetrics(); setupListViewMetrics(); @@ -325,6 +330,7 @@ void Editor::setupColors( QskAspect::Section section, const QskFluent2Theme& the setupCheckBoxColors( section, theme ); setupComboBoxColors( section, theme ); setupDialogButtonBoxColors( section, theme ); + setupDrawerColors( section, theme ); setupFocusIndicatorColors( section, theme ); setupGraphicLabelColors( section, theme ); setupGraphicLabelMetrics(); @@ -597,6 +603,26 @@ void Editor::setupDialogButtonBoxColors( theme.palette.background.solid.base ); } +void Editor::setupDrawerMetrics() +{ + using Q = QskDrawer; + + setPadding( Q::Panel, 5 ); + setHint( Q::Overlay | QskAspect::Style, false ); + +#if 1 + setAnimation( Q::Panel | QskAspect::Position, 200 ); +#endif +} + +void Editor::setupDrawerColors( + QskAspect::Section section, const QskFluent2Theme& theme ) +{ + using Q = QskDrawer; + + setGradient( Q::Panel | section, theme.palette.background.solid.base ); +} + void Editor::setupFocusIndicatorMetrics() { using Q = QskFocusIndicator; diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index f9537a55..957c1ba2 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -812,8 +812,9 @@ void Editor::setupDrawer() setPadding( Q::Panel, 5_dp ); setGradient( Q::Panel, m_pal.background ); - setAnimation( Q::Panel | QskAspect::Position, qskDuration ); setHint( Q::Overlay | QskAspect::Style, false ); + + setAnimation( Q::Panel | QskAspect::Position, qskDuration ); } void Editor::setupSlider() diff --git a/src/controls/QskDrawer.h b/src/controls/QskDrawer.h index 240f3f5d..442f95e7 100644 --- a/src/controls/QskDrawer.h +++ b/src/controls/QskDrawer.h @@ -15,15 +15,15 @@ class QSK_EXPORT QskDrawer : public QskPopup public: QSK_SUBCONTROLS( Panel, Overlay ) - QskDrawer( QQuickItem* parentItem = nullptr ); + QskDrawer( QQuickItem* = nullptr ); ~QskDrawer() override; - void setEdge( Qt::Edge edge ); + void setEdge( Qt::Edge ); Qt::Edge edge() const; void updateLayout() override; - void setContent( QskControl* t ); + void setContent( QskControl* ); Q_SIGNALS: void edgeChanged( Qt::Edge ); From ac4f1907333c908a861b59b446ab107e78e58f90 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 10 Aug 2023 13:38:21 +0200 Subject: [PATCH 14/21] QskGestureFilterEvent introduced to allow adding gestures to foreign controls using event filtering ( f.e QskDrawer needs this ) --- src/controls/QskControl.cpp | 47 ++++++++++++++++++++++++++++-- src/controls/QskControlPrivate.cpp | 45 +--------------------------- src/controls/QskControlPrivate.h | 2 -- src/controls/QskEvent.cpp | 38 ++++++++++++++++++++++++ src/controls/QskEvent.h | 26 +++++++++++++++++ 5 files changed, 109 insertions(+), 49 deletions(-) diff --git a/src/controls/QskControl.cpp b/src/controls/QskControl.cpp index 0ea473d0..7aa7b64c 100644 --- a/src/controls/QskControl.cpp +++ b/src/controls/QskControl.cpp @@ -31,6 +31,20 @@ static inline void qskSendEventTo( QObject* object, QEvent::Type type ) QCoreApplication::sendEvent( object, &event ); } +static inline bool qskMaybeGesture( QQuickItem* item, + const QQuickItem* child, const QEvent* event ) +{ + if ( qskIsTouchOrMouseEvent( event->type() ) ) + { + QskGestureFilterEvent ev( child, event ); + QCoreApplication::sendEvent( item, &ev ); + + return ev.maybeGesture(); + } + + return false; +} + QskControl::QskControl( QQuickItem* parent ) : QskQuickItem( *( new QskControlPrivate() ), parent ) { @@ -791,6 +805,33 @@ bool QskControl::event( QEvent* event ) break; } + case QskEvent::GestureFilter: + { + /* + qskMaybeGesture is sending an event, so that it can be manipulated + by event filters. F.e QskDrawer wants to add a gesture to + some other control to initiate its appearance. + */ + + auto ev = static_cast< QskGestureFilterEvent* >( event ); + + if ( ev->event()->type() == QEvent::MouseButtonPress ) + { + auto mouseEvent = static_cast< const QMouseEvent* >( ev->event() ); + const auto pos = + mapFromItem( ev->item(), qskMousePosition( mouseEvent ) ); + + if ( !gestureRect().contains( pos ) ) + { + ev->setMaybeGesture( false ); + break; + } + } + + ev->setMaybeGesture( gestureFilter( ev->item(), ev->event() ) ); + + break; + } case QskEvent::Gesture: { gestureEvent( static_cast< QskGestureEvent* >( event ) ); @@ -798,15 +839,15 @@ bool QskControl::event( QEvent* event ) } } - if ( d_func()->maybeGesture( this, event ) ) + if ( qskMaybeGesture( this, this, event ) ) return true; return Inherited::event( event ); } -bool QskControl::childMouseEventFilter( QQuickItem* item, QEvent* event ) +bool QskControl::childMouseEventFilter( QQuickItem* child, QEvent* event ) { - return d_func()->maybeGesture( item, event ); + return qskMaybeGesture( this, child, event ); } bool QskControl::gestureFilter( const QQuickItem*, const QEvent* ) diff --git a/src/controls/QskControlPrivate.cpp b/src/controls/QskControlPrivate.cpp index 88e4747d..84cc755f 100644 --- a/src/controls/QskControlPrivate.cpp +++ b/src/controls/QskControlPrivate.cpp @@ -8,6 +8,7 @@ #include "QskLayoutMetrics.h" #include "QskObjectTree.h" #include "QskWindow.h" +#include "QskEvent.h" static inline void qskSendEventTo( QObject* object, QEvent::Type type ) { @@ -15,15 +16,6 @@ static inline void qskSendEventTo( QObject* object, QEvent::Type type ) QCoreApplication::sendEvent( object, &event ); } -static inline QPointF qskScenePosition( const QMouseEvent* event ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - return event->scenePosition(); -#else - return event->windowPos(); -#endif -} - extern bool qskInheritLocale( QskWindow*, const QLocale& ); namespace @@ -309,41 +301,6 @@ QSizeF QskControlPrivate::explicitSizeHint( Qt::SizeHint whichHint ) const return QSizeF(); } -bool QskControlPrivate::maybeGesture( const QQuickItem* child, const QEvent* event ) -{ - Q_Q( QskControl ); - - switch ( event->type() ) - { - case QEvent::MouseButtonPress: - { - const auto mouseEvent = static_cast< const QMouseEvent* >( event ); - - auto pos = qskScenePosition( mouseEvent ); - pos = q->mapFromScene( pos ); - - if ( !q->gestureRect().contains( pos ) ) - return false; - - break; - } - - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::UngrabMouse: - case QEvent::TouchBegin: - case QEvent::TouchCancel: - case QEvent::TouchUpdate: - break; - - default: - return false; - } - - return q->gestureFilter( child, event ); -} - bool QskControlPrivate::inheritLocale( QskControl* control, const QLocale& locale ) { auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) ); diff --git a/src/controls/QskControlPrivate.h b/src/controls/QskControlPrivate.h index 06815304..9a9dd09a 100644 --- a/src/controls/QskControlPrivate.h +++ b/src/controls/QskControlPrivate.h @@ -36,8 +36,6 @@ class QskControlPrivate : public QskQuickItemPrivate void implicitSizeChanged() override final; void layoutConstraintChanged() override final; - bool maybeGesture( const QQuickItem*, const QEvent* ); - QskPlacementPolicy::Policy placementPolicy( bool visible ) const noexcept; void setPlacementPolicy( bool visible, QskPlacementPolicy::Policy ); diff --git a/src/controls/QskEvent.cpp b/src/controls/QskEvent.cpp index e430e8b1..b3aeae87 100644 --- a/src/controls/QskEvent.cpp +++ b/src/controls/QskEvent.cpp @@ -154,6 +154,28 @@ bool qskIsButtonPressKey( const QKeyEvent* event ) #endif } +bool qskIsTouchOrMouseEvent( QEvent::Type type ) +{ + switch ( type ) + { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::UngrabMouse: + + case QEvent::TouchBegin: + case QEvent::TouchCancel: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + + return true; + + default: + return false; + } +} + QskEvent::QskEvent( QskEvent::Type type ) : QEvent( static_cast< QEvent::Type >( type ) ) { @@ -249,3 +271,19 @@ QskAnimatorEvent* QskAnimatorEvent::clone() const { return new QskAnimatorEvent( *this ); } + +// -- QskGestureFilterEvent + +QskGestureFilterEvent::QskGestureFilterEvent( + const QQuickItem* item, const QEvent* event ) + : QskEvent( QskEvent::GestureFilter ) + , m_item( item ) + , m_event( event ) + , m_maybeGesture( false ) +{ +} + +QskGestureFilterEvent* QskGestureFilterEvent::clone() const +{ + return new QskGestureFilterEvent( *this ); +} diff --git a/src/controls/QskEvent.h b/src/controls/QskEvent.h index 3ba04c09..094090e9 100644 --- a/src/controls/QskEvent.h +++ b/src/controls/QskEvent.h @@ -47,6 +47,7 @@ class QSK_EXPORT QskEvent : public QEvent PopupRemoved, Gesture, + GestureFilter, Animator, @@ -118,6 +119,29 @@ class QSK_EXPORT QskPopupEvent : public QskEvent QskPopup* m_popup; }; +class QSK_EXPORT QskGestureFilterEvent : public QskEvent +{ + public: + QskGestureFilterEvent( const QQuickItem*, const QEvent* ); + + inline const QQuickItem* item() const { return m_item; } + inline const QEvent* event() const { return m_event; } + + inline void setMaybeGesture( bool on ) { m_maybeGesture = on; } + inline bool maybeGesture() const { return m_maybeGesture; } + + QskGestureFilterEvent* clone() const override; + + protected: + QSK_EVENT_DISABLE_COPY( QskGestureFilterEvent ) + + private: + const QQuickItem* m_item; + const QEvent* m_event; + + bool m_maybeGesture; +}; + class QSK_EXPORT QskGestureEvent : public QskEvent { public: @@ -178,4 +202,6 @@ QSK_EXPORT qreal qskWheelIncrement( const QWheelEvent* ); QSK_EXPORT bool qskIsStandardKeyInput( const QKeyEvent*, QKeySequence::StandardKey ); QSK_EXPORT bool qskIsButtonPressKey( const QKeyEvent* ); +QSK_EXPORT bool qskIsTouchOrMouseEvent( QEvent::Type ); + #endif From 3eb62bb692b299bccc611c4f8fe48932de49201d Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 10 Aug 2023 18:43:24 +0200 Subject: [PATCH 15/21] smooth state transitions for listbox row selections --- src/controls/QskListView.cpp | 11 +++------ src/controls/QskListViewSkinlet.cpp | 4 +-- src/controls/QskSkinStateChanger.h | 11 ++++----- src/controls/QskSkinlet.cpp | 38 +---------------------------- src/controls/QskSkinlet.h | 4 --- src/controls/QskSkinnable.cpp | 25 ++++++++++++++----- src/controls/QskSkinnable.h | 6 +++-- 7 files changed, 34 insertions(+), 65 deletions(-) diff --git a/src/controls/QskListView.cpp b/src/controls/QskListView.cpp index e1bae2f7..b74e0c38 100644 --- a/src/controls/QskListView.cpp +++ b/src/controls/QskListView.cpp @@ -76,15 +76,10 @@ class QskListView::PrivateData inline void startTransitions( QskListView* listView, int row, QskAspect::States oldStates, QskAspect::States newStates ) { - /* - working implementation can be found in - https://github.com/uwerat/qskinny/tree/features/listview - */ + using Q = QskListView; - Q_UNUSED( listView ); - Q_UNUSED( row ); - Q_UNUSED( oldStates ); - Q_UNUSED( newStates ); + listView->startHintTransitions( + { Q::Cell, Q::Text }, oldStates, newStates, row ); } public: diff --git a/src/controls/QskListViewSkinlet.cpp b/src/controls/QskListViewSkinlet.cpp index 33de9cba..77907d75 100644 --- a/src/controls/QskListViewSkinlet.cpp +++ b/src/controls/QskListViewSkinlet.cpp @@ -204,7 +204,7 @@ void QskListViewSkinlet::updateBackgroundNodes( for ( int row = listViewNode->rowMin(); row <= listViewNode->rowMax(); row++ ) { QskSkinStateChanger stateChanger( listView ); - stateChanger.setStates( sampleStates( listView, Q::Cell, row ) ); + stateChanger.setStates( sampleStates( listView, Q::Cell, row ), row ); const auto rect = sampleRect( listView, listView->contentsRect(), Q::Cell, row ); @@ -391,7 +391,7 @@ QSGNode* QskListViewSkinlet::updateCellNode( const QskListView* listView, using namespace QskSGNode; QskSkinStateChanger stateChanger( listView ); - stateChanger.setStates( sampleStates( listView, Q::Cell, row ) ); + stateChanger.setStates( sampleStates( listView, Q::Cell, row ), row ); QSGNode* newNode = nullptr; diff --git a/src/controls/QskSkinStateChanger.h b/src/controls/QskSkinStateChanger.h index f13d3145..906fdbae 100644 --- a/src/controls/QskSkinStateChanger.h +++ b/src/controls/QskSkinStateChanger.h @@ -15,7 +15,7 @@ class QskSkinStateChanger QskSkinStateChanger( const QskSkinnable* ); ~QskSkinStateChanger(); - void setStates( QskAspect::States ); + void setStates( QskAspect::States, int sampleIndex = -1 ); void resetStates(); private: @@ -34,16 +34,15 @@ inline QskSkinStateChanger::~QskSkinStateChanger() resetStates(); } -inline void QskSkinStateChanger::setStates( QskAspect::States states ) +inline void QskSkinStateChanger::setStates( + QskAspect::States states, int sampleIndex ) { - if ( states != m_skinnable->skinStates() ) - m_skinnable->replaceSkinStates( states ); + m_skinnable->replaceSkinStates( states, sampleIndex ); } inline void QskSkinStateChanger::resetStates() { - if ( m_oldStates != m_skinnable->skinStates() ) - m_skinnable->replaceSkinStates( m_oldStates ); + m_skinnable->replaceSkinStates( m_oldStates, -1 ); } #endif diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index d19a1e93..e6351519 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -282,8 +282,6 @@ class QskSkinlet::PrivateData QskSkin* skin; QVector< quint8 > nodeRoles; - int animatorIndex = -1; - bool ownedBySkinnable : 1; }; @@ -311,21 +309,6 @@ bool QskSkinlet::isOwnedBySkinnable() const return m_data->ownedBySkinnable; } -void QskSkinlet::setAnimatorIndex( int index ) -{ - m_data->animatorIndex = index; -} - -void QskSkinlet::resetAnimatorIndex() -{ - m_data->animatorIndex = -1; -} - -int QskSkinlet::animatorIndex() const -{ - return m_data->animatorIndex; -} - void QskSkinlet::setNodeRoles( const QVector< quint8 >& nodeRoles ) { m_data->nodeRoles = nodeRoles; @@ -796,26 +779,7 @@ QSGNode* QskSkinlet::updateSeriesNode( const QskSkinnable* skinnable, const auto newStates = sampleStates( skinnable, subControl, i ); QskSkinStateChanger stateChanger( skinnable ); - stateChanger.setStates( newStates ); - - class IndexChanger - { - public: - inline IndexChanger( const QskSkinlet* skinlet, int index ) - : m_skinlet( const_cast< QskSkinlet* >( skinlet ) ) - { - m_skinlet->setAnimatorIndex( index ); - } - - inline ~IndexChanger() - { - m_skinlet->resetAnimatorIndex(); - } - private: - QskSkinlet* m_skinlet; - }; - - IndexChanger indexChanger( this, i ); + stateChanger.setStates( newStates, i ); newNode = updateSampleNode( skinnable, subControl, i, node ); } diff --git a/src/controls/QskSkinlet.h b/src/controls/QskSkinlet.h index 44291712..3cfc7db0 100644 --- a/src/controls/QskSkinlet.h +++ b/src/controls/QskSkinlet.h @@ -71,10 +71,6 @@ class QSK_EXPORT QskSkinlet void setOwnedBySkinnable( bool on ); bool isOwnedBySkinnable() const; - void setAnimatorIndex( int ); - void resetAnimatorIndex(); - int animatorIndex() const; - // Helper functions for creating nodes static QSGNode* updateBoxNode( const QskSkinnable*, QSGNode*, diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 52430f34..fa97a45f 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -248,6 +248,8 @@ class QskSkinnable::PrivateData QskSkinHintTable hintTable; QskHintAnimatorTable animators; + int sampleIndex = -1; // for the ugly QskSkinStateChanger hack + typedef std::map< QskAspect::Subcontrol, QskAspect::Subcontrol > ProxyMap; ProxyMap* subcontrolProxies = nullptr; @@ -993,11 +995,11 @@ QVariant QskSkinnable::animatedHint( if ( !m_data->animators.isEmpty() ) { - const int index = effectiveSkinlet()->animatorIndex(); + const auto a = qskAnimatorAspect( aspect ); - v = m_data->animators.currentValue( aspect, index ); - if ( !v.isValid() && index >= 0 ) - v = m_data->animators.currentValue( aspect, -1 ); + v = m_data->animators.currentValue( a, m_data->sampleIndex ); + if ( !v.isValid() && m_data->sampleIndex >= 0 ) + v = m_data->animators.currentValue( a, -1 ); } if ( status && v.isValid() ) @@ -1309,7 +1311,7 @@ void QskSkinnable::startHintTransition( QskAspect aspect, int index, aspect = qskAnimatorAspect( aspect ); #if DEBUG_ANIMATOR - qDebug() << aspect << animationHint.duration; + qDebug() << aspect << index << animationHint.duration; #endif auto animator = m_data->animators.animator( aspect, index ); @@ -1328,9 +1330,20 @@ void QskSkinnable::setSkinStateFlag( QskAspect::State stateFlag, bool on ) setSkinStates( newState ); } -void QskSkinnable::replaceSkinStates( QskAspect::States newStates ) +void QskSkinnable::replaceSkinStates( + QskAspect::States newStates, int sampleIndex ) { + /* + Hack time: we might need different hints for a specific instance + of a subcontrol ( f.e the selected row in a list box ), what is not + supported by QskAspect. + + As a workaround we use QskSkinStateChanger, that sets/restores this state/index + while retrieving the skin hints. + */ + m_data->skinStates = newStates; + m_data->sampleIndex = sampleIndex; // needed to find specific animators } void QskSkinnable::addSkinStates( QskAspect::States states ) diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index d8e455a2..51d2f1a7 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -39,6 +39,7 @@ class QskGraphic; class QskSkin; class QskSkinlet; class QskSkinHintTable; +class QskSkinStateChanger; class QSK_EXPORT QskSkinHintStatus { @@ -149,8 +150,6 @@ class QSK_EXPORT QskSkinnable void addSkinStates( QskAspect::States ); void clearSkinStates( QskAspect::States ); - void replaceSkinStates( QskAspect::States ); - bool hasSkinState( QskAspect::State ) const; QskAspect::States skinStates() const; @@ -281,6 +280,9 @@ class QSK_EXPORT QskSkinnable QVariant interpolatedHint( QskAspect, QskSkinHintStatus* ) const; const QVariant& storedHint( QskAspect, QskSkinHintStatus* = nullptr ) const; + friend class QskSkinStateChanger; + void replaceSkinStates( QskAspect::States, int sampleIndex = -1 ); + class PrivateData; std::unique_ptr< PrivateData > m_data; }; From b6b9cf0039f8573235d41caa78f9212e413ee693 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 10 Aug 2023 19:10:31 +0200 Subject: [PATCH 16/21] basic adjustments --- src/controls/QskSwipeView.cpp | 62 ++++++++++++++++++++--------------- src/controls/QskSwipeView.h | 16 +++++---- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/controls/QskSwipeView.cpp b/src/controls/QskSwipeView.cpp index 97cb574e..3994c6c7 100644 --- a/src/controls/QskSwipeView.cpp +++ b/src/controls/QskSwipeView.cpp @@ -14,8 +14,7 @@ class QskSwipeView::PrivateData { public: QskPanGestureRecognizer panRecognizer; - int panRecognizerTimeout = 100; - int duration = 500; + int duration = -1; // should be a skinHint }; QSK_SUBCONTROL( QskSwipeView, Panel ) @@ -24,21 +23,33 @@ QskSwipeView::QskSwipeView( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - setSubcontrolProxy( QskBox::Panel, Panel ); - setFiltersChildMouseEvents( true ); - setAcceptedMouseButtons( Qt::LeftButton ); - m_data->panRecognizer.setWatchedItem( this ); - m_data->panRecognizer.setOrientations( Qt::Horizontal ); - m_data->panRecognizer.setMinDistance( 50 ); + auto& recognizer = m_data->panRecognizer; + + recognizer.setWatchedItem( this ); + + // should be skin hints + recognizer.setOrientations( Qt::Horizontal ); + recognizer.setMinDistance( 50 ); + recognizer.setTimeout( 100 ); + + resetDuration(); } QskSwipeView::~QskSwipeView() { } +QskAspect::Subcontrol QskSwipeView::effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const +{ + if ( subControl == QskBox::Panel ) + return QskSwipeView::Panel; + + return Inherited::effectiveSubcontrol( subControl ); +} int QskSwipeView::duration() const { @@ -50,8 +61,12 @@ void QskSwipeView::setDuration( int duration ) m_data->duration = duration; } +void QskSwipeView::resetDuration() +{ + m_data->duration = 500; +} -bool QskSwipeView::gestureFilter( QQuickItem* item, QEvent* event ) +bool QskSwipeView::gestureFilter( const QQuickItem* item, const QEvent* event ) { // see QskScrollBox.cpp @@ -59,26 +74,21 @@ bool QskSwipeView::gestureFilter( QQuickItem* item, QEvent* event ) if ( event->type() == QEvent::MouseButtonPress ) { - if ( ( item != this ) && ( recognizer.timeout() < 0 ) ) - { - const auto mouseEvent = static_cast< QMouseEvent* >( event ); - - if ( recognizer.hasProcessedBefore( mouseEvent ) ) - return false; - } - - recognizer.setTimeout( ( item == this ) ? -1 : m_data->panRecognizerTimeout ); + auto mouseEvent = static_cast< const QMouseEvent* >( event ); + if ( recognizer.hasProcessedBefore( mouseEvent ) ) + return false; } - return m_data->panRecognizer.processEvent( item, event ); + return recognizer.processEvent( item, event ); + } void QskSwipeView::gestureEvent( QskGestureEvent* event ) { - if( event->gesture()->type() == QskGesture::Pan && event->gesture()->state() == QskGesture::Started ) - { - const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() ); + const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() ); + if( gesture->type() == QskGesture::Pan && gesture->state() == QskGesture::Started ) + { auto animator = dynamic_cast< QskStackBoxAnimator1* >( this->animator() ); if ( animator == nullptr ) @@ -88,7 +98,7 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) } animator->setDuration( m_data->duration ); - setAnimator( animator ); + QskStackBox::setAnimator( animator ); const auto direction = ( ( gesture->angle() < 90.0 ) || ( gesture->angle() > 270.0 ) ) ? Qsk::LeftToRight : Qsk::RightToLeft; @@ -101,12 +111,10 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) newIndex %= itemCount(); setCurrentIndex( newIndex ); - } - else - { - Inherited::gestureEvent( event ); + return; } + Inherited::gestureEvent( event ); } #include "moc_QskSwipeView.cpp" diff --git a/src/controls/QskSwipeView.h b/src/controls/QskSwipeView.h index cbcc9c0e..bebb976d 100644 --- a/src/controls/QskSwipeView.h +++ b/src/controls/QskSwipeView.h @@ -8,14 +8,13 @@ #include "QskStackBox.h" -class QskTabBar; -class QskTabButton; - class QSK_EXPORT QskSwipeView : public QskStackBox { Q_OBJECT - typedef QskStackBox Inherited; + Q_PROPERTY( int duration READ duration WRITE setDuration RESET resetDuration ) + + using Inherited = QskStackBox; public: QSK_SUBCONTROLS( Panel ) @@ -25,12 +24,17 @@ class QSK_EXPORT QskSwipeView : public QskStackBox int duration() const; void setDuration( int ); + void resetDuration(); + + QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol ) const; protected: - bool gestureFilter( QQuickItem*, QEvent* ) override final; - void gestureEvent( QskGestureEvent* ) override final; + bool gestureFilter( const QQuickItem*, const QEvent* ) override; + void gestureEvent( QskGestureEvent* ) override; private: + void setAnimator( QskStackBoxAnimator* ) = delete; + class PrivateData; std::unique_ptr< PrivateData > m_data; }; From bf2c2b981e3a6ea1187826645da4fab75723222c Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 10 Aug 2023 19:54:06 +0200 Subject: [PATCH 17/21] QskSwipeView improvements --- examples/layouts/SwipeViewPage.cpp | 1 + src/controls/QskSwipeView.cpp | 70 ++++++++++++++++++++++++------ src/controls/QskSwipeView.h | 30 ++++++++++++- 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/examples/layouts/SwipeViewPage.cpp b/examples/layouts/SwipeViewPage.cpp index 012e1951..9ce05c65 100644 --- a/examples/layouts/SwipeViewPage.cpp +++ b/examples/layouts/SwipeViewPage.cpp @@ -23,6 +23,7 @@ class SwipeView : public QskSwipeView setBackgroundColor( Qt::white ); setDefaultAlignment( Qt::AlignCenter ); + setOrientation( Qt::Horizontal ); addRectangle( "Gold" ); addRectangle( "SeaGreen" ); diff --git a/src/controls/QskSwipeView.cpp b/src/controls/QskSwipeView.cpp index 3994c6c7..8e712a16 100644 --- a/src/controls/QskSwipeView.cpp +++ b/src/controls/QskSwipeView.cpp @@ -9,6 +9,7 @@ #include "QskGesture.h" #include "QskPanGestureRecognizer.h" #include "QskStackBoxAnimator.h" +#include "QskPlatform.h" class QskSwipeView::PrivateData { @@ -30,11 +31,11 @@ QskSwipeView::QskSwipeView( QQuickItem* parent ) recognizer.setWatchedItem( this ); - // should be skin hints + // should be skin hints, TODO recognizer.setOrientations( Qt::Horizontal ); - recognizer.setMinDistance( 50 ); recognizer.setTimeout( 100 ); + resetSwipeDistance(); resetDuration(); } @@ -42,13 +43,38 @@ QskSwipeView::~QskSwipeView() { } -QskAspect::Subcontrol QskSwipeView::effectiveSubcontrol( - QskAspect::Subcontrol subControl ) const +void QskSwipeView::setOrientation( Qt::Orientation orientation ) { - if ( subControl == QskBox::Panel ) - return QskSwipeView::Panel; + if ( orientation != this->orientation() ) + { + m_data->panRecognizer.setOrientations( orientation ); + Q_EMIT orientationChanged( orientation ); + } +} - return Inherited::effectiveSubcontrol( subControl ); +Qt::Orientation QskSwipeView::orientation() const +{ + return ( m_data->panRecognizer.orientations() == Qt::Vertical ) + ? Qt::Vertical : Qt::Horizontal; +} + +int QskSwipeView::swipeDistance() const +{ + return m_data->panRecognizer.minDistance(); +} + +void QskSwipeView::setSwipeDistance( int distance ) +{ + const auto oldDistance = m_data->panRecognizer.minDistance(); + m_data->panRecognizer.setMinDistance( distance ); + + if ( oldDistance != m_data->panRecognizer.minDistance() ) + Q_EMIT swipeDistanceChanged( m_data->panRecognizer.minDistance() ); +} + +void QskSwipeView::resetSwipeDistance() +{ + setSwipeDistance( qRound( qskDpToPixels( 40 ) ) ); } int QskSwipeView::duration() const @@ -58,12 +84,16 @@ int QskSwipeView::duration() const void QskSwipeView::setDuration( int duration ) { - m_data->duration = duration; + if ( duration != m_data->duration ) + { + m_data->duration = duration; + Q_EMIT durationChanged( duration ); + } } void QskSwipeView::resetDuration() { - m_data->duration = 500; + setDuration( 500 ); } bool QskSwipeView::gestureFilter( const QQuickItem* item, const QEvent* event ) @@ -94,16 +124,20 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) if ( animator == nullptr ) { animator = new QskStackBoxAnimator1( this ); - animator->setOrientation( Qt::Horizontal ); + animator->setOrientation( orientation() ); } animator->setDuration( m_data->duration ); QskStackBox::setAnimator( animator ); - const auto direction = ( ( gesture->angle() < 90.0 ) || ( gesture->angle() > 270.0 ) ) - ? Qsk::LeftToRight : Qsk::RightToLeft; + bool forwards; - auto newIndex = ( direction == Qsk::LeftToRight ) ? currentIndex() - 1 : currentIndex() + 1; + if ( orientation() == Qt::Horizontal ) + forwards = gesture->angle() >= 90.0 && gesture->angle() <= 270.0; + else + forwards = gesture->angle() >= 180.0; + + auto newIndex = forwards ? currentIndex() + 1 : currentIndex() - 1; if( newIndex < 0 ) newIndex += itemCount(); @@ -117,4 +151,14 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) Inherited::gestureEvent( event ); } +QskAspect::Subcontrol QskSwipeView::effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const +{ + if ( subControl == QskBox::Panel ) + return QskSwipeView::Panel; + + return Inherited::effectiveSubcontrol( subControl ); +} + + #include "moc_QskSwipeView.cpp" diff --git a/src/controls/QskSwipeView.h b/src/controls/QskSwipeView.h index bebb976d..868a6d4b 100644 --- a/src/controls/QskSwipeView.h +++ b/src/controls/QskSwipeView.h @@ -12,7 +12,14 @@ class QSK_EXPORT QskSwipeView : public QskStackBox { Q_OBJECT - Q_PROPERTY( int duration READ duration WRITE setDuration RESET resetDuration ) + Q_PROPERTY( int duration READ duration + WRITE setDuration RESET resetDuration NOTIFY durationChanged ) + + Q_PROPERTY( int swipeDistance READ swipeDistance + WRITE setSwipeDistance RESET resetSwipeDistance NOTIFY swipeDistanceChanged ) + + Q_PROPERTY( Qt::Orientation orientation READ orientation + WRITE setOrientation NOTIFY orientationChanged ) using Inherited = QskStackBox; @@ -22,12 +29,33 @@ class QSK_EXPORT QskSwipeView : public QskStackBox QskSwipeView( QQuickItem* parent = nullptr ); ~QskSwipeView() override; + void setOrientation( Qt::Orientation ); + Qt::Orientation orientation() const; + + // Duration is the time ( in ms ) used for changing between pages + int duration() const; void setDuration( int ); void resetDuration(); + /* + Even if called "swipe view" we use a pan - no swipe - gesture. + ( = pages are moved before the gesture has been confirmed ) + + The swipe distance is the minimum distance in pixels of the pan gesture + */ + + int swipeDistance() const; + void setSwipeDistance( int ); + void resetSwipeDistance(); + QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol ) const; + Q_SIGNALS: + void orientationChanged( Qt::Orientation ); + void durationChanged( int ); + void swipeDistanceChanged( int ); + protected: bool gestureFilter( const QQuickItem*, const QEvent* ) override; void gestureEvent( QskGestureEvent* ) override; From 65e3290fc49acf26ab5750b3f2a698c3515aa3fa Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 13 Sep 2023 18:01:46 +0200 Subject: [PATCH 18/21] confusing event filtering and forwarding simplified --- examples/iotdashboard/MainItem.cpp | 72 ++++++++++++------------------ examples/iotdashboard/MainItem.h | 16 +++---- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/examples/iotdashboard/MainItem.cpp b/examples/iotdashboard/MainItem.cpp index cf84cd67..d0c5ed29 100644 --- a/examples/iotdashboard/MainItem.cpp +++ b/examples/iotdashboard/MainItem.cpp @@ -193,35 +193,6 @@ void Cube::switchToPosition( const Position position ) doSwitch( direction, nextPosition ); } -void Cube::keyPressEvent( QKeyEvent* event ) -{ - Qsk::Direction direction; - - switch( event->key() ) - { - case Qt::Key_Up: - direction = Qsk::TopToBottom; - break; - - case Qt::Key_Down: - direction = Qsk::BottomToTop; - break; - - case Qt::Key_Left: - direction = Qsk::LeftToRight; - break; - - case Qt::Key_Right: - direction = Qsk::RightToLeft; - break; - - default: - return; - } - - switchPosition( direction ); -} - Cube::Position Cube::currentPosition() const { return static_cast< Position >( currentIndex() ); @@ -304,8 +275,6 @@ MainItem::MainItem( QQuickItem* parent ) // the current item needs to be the one at the Front: m_cube->setCurrentItem( dashboardPage ); - - installEventFilter( this ); } void MainItem::gestureEvent( QskGestureEvent* event ) @@ -332,19 +301,36 @@ void MainItem::gestureEvent( QskGestureEvent* event ) } } -bool MainItem::eventFilter( QObject* object, QEvent* event ) +void MainItem::keyPressEvent( QKeyEvent* event ) { - if ( event->type() == QEvent::KeyPress ) - { - QCoreApplication::sendEvent( m_cube, event ); - return true; - } - else - { - return QObject::eventFilter( object, event ); - } -} + // maybe using shortcuts ? + Qsk::Direction direction; + + switch( event->key() ) + { + case Qt::Key_Up: + direction = Qsk::TopToBottom; + break; + + case Qt::Key_Down: + direction = Qsk::BottomToTop; + break; + + case Qt::Key_Left: + direction = Qsk::LeftToRight; + break; + + case Qt::Key_Right: + direction = Qsk::RightToLeft; + break; + + default: + return; + } + + m_cube->switchPosition( direction ); +} bool MainItem::gestureFilter( const QQuickItem* item, const QEvent* event ) { auto& recognizer = m_panRecognizer; @@ -356,9 +342,7 @@ bool MainItem::gestureFilter( const QQuickItem* item, const QEvent* event ) if( ( item != this ) || ( recognizer.timeout() < 0 ) ) { if( recognizer.hasProcessedBefore( mouseEvent ) ) - { return false; - } } recognizer.setTimeout( ( item == this ) ? -1 : 100 ); diff --git a/examples/iotdashboard/MainItem.h b/examples/iotdashboard/MainItem.h index 44e7e478..daf2b2e5 100644 --- a/examples/iotdashboard/MainItem.h +++ b/examples/iotdashboard/MainItem.h @@ -40,22 +40,19 @@ class Cube : public QskStackBox explicit Cube( QQuickItem* parent = nullptr ); public Q_SLOTS: - void switchPosition( const Qsk::Direction direction ); - void switchToPosition( const Cube::Position position ); + void switchPosition( const Qsk::Direction ); + void switchToPosition( const Cube::Position ); Q_SIGNALS: // might be different from indexChanged: void cubeIndexChanged( const int index ); - protected: - void keyPressEvent( QKeyEvent* event ) override; - private: Position currentPosition() const; - Position neighbor( const Position position, const Qsk::Direction direction ) const; + Position neighbor( const Position, const Qsk::Direction ) const; Qsk::Direction direction( const Position from, const Position to ) const; - void updateEdge( Qsk::Direction direction, Position position ); - void doSwitch( Qsk::Direction direction, Position position ); + void updateEdge( Qsk::Direction, Position ); + void doSwitch( Qsk::Direction, Position ); Position m_destination; Edge m_currentEdge; @@ -73,7 +70,8 @@ class MainItem : public QskControl MainItem( QQuickItem* parent = nullptr ); protected: - bool eventFilter(QObject* obj, QEvent* event) override final; + void keyPressEvent( QKeyEvent* ) override final; + bool gestureFilter( const QQuickItem*, const QEvent* ) override final; void gestureEvent( QskGestureEvent* ) override final; From 19f7e344ff17a0e4a8a224b544dd852165aeb49b Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 26 Sep 2023 16:42:28 +0200 Subject: [PATCH 19/21] whitespaces removed --- src/controls/QskQuick.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 07d23196..436f6f88 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -540,7 +540,6 @@ void qskUngrabMouse( QQuickItem* item ) if ( qskIsMouseGrabber( item ) ) item->ungrabMouse(); - } } From 27f41dd5f612a2d5f7ae45485017caf5bbaa152f Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 26 Sep 2023 16:42:45 +0200 Subject: [PATCH 20/21] crash for items <= 1 fixed --- src/controls/QskSwipeView.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controls/QskSwipeView.cpp b/src/controls/QskSwipeView.cpp index 8e712a16..c71b0f07 100644 --- a/src/controls/QskSwipeView.cpp +++ b/src/controls/QskSwipeView.cpp @@ -119,6 +119,9 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) if( gesture->type() == QskGesture::Pan && gesture->state() == QskGesture::Started ) { + if ( itemCount() <= 1 ) + return; + auto animator = dynamic_cast< QskStackBoxAnimator1* >( this->animator() ); if ( animator == nullptr ) From 267c559330b20ad910b32c7707a272b5002383de Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 27 Sep 2023 11:07:05 +0200 Subject: [PATCH 21/21] QskStackBoxAnimator1::setOrientation replaced by QskStackBoxAnimator1::setDirection. Autodetection the direction from the indexes did not work when having only 2 items. --- examples/layouts/StackLayoutPage.cpp | 8 +++- src/controls/QskSwipeView.cpp | 25 ++++++----- src/layouts/QskStackBoxAnimator.cpp | 66 ++++------------------------ src/layouts/QskStackBoxAnimator.h | 7 ++- 4 files changed, 31 insertions(+), 75 deletions(-) diff --git a/examples/layouts/StackLayoutPage.cpp b/examples/layouts/StackLayoutPage.cpp index 550359e6..39241fb4 100644 --- a/examples/layouts/StackLayoutPage.cpp +++ b/examples/layouts/StackLayoutPage.cpp @@ -101,7 +101,7 @@ namespace void incrementScrolling( Qt::Orientation orientation, int offset ) { - auto animator = dynamic_cast< QskStackBoxAnimator1* >( this->animator() ); + auto animator = qobject_cast< QskStackBoxAnimator1* >( this->animator() ); if ( animator == nullptr ) { @@ -109,7 +109,11 @@ namespace animator->setDuration( 1000 ); } - animator->setOrientation( orientation ); + if ( orientation == Qt::Horizontal ) + animator->setDirection( offset > 0 ? Qsk::LeftToRight : Qsk::RightToLeft ); + else + animator->setDirection( offset > 0 ? Qsk::TopToBottom : Qsk::BottomToTop ); + setAnimator( animator ); setCurrentIndex( incrementedIndex( offset ) ); diff --git a/src/controls/QskSwipeView.cpp b/src/controls/QskSwipeView.cpp index c71b0f07..ebe702e7 100644 --- a/src/controls/QskSwipeView.cpp +++ b/src/controls/QskSwipeView.cpp @@ -122,17 +122,6 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) if ( itemCount() <= 1 ) return; - auto animator = dynamic_cast< QskStackBoxAnimator1* >( this->animator() ); - - if ( animator == nullptr ) - { - animator = new QskStackBoxAnimator1( this ); - animator->setOrientation( orientation() ); - } - - animator->setDuration( m_data->duration ); - QskStackBox::setAnimator( animator ); - bool forwards; if ( orientation() == Qt::Horizontal ) @@ -140,8 +129,20 @@ void QskSwipeView::gestureEvent( QskGestureEvent* event ) else forwards = gesture->angle() >= 180.0; - auto newIndex = forwards ? currentIndex() + 1 : currentIndex() - 1; + auto animator = qobject_cast< QskStackBoxAnimator1* >( this->animator() ); + if ( animator == nullptr ) + animator = new QskStackBoxAnimator1( this ); + + if ( orientation() == Qt::Horizontal ) + animator->setDirection( forwards ? Qsk::LeftToRight : Qsk::RightToLeft ); + else + animator->setDirection( forwards ? Qsk::TopToBottom : Qsk::BottomToTop ); + + animator->setDuration( m_data->duration ); + QskStackBox::setAnimator( animator ); + + auto newIndex = forwards ? currentIndex() + 1 : currentIndex() - 1; if( newIndex < 0 ) newIndex += itemCount(); diff --git a/src/layouts/QskStackBoxAnimator.cpp b/src/layouts/QskStackBoxAnimator.cpp index e95b022a..5171537b 100644 --- a/src/layouts/QskStackBoxAnimator.cpp +++ b/src/layouts/QskStackBoxAnimator.cpp @@ -13,53 +13,6 @@ QSK_QT_PRIVATE_BEGIN #include QSK_QT_PRIVATE_END -static Qsk::Direction qskDirection( - Qt::Orientation orientation, int from, int to, int itemCount ) -{ - Qsk::Direction direction; - - if ( orientation == Qt::Horizontal ) - { - direction = Qsk::RightToLeft; - - if ( to > from ) - { - const bool isWrapping = ( from == 0 ) && ( to == itemCount - 1 ); - - if ( !isWrapping ) - direction = Qsk::LeftToRight; - } - else - { - const bool isWrapping = ( to == 0 ) && ( from == itemCount - 1 ); - - if ( isWrapping ) - direction = Qsk::LeftToRight; - } - } - else - { - direction = Qsk::BottomToTop; - - if ( to > from ) - { - const bool isWrapping = ( from == 0 ) && ( to == itemCount - 1 ); - - if ( !isWrapping ) - direction = Qsk::TopToBottom; - } - else - { - const bool isWrapping = ( to == 0 ) && ( from == itemCount - 1 ); - - if ( isWrapping ) - direction = Qsk::TopToBottom; - } - } - - return direction; -} - namespace { class RotationTransform : public QQuickTransform @@ -240,7 +193,7 @@ void QskStackBoxAnimator::advance( qreal progress ) QskStackBoxAnimator1::QskStackBoxAnimator1( QskStackBox* parent ) : QskStackBoxAnimator( parent ) - , m_orientation( Qt::Horizontal ) + , m_direction( Qsk::LeftToRight ) , m_isDirty( false ) , m_hasClip( false ) { @@ -251,27 +204,24 @@ QskStackBoxAnimator1::~QskStackBoxAnimator1() { } -void QskStackBoxAnimator1::setOrientation( Qt::Orientation orientation ) +void QskStackBoxAnimator1::setDirection( Qsk::Direction direction ) { - if ( m_orientation != orientation ) + if ( m_direction != direction ) { stop(); - m_orientation = orientation; + m_direction = direction; } } -Qt::Orientation QskStackBoxAnimator1::orientation() const +Qsk::Direction QskStackBoxAnimator1::direction() const { - return m_orientation; + return m_direction; } void QskStackBoxAnimator1::setup() { auto stackBox = this->stackBox(); - m_direction = qskDirection( m_orientation, - startIndex(), endIndex(), stackBox->itemCount() ); - m_hasClip = stackBox->clip(); if ( !m_hasClip ) stackBox->setClip( true ); @@ -283,7 +233,9 @@ void QskStackBoxAnimator1::setup() void QskStackBoxAnimator1::advanceIndex( qreal value ) { auto stackBox = this->stackBox(); - const bool isHorizontal = m_orientation == Qt::Horizontal; + + const bool isHorizontal = ( m_direction == Qsk::LeftToRight ) + || ( m_direction == Qsk::RightToLeft ); for ( int i = 0; i < 2; i++ ) { diff --git a/src/layouts/QskStackBoxAnimator.h b/src/layouts/QskStackBoxAnimator.h index c0fb07f2..8fd0d994 100644 --- a/src/layouts/QskStackBoxAnimator.h +++ b/src/layouts/QskStackBoxAnimator.h @@ -49,14 +49,14 @@ class QSK_EXPORT QskStackBoxAnimator1 : public QskStackBoxAnimator { Q_OBJECT - Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( Qsk::Direction direction READ direction WRITE setDirection ) public: QskStackBoxAnimator1( QskStackBox* ); ~QskStackBoxAnimator1() override; - void setOrientation( Qt::Orientation ); - Qt::Orientation orientation() const; + void setDirection( Qsk::Direction ); + Qsk::Direction direction() const; protected: bool eventFilter( QObject*, QEvent* ) override; @@ -68,7 +68,6 @@ class QSK_EXPORT QskStackBoxAnimator1 : public QskStackBoxAnimator private: qreal m_itemOffset[ 2 ]; - Qt::Orientation m_orientation : 2; Qsk::Direction m_direction : 4; bool m_isDirty : 1; bool m_hasClip : 1;