diff --git a/src/controls/QskHirachicalGroup.cpp b/src/controls/QskHirachicalGroup.cpp new file mode 100644 index 00000000..c85590e6 --- /dev/null +++ b/src/controls/QskHirachicalGroup.cpp @@ -0,0 +1,162 @@ +#include "QskHirachicalGroup.h" + +#include "QskTristateCheckBox.h" +#include "QskCheckBox.h" + +#include + +#include + +class QskHirachicalGroup::PrivateData +{ + public: + QskTristateCheckBox* head; + int checked = 0; + QSet< QskCheckBox* > group; + bool headChanging = false; +}; + +QskHirachicalGroup::QskHirachicalGroup( QObject* parent ) + : Inherited( parent ) + , m_data( new PrivateData() ) +{ +} + +QskHirachicalGroup::QskHirachicalGroup( QskTristateCheckBox* head ) + : Inherited( head ) + , m_data( new PrivateData() ) +{ + setHead(head); +} + +QskHirachicalGroup::~QskHirachicalGroup() +{ +} + +void QskHirachicalGroup::setHead( QskTristateCheckBox* head ) +{ + if( m_data->head == head ) + { + return; + } + + m_data->head = head; + + if( head == nullptr ) + { + return; + } + + connect( head, &QskTristateCheckBox::checkStateChanged, head, + [ this ]( Qt::CheckState checkedState ) + { + if( checkedState == Qt::PartiallyChecked ) + { + return; + } + + m_data->headChanging = true; + for(auto child : qAsConst(m_data->group) ) + { + child->setChecked(checkedState == Qt::Checked); + } + m_data->headChanging = false; + + if(checkedState == Qt::Checked) + { + m_data->checked = m_data->group.size(); + } + else + { + m_data->checked = 0; + } + }); + + Q_EMIT headChanged( head ); +} + +QskTristateCheckBox* QskHirachicalGroup::head() const +{ + return m_data->head; +} + +void QskHirachicalGroup::add( QskCheckBox* newMember ) +{ + if( m_data->group.contains( newMember ) ) + { + return; + } + + connect( newMember, &QObject::destroyed, this, [this, newMember ]( QObject* ) + { + remove( newMember ); + }); + + if( m_data->head != nullptr ) + { + connect( newMember, &QskAbstractButton::toggled, + m_data->head, [ this ]( bool toggled ) + { + if( m_data->headChanging == true ) + { + return; + } + + if( toggled ) + { + ++m_data->checked; + } + else + { + --m_data->checked; + } + + update(); + }); + } + + m_data->group.insert( newMember ); + + Q_EMIT added( newMember ); +} + +bool QskHirachicalGroup::remove( QskCheckBox* removal ) +{ + if( !m_data->group.contains( removal ) ) + { + return false; + } + + m_data->group.remove( removal ); + + // We need to count again, since if removal is about to be deletet its m_data is + // already deleted thus we can not check its CheckState. + m_data->checked = std::count_if( m_data->group.begin(), m_data->group.end(), + []( QskCheckBox* box ) { return box->isChecked(); } ); + update(); + + Q_EMIT removed( removal ); + return true; +} + +void QskHirachicalGroup::update() { + if( m_data->head == nullptr ) + { + return; + } + + if( m_data->group.size() == m_data->checked ) + { + m_data->head->setCheckState( Qt::Checked ); + return; + } + + if( m_data->checked == 0 ) + { + m_data->head->setCheckState( Qt::Unchecked ); + return; + } + + m_data->head->setCheckState( Qt::CheckState::PartiallyChecked ); +} +#include "moc_QskHirachicalGroup.cpp" diff --git a/src/controls/QskHirachicalGroup.h b/src/controls/QskHirachicalGroup.h new file mode 100644 index 00000000..80648ebc --- /dev/null +++ b/src/controls/QskHirachicalGroup.h @@ -0,0 +1,42 @@ +#ifndef QSK_HIRACHICAL_GROUP +#define QSK_HIRACHICAL_GROUP + +#include +#include + +#include "QskGlobal.h" + +class QskTristateCheckBox; +class QskCheckBox; + +class QSK_EXPORT QskHirachicalGroup : public QObject +{ + Q_OBJECT + + using Inherited = QObject; + + public: + QskHirachicalGroup( QObject* parent = nullptr ); + QskHirachicalGroup( QskTristateCheckBox* head ); + ~QskHirachicalGroup() override; + + QskTristateCheckBox* head() const; + + public Q_SLOTS: + void setHead( QskTristateCheckBox* head ); + void add( QskCheckBox* newMember ); + bool remove( QskCheckBox* removal ); + + Q_SIGNALS: + void headChanged( QskTristateCheckBox* newHead ); + void added( QskCheckBox* added ); + void removed( QskCheckBox* removed ); + + private: + void update(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/src.pro b/src/src.pro index 9254cd34..c4905748 100644 --- a/src/src.pro +++ b/src/src.pro @@ -159,6 +159,7 @@ HEADERS += \ controls/QskGraphicLabel.h \ controls/QskGraphicLabelSkinlet.h \ controls/QskHintAnimator.h \ + controls/QskHirachicalGroup.h \ controls/QskInputGrabber.h \ controls/QskListView.h \ controls/QskListViewSkinlet.h \ @@ -245,6 +246,7 @@ SOURCES += \ controls/QskGraphicLabel.cpp \ controls/QskGraphicLabelSkinlet.cpp \ controls/QskHintAnimator.cpp \ + controls/QskHirachicalGroup.cpp \ controls/QskInputGrabber.cpp \ controls/QskListView.cpp \ controls/QskListViewSkinlet.cpp \