From d403af14ba9a696ba060fa4cbe02f0efc82fb8d3 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Mon, 27 Jun 2022 12:54:13 +0200 Subject: [PATCH 1/5] making QskHctColor a class, tone calculation from rgb added --- examples/boxes/Box.cpp | 26 +++---- .../gallery/progressbar/ProgressBarPage.cpp | 11 ++- src/common/QskHctColor.cpp | 69 +++++++++++++++- src/common/QskHctColor.h | 78 ++++++++++++++----- 4 files changed, 142 insertions(+), 42 deletions(-) diff --git a/examples/boxes/Box.cpp b/examples/boxes/Box.cpp index defdc82c..dbad97ac 100644 --- a/examples/boxes/Box.cpp +++ b/examples/boxes/Box.cpp @@ -63,17 +63,16 @@ void Box::setBackground( FillType type, const QRgb base, bool inverted ) return; } - double hue, chroma; - QskHctColor::getHueAndChroma( base, hue, chroma ); + const QskHctColor htcColor( base ); if ( type == Solid ) { - setGradient( QskHctColor::rgb( hue, chroma, 50 ) ); + setGradient( htcColor.toned( 50 ).rgb() ); } else { - const auto dark = QskHctColor::rgb( hue, chroma, 40 ); - const auto light = QskHctColor::rgb( hue, chroma, 70 ); + const auto dark = htcColor.toned( 40 ).rgb(); + const auto light = htcColor.toned( 70 ).rgb(); const auto orientation = static_cast< QskGradient::Orientation >( type - 2 ); @@ -100,12 +99,11 @@ void Box::setBorder( BorderType type, const QRgb base ) return; } - double hue, chroma; - QskHctColor::getHueAndChroma( base, hue, chroma ); + const QskHctColor htcColor( base ); - const auto dark = QskHctColor::rgb( hue, chroma, 40 ); - const auto mid = QskHctColor::rgb( hue, chroma, 65 ); - const auto light = QskHctColor::rgb( hue, chroma, 90 ); + const auto dark = htcColor.toned( 40 ).rgb(); + const auto mid = htcColor.toned( 65 ).rgb(); + const auto light = htcColor.toned( 90 ).rgb(); switch ( static_cast< int >( type ) ) { @@ -206,17 +204,13 @@ void Box::setGradient( const QskGradient& gradient ) void Box::setGradient( const QskGradient::Orientation orientation, const QRgb base ) { - double hue, chroma; - QskHctColor::getHueAndChroma( base, hue, chroma ); + const QskHctColor hctColor( base ); QVector< QRgb > rgb; rgb.reserve( 10 ); for ( int i = 0; i < 10; i++ ) - { - const auto tone = 90 - i * 7; - rgb += QskHctColor::rgb( hue, chroma, tone ); - } + rgb += hctColor.toned( 90 - i * 7 ).rgb(); setGradient( QskGradient( orientation, QskGradient::colorStops( rgb, true ) ) ); } diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index 5bd7a1d7..e3e0146b 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -27,14 +27,13 @@ namespace void setTheme( const QRgb base ) { - double hue, chroma; - QskHctColor::getHueAndChroma( base, hue, chroma ); + const QskHctColor hctColor( base ); QVector< QRgb > rgb; - rgb += QskHctColor::rgb( hue, chroma, 75 ); - rgb += QskHctColor::rgb( hue, chroma, 60 ); - rgb += QskHctColor::rgb( hue, chroma, 45 ); - rgb += QskHctColor::rgb( hue, chroma, 30 ); + rgb += hctColor.toned( 75 ).rgb(); + rgb += hctColor.toned( 60 ).rgb(); + rgb += hctColor.toned( 45 ).rgb(); + rgb += hctColor.toned( 30 ).rgb(); const auto stops = QskGradient::colorStops( rgb, true ); diff --git a/src/common/QskHctColor.cpp b/src/common/QskHctColor.cpp index 61a51f55..78adff96 100644 --- a/src/common/QskHctColor.cpp +++ b/src/common/QskHctColor.cpp @@ -349,6 +349,21 @@ static inline int delinearized( double rgbComponent ) return qBound( 0, qRound( v * 255 ), 255 ); } +static inline double labF( double t ) +{ + constexpr double e = 216.0 / 24389.0; + + if ( t > e ) + { + return pow( t, 1.0 / 3.0 ); + } + else + { + constexpr double kappa = 24389.0 / 27.0; + return ( kappa * t + 16 ) / 116; + } +} + static inline double labInvf( double ft ) { const double e = 216.0 / 24389.0; @@ -732,7 +747,7 @@ static QRgb findResultByJ( double hueRadians, double chroma, double y ) return 0; } -QRgb QskHctColor::rgb( double hue, double chroma, double tone ) +static inline QRgb getRgb( double hue, double chroma, double tone ) { if ( chroma < 0.0001 || tone < 0.0001 || tone > 99.9999 ) return argbFromLstar( tone ); @@ -778,7 +793,7 @@ static XYZ xyzFromArgb( QRgb rgb) return matrixMultiply( xyz, SRGB_TO_XYZ ); } -void QskHctColor::getHueAndChroma( QRgb rgb, double& hue, double& chroma ) +static void getHTC( QRgb rgb, double& hue, double& chroma, double& tone ) { ViewingConditions vc; @@ -832,4 +847,54 @@ void QskHctColor::getHueAndChroma( QRgb rgb, double& hue, double& chroma ) chroma = alpha * sqrt(J / 100.0); } + + { + tone = 116.0 * labF( y / 100.0 ) - 16.0; + } } + +QskHctColor::QskHctColor( QRgb rgb ) +{ + getHTC( rgb, m_hue, m_chroma, m_tone ); +} + +void QskHctColor::setHue( qreal hue ) +{ + m_hue = fmod( hue, 360.0 ); + if ( m_hue < 0.0 ) + m_hue += 360.0; +} + +void QskHctColor::setChroma( qreal chroma ) noexcept +{ + m_chroma = ( chroma < 0.0 ) ? 0.0 : chroma; +} + +void QskHctColor::setTone( qreal tone ) noexcept +{ + m_tone = qBound( 0.0, tone, 100.0 ); +} + +void QskHctColor::setRgb( QRgb rgb ) +{ + getHTC( rgb, m_hue, m_chroma, m_tone ); +} + +QRgb QskHctColor::rgb() const +{ + return getRgb( m_hue, m_chroma, m_tone ); +} + +#ifndef QT_NO_DEBUG_STREAM + +#include + +QDebug operator<<( QDebug debug, const QskHctColor& color ) +{ + debug.nospace() << "HTC(" + << color.hue() << "," << color.chroma() << "," << color.tone() << ")"; + + return debug.space(); +} + +#endif diff --git a/src/common/QskHctColor.h b/src/common/QskHctColor.h index d8782e12..041c0e4e 100644 --- a/src/common/QskHctColor.h +++ b/src/common/QskHctColor.h @@ -20,26 +20,68 @@ https://material-foundation.github.io/material-theme-builder/#/custom shows how to create a tonal palette from a given RGB color. + */ - The methods in QskHctColor allow to do the same: - - QVector palette( const QRgb rgb ) - { - double hue, chroma; - QskHctColor::getHueAndChroma( rgb, hue, chroma ); - - QVector< QRgb > pal; - for ( int tone = 0; tone <= 100; tone += 10 ) - pal += QskHctColor::rgb( hue, chroma, tone ); - - return pal; - } -*/ - -namespace QskHctColor +class QSK_EXPORT QskHctColor +{ + public: + constexpr QskHctColor() noexcept = default; + constexpr QskHctColor( qreal hue, qreal chrome, qreal tone = 40 ) noexcept; + + QskHctColor( QRgb ); + + void setHue( qreal hue ); + constexpr qreal hue() const noexcept; + + void setChroma( qreal chroma ) noexcept; + constexpr qreal chroma() const noexcept; + + void setTone( qreal tone ) noexcept; + constexpr qreal tone() const noexcept; + + constexpr QskHctColor toned( qreal tone ) const noexcept; + + void setRgb( QRgb ); + QRgb rgb() const; + + private: + qreal m_hue = 0; // [0.0, 360.0[ + qreal m_chroma = 0; + qreal m_tone = 0; // [0.0, 100.0] +}; + +Q_DECLARE_TYPEINFO( QskHctColor, Q_MOVABLE_TYPE ); + +inline constexpr QskHctColor::QskHctColor( qreal hue, qreal chroma, qreal tone ) noexcept + : m_hue( hue ) + , m_chroma( chroma ) + , m_tone( tone ) { - QSK_EXPORT QRgb rgb( double hue, double chroma, double tone ); - QSK_EXPORT void getHueAndChroma( QRgb rgb, double& hue, double& chroma ); } +inline constexpr qreal QskHctColor::hue() const noexcept +{ + return m_hue; +} + +inline constexpr qreal QskHctColor::chroma() const noexcept +{ + return m_chroma; +} + +inline constexpr qreal QskHctColor::tone() const noexcept +{ + return m_tone; +} + +inline constexpr QskHctColor QskHctColor::toned( qreal tone ) const noexcept +{ + return QskHctColor( m_hue, m_chroma, tone ); +} + +#ifndef QT_NO_DEBUG_STREAM + class QDebug; + QSK_EXPORT QDebug operator<<( QDebug, const QskHctColor& ); +#endif + #endif From bbaf7b28843bcd445292224fea8c0daecc9d05b3 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Thu, 16 Jun 2022 09:53:12 +0200 Subject: [PATCH 2/5] segmented bar: Add states for when cursor is at beginning and end ... so we can style the cursor accordingly --- src/controls/QskSegmentedBar.cpp | 7 ++++++- src/controls/QskSegmentedBar.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/controls/QskSegmentedBar.cpp b/src/controls/QskSegmentedBar.cpp index b9b1c94a..6f5dd5e1 100644 --- a/src/controls/QskSegmentedBar.cpp +++ b/src/controls/QskSegmentedBar.cpp @@ -22,7 +22,9 @@ QSK_SUBCONTROL( QskSegmentedBar, Cursor ) QSK_SUBCONTROL( QskSegmentedBar, Text ) QSK_SUBCONTROL( QskSegmentedBar, Graphic ) -QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 2 ) +QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 1 ) +QSK_SYSTEM_STATE( QskSegmentedBar, Minimum, QskAspect::FirstSystemState << 2 ) +QSK_SYSTEM_STATE( QskSegmentedBar, Maximum, QskAspect::FirstSystemState << 3 ) namespace { @@ -381,6 +383,9 @@ void QskSegmentedBar::setSelectedIndex( int index ) update(); Q_EMIT selectedIndexChanged( index ); + + setSkinStateFlag( Minimum, ( m_data->selectedIndex == 0 ) ); + setSkinStateFlag( Maximum, ( m_data->selectedIndex == count() - 1 ) ); } } diff --git a/src/controls/QskSegmentedBar.h b/src/controls/QskSegmentedBar.h index e01b4669..3777432d 100644 --- a/src/controls/QskSegmentedBar.h +++ b/src/controls/QskSegmentedBar.h @@ -33,7 +33,7 @@ class QSK_EXPORT QskSegmentedBar : public QskControl public: QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Graphic ) - QSK_STATES( Selected ) + QSK_STATES( Selected, Minimum, Maximum ) QskSegmentedBar( QQuickItem* parent = nullptr ); QskSegmentedBar( Qt::Orientation, QQuickItem* parent = nullptr ); From 00b7df5b30067ec208f230631eee36576cb84755 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Thu, 16 Jun 2022 12:00:10 +0200 Subject: [PATCH 3/5] segmented bar: Don't constrain segment and cursor to inner box In case we want to fill e.g. the cursor completely, we want to access the contents rect. This means that it is our responsibility to not draw beyond the panel. --- src/controls/QskSegmentedBarSkinlet.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/controls/QskSegmentedBarSkinlet.cpp b/src/controls/QskSegmentedBarSkinlet.cpp index a7aedc14..732cebad 100644 --- a/src/controls/QskSegmentedBarSkinlet.cpp +++ b/src/controls/QskSegmentedBarSkinlet.cpp @@ -49,7 +49,6 @@ QRectF QskSegmentedBarSkinlet::cursorRect( return QRectF(); auto rect = subControlRect( bar, contentsRect, Q::Panel ); - rect = bar->innerBox( Q::Panel, rect ); if( rect.isEmpty() ) return QRectF(); @@ -81,7 +80,6 @@ QRectF QskSegmentedBarSkinlet::segmentRect( const auto count = bar->count(); auto rect = subControlRect( bar, contentsRect, Q::Panel ); - rect = bar->innerBox( Q::Panel, rect ); if( bar->orientation() == Qt::Horizontal ) { @@ -238,7 +236,7 @@ QRectF QskSegmentedBarSkinlet::sampleRect( const QskSkinnable* skinnable, if ( subControl == Q::Text || subControl == Q::Graphic ) { const auto rect = sampleRect( skinnable, contentsRect, Q::Segment, index ); - return skinnable->innerBox( Q::Segment, rect ); + return rect; } return Inherited::sampleRect( skinnable, contentsRect, subControl, index ); From e7d05b5c982b8b05d2cd2a3dc735ccd6b96d4c6a Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 17 Jun 2022 09:32:38 +0200 Subject: [PATCH 4/5] segmented bar: Respect padding hint for graphic --- src/controls/QskSegmentedBarSkinlet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/controls/QskSegmentedBarSkinlet.cpp b/src/controls/QskSegmentedBarSkinlet.cpp index 732cebad..1e1168a4 100644 --- a/src/controls/QskSegmentedBarSkinlet.cpp +++ b/src/controls/QskSegmentedBarSkinlet.cpp @@ -304,9 +304,11 @@ QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable { const auto graphic = value.value< QskGraphic >(); const auto filter = bar->effectiveGraphicFilter( subControl ); + const auto padding = bar->paddingHint( Q::Graphic ); + const auto graphicRect = rect.marginsRemoved( padding ); return QskSkinlet::updateGraphicNode( - bar, node, graphic, filter, rect, alignment ); + bar, node, graphic, filter, graphicRect, alignment ); } return nullptr; From 8ee9e8ed22d3ea6ca178d431f0f6faefbcf8de64 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 17 Jun 2022 10:01:10 +0200 Subject: [PATCH 5/5] segmented bar: Add separator subcontrol --- src/controls/QskSegmentedBar.cpp | 1 + src/controls/QskSegmentedBar.h | 2 +- src/controls/QskSegmentedBarSkinlet.cpp | 54 ++++++++++++++++++++++++- src/controls/QskSegmentedBarSkinlet.h | 2 + 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/controls/QskSegmentedBar.cpp b/src/controls/QskSegmentedBar.cpp index 6f5dd5e1..2129a48a 100644 --- a/src/controls/QskSegmentedBar.cpp +++ b/src/controls/QskSegmentedBar.cpp @@ -18,6 +18,7 @@ QSK_SUBCONTROL( QskSegmentedBar, Panel ) QSK_SUBCONTROL( QskSegmentedBar, Segment ) +QSK_SUBCONTROL( QskSegmentedBar, Separator ) QSK_SUBCONTROL( QskSegmentedBar, Cursor ) QSK_SUBCONTROL( QskSegmentedBar, Text ) QSK_SUBCONTROL( QskSegmentedBar, Graphic ) diff --git a/src/controls/QskSegmentedBar.h b/src/controls/QskSegmentedBar.h index 3777432d..e2afa911 100644 --- a/src/controls/QskSegmentedBar.h +++ b/src/controls/QskSegmentedBar.h @@ -32,7 +32,7 @@ class QSK_EXPORT QskSegmentedBar : public QskControl using Inherited = QskControl; public: - QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Graphic ) + QSK_SUBCONTROLS( Panel, Segment, Separator, Cursor, Text, Graphic ) QSK_STATES( Selected, Minimum, Maximum ) QskSegmentedBar( QQuickItem* parent = nullptr ); diff --git a/src/controls/QskSegmentedBarSkinlet.cpp b/src/controls/QskSegmentedBarSkinlet.cpp index 1e1168a4..b6f54789 100644 --- a/src/controls/QskSegmentedBarSkinlet.cpp +++ b/src/controls/QskSegmentedBarSkinlet.cpp @@ -18,7 +18,7 @@ QskSegmentedBarSkinlet::QskSegmentedBarSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { PanelRole, SegmentRole, CursorRole, TextRole, GraphicRole } ); + setNodeRoles( { PanelRole, SegmentRole, SeparatorRole, CursorRole, TextRole, GraphicRole } ); } QskSegmentedBarSkinlet::~QskSegmentedBarSkinlet() = default; @@ -99,6 +99,34 @@ QRectF QskSegmentedBarSkinlet::segmentRect( return rect; } +QRectF QskSegmentedBarSkinlet::separatorRect( + const QskSegmentedBar* bar, const QRectF& contentsRect, int index ) const +{ + using Q = QskSegmentedBar; + + auto rect = segmentRect( bar, contentsRect, index ); + + auto sh = bar->sizeHint(); + + const QSizeF strutSize = bar->strutSizeHint( Q::Separator ); + + if( bar->orientation() == Qt::Horizontal ) + { + rect.setLeft( rect.right() ); + rect.setSize( { strutSize.width(), sh.height() } ); + } + else + { + rect.setTop( rect.bottom() ); + rect.setSize( { sh.width(), strutSize.height() } ); + } + + const auto padding = bar->paddingHint( Q::Separator ); + rect = rect.marginsRemoved( padding ); + + return rect; +} + QSGNode* QskSegmentedBarSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { @@ -115,6 +143,9 @@ QSGNode* QskSegmentedBarSkinlet::updateSubNode( case SegmentRole: return updateSeriesNode( skinnable, Q::Segment, node ); + case SeparatorRole: + return updateSeriesNode( skinnable, Q::Separator, node ); + case TextRole: return updateSeriesNode( skinnable, Q::Text, node ); @@ -226,13 +257,18 @@ QRectF QskSegmentedBarSkinlet::sampleRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl, int index ) const { using Q = QskSegmentedBar; + const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); if ( subControl == Q::Segment ) { - const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); return segmentRect( bar, contentsRect, index ); } + if ( subControl == Q::Separator ) + { + return separatorRect( bar, contentsRect, index ); + } + if ( subControl == Q::Text || subControl == Q::Graphic ) { const auto rect = sampleRect( skinnable, contentsRect, Q::Segment, index ); @@ -281,6 +317,20 @@ QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable return updateBoxNode( skinnable, node, rect, subControl ); } + if( subControl == Q::Separator ) + { + if( index == bar->count() - 1 ) + { + return nullptr; + } + else + { + const auto rect = sampleRect( bar, bar->contentsRect(), subControl, index ); + + return updateBoxNode( skinnable, node, rect, subControl ); + } + } + const auto alignment = bar->alignmentHint( subControl, Qt::AlignCenter ); if ( subControl == Q::Text ) diff --git a/src/controls/QskSegmentedBarSkinlet.h b/src/controls/QskSegmentedBarSkinlet.h index 5c878255..8bb8faa8 100644 --- a/src/controls/QskSegmentedBarSkinlet.h +++ b/src/controls/QskSegmentedBarSkinlet.h @@ -21,6 +21,7 @@ class QSK_EXPORT QskSegmentedBarSkinlet : public QskSkinlet { PanelRole, SegmentRole, + SeparatorRole, CursorRole, TextRole, @@ -57,6 +58,7 @@ class QSK_EXPORT QskSegmentedBarSkinlet : public QskSkinlet QSizeF segmentSizeHint( const QskSegmentedBar* ) const; QRectF segmentRect( const QskSegmentedBar*, const QRectF&, int index ) const; + QRectF separatorRect( const QskSegmentedBar*, const QRectF&, int index ) const; QRectF cursorRect( const QskSegmentedBar*, const QRectF& ) const; };