From ff316ce1ede1c192b10fa1a3421e40ea8e36627a Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 3 Apr 2022 16:25:13 +0200 Subject: [PATCH 1/8] Q_DECLARE_TYPEINFO fixed --- src/common/QskGradientStop.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/QskGradientStop.h b/src/common/QskGradientStop.h index 926803c9..3149eea4 100644 --- a/src/common/QskGradientStop.h +++ b/src/common/QskGradientStop.h @@ -48,7 +48,10 @@ class QSK_EXPORT QskGradientStop QColor m_color; // using RGBA instead ? }; -Q_DECLARE_TYPEINFO( QskGradientStop, Q_MOVABLE_TYPE ); +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + Q_DECLARE_TYPEINFO( QskGradientStop, Q_RELOCATABLE_TYPE ); +#endif + Q_DECLARE_METATYPE( QskGradientStop ) inline constexpr QskGradientStop::QskGradientStop() noexcept From 6728ad2bdc3f69f7777c2d0e656d53e43920e65c Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 3 Apr 2022 16:25:46 +0200 Subject: [PATCH 2/8] only check if types do match without doing the conversion --- src/controls/QskSkinnable.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 15d8b0b5..29207849 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -1145,12 +1145,12 @@ void QskSkinnable::startHintTransition( QskAspect aspect, if ( control->window() == nullptr || !isTransitionAccepted( aspect ) ) return; + if ( !QskVariantAnimator::maybeInterpolate( from, to ) ) + return; + auto v1 = from; auto v2 = to; - if ( !QskVariantAnimator::convertValues( v1, v2 ) ) - return; - if ( aspect.flagPrimitive() == QskAspect::GraphicRole ) { const auto skin = effectiveSkin(); From f4a9b15f11cd37d4e3b674358ff1d56da8b4c41f Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 3 Apr 2022 16:27:10 +0200 Subject: [PATCH 3/8] stop animation, when start/endValues are chaged. Otherwise we will run into crashes, when value types have not been aligned or can't be aligned --- src/controls/QskVariantAnimator.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index a23fd356..c54d4ea3 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -53,13 +53,13 @@ Q_CONSTRUCTOR_FUNCTION( qskRegisterInterpolator ) #endif #if defined( Q_CC_CLANG ) -#if __has_feature( address_sanitizer ) -#define QSK_DECL_INSANE __attribute__( ( no_sanitize( "undefined" ) ) ) -#endif + #if __has_feature( address_sanitizer ) + #define QSK_DECL_INSANE __attribute__( ( no_sanitize( "undefined" ) ) ) + #endif #endif #if !defined( QSK_DECL_INSANE ) -#define QSK_DECL_INSANE + #define QSK_DECL_INSANE #endif QSK_DECL_INSANE static inline QVariant qskInterpolate( @@ -112,11 +112,13 @@ QskVariantAnimator::~QskVariantAnimator() void QskVariantAnimator::setStartValue( const QVariant& value ) { + stop(); m_startValue = value; } void QskVariantAnimator::setEndValue( const QVariant& value ) { + stop(); m_endValue = value; } @@ -171,7 +173,7 @@ void QskVariantAnimator::setup() if ( convertValues( m_startValue, m_endValue ) ) { - if ( m_startValue != m_endValue ) + if ( m_startValue != m_endValue ) { const auto id = m_startValue.userType(); @@ -191,6 +193,8 @@ void QskVariantAnimator::advance( qreal progress ) if ( qFuzzyCompare( progress, 1.0 ) ) progress = 1.0; + Q_ASSERT( qskMetaType( m_startValue ) == qskMetaType( m_endValue ) ); + m_currentValue = qskInterpolate( m_interpolator, m_startValue, m_endValue, progress ); } From de48deb7c8bf92b0573279319185f5937cd6e82c Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 3 Apr 2022 16:37:36 +0200 Subject: [PATCH 4/8] more switches --- examples/gallery/button/ButtonPage.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/gallery/button/ButtonPage.cpp b/examples/gallery/button/ButtonPage.cpp index 9a347c37..d2e0ab3d 100644 --- a/examples/gallery/button/ButtonPage.cpp +++ b/examples/gallery/button/ButtonPage.cpp @@ -67,7 +67,14 @@ namespace (void) new QskSwitchButton( orientation, this ); auto button = new QskSwitchButton( orientation, this ); + button->setChecked( true ); + + button = new QskSwitchButton( orientation, this ); button->setInverted( true ); + + button = new QskSwitchButton( orientation, this ); + button->setInverted( true ); + button->setChecked( true ); } } }; From a05d1e34710a5d486f5bfd824409d0bd78b18bfd Mon Sep 17 00:00:00 2001 From: Clemens Manert Date: Mon, 4 Apr 2022 08:53:20 +0200 Subject: [PATCH 5/8] Add a Checkbox (#169) * Add checkbox * Add checkbox tests * Add checkbox-groups * Add checkbox-groups tests --- qskinny.pro | 1 + skins/material/QskMaterialSkin.cpp | 21 +++ skins/squiek/QskSquiekSkin.cpp | 20 +++ src/controls/QskCheckBox.cpp | 185 ++++++++++++++++++++++++ src/controls/QskCheckBox.h | 48 +++++++ src/controls/QskCheckBoxSkinlet.cpp | 134 ++++++++++++++++++ src/controls/QskCheckBoxSkinlet.h | 32 +++++ src/controls/QskSkin.cpp | 4 + src/src.pro | 4 + tests/checkboxes/checkboxes.pro | 11 ++ tests/checkboxes/main.cpp | 209 ++++++++++++++++++++++++++++ tests/checkboxes/main.h | 30 ++++ tests/tests.pro | 5 + 13 files changed, 704 insertions(+) create mode 100644 src/controls/QskCheckBox.cpp create mode 100644 src/controls/QskCheckBox.h create mode 100644 src/controls/QskCheckBoxSkinlet.cpp create mode 100644 src/controls/QskCheckBoxSkinlet.h create mode 100644 tests/checkboxes/checkboxes.pro create mode 100644 tests/checkboxes/main.cpp create mode 100644 tests/checkboxes/main.h create mode 100644 tests/tests.pro diff --git a/qskinny.pro b/qskinny.pro index 7a41eef1..2443087c 100644 --- a/qskinny.pro +++ b/qskinny.pro @@ -7,6 +7,7 @@ SUBDIRS = \ qmlexport \ tools \ support \ + tests \ examples \ playground diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index a096179e..a89cbeec 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -112,6 +113,7 @@ namespace void setupControl(); void setupBox(); + void setupCheckBox(); void setupDialogButtonBox(); void setupDialogButton(); void setupFocusIndicator(); @@ -142,6 +144,7 @@ void Editor::setup() setupControl(); setupBox(); + setupCheckBox(); setupDialogButtonBox(); setupDialogButton(); setupFocusIndicator(); @@ -177,6 +180,24 @@ void Editor::setupControl() qskShadedColor( m_pal.textColor, 0.6 ) ); } +void Editor::setupCheckBox() +{ + using Q = QskCheckBox; + + const qreal radius = qskDpiScaled( 18 ); + + setMargin( QskCheckBox::Tick, QMarginsF( 3, 5, 3, 3 ) ); + setStrutSize( Q::Box, radius, radius ); + + setBoxShape( Q::Box, 2 ); + + setColor( Q::Box, m_pal.baseColor); + setColor( Q::Box | Q::Checked, m_pal.accentColor ); + setGradient( Q::Box | Q::Checked | Q::Disabled, QskRgb::Grey ); + + setColor( Q::Tick, m_pal.contrastColor ); +} + void Editor::setupBox() { using Q = QskBox; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index bfdd6706..a79fc20c 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -132,6 +133,7 @@ namespace void setupControl(); void setupBox(); + void setupCheckBox(); void setupDialogButton(); void setupDialogButtonBox(); void setupFocusIndicator(); @@ -251,6 +253,7 @@ void Editor::setup() setupControl(); setupBox(); + setupCheckBox(); setupDialogButtonBox(); setupDialogButton(); setupFocusIndicator(); @@ -292,6 +295,23 @@ void Editor::setupBox() setPanel( QskBox::Panel, Plain ); } +void Editor::setupCheckBox() +{ + using Q = QskCheckBox; + + const qreal size = qskDpiScaled( 26 ); + + setMargin( Q::Tick, QskMargins( qskDpiScaled( 5 ) ) ); + + setStrutSize( Q::Box, QSizeF( size, size ) ); + setBoxShape( Q::Box, qskDpiScaled( 3 ) ); + setBoxBorderMetrics( Q::Box, qskDpiScaled( 1 ) ); + setBoxBorderColors( Q::Box, m_pal.darker125 ); + setColor( Q::Box, m_pal.lighter135 ); + setColor( Q::Box | Q::Checked, m_pal.highlighted ); + setColor( Q::Tick, m_pal.lighter135 ); +} + void Editor::setupPopup() { using A = QskAspect; diff --git a/src/controls/QskCheckBox.cpp b/src/controls/QskCheckBox.cpp new file mode 100644 index 00000000..7251db0a --- /dev/null +++ b/src/controls/QskCheckBox.cpp @@ -0,0 +1,185 @@ +#include "QskCheckBox.h" + +#include "QskAspect.h" + +#include + +QSK_SUBCONTROL( QskCheckBox, Box ) +QSK_SUBCONTROL( QskCheckBox, Tick ) + +QSK_SYSTEM_STATE( QskCheckBox, PartiallyChecked, QskAspect::LastUserState << 2 ) + +struct QskCheckBox::PrivateData +{ + QSet< QskAbstractButton* > group; + int groupItemsChecked; + Qt::CheckState checkState; + bool checkStateChanging : 1; + bool toggleChanging : 1; + bool triState : 1; +}; + +QskCheckBox::QskCheckBox( QQuickItem* parent ) + : Inherited( parent ) + , m_data( new PrivateData { QSet< QskAbstractButton* >(), 0, + Qt::Unchecked, false, false, false } ) { + setAcceptHoverEvents( true ); + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + + connect( this, &QskCheckBox::checkedChanged, this, [ this ]( bool t ) + { + setCheckStateInternal( t ? Qt::CheckState::Checked : + Qt::CheckState::Unchecked ); + } ); +} + +QskCheckBox::~QskCheckBox() +{ + Q_EMIT removeFromAllGroupsRequested(); +} + +bool QskCheckBox::isCheckable() const +{ + return true; +} + +Qt::CheckState QskCheckBox::checkState() const +{ + return m_data->checkState; +} + +void QskCheckBox::setCheckStateInternal( Qt::CheckState checkState ) +{ + if( m_data->checkStateChanging ) + { + return; + } + + setSkinStateFlag( PartiallyChecked, + checkState == Qt::CheckState::PartiallyChecked ); + + m_data->checkState = checkState; + Q_EMIT checkStateChanged( checkState ); +} + +void QskCheckBox::setCheckState( Qt::CheckState checkState ) +{ + if( checkState == m_data->checkState ) + return; + + m_data->checkStateChanging = true; + if( checkState == Qt::CheckState::PartiallyChecked ) + { + setChecked( true ); + setTriState( true ); + } + else + { + setChecked( checkState == Qt::CheckState::Checked ); + } + m_data->checkStateChanging = false; + + setCheckStateInternal( checkState ); +} + +bool QskCheckBox::isTriState() const +{ + return m_data->triState; +} + +void QskCheckBox::setTriState( bool triState ) +{ + if( m_data->triState != triState ) + { + m_data->triState = triState; + Q_EMIT isTriStateChanged( triState ); + } +} + +void QskCheckBox::updated() +{ + if( m_data->toggleChanging ) + { + return; + } + + auto& groupItemsChecked = m_data->groupItemsChecked; + + if( groupItemsChecked == m_data->group.size() ) + { + this->setCheckState( Qt::CheckState::Checked ); + } + else if ( groupItemsChecked == 0 ) + { + this->setCheckState( Qt::CheckState::Unchecked ); + } + else + { + this->setCheckState( Qt::CheckState::PartiallyChecked ); + } +} + +void QskCheckBox::addToGroup( QskCheckBox* groupItem ) { + if( m_data->group.contains( groupItem ) ) + { + return; + } + + m_data->group.insert( groupItem ); + + if( groupItem->checkState() == Qt::CheckState::Checked ) + { + m_data->groupItemsChecked += 1; + } + + updated(); + + connect( this, &QskCheckBox::checkStateChanged, + groupItem, [ this, groupItem ] ( Qt::CheckState checkState ) + { + if( checkState == Qt::Checked ) + { + m_data->toggleChanging = true; + groupItem->setChecked( true ); + m_data->groupItemsChecked = m_data->group.size(); + m_data->toggleChanging = false; + } + else if ( checkState == Qt::Unchecked ) + { + m_data->toggleChanging = true; + groupItem->setChecked( false ); + m_data->groupItemsChecked = 0; + m_data->toggleChanging = false; + } + } ); + + connect( groupItem, &QskAbstractButton::toggled, + this, [ this, groupItem ] ( bool toggled ) + { + auto& groupItemsChecked = m_data->groupItemsChecked; + groupItemsChecked += toggled ? 1 : -1; + updated(); + } ); + + connect( groupItem, &QskCheckBox::removeFromAllGroupsRequested, + this, [ this, groupItem ] ( ) + { + removeFromGroup( groupItem ); + } ); +} + +void QskCheckBox::removeFromGroup( QskCheckBox* groupItem ) { + if( !m_data->group.remove( groupItem ) ) + { + return; + } + + if( groupItem->checkState() == Qt::CheckState::Checked ) + { + m_data->groupItemsChecked -= 1; + } + + updated(); +} + +#include "moc_QskCheckBox.cpp" diff --git a/src/controls/QskCheckBox.h b/src/controls/QskCheckBox.h new file mode 100644 index 00000000..eed70a7b --- /dev/null +++ b/src/controls/QskCheckBox.h @@ -0,0 +1,48 @@ +#ifndef QSK_CHECK_BOX_H +#define QSK_CHECK_BOX_H + +#include "QskAbstractButton.h" + +class QSK_EXPORT QskCheckBox : public QskAbstractButton +{ + Q_OBJECT + + Q_PROPERTY( Qt::CheckState checkState READ checkState + WRITE setCheckState NOTIFY checkStateChanged FINAL ) + Q_PROPERTY( bool isTriState READ isTriState + WRITE setTriState NOTIFY isTriStateChanged FINAL ) + + using Inherited = QskAbstractButton; + + public: + QSK_SUBCONTROLS( Box, Tick ) + QSK_STATES( PartiallyChecked ) + + QskCheckBox( QQuickItem* parent = nullptr ); + ~QskCheckBox() override; + + Qt::CheckState checkState() const; + bool isTriState() const; + bool isCheckable() const override final; + + void addToGroup( QskCheckBox* groupItem ); + void removeFromGroup( QskCheckBox* groupItem ); + + public Q_SLOTS: + void setCheckState( Qt::CheckState ); + void setTriState( bool triState = true ); + + Q_SIGNALS: + void checkStateChanged( Qt::CheckState ); + void isTriStateChanged( bool ); + void removeFromAllGroupsRequested(); + + private: + void setCheckStateInternal( Qt::CheckState ); + void updated(); + + struct PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif // QSK_CHECK_BOX_H diff --git a/src/controls/QskCheckBoxSkinlet.cpp b/src/controls/QskCheckBoxSkinlet.cpp new file mode 100644 index 00000000..946394b6 --- /dev/null +++ b/src/controls/QskCheckBoxSkinlet.cpp @@ -0,0 +1,134 @@ +#include "QskCheckBoxSkinlet.h" +#include "QskCheckBox.h" + +#include +#include + +class Tic : public QSGGeometryNode { + QSGFlatColorMaterial material; + QSGGeometry geometry = QSGGeometry( + QSGGeometry::defaultAttributes_Point2D(), 3 ); + const QRectF& target; + public: + Tic( const QRectF& rect, const QColor& color ): target( rect ) { + geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); + geometry.setLineWidth( 2 ); + setGeometry( &geometry ); + + material.setColor( color ); + setMaterial( &material ); + + markDirty( QSGNode::DirtyGeometry ); + } + + void setColor( const QColor& color ) { + material.setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } + + void makeTic() { + const auto& size = target.size(); + const auto x = target.x(); + const auto y = target.y(); + + geometry.vertexDataAsPoint2D()[0].set( x, y + size.height() / 2 ); + geometry.vertexDataAsPoint2D()[1].set( x + size.width() / 3, + y + size.height() ); + geometry.vertexDataAsPoint2D()[2].set( x + size.width(), y ); + markDirty( QSGNode::DirtyGeometry ); + } + + void makePartially() { + const auto& size = target.size(); + const auto x = target.x(); + const auto y = target.y(); + + geometry.vertexDataAsPoint2D()[0].set( x, y + size.height() / 2 ); + geometry.vertexDataAsPoint2D()[1].set( x, y + size.height() / 2 ); + geometry.vertexDataAsPoint2D()[2].set( x + size.width(), + y + size.height() / 2 ); + + markDirty( QSGNode::DirtyGeometry ); + } + + void makeEmpty() { + const auto x = target.x(); + const auto y = target.y(); + + geometry.vertexDataAsPoint2D()[0].set( x, y ); + geometry.vertexDataAsPoint2D()[1].set( x, y ); + geometry.vertexDataAsPoint2D()[2].set( x, y ); + + markDirty( QSGNode::DirtyGeometry ); + } +}; + +QskCheckBoxSkinlet::QskCheckBoxSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { BoxRole, TickRole } ); +} + +QskCheckBoxSkinlet::~QskCheckBoxSkinlet() +{ +} + +QRectF QskCheckBoxSkinlet::subControlRect( + const QskSkinnable*, + const QRectF& contentsRect, + QskAspect::Subcontrol ) const +{ + return contentsRect; +} + +QSGNode* QskCheckBoxSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + switch( nodeRole ) { + case BoxRole: + return updateBoxNode( skinnable, node, + QskCheckBox::Box ); + case TickRole: + auto control = dynamic_cast< const QskCheckBox* >( skinnable ); + auto rect = control->subControlRect( QskCheckBox::Tick ); + rect = rect.marginsRemoved( + skinnable->marginHint( QskCheckBox::Tick ) ); + + Tic* tic; + if ( static_cast< Tic* >( node ) == nullptr ) + { + tic = new Tic( rect, skinnable->color( QskCheckBox::Tick ) ); + } + else + { + tic = static_cast< Tic* >( node ); + } + + switch ( control->checkState() ) { + case Qt::CheckState::Unchecked: + tic->setColor( skinnable->color( QskCheckBox::Tick ) ); + tic->makeEmpty(); + break; + case Qt::CheckState::PartiallyChecked: + tic->setColor( skinnable->color( + QskCheckBox::Tick | QskCheckBox::PartiallyChecked ) ); + tic->makePartially(); + break; + case Qt::CheckState::Checked: + tic->setColor( skinnable->color( + QskCheckBox::Tick | QskCheckBox::Checked ) ); + tic->makeTic(); + break; + } + + return tic; + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskCheckBoxSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint, const QSizeF& ) const +{ + return skinnable->strutSizeHint( QskCheckBox::Box ); +} diff --git a/src/controls/QskCheckBoxSkinlet.h b/src/controls/QskCheckBoxSkinlet.h new file mode 100644 index 00000000..db19bdd2 --- /dev/null +++ b/src/controls/QskCheckBoxSkinlet.h @@ -0,0 +1,32 @@ +#ifndef QSK_CHECK_BOX_SKINLET_H +#define QSK_CHECK_BOX_SKINLET_H + +#include "QskSkinlet.h" + +class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet +{ + Q_GADGET + + using Inherited = QskSkinlet; + public: + enum NodeRole + { + BoxRole, + TickRole, + }; + + Q_INVOKABLE QskCheckBoxSkinlet( QskSkin* = nullptr ); + ~QskCheckBoxSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; +}; + +#endif // QSK_CHECK_BOX_SKINLET_H diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index 1f8b67db..4a8e20e5 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -29,6 +29,9 @@ QSK_QT_PRIVATE_END #include "QskBox.h" #include "QskBoxSkinlet.h" +#include "QskCheckBox.h" +#include "QskCheckBoxSkinlet.h" + #include "QskFocusIndicator.h" #include "QskFocusIndicatorSkinlet.h" @@ -143,6 +146,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskControl, QskSkinlet >(); declareSkinlet< QskBox, QskBoxSkinlet >(); + declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >(); declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >(); declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >(); declareSkinlet< QskListView, QskListViewSkinlet >(); diff --git a/src/src.pro b/src/src.pro index 0c00a0ec..2f51cb2b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -141,6 +141,8 @@ HEADERS += \ controls/QskBoundedValueInput.h \ controls/QskBox.h \ controls/QskBoxSkinlet.h \ + controls/QskCheckBox.h \ + controls/QskCheckBoxSkinlet.h \ controls/QskControl.h \ controls/QskControlPrivate.h \ controls/QskDirtyItemFilter.h \ @@ -221,6 +223,8 @@ SOURCES += \ controls/QskBoundedValueInput.cpp \ controls/QskBox.cpp \ controls/QskBoxSkinlet.cpp \ + controls/QskCheckBox.cpp \ + controls/QskCheckBoxSkinlet.cpp \ controls/QskControl.cpp \ controls/QskControlPrivate.cpp \ controls/QskDirtyItemFilter.cpp \ diff --git a/tests/checkboxes/checkboxes.pro b/tests/checkboxes/checkboxes.pro new file mode 100644 index 00000000..79376a0b --- /dev/null +++ b/tests/checkboxes/checkboxes.pro @@ -0,0 +1,11 @@ +CONFIG += qskexample + CONFIG += console + CONFIG += testcase + + QT += testlib + + HEADERS += \ + main.h + + SOURCES += \ + main.cpp diff --git a/tests/checkboxes/main.cpp b/tests/checkboxes/main.cpp new file mode 100644 index 00000000..234753c0 --- /dev/null +++ b/tests/checkboxes/main.cpp @@ -0,0 +1,209 @@ +#include "main.h" + +#include + +void CheckBoxTests::init() { + root = new QskControl(); +} +void CheckBoxTests::cleanup() { + delete root; +} + +void CheckBoxTests::checkbox() { + auto t = new QskCheckBox( root ); + + QVERIFY( t->isCheckable() ); +} + +void CheckBoxTests::click() { + auto t = new QskCheckBox( root ); + + QVERIFY( t->isChecked() == false ); + t->click(); + QVERIFY( t->isChecked() ); +} + +void CheckBoxTests::toggle() { + auto t = new QskCheckBox( root ); + + QVERIFY( t->isChecked() == false ); + t->toggle(); + QVERIFY( t->isChecked() ); + + t->toggle(); + QVERIFY( t->isChecked() == false ); +} + +void CheckBoxTests::triState() { + auto t = new QskCheckBox( root ); + + QVERIFY( t->isChecked() == false ); + QVERIFY( t->isTriState() == false ); + + t->setCheckState( Qt::CheckState::PartiallyChecked ); + + QVERIFY( t->isChecked() == true ); + QVERIFY( t->isTriState() == true ); +} + +void CheckBoxTests::higherGroupUpdatesLower() { + auto t = new QskCheckBox( root ); + auto t1 = new QskCheckBox( root ); + auto t2 = new QskCheckBox( root ); + auto t3 = new QskCheckBox( root ); + + t->addToGroup( t1 ); + t->addToGroup( t2 ); + t->addToGroup( t3 ); + + QVERIFY( t->isChecked() == false ); + QVERIFY( t1->isChecked() == false ); + QVERIFY( t2->isChecked() == false ); + QVERIFY( t3->isChecked() == false ); + + t->setChecked( true ); + QVERIFY( t->isChecked() ); + QVERIFY( t1->isChecked() ); + QVERIFY( t2->isChecked() ); + QVERIFY( t3->isChecked() ); + + t->setChecked( false ); + QVERIFY( t->isChecked() == false ); + QVERIFY( t1->isChecked() == false ); + QVERIFY( t2->isChecked() == false ); + QVERIFY( t3->isChecked() == false ); + +} +void CheckBoxTests::lowerGroupUpdatesHigher() { + auto t = new QskCheckBox( root ); + + auto t1 = new QskCheckBox( root ); + auto t2 = new QskCheckBox( root ); + + t->addToGroup( t1 ); + t->addToGroup( t2 ); + + t1->setChecked( true ); + QVERIFY( t->isChecked() ); + QVERIFY( t->isTriState() ); + QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); + QVERIFY( t1->isChecked() == true ); + QVERIFY( t2->isChecked() == false ); + + + t2->setChecked( true ); + QVERIFY( t->isChecked() ); + QVERIFY( t->isTriState() ); + QVERIFY( t->checkState() == Qt::CheckState::Checked ); + QVERIFY( t1->isChecked() == true ); + QVERIFY( t2->isChecked() == true ); + + t1->setChecked( false ); + QVERIFY( t->isChecked() ); + QVERIFY( t->isTriState() ); + QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); + QVERIFY( t1->isChecked() == false ); + QVERIFY( t2->isChecked() == true ); + + t2->setChecked( false ); + QVERIFY( t->isChecked() == false ); + QVERIFY( t->isTriState() ); + QVERIFY( t->checkState() == Qt::CheckState::Unchecked ); + QVERIFY( t1->isChecked() == false ); + QVERIFY( t2->isChecked() == false ); +} + +void CheckBoxTests::addToGroup() { + auto t = new QskCheckBox( root ); + + auto t1 = new QskCheckBox( root ); + auto t2 = new QskCheckBox( root ); + + t->addToGroup( t1 ); + t->addToGroup( t2 ); + + t->setChecked( true ); + QVERIFY( t->isChecked() ); + QVERIFY( t1->isChecked() ); + QVERIFY( t2->isChecked() ); + + auto t3 = new QskCheckBox( root ); + t->addToGroup( t3 ); + + QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); + + t3->setChecked( true ); + QVERIFY( t->checkState() == Qt::CheckState::Checked ); + auto t4 = new QskCheckBox( root ); + t4->setChecked( true ); + t->addToGroup( t4 ); + QVERIFY( t->checkState() == Qt::CheckState::Checked ); +} + +void CheckBoxTests::addPartlyToGroup() { + auto t = new QskCheckBox( root ); + + auto t1 = new QskCheckBox( root ); + auto t1a = new QskCheckBox( root ); + auto t1b = new QskCheckBox( root ); + + t1->addToGroup( t1a ); + t1->addToGroup( t1b ); + + t1a->setChecked( true ); + + QVERIFY( t1->checkState() == Qt::CheckState::PartiallyChecked ); + t->addToGroup( t1 ); + QVERIFY( t1->checkState() == Qt::CheckState::PartiallyChecked ); +} + + +void CheckBoxTests::removeFromGroup() { + auto t = new QskCheckBox( root ); + + auto t1 = new QskCheckBox( root ); + auto t2 = new QskCheckBox( root ); + + t->addToGroup( t1 ); + t->addToGroup( t2 ); + + t2->setChecked( true ); + QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); + + t->removeFromGroup( t2 ); + QVERIFY( t->isChecked() == false ); +} + +void CheckBoxTests::groupMemberGetsDeleted() { + auto t = new QskCheckBox( root ); + auto t1 = new QskCheckBox( root ); + auto t2 = new QskCheckBox( root ); + + t->addToGroup( t1 ); + t->addToGroup( t2 ); + + t2->setChecked( true ); + QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); + + delete t2; + QVERIFY( t->isChecked() == false ); +} + +void CheckBoxTests::addTwiceToSameGroup() { + auto t = new QskCheckBox( root ); + + auto t1 = new QskCheckBox( root ); + auto t2 = new QskCheckBox( root ); + + t->addToGroup( t1 ); + t->addToGroup( t1 ); + t->removeFromGroup( t1 ); + + t->addToGroup( t2 ); + + t2->setChecked( true ); + + QVERIFY( t->checkState() == Qt::CheckState::Checked ); +} +#include "moc_main.cpp" + diff --git a/tests/checkboxes/main.h b/tests/checkboxes/main.h new file mode 100644 index 00000000..5f5b5e93 --- /dev/null +++ b/tests/checkboxes/main.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +class QskControl; +class CheckBoxTests : public QObject +{ + Q_OBJECT + + QskControl * root; + private Q_SLOTS: + void init(); + void cleanup(); + + void checkbox(); + void click(); + void toggle(); + void triState(); + void higherGroupUpdatesLower(); + void lowerGroupUpdatesHigher(); + void addToGroup(); + void addPartlyToGroup(); + void removeFromGroup(); + void groupMemberGetsDeleted(); + void addTwiceToSameGroup(); +}; + +QTEST_MAIN(CheckBoxTests) + diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 00000000..60e10ea4 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + checkboxes + From 49cdc1a029f4b37173efea283a759e38a9f1a8c2 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 4 Apr 2022 14:14:07 +0200 Subject: [PATCH 6/8] formal cheges, check box added to gallery --- examples/gallery/button/ButtonPage.cpp | 21 +++ src/controls/QskCheckBox.cpp | 88 +++++------ src/controls/QskCheckBox.h | 10 +- src/controls/QskCheckBoxSkinlet.cpp | 204 ++++++++++++++----------- src/controls/QskCheckBoxSkinlet.h | 13 +- 5 files changed, 203 insertions(+), 133 deletions(-) diff --git a/examples/gallery/button/ButtonPage.cpp b/examples/gallery/button/ButtonPage.cpp index d2e0ab3d..d4610b43 100644 --- a/examples/gallery/button/ButtonPage.cpp +++ b/examples/gallery/button/ButtonPage.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -78,6 +79,24 @@ namespace } } }; + + class CheckButtonBox : public QskLinearBox + { + public: + CheckButtonBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Horizontal, parent ) + { + setSpacing( 20 ); + setExtraSpacingAt( Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge ); + + for ( auto state : { Qt::Unchecked, Qt::PartiallyChecked, Qt::Checked } ) + { + auto button = new QskCheckBox( this ); + button->setTriState( true ); + button->setCheckState( state ); + } + } + }; } ButtonPage::ButtonPage( QQuickItem* parent ) @@ -92,4 +111,6 @@ void ButtonPage::populate() new ButtonBox( this ); new QskSeparator( Qt::Horizontal, this ); new SwitchButtonBox( this ); + new QskSeparator( Qt::Horizontal, this ); + new CheckButtonBox( this ); } diff --git a/src/controls/QskCheckBox.cpp b/src/controls/QskCheckBox.cpp index 7251db0a..32bfbbb7 100644 --- a/src/controls/QskCheckBox.cpp +++ b/src/controls/QskCheckBox.cpp @@ -1,5 +1,9 @@ -#include "QskCheckBox.h" +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ +#include "QskCheckBox.h" #include "QskAspect.h" #include @@ -9,11 +13,22 @@ QSK_SUBCONTROL( QskCheckBox, Tick ) QSK_SYSTEM_STATE( QskCheckBox, PartiallyChecked, QskAspect::LastUserState << 2 ) -struct QskCheckBox::PrivateData +class QskCheckBox::PrivateData { + public: + PrivateData() + : checkState( Qt::Unchecked ) + , checkStateChanging( false ) + , toggleChanging( false ) + , triState( false ) + { + } + QSet< QskAbstractButton* > group; - int groupItemsChecked; - Qt::CheckState checkState; + + int groupItemsChecked = 0; + + int checkState : 2; bool checkStateChanging : 1; bool toggleChanging : 1; bool triState : 1; @@ -21,16 +36,13 @@ struct QskCheckBox::PrivateData QskCheckBox::QskCheckBox( QQuickItem* parent ) : Inherited( parent ) - , m_data( new PrivateData { QSet< QskAbstractButton* >(), 0, - Qt::Unchecked, false, false, false } ) { + , m_data( new PrivateData() ) +{ setAcceptHoverEvents( true ); initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - connect( this, &QskCheckBox::checkedChanged, this, [ this ]( bool t ) - { - setCheckStateInternal( t ? Qt::CheckState::Checked : - Qt::CheckState::Unchecked ); - } ); + connect( this, &QskCheckBox::checkedChanged, this, + [ this ]( bool on ) { setCheckStateInternal( on ? Qt::Checked : Qt::Unchecked ); } ); } QskCheckBox::~QskCheckBox() @@ -45,18 +57,15 @@ bool QskCheckBox::isCheckable() const Qt::CheckState QskCheckBox::checkState() const { - return m_data->checkState; + return static_cast< Qt::CheckState >( m_data->checkState ); } void QskCheckBox::setCheckStateInternal( Qt::CheckState checkState ) { if( m_data->checkStateChanging ) - { return; - } - setSkinStateFlag( PartiallyChecked, - checkState == Qt::CheckState::PartiallyChecked ); + setSkinStateFlag( PartiallyChecked, checkState == Qt::PartiallyChecked ); m_data->checkState = checkState; Q_EMIT checkStateChanged( checkState ); @@ -68,15 +77,17 @@ void QskCheckBox::setCheckState( Qt::CheckState checkState ) return; m_data->checkStateChanging = true; - if( checkState == Qt::CheckState::PartiallyChecked ) + + if( checkState == Qt::PartiallyChecked ) { setChecked( true ); setTriState( true ); } else { - setChecked( checkState == Qt::CheckState::Checked ); + setChecked( checkState == Qt::Checked ); } + m_data->checkStateChanging = false; setCheckStateInternal( checkState ); @@ -99,43 +110,38 @@ void QskCheckBox::setTriState( bool triState ) void QskCheckBox::updated() { if( m_data->toggleChanging ) - { return; - } - auto& groupItemsChecked = m_data->groupItemsChecked; + const auto& groupItemsChecked = m_data->groupItemsChecked; if( groupItemsChecked == m_data->group.size() ) { - this->setCheckState( Qt::CheckState::Checked ); + setCheckState( Qt::Checked ); } else if ( groupItemsChecked == 0 ) { - this->setCheckState( Qt::CheckState::Unchecked ); + setCheckState( Qt::Unchecked ); } else { - this->setCheckState( Qt::CheckState::PartiallyChecked ); + setCheckState( Qt::PartiallyChecked ); } } -void QskCheckBox::addToGroup( QskCheckBox* groupItem ) { +void QskCheckBox::addToGroup( QskCheckBox* groupItem ) +{ if( m_data->group.contains( groupItem ) ) - { return; - } m_data->group.insert( groupItem ); - if( groupItem->checkState() == Qt::CheckState::Checked ) - { - m_data->groupItemsChecked += 1; - } + if( groupItem->checkState() == Qt::Checked ) + m_data->groupItemsChecked++; updated(); connect( this, &QskCheckBox::checkStateChanged, - groupItem, [ this, groupItem ] ( Qt::CheckState checkState ) + groupItem, [ this, groupItem ]( Qt::CheckState checkState ) { if( checkState == Qt::Checked ) { @@ -154,7 +160,7 @@ void QskCheckBox::addToGroup( QskCheckBox* groupItem ) { } ); connect( groupItem, &QskAbstractButton::toggled, - this, [ this, groupItem ] ( bool toggled ) + this, [ this, groupItem ]( bool toggled ) { auto& groupItemsChecked = m_data->groupItemsChecked; groupItemsChecked += toggled ? 1 : -1; @@ -162,22 +168,16 @@ void QskCheckBox::addToGroup( QskCheckBox* groupItem ) { } ); connect( groupItem, &QskCheckBox::removeFromAllGroupsRequested, - this, [ this, groupItem ] ( ) - { - removeFromGroup( groupItem ); - } ); + this, [ this, groupItem ]( ) { removeFromGroup( groupItem ); } ); } -void QskCheckBox::removeFromGroup( QskCheckBox* groupItem ) { +void QskCheckBox::removeFromGroup( QskCheckBox* groupItem ) +{ if( !m_data->group.remove( groupItem ) ) - { return; - } - if( groupItem->checkState() == Qt::CheckState::Checked ) - { - m_data->groupItemsChecked -= 1; - } + if( groupItem->checkState() == Qt::Checked ) + m_data->groupItemsChecked--; updated(); } diff --git a/src/controls/QskCheckBox.h b/src/controls/QskCheckBox.h index eed70a7b..2c273ca5 100644 --- a/src/controls/QskCheckBox.h +++ b/src/controls/QskCheckBox.h @@ -1,3 +1,8 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + #ifndef QSK_CHECK_BOX_H #define QSK_CHECK_BOX_H @@ -9,6 +14,7 @@ class QSK_EXPORT QskCheckBox : public QskAbstractButton Q_PROPERTY( Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL ) + Q_PROPERTY( bool isTriState READ isTriState WRITE setTriState NOTIFY isTriStateChanged FINAL ) @@ -41,8 +47,8 @@ class QSK_EXPORT QskCheckBox : public QskAbstractButton void setCheckStateInternal( Qt::CheckState ); void updated(); - struct PrivateData; + class PrivateData; std::unique_ptr< PrivateData > m_data; }; -#endif // QSK_CHECK_BOX_H +#endif diff --git a/src/controls/QskCheckBoxSkinlet.cpp b/src/controls/QskCheckBoxSkinlet.cpp index 946394b6..fd9f842f 100644 --- a/src/controls/QskCheckBoxSkinlet.cpp +++ b/src/controls/QskCheckBoxSkinlet.cpp @@ -1,67 +1,88 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + #include "QskCheckBoxSkinlet.h" #include "QskCheckBox.h" #include #include -class Tic : public QSGGeometryNode { - QSGFlatColorMaterial material; - QSGGeometry geometry = QSGGeometry( - QSGGeometry::defaultAttributes_Point2D(), 3 ); - const QRectF& target; - public: - Tic( const QRectF& rect, const QColor& color ): target( rect ) { - geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); - geometry.setLineWidth( 2 ); - setGeometry( &geometry ); +namespace +{ + class Tick : public QSGGeometryNode + { + public: + Tick( const QRectF& rect, const QColor& color ) + : m_target( rect ) + , geometry( QSGGeometry::defaultAttributes_Point2D(), 3 ) + { + geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); + geometry.setLineWidth( 2 ); + setGeometry( &geometry ); - material.setColor( color ); - setMaterial( &material ); + material.setColor( color ); + setMaterial( &material ); - markDirty( QSGNode::DirtyGeometry ); - } + markDirty( QSGNode::DirtyGeometry ); + } - void setColor( const QColor& color ) { - material.setColor( color ); - markDirty( QSGNode::DirtyMaterial ); - } + void setColor( const QColor& color ) { + material.setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } - void makeTic() { - const auto& size = target.size(); - const auto x = target.x(); - const auto y = target.y(); + void makeTick() + { + const auto x = m_target.x(); + const auto y = m_target.y(); - geometry.vertexDataAsPoint2D()[0].set( x, y + size.height() / 2 ); - geometry.vertexDataAsPoint2D()[1].set( x + size.width() / 3, - y + size.height() ); - geometry.vertexDataAsPoint2D()[2].set( x + size.width(), y ); - markDirty( QSGNode::DirtyGeometry ); - } + auto vertexData = geometry.vertexDataAsPoint2D(); - void makePartially() { - const auto& size = target.size(); - const auto x = target.x(); - const auto y = target.y(); + vertexData[0].set( x, y + m_target.height() / 2 ); + vertexData[1].set( x + m_target.width() / 3, y + m_target.height() ); + vertexData[2].set( x + m_target.width(), y ); - geometry.vertexDataAsPoint2D()[0].set( x, y + size.height() / 2 ); - geometry.vertexDataAsPoint2D()[1].set( x, y + size.height() / 2 ); - geometry.vertexDataAsPoint2D()[2].set( x + size.width(), - y + size.height() / 2 ); + markDirty( QSGNode::DirtyGeometry ); + } - markDirty( QSGNode::DirtyGeometry ); - } + void makePartially() + { + const auto x = m_target.x(); + const auto y = m_target.y(); - void makeEmpty() { - const auto x = target.x(); - const auto y = target.y(); + auto vertexData = geometry.vertexDataAsPoint2D(); - geometry.vertexDataAsPoint2D()[0].set( x, y ); - geometry.vertexDataAsPoint2D()[1].set( x, y ); - geometry.vertexDataAsPoint2D()[2].set( x, y ); + vertexData[0].set( x, y + m_target.height() / 2 ); + vertexData[1].set( x, y + m_target.height() / 2 ); + vertexData[2].set( x + m_target.width(), y + m_target.height() / 2 ); - markDirty( QSGNode::DirtyGeometry ); - } -}; + markDirty( QSGNode::DirtyGeometry ); + } + + void makeEmpty() + { + const auto x = m_target.x(); + const auto y = m_target.y(); + + auto vertexData = geometry.vertexDataAsPoint2D(); + + vertexData[0].set( x, y ); + vertexData[1].set( x, y ); + vertexData[2].set( x, y ); + + markDirty( QSGNode::DirtyGeometry ); + } + + private: + + const QRectF m_target; + + QSGFlatColorMaterial material; + QSGGeometry geometry; + }; +} QskCheckBoxSkinlet::QskCheckBoxSkinlet( QskSkin* skin ) : QskSkinlet( skin ) @@ -74,9 +95,7 @@ QskCheckBoxSkinlet::~QskCheckBoxSkinlet() } QRectF QskCheckBoxSkinlet::subControlRect( - const QskSkinnable*, - const QRectF& contentsRect, - QskAspect::Subcontrol ) const + const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const { return contentsRect; } @@ -84,49 +103,62 @@ QRectF QskCheckBoxSkinlet::subControlRect( QSGNode* QskCheckBoxSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { - switch( nodeRole ) { + switch( nodeRole ) + { case BoxRole: - return updateBoxNode( skinnable, node, - QskCheckBox::Box ); + { + return updateBoxNode( skinnable, node, QskCheckBox::Box ); + } + case TickRole: - auto control = dynamic_cast< const QskCheckBox* >( skinnable ); - auto rect = control->subControlRect( QskCheckBox::Tick ); - rect = rect.marginsRemoved( - skinnable->marginHint( QskCheckBox::Tick ) ); - - Tic* tic; - if ( static_cast< Tic* >( node ) == nullptr ) - { - tic = new Tic( rect, skinnable->color( QskCheckBox::Tick ) ); - } - else - { - tic = static_cast< Tic* >( node ); - } - - switch ( control->checkState() ) { - case Qt::CheckState::Unchecked: - tic->setColor( skinnable->color( QskCheckBox::Tick ) ); - tic->makeEmpty(); - break; - case Qt::CheckState::PartiallyChecked: - tic->setColor( skinnable->color( - QskCheckBox::Tick | QskCheckBox::PartiallyChecked ) ); - tic->makePartially(); - break; - case Qt::CheckState::Checked: - tic->setColor( skinnable->color( - QskCheckBox::Tick | QskCheckBox::Checked ) ); - tic->makeTic(); - break; - } - - return tic; + { + auto checkBox = static_cast< const QskCheckBox* >( skinnable ); + return updateTickNode( checkBox, node ); + } } return Inherited::updateSubNode( skinnable, nodeRole, node ); } +QSGNode* QskCheckBoxSkinlet::updateTickNode( + const QskCheckBox* checkBox, QSGNode* node ) const +{ + using Q = QskCheckBox; + + auto rect = checkBox->subControlRect( Q::Tick ); + rect = rect.marginsRemoved( checkBox->marginHint( Q::Tick ) ); + + auto tick = static_cast< Tick* >( node ); + if ( tick == nullptr ) + tick = new Tick( rect, checkBox->color( Q::Tick ) ); + + switch ( checkBox->checkState() ) + { + case Qt::Unchecked: + + tick->setColor( checkBox->color( Q::Tick ) ); + tick->makeEmpty(); + + break; + + case Qt::PartiallyChecked: + + tick->setColor( checkBox->color( Q::Tick | Q::PartiallyChecked ) ); + tick->makePartially(); + + break; + + case Qt::Checked: + + tick->setColor( checkBox->color( Q::Tick | Q::Checked ) ); + tick->makeTick(); + + break; + } + + return tick; +} + QSizeF QskCheckBoxSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint, const QSizeF& ) const { diff --git a/src/controls/QskCheckBoxSkinlet.h b/src/controls/QskCheckBoxSkinlet.h index db19bdd2..d703600c 100644 --- a/src/controls/QskCheckBoxSkinlet.h +++ b/src/controls/QskCheckBoxSkinlet.h @@ -1,13 +1,21 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + #ifndef QSK_CHECK_BOX_SKINLET_H #define QSK_CHECK_BOX_SKINLET_H #include "QskSkinlet.h" +class QskCheckBox; + class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet { Q_GADGET using Inherited = QskSkinlet; + public: enum NodeRole { @@ -27,6 +35,9 @@ class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet protected: QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; + + private: + QSGNode* updateTickNode( const QskCheckBox*, QSGNode* ) const; }; -#endif // QSK_CHECK_BOX_SKINLET_H +#endif From ae5a348b50fdd3d4be6a7ad742c33750dd9c35d5 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 4 Apr 2022 14:39:24 +0200 Subject: [PATCH 7/8] spelling aligned with Qt checkboxes ( triState -> tristate ) --- examples/gallery/button/ButtonPage.cpp | 2 +- src/controls/QskCheckBox.cpp | 18 ++++----- src/controls/QskCheckBox.h | 14 +++---- tests/checkboxes/main.cpp | 52 ++++++++++++++++---------- tests/checkboxes/main.h | 2 +- 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/examples/gallery/button/ButtonPage.cpp b/examples/gallery/button/ButtonPage.cpp index d4610b43..e6cb217d 100644 --- a/examples/gallery/button/ButtonPage.cpp +++ b/examples/gallery/button/ButtonPage.cpp @@ -92,7 +92,7 @@ namespace for ( auto state : { Qt::Unchecked, Qt::PartiallyChecked, Qt::Checked } ) { auto button = new QskCheckBox( this ); - button->setTriState( true ); + button->setTristate( true ); button->setCheckState( state ); } } diff --git a/src/controls/QskCheckBox.cpp b/src/controls/QskCheckBox.cpp index 32bfbbb7..5a5cdbee 100644 --- a/src/controls/QskCheckBox.cpp +++ b/src/controls/QskCheckBox.cpp @@ -20,7 +20,7 @@ class QskCheckBox::PrivateData : checkState( Qt::Unchecked ) , checkStateChanging( false ) , toggleChanging( false ) - , triState( false ) + , tristate( false ) { } @@ -31,7 +31,7 @@ class QskCheckBox::PrivateData int checkState : 2; bool checkStateChanging : 1; bool toggleChanging : 1; - bool triState : 1; + bool tristate : 1; }; QskCheckBox::QskCheckBox( QQuickItem* parent ) @@ -81,7 +81,7 @@ void QskCheckBox::setCheckState( Qt::CheckState checkState ) if( checkState == Qt::PartiallyChecked ) { setChecked( true ); - setTriState( true ); + setTristate( true ); } else { @@ -93,17 +93,17 @@ void QskCheckBox::setCheckState( Qt::CheckState checkState ) setCheckStateInternal( checkState ); } -bool QskCheckBox::isTriState() const +bool QskCheckBox::isTristate() const { - return m_data->triState; + return m_data->tristate; } -void QskCheckBox::setTriState( bool triState ) +void QskCheckBox::setTristate( bool tristate ) { - if( m_data->triState != triState ) + if( m_data->tristate != tristate ) { - m_data->triState = triState; - Q_EMIT isTriStateChanged( triState ); + m_data->tristate = tristate; + Q_EMIT tristateChanged( tristate ); } } diff --git a/src/controls/QskCheckBox.h b/src/controls/QskCheckBox.h index 2c273ca5..b1443c1d 100644 --- a/src/controls/QskCheckBox.h +++ b/src/controls/QskCheckBox.h @@ -15,8 +15,8 @@ class QSK_EXPORT QskCheckBox : public QskAbstractButton Q_PROPERTY( Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL ) - Q_PROPERTY( bool isTriState READ isTriState - WRITE setTriState NOTIFY isTriStateChanged FINAL ) + Q_PROPERTY( bool tristate READ isTristate + WRITE setTristate NOTIFY tristateChanged FINAL ) using Inherited = QskAbstractButton; @@ -28,19 +28,19 @@ class QSK_EXPORT QskCheckBox : public QskAbstractButton ~QskCheckBox() override; Qt::CheckState checkState() const; - bool isTriState() const; + bool isTristate() const; bool isCheckable() const override final; - void addToGroup( QskCheckBox* groupItem ); - void removeFromGroup( QskCheckBox* groupItem ); + void addToGroup( QskCheckBox* ); + void removeFromGroup( QskCheckBox* ); public Q_SLOTS: void setCheckState( Qt::CheckState ); - void setTriState( bool triState = true ); + void setTristate( bool triState = true ); Q_SIGNALS: void checkStateChanged( Qt::CheckState ); - void isTriStateChanged( bool ); + void tristateChanged( bool ); void removeFromAllGroupsRequested(); private: diff --git a/tests/checkboxes/main.cpp b/tests/checkboxes/main.cpp index 234753c0..711a9d4a 100644 --- a/tests/checkboxes/main.cpp +++ b/tests/checkboxes/main.cpp @@ -2,20 +2,25 @@ #include -void CheckBoxTests::init() { +void CheckBoxTests::init() +{ root = new QskControl(); } -void CheckBoxTests::cleanup() { + +void CheckBoxTests::cleanup() +{ delete root; } -void CheckBoxTests::checkbox() { +void CheckBoxTests::checkbox() +{ auto t = new QskCheckBox( root ); QVERIFY( t->isCheckable() ); } -void CheckBoxTests::click() { +void CheckBoxTests::click() +{ auto t = new QskCheckBox( root ); QVERIFY( t->isChecked() == false ); @@ -23,7 +28,8 @@ void CheckBoxTests::click() { QVERIFY( t->isChecked() ); } -void CheckBoxTests::toggle() { +void CheckBoxTests::toggle() +{ auto t = new QskCheckBox( root ); QVERIFY( t->isChecked() == false ); @@ -34,19 +40,21 @@ void CheckBoxTests::toggle() { QVERIFY( t->isChecked() == false ); } -void CheckBoxTests::triState() { +void CheckBoxTests::tristate() +{ auto t = new QskCheckBox( root ); QVERIFY( t->isChecked() == false ); - QVERIFY( t->isTriState() == false ); + QVERIFY( t->isTristate() == false ); t->setCheckState( Qt::CheckState::PartiallyChecked ); QVERIFY( t->isChecked() == true ); - QVERIFY( t->isTriState() == true ); + QVERIFY( t->isTristate() == true ); } -void CheckBoxTests::higherGroupUpdatesLower() { +void CheckBoxTests::higherGroupUpdatesLower() +{ auto t = new QskCheckBox( root ); auto t1 = new QskCheckBox( root ); auto t2 = new QskCheckBox( root ); @@ -74,7 +82,9 @@ void CheckBoxTests::higherGroupUpdatesLower() { QVERIFY( t3->isChecked() == false ); } -void CheckBoxTests::lowerGroupUpdatesHigher() { + +void CheckBoxTests::lowerGroupUpdatesHigher() +{ auto t = new QskCheckBox( root ); auto t1 = new QskCheckBox( root ); @@ -85,7 +95,7 @@ void CheckBoxTests::lowerGroupUpdatesHigher() { t1->setChecked( true ); QVERIFY( t->isChecked() ); - QVERIFY( t->isTriState() ); + QVERIFY( t->isTristate() ); QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); QVERIFY( t1->isChecked() == true ); QVERIFY( t2->isChecked() == false ); @@ -93,27 +103,28 @@ void CheckBoxTests::lowerGroupUpdatesHigher() { t2->setChecked( true ); QVERIFY( t->isChecked() ); - QVERIFY( t->isTriState() ); + QVERIFY( t->isTristate() ); QVERIFY( t->checkState() == Qt::CheckState::Checked ); QVERIFY( t1->isChecked() == true ); QVERIFY( t2->isChecked() == true ); t1->setChecked( false ); QVERIFY( t->isChecked() ); - QVERIFY( t->isTriState() ); + QVERIFY( t->isTristate() ); QVERIFY( t->checkState() == Qt::CheckState::PartiallyChecked ); QVERIFY( t1->isChecked() == false ); QVERIFY( t2->isChecked() == true ); t2->setChecked( false ); QVERIFY( t->isChecked() == false ); - QVERIFY( t->isTriState() ); + QVERIFY( t->isTristate() ); QVERIFY( t->checkState() == Qt::CheckState::Unchecked ); QVERIFY( t1->isChecked() == false ); QVERIFY( t2->isChecked() == false ); } -void CheckBoxTests::addToGroup() { +void CheckBoxTests::addToGroup() +{ auto t = new QskCheckBox( root ); auto t1 = new QskCheckBox( root ); @@ -157,8 +168,8 @@ void CheckBoxTests::addPartlyToGroup() { QVERIFY( t1->checkState() == Qt::CheckState::PartiallyChecked ); } - -void CheckBoxTests::removeFromGroup() { +void CheckBoxTests::removeFromGroup() +{ auto t = new QskCheckBox( root ); auto t1 = new QskCheckBox( root ); @@ -174,7 +185,8 @@ void CheckBoxTests::removeFromGroup() { QVERIFY( t->isChecked() == false ); } -void CheckBoxTests::groupMemberGetsDeleted() { +void CheckBoxTests::groupMemberGetsDeleted() +{ auto t = new QskCheckBox( root ); auto t1 = new QskCheckBox( root ); auto t2 = new QskCheckBox( root ); @@ -189,7 +201,8 @@ void CheckBoxTests::groupMemberGetsDeleted() { QVERIFY( t->isChecked() == false ); } -void CheckBoxTests::addTwiceToSameGroup() { +void CheckBoxTests::addTwiceToSameGroup() +{ auto t = new QskCheckBox( root ); auto t1 = new QskCheckBox( root ); @@ -205,5 +218,6 @@ void CheckBoxTests::addTwiceToSameGroup() { QVERIFY( t->checkState() == Qt::CheckState::Checked ); } + #include "moc_main.cpp" diff --git a/tests/checkboxes/main.h b/tests/checkboxes/main.h index 5f5b5e93..f657b6c8 100644 --- a/tests/checkboxes/main.h +++ b/tests/checkboxes/main.h @@ -16,7 +16,7 @@ class CheckBoxTests : public QObject void checkbox(); void click(); void toggle(); - void triState(); + void tristate(); void higherGroupUpdatesLower(); void lowerGroupUpdatesHigher(); void addToGroup(); From b06687662b850f2b970edf831bfa44c9e2887058 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 4 Apr 2022 17:45:49 +0200 Subject: [PATCH 8/8] custom sliders moved from gallery to tabview. we do not want to have custom controlsin the gallery, that is intended to show the default versions --- examples/gallery/gallery.pro | 6 -- examples/gallery/slider/SliderPage.cpp | 77 +++++-------------- .../slider => tabview}/CustomSlider.cpp | 0 .../slider => tabview}/CustomSlider.h | 0 .../CustomSliderSkinlet.cpp | 0 .../slider => tabview}/CustomSliderSkinlet.h | 0 .../slider => tabview}/OtherSlider.cpp | 0 .../{gallery/slider => tabview}/OtherSlider.h | 0 examples/tabview/main.cpp | 48 +++++++++++- examples/tabview/tabview.pro | 8 ++ 10 files changed, 74 insertions(+), 65 deletions(-) rename examples/{gallery/slider => tabview}/CustomSlider.cpp (100%) rename examples/{gallery/slider => tabview}/CustomSlider.h (100%) rename examples/{gallery/slider => tabview}/CustomSliderSkinlet.cpp (100%) rename examples/{gallery/slider => tabview}/CustomSliderSkinlet.h (100%) rename examples/{gallery/slider => tabview}/OtherSlider.cpp (100%) rename examples/{gallery/slider => tabview}/OtherSlider.h (100%) diff --git a/examples/gallery/gallery.pro b/examples/gallery/gallery.pro index 2c1eedb3..c21ad9a3 100644 --- a/examples/gallery/gallery.pro +++ b/examples/gallery/gallery.pro @@ -7,15 +7,9 @@ SOURCES += \ label/LabelPage.cpp \ HEADERS += \ - slider/CustomSlider.h \ - slider/CustomSliderSkinlet.h \ - slider/OtherSlider.h \ slider/SliderPage.h SOURCES += \ - slider/CustomSlider.cpp \ - slider/CustomSliderSkinlet.cpp \ - slider/OtherSlider.cpp \ slider/SliderPage.cpp HEADERS += \ diff --git a/examples/gallery/slider/SliderPage.cpp b/examples/gallery/slider/SliderPage.cpp index 8e164a5c..733692d6 100644 --- a/examples/gallery/slider/SliderPage.cpp +++ b/examples/gallery/slider/SliderPage.cpp @@ -4,14 +4,27 @@ *****************************************************************************/ #include "SliderPage.h" -#include "CustomSlider.h" -#include "OtherSlider.h" +#include -#include -#include +namespace +{ + class Slider : public QskSlider + { + public: + Slider( Qt::Orientation orientation, QQuickItem* parent = nullptr ) + : QskSlider( orientation, parent ) + { + setBoundaries( 0, 1000 ); -SliderPage::SliderPage( QQuickItem* parentItem ) - : Page( Qt::Vertical, parentItem ) + setPageSize( 10 ); + setStepSize( 10 ); + setSnap( true ); + } + }; +} + +SliderPage::SliderPage( QQuickItem* parent ) + : Page( Qt::Horizontal, parent ) { setMargins( 10 ); setSpacing( 20 ); @@ -35,54 +48,6 @@ SliderPage::SliderPage( QQuickItem* parentItem ) void SliderPage::populate() { - { - auto slider = new QskSlider( this ); - - slider->setMinimum( 0 ); - slider->setMaximum( 1000 ); - slider->setPageSize( 10 ); - slider->setStepSize( 10 ); - slider->setSnap( true ); - } - - { - auto slider = new OtherSlider( this ); - - slider->setMinimum( 0 ); - slider->setMaximum( 10 ); - slider->setStepSize( 1 ); - } - - - auto hBox = new QskLinearBox( Qt::Horizontal, this ); - - { - auto slider = new QskSlider( Qt::Vertical, hBox ); - - slider->setMinimum( 0 ); - slider->setMaximum( 1000 ); - slider->setPageSize( 10 ); - slider->setStepSize( 10 ); - slider->setSnap( true ); - } - - { - auto slider = new OtherSlider( hBox ); - slider->setOrientation( Qt::Vertical ); - - slider->setMinimum( 0 ); - slider->setMaximum( 10 ); - slider->setStepSize( 1 ); - } - - { - auto slider = new CustomSlider( this ); - - slider->setMargins( QskMargins( 0, 15 ) ); - slider->setSnap( true ); - slider->setMinimum( 0 ); - slider->setMaximum( 2000 ); - slider->setStepSize( 10 ); - slider->setPageSize( 10 ); - } + ( void ) new Slider( Qt::Horizontal, this ); + ( void ) new Slider( Qt::Vertical, this ); } diff --git a/examples/gallery/slider/CustomSlider.cpp b/examples/tabview/CustomSlider.cpp similarity index 100% rename from examples/gallery/slider/CustomSlider.cpp rename to examples/tabview/CustomSlider.cpp diff --git a/examples/gallery/slider/CustomSlider.h b/examples/tabview/CustomSlider.h similarity index 100% rename from examples/gallery/slider/CustomSlider.h rename to examples/tabview/CustomSlider.h diff --git a/examples/gallery/slider/CustomSliderSkinlet.cpp b/examples/tabview/CustomSliderSkinlet.cpp similarity index 100% rename from examples/gallery/slider/CustomSliderSkinlet.cpp rename to examples/tabview/CustomSliderSkinlet.cpp diff --git a/examples/gallery/slider/CustomSliderSkinlet.h b/examples/tabview/CustomSliderSkinlet.h similarity index 100% rename from examples/gallery/slider/CustomSliderSkinlet.h rename to examples/tabview/CustomSliderSkinlet.h diff --git a/examples/gallery/slider/OtherSlider.cpp b/examples/tabview/OtherSlider.cpp similarity index 100% rename from examples/gallery/slider/OtherSlider.cpp rename to examples/tabview/OtherSlider.cpp diff --git a/examples/gallery/slider/OtherSlider.h b/examples/tabview/OtherSlider.h similarity index 100% rename from examples/gallery/slider/OtherSlider.h rename to examples/tabview/OtherSlider.h diff --git a/examples/tabview/main.cpp b/examples/tabview/main.cpp index 5fbd43dc..9c8bbda2 100644 --- a/examples/tabview/main.cpp +++ b/examples/tabview/main.cpp @@ -3,6 +3,9 @@ * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ +#include "CustomSlider.h" +#include "OtherSlider.h" + #include #include @@ -33,6 +36,36 @@ class Label : public QskTextLabel } }; +class SliderBox : public QskLinearBox +{ + public: + SliderBox( QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Vertical, parent ) + { + setMargins( 30 ); + setSpacing( 50 ); + setExtraSpacingAt( Qt::BottomEdge ); + + { + auto slider = new OtherSlider( this ); + + slider->setMinimum( 0 ); + slider->setMaximum( 10 ); + slider->setStepSize( 1 ); + } + + { + auto slider = new CustomSlider( this ); + + slider->setSnap( true ); + slider->setMinimum( 0 ); + slider->setMaximum( 2000 ); + slider->setStepSize( 10 ); + slider->setPageSize( 10 ); + } + } +}; + class TabView : public QskTabView { public: @@ -43,11 +76,20 @@ class TabView : public QskTabView { QString text; if ( i == 4 ) - text = QString( "Another Tab" ); + { + const auto text = QStringLiteral( "Another Tab" ); + addTab( text, new Label( text ) ); + } + else if ( i == 7 ) + { + addTab( "Sliders", new SliderBox() ); + } else - text = QString( "Tab %1" ).arg( i + 1 ); + { + const auto text = QString( "Tab %1" ).arg( i + 1 ); + addTab( text, new Label( text ) ); + } - addTab( text, new Label( text ) ); } buttonAt( 2 )->setEnabled( false ); diff --git a/examples/tabview/tabview.pro b/examples/tabview/tabview.pro index 0a7f08c9..dbef219c 100644 --- a/examples/tabview/tabview.pro +++ b/examples/tabview/tabview.pro @@ -1,4 +1,12 @@ CONFIG += qskexample +HEADERS += \ + CustomSlider.h \ + CustomSliderSkinlet.h \ + OtherSlider.h \ + SOURCES += \ + CustomSlider.cpp \ + CustomSliderSkinlet.cpp \ + OtherSlider.cpp \ main.cpp