From bba5ec1fa88a1dcce9ab44299d9e235e0b4ee938 Mon Sep 17 00:00:00 2001 From: Clemens Manert Date: Mon, 18 Apr 2022 12:36:17 +0200 Subject: [PATCH] Add QskTristateCheckBox --- skins/material/QskMaterialSkin.cpp | 1 + skins/squiek/QskSquiekSkin.cpp | 1 + src/controls/QskSkin.cpp | 4 + src/controls/QskTristateCheckBox.cpp | 66 +++++++++++++ src/controls/QskTristateCheckBox.h | 38 ++++++++ src/controls/QskTristateCheckBoxSkinlet.cpp | 103 ++++++++++++++++++++ src/controls/QskTristateCheckBoxSkinlet.h | 22 +++++ src/src.pro | 4 + 8 files changed, 239 insertions(+) create mode 100644 src/controls/QskTristateCheckBox.cpp create mode 100644 src/controls/QskTristateCheckBox.h create mode 100644 src/controls/QskTristateCheckBoxSkinlet.cpp create mode 100644 src/controls/QskTristateCheckBoxSkinlet.h diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index a5f3e2c1..2efe3ae6 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index e029fe95..05810f7c 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index 77f59bb7..c466740d 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -32,6 +32,9 @@ QSK_QT_PRIVATE_END #include "QskCheckBox.h" #include "QskCheckBoxSkinlet.h" +#include "QskTristateCheckBox.h" +#include "QskTristateCheckBoxSkinlet.h" + #include "QskFocusIndicator.h" #include "QskFocusIndicatorSkinlet.h" @@ -150,6 +153,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskBox, QskBoxSkinlet >(); declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >(); + declareSkinlet< QskTristateCheckBox, QskTristateCheckBoxSkinlet >(); declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >(); declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >(); declareSkinlet< QskListView, QskListViewSkinlet >(); diff --git a/src/controls/QskTristateCheckBox.cpp b/src/controls/QskTristateCheckBox.cpp new file mode 100644 index 00000000..d489e316 --- /dev/null +++ b/src/controls/QskTristateCheckBox.cpp @@ -0,0 +1,66 @@ +#include "QskTristateCheckBox.h" +#include "QskAspect.h" + +#include + +QSK_SYSTEM_STATE( QskTristateCheckBox, PartiallyChecked, QskAspect::LastUserState << 2 ) + +class QskTristateCheckBox::PrivateData +{ + public: + PrivateData() + : checkState( Qt::Unchecked ) + , checkStateChanging( false ) + { + } + Qt::CheckState checkState : 2; + bool checkStateChanging : 1; +}; + +QskTristateCheckBox::QskTristateCheckBox( QQuickItem* parent ) + : Inherited( parent ) + , m_data( new PrivateData() ) +{ + setAcceptHoverEvents( true ); + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + + connect( this, &QskAbstractButton::checkedChanged, this, + [ this ]( bool on ) { setCheckStateInternal( on ? Qt::Checked : Qt::Unchecked ); } ); +} + +QskTristateCheckBox::~QskTristateCheckBox() +{ +} + +Qt::CheckState QskTristateCheckBox::checkState() const +{ + return m_data->checkState; +} + +void QskTristateCheckBox::setCheckStateInternal( Qt::CheckState checkState ) +{ + if( m_data->checkStateChanging ) + return; + + setSkinStateFlag( PartiallyChecked, checkState == Qt::PartiallyChecked ); + + m_data->checkState = checkState; + Q_EMIT checkStateChanged( checkState ); +} + +void QskTristateCheckBox::setCheckState( Qt::CheckState checkState ) +{ + if( checkState == m_data->checkState ) + return; + + m_data->checkStateChanging = true; + + setChecked( checkState == Qt::PartiallyChecked + || checkState == Qt::Checked ); + + m_data->checkStateChanging = false; + + setCheckStateInternal( checkState ); +} + +#include "moc_QskTristateCheckBox.cpp" diff --git a/src/controls/QskTristateCheckBox.h b/src/controls/QskTristateCheckBox.h new file mode 100644 index 00000000..f99d3ee2 --- /dev/null +++ b/src/controls/QskTristateCheckBox.h @@ -0,0 +1,38 @@ +#ifndef QSK_TRISTATE_CHECK_BOX_H +#define QSK_TRISTATE_CHECK_BOX_H + +#include "QskCheckBox.h" + +class QSK_EXPORT QskTristateCheckBox : public QskCheckBox +{ + Q_OBJECT + + Q_PROPERTY( Qt::CheckState checkState READ checkState + WRITE setCheckState NOTIFY checkStateChanged FINAL ) + + using Inherited = QskCheckBox; + + public: + QSK_STATES( PartiallyChecked ) + + QskTristateCheckBox( QQuickItem* parent = nullptr ); + ~QskTristateCheckBox() override; + + Qt::CheckState checkState() const; + + public Q_SLOTS: + void setCheckState( Qt::CheckState ); + + Q_SIGNALS: + void checkStateChanged( Qt::CheckState ); + void tristateChanged( bool ); + + private: + void setCheckStateInternal( Qt::CheckState ); + void updated(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskTristateCheckBoxSkinlet.cpp b/src/controls/QskTristateCheckBoxSkinlet.cpp new file mode 100644 index 00000000..5c4ac5cd --- /dev/null +++ b/src/controls/QskTristateCheckBoxSkinlet.cpp @@ -0,0 +1,103 @@ +#include "QskTristateCheckBoxSkinlet.h" +#include "QskTristateCheckBox.h" +#include "QskSGNode.h" + +#include +#include + +namespace +{ + class IndicatorNode : public QSGGeometryNode + { + public: + IndicatorNode() + : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 3 ) + { + m_geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); + m_geometry.setLineWidth( 2 ); + setGeometry( &m_geometry ); + + setMaterial( &m_material ); + } + + void update( bool isPartially, const QRectF& rect, const QColor& color ) + { + if ( color != m_material.color() ) + { + m_material.setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } + + if ( rect != m_rect || isPartially != m_isPartially ) + { + m_rect = rect; + m_isPartially = isPartially; + + const auto x = rect.x(); + const auto y = rect.y(); + const auto w = rect.width(); + const auto h = rect.height(); + + auto points = m_geometry.vertexDataAsPoint2D(); + + if ( isPartially ) + { + points[0].set( x, y + h / 2 ); + points[1] = points[0]; + points[2].set( x + w, y + h / 2 ); + } + else + { + points[0].set( x, y + h / 2 ); + points[1].set( x + w / 3, y + h ); + points[2].set( x + w, y ); + } + + markDirty( QSGNode::DirtyGeometry ); + } + } + + private: + + QSGFlatColorMaterial m_material; + QSGGeometry m_geometry; + + QRectF m_rect; + bool m_isPartially; + }; +} + +QskTristateCheckBoxSkinlet::QskTristateCheckBoxSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ +} + +QskTristateCheckBoxSkinlet::~QskTristateCheckBoxSkinlet() +{ +} + +QSGNode* QskTristateCheckBoxSkinlet::updateIndicatorNode( + const QskCheckBox* checkBox, QSGNode* node ) const +{ + using Q = QskCheckBox; + + const auto tristate = qobject_cast< const QskTristateCheckBox* >( checkBox ); + + if ( checkBox->isChecked() == false ) + return nullptr; + + auto rect = checkBox->subControlRect(Q::IndicatorTic) + .marginsRemoved(checkBox->marginHint(Q::IndicatorTic) ); + + if ( rect.isEmpty() ) + return nullptr; + + auto indicatorNode = QskSGNode::ensureNode< IndicatorNode >( node ); + indicatorNode->update( + tristate->checkState() == Qt::CheckState::PartiallyChecked, rect, + checkBox->color( Q::IndicatorTic ) ); + + return indicatorNode; +} + +#include "moc_QskTristateCheckBoxSkinlet.cpp" diff --git a/src/controls/QskTristateCheckBoxSkinlet.h b/src/controls/QskTristateCheckBoxSkinlet.h new file mode 100644 index 00000000..b7b9b46d --- /dev/null +++ b/src/controls/QskTristateCheckBoxSkinlet.h @@ -0,0 +1,22 @@ +#ifndef QSK_TRISTATE_CHECK_BOX_SKINLET_H +#define QSK_TRISTATE_CHECK_BOX_SKINLET_H + +#include "QskCheckBoxSkinlet.h" + +class QskCheckBox; + +class QSK_EXPORT QskTristateCheckBoxSkinlet : public QskCheckBoxSkinlet +{ + Q_GADGET + + using Inherited = QskCheckBoxSkinlet; + + public: + Q_INVOKABLE QskTristateCheckBoxSkinlet( QskSkin* = nullptr ); + ~QskTristateCheckBoxSkinlet() override; + + protected: + QSGNode* updateIndicatorNode( const QskCheckBox*, QSGNode* ) const override; +}; + +#endif diff --git a/src/src.pro b/src/src.pro index 366590c0..9254cd34 100644 --- a/src/src.pro +++ b/src/src.pro @@ -145,6 +145,8 @@ HEADERS += \ controls/QskBoxSkinlet.h \ controls/QskCheckBox.h \ controls/QskCheckBoxSkinlet.h \ + controls/QskTristateCheckBox.h \ + controls/QskTristateCheckBoxSkinlet.h \ controls/QskControl.h \ controls/QskControlPrivate.h \ controls/QskDirtyItemFilter.h \ @@ -229,6 +231,8 @@ SOURCES += \ controls/QskBoxSkinlet.cpp \ controls/QskCheckBox.cpp \ controls/QskCheckBoxSkinlet.cpp \ + controls/QskTristateCheckBox.cpp \ + controls/QskTristateCheckBoxSkinlet.cpp \ controls/QskControl.cpp \ controls/QskControlPrivate.cpp \ controls/QskDirtyItemFilter.cpp \