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