diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index 4c8433f2..a5f3e2c1 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -191,20 +191,24 @@ void Editor::setupCheckBox() using A = QskAspect; using Q = QskCheckBox; - const qreal size = qskDpiScaled( 18 ); + const qreal sizeTic = qskDpiScaled( 16 ); // - Border; + const qreal sizeBox = qskDpiScaled( 26 ); - setStrutSize( Q::Panel, size, size ); - setPadding( Q::Panel, 3 ); + setStrutSize( Q::IndicatorTic, sizeTic, sizeTic ); + setStrutSize( Q::IndicatorBox, sizeBox, sizeBox); - setBoxShape( Q::Panel, 2 ); + setMargin( Q::Text, QskMargins( qskDpiScaled( 5 ), 0,qskDpiScaled( 5 ), 0 ) ); + setMargin( Q::IndicatorTic, QskMargins( qskDpiScaled( 5 ) ) ); - setGradient( Q::Panel, m_pal.baseColor); - setGradient( Q::Panel | Q::Checked, m_pal.accentColor ); - setGradient( Q::Panel | Q::Checked | Q::Disabled, QskRgb::Grey ); + setBoxShape( Q::IndicatorBox, qskDpiScaled( 3 ) ); + setBoxBorderMetrics( Q::IndicatorBox, qskDpiScaled( 1 ) ); + setBoxBorderColors( Q::IndicatorBox, m_pal.darker125 ); - setColor( Q::Indicator, m_pal.contrastColor ); + setGradient( Q::IndicatorBox | Q::Checked, m_pal.accentColor); + setColor( Q::IndicatorTic, m_pal.lighter200 ); + setGradient( Q::Panel, m_pal.baseColor ); - setAnimation( Q::Panel | A::Color, qskDuration ); + setAnimation( Q::IndicatorBox | A::Color, qskDuration ); } void Editor::setupBox() diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 5a729df0..e029fe95 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -303,21 +303,27 @@ void Editor::setupCheckBox() using A = QskAspect; using Q = QskCheckBox; - const qreal size = qskDpiScaled( 26 ); + const qreal sizeTic = qskDpiScaled( 24 ); // - Border; + const qreal sizeBox = qskDpiScaled( 26 ); - setStrutSize( Q::Panel, size, size ); + setStrutSize( Q::IndicatorTic, sizeTic, sizeTic ); + setStrutSize( Q::IndicatorBox, sizeBox, sizeBox); - setPadding( Q::Panel, qskDpiScaled( 5 ) ); - setBoxShape( Q::Panel, qskDpiScaled( 3 ) ); - setBoxBorderMetrics( Q::Panel, qskDpiScaled( 1 ) ); + setMargin( Q::Text, QskMargins(qskDpiScaled(5),0,qskDpiScaled(5),0) ); + setMargin( Q::IndicatorTic, QskMargins( qskDpiScaled( 1 ) ) ); - setBoxBorderColors( Q::Panel, m_pal.darker125 ); - setGradient( Q::Panel, m_pal.lighter135 ); - setGradient( Q::Panel | Q::Checked, m_pal.highlighted ); + setBoxShape( Q::IndicatorBox, qskDpiScaled( 3 ) ); + setBoxBorderMetrics( Q::IndicatorBox, qskDpiScaled( 1 ) ); + setBoxBorderColors( Q::IndicatorBox, m_pal.darker125 ); - setColor( Q::Indicator, m_pal.lighter135 ); + setGradient( Q::IndicatorBox, m_pal.lighter135 ); + setGradient( Q::IndicatorBox | Q::Checked, m_pal.highlighted ); - setAnimation( Q::Panel | A::Color, qskDuration ); + setGradient( Q::IndicatorTic, m_pal.lighter135 ); + + setGradient( Q::Panel, m_pal.base ); + + setAnimation( Q::IndicatorBox | A::Color, qskDuration ); } void Editor::setupPopup() diff --git a/src/controls/QskCheckBox.cpp b/src/controls/QskCheckBox.cpp index b7f377b3..34debffe 100644 --- a/src/controls/QskCheckBox.cpp +++ b/src/controls/QskCheckBox.cpp @@ -6,11 +6,21 @@ #include "QskCheckBox.h" #include "QskAspect.h" +#include "QskTextOptions.h" + QSK_SUBCONTROL( QskCheckBox, Panel ) -QSK_SUBCONTROL( QskCheckBox, Indicator ) +QSK_SUBCONTROL( QskCheckBox, IndicatorBox ) +QSK_SUBCONTROL( QskCheckBox, IndicatorTic ) +QSK_SUBCONTROL( QskCheckBox, Text ) + +class QskCheckBox::PrivateData { + public: + QString text; + QskTextOptions textOptions; +}; QskCheckBox::QskCheckBox( QQuickItem* parent ) - : Inherited( parent ) + : Inherited( parent ), m_data( new PrivateData ) { setAcceptHoverEvents( true ); initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); @@ -25,4 +35,34 @@ bool QskCheckBox::isCheckable() const return true; } +QString QskCheckBox::text() const { + return m_data->text; +} + +QskTextOptions QskCheckBox::textOptions() const { + return m_data->textOptions; +} + +void QskCheckBox::setText( const QString& text ) { + if( m_data->text == text ) + { + return; + } + + m_data->text = text; + + Q_EMIT textChanged( text ); +} + +void QskCheckBox::setTextOptions( const QskTextOptions& textOptions) { + if( m_data->textOptions == textOptions ) + { + return; + } + + m_data->textOptions = textOptions; + + Q_EMIT textOptionsChanged( textOptions ); +} + #include "moc_QskCheckBox.cpp" diff --git a/src/controls/QskCheckBox.h b/src/controls/QskCheckBox.h index ce213fa0..d9623b55 100644 --- a/src/controls/QskCheckBox.h +++ b/src/controls/QskCheckBox.h @@ -8,19 +8,42 @@ #include "QskAbstractButton.h" +class QskTextOptions; + class QSK_EXPORT QskCheckBox : public QskAbstractButton { Q_OBJECT + Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged ) + Q_PROPERTY( QskTextOptions textOptions READ textOptions + WRITE setTextOptions NOTIFY textOptionsChanged ) + + using Inherited = QskAbstractButton; public: - QSK_SUBCONTROLS( Panel, Indicator ) + QSK_SUBCONTROLS( Panel, IndicatorBox, IndicatorTic, Text) QskCheckBox( QQuickItem* parent = nullptr ); ~QskCheckBox() override; bool isCheckable() const override final; + + QString text() const; + QskTextOptions textOptions() const; + + Q_SIGNALS: + void textChanged( const QString& ); + void textOptionsChanged( const QskTextOptions& ); + + public Q_SLOTS: + void setText( const QString& ); + void setTextOptions( const QskTextOptions& ); + + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; }; #endif diff --git a/src/controls/QskCheckBoxSkinlet.cpp b/src/controls/QskCheckBoxSkinlet.cpp index 1cca8f86..a3dc4bee 100644 --- a/src/controls/QskCheckBoxSkinlet.cpp +++ b/src/controls/QskCheckBoxSkinlet.cpp @@ -6,10 +6,14 @@ #include "QskCheckBoxSkinlet.h" #include "QskCheckBox.h" #include "QskSGNode.h" +#include "QskTextOptions.h" +#include #include #include +#include + namespace { class IndicatorNode : public QSGGeometryNode @@ -62,7 +66,7 @@ namespace QskCheckBoxSkinlet::QskCheckBoxSkinlet( QskSkin* skin ) : QskSkinlet( skin ) { - setNodeRoles( { PanelRole, IndicatorRole } ); + setNodeRoles( { PanelRole, TextRole, IndicatorBoxRole, IndicatorRole } ); } QskCheckBoxSkinlet::~QskCheckBoxSkinlet() @@ -72,10 +76,26 @@ QskCheckBoxSkinlet::~QskCheckBoxSkinlet() QRectF QskCheckBoxSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - if ( subControl == QskCheckBox::Indicator ) - return skinnable->innerBox( QskCheckBox::Panel, contentsRect ); + const auto checkBox = static_cast< const QskCheckBox* >( skinnable ); - return contentsRect; + if ( subControl == QskCheckBox::Panel) + { + return contentsRect; + } + else if ( subControl == QskCheckBox::Text) + { + return textRect(checkBox, contentsRect ); + } + else if ( subControl == QskCheckBox::IndicatorTic ) + { + return indicatorRect(checkBox, contentsRect ); + } + else if ( subControl == QskCheckBox::IndicatorBox ) + { + return indicatorBoxRect(checkBox, contentsRect ); + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); } QSGNode* QskCheckBoxSkinlet::updateSubNode( @@ -88,6 +108,12 @@ QSGNode* QskCheckBoxSkinlet::updateSubNode( case PanelRole: return updateBoxNode( skinnable, node, QskCheckBox::Panel ); + case TextRole: + return updateTextNode( checkBox, node ); + + case IndicatorBoxRole: + return updateBoxNode( skinnable, node, QskCheckBox::IndicatorBox ); + case IndicatorRole: return updateIndicatorNode( checkBox, node ); } @@ -95,6 +121,64 @@ QSGNode* QskCheckBoxSkinlet::updateSubNode( return Inherited::updateSubNode( skinnable, nodeRole, node ); } +QRectF QskCheckBoxSkinlet::textRect( const QskCheckBox* checkBox, + const QRectF& contentsRect ) const { + using Q = QskCheckBox; + + QSizeF size; + const QFontMetricsF fm( checkBox->effectiveFont(Q::Text) ); + if ( !checkBox->text().isEmpty() ) + { + size += fm.size( Qt::TextShowMnemonic, checkBox->text() ); + } + + auto result = contentsRect; + + result.setTopLeft( QPointF(result.right() - size.width() - + checkBox->marginHint( Q::Text ).right(), + result.y() + ( result.height() - size.height() ) / 2) ); + + return result; +} + +QRectF QskCheckBoxSkinlet::indicatorRect( const QskCheckBox* checkBox, + const QRectF& contentsRect) const { + using Q = QskCheckBox; + + auto rect = contentsRect; + auto size = + checkBox->strutSizeHint( Q::IndicatorTic ).grownBy(checkBox->marginHint( Q::IndicatorTic ) ); + rect.setSize(size); + + return rect; +} + +QRectF QskCheckBoxSkinlet::indicatorBoxRect( const QskCheckBox* checkBox, + const QRectF& contentsRect ) const { + using Q = QskCheckBox; + + auto rect = contentsRect; + auto size = + checkBox->strutSizeHint( Q::IndicatorBox ).grownBy(checkBox->marginHint(Q::IndicatorBox) ); + rect.setSize(size); + + return rect; +} + +QSGNode* QskCheckBoxSkinlet::updateTextNode( + const QskCheckBox* checkBox, QSGNode* node ) const +{ + using Q = QskCheckBox; + const auto rect = checkBox->subControlRect( Q::Text ); + + const auto textHeight = checkBox->effectiveFontHeight( Q::Text ); + if ( !checkBox->clip() && ( rect.height() < textHeight ) ) + return nullptr; + + return QskSkinlet::updateTextNode( checkBox, node, + checkBox->text(), checkBox->textOptions(), Q::Text ); +} + QSGNode* QskCheckBoxSkinlet::updateIndicatorNode( const QskCheckBox* checkBox, QSGNode* node ) const { @@ -103,12 +187,14 @@ QSGNode* QskCheckBoxSkinlet::updateIndicatorNode( if ( checkBox->isChecked() == false ) return nullptr; - const auto rect = checkBox->subControlRect( Q::Indicator ); + auto rect = checkBox->subControlRect(Q::IndicatorTic) + .marginsRemoved(checkBox->marginHint(Q::IndicatorTic) ); + if ( rect.isEmpty() ) return nullptr; auto indicatorNode = QskSGNode::ensureNode< IndicatorNode >( node ); - indicatorNode->update( rect, checkBox->color( Q::Indicator ) ); + indicatorNode->update( rect, checkBox->color( Q::IndicatorTic ) ); return indicatorNode; } @@ -116,7 +202,31 @@ QSGNode* QskCheckBoxSkinlet::updateIndicatorNode( QSizeF QskCheckBoxSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint, const QSizeF& ) const { - return skinnable->strutSizeHint( QskCheckBox::Panel ); + using Q = QskCheckBox; + const auto checkBox = static_cast< const QskCheckBox* >( skinnable ); + + + const QFontMetricsF fm( checkBox->effectiveFont(Q::Text) ); + QSizeF sizeText; + if ( !checkBox->text().isEmpty() ) + { + sizeText += fm.size( Qt::TextShowMnemonic, checkBox->text() ) + .grownBy(checkBox->marginHint(Q::Text) ); + } + + auto sizeIndicator = + checkBox->strutSizeHint(Q::IndicatorTic).grownBy(checkBox->marginHint(Q::IndicatorTic) ); + auto sizeIndicatorBox = + checkBox->strutSizeHint(Q::IndicatorBox).grownBy(checkBox->marginHint(Q::IndicatorBox) ); + + QSizeF size; + size.setWidth( sizeText.width() + + qMax( sizeIndicator.width(), sizeIndicatorBox.width() ) ); + + size.setHeight(std::max({ sizeIndicator.height(), + sizeIndicatorBox.height(), + sizeText.height() }) ); + return size; } #include "moc_QskCheckBoxSkinlet.cpp" diff --git a/src/controls/QskCheckBoxSkinlet.h b/src/controls/QskCheckBoxSkinlet.h index 9164deb9..a5935752 100644 --- a/src/controls/QskCheckBoxSkinlet.h +++ b/src/controls/QskCheckBoxSkinlet.h @@ -20,6 +20,8 @@ class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet enum NodeRole { PanelRole, + TextRole, + IndicatorBoxRole, IndicatorRole, RoleCount @@ -38,8 +40,14 @@ class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; - protected: + virtual QSGNode* updateTextNode( const QskCheckBox*, QSGNode* ) const; virtual QSGNode* updateIndicatorNode( const QskCheckBox*, QSGNode* ) const; + + private: + QRectF textRect( const QskCheckBox*, const QRectF& ) const; + QRectF indicatorRect( const QskCheckBox*, const QRectF& ) const; + QRectF indicatorBoxRect( const QskCheckBox*, const QRectF& ) const; + }; #endif