From a05d1e34710a5d486f5bfd824409d0bd78b18bfd Mon Sep 17 00:00:00 2001 From: Clemens Manert Date: Mon, 4 Apr 2022 08:53:20 +0200 Subject: [PATCH] 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 +