From b49305248ac1423638b47c1f685e5e82d4465426 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 12 Jul 2021 15:29:15 +0200 Subject: [PATCH] QskSkinHintTableEditor: add overloads for all states ... also, remove part of the logic when looking up current states. --- qskinny.pro | 4 +- src/controls/QskSkinHintTable.cpp | 4 +- src/controls/QskSkinHintTableEditor.cpp | 151 ++++++++++++++++++++++++ src/controls/QskSkinHintTableEditor.h | 20 ++++ tests/skinhints/SkinHints.cpp | 102 ++++++++++++++++ tests/skinhints/skinhints.pro | 6 + tests/tests.pro | 4 + 7 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 tests/skinhints/SkinHints.cpp create mode 100644 tests/skinhints/skinhints.pro create mode 100644 tests/tests.pro diff --git a/qskinny.pro b/qskinny.pro index 97705c65..7b7b92e6 100644 --- a/qskinny.pro +++ b/qskinny.pro @@ -8,7 +8,8 @@ SUBDIRS = \ tools \ support \ examples \ - playground + playground \ + tests OTHER_FILES = \ doc/Doxyfile \ @@ -24,3 +25,4 @@ skins.depends = src support.depends = src skins examples.depends = tools support skins qmlexport playground.depends = tools support skins qmlexport +tests.depends = src diff --git a/src/controls/QskSkinHintTable.cpp b/src/controls/QskSkinHintTable.cpp index 2b2a826c..e0807692 100644 --- a/src/controls/QskSkinHintTable.cpp +++ b/src/controls/QskSkinHintTable.cpp @@ -27,9 +27,9 @@ inline const QVariant* qskResolvedHint( QskAspect aspect, return &it->second; } - if ( const auto topState = aspect.topState() ) + if ( aspect.hasState() ) { - aspect.clearState( topState ); + aspect.clearStates(); continue; } diff --git a/src/controls/QskSkinHintTableEditor.cpp b/src/controls/QskSkinHintTableEditor.cpp index 56f77dd8..f167fe7c 100644 --- a/src/controls/QskSkinHintTableEditor.cpp +++ b/src/controls/QskSkinHintTableEditor.cpp @@ -12,6 +12,8 @@ #include "QskBoxBorderColors.h" #include "QskGradient.h" +#include + namespace { inline QskAspect aspectStrutSize( QskAspect aspect ) @@ -75,6 +77,31 @@ QskSkinHintTable* QskSkinHintTableEditor::table() const return m_table; } +void QskSkinHintTableEditor::setHint( QskAspect aspect, const QVariant& hint, QskAspect::State state ) +{ + forAllCombinationsSetHint( state, aspect, hint ); +} + +bool QskSkinHintTableEditor::removeHint( QskAspect aspect, QskAspect::State state ) +{ + return forAllCombinationsRemoveHint( state, aspect ); +} + +void QskSkinHintTableEditor::setFlagHint( QskAspect aspect, const QVariant& hint, QskAspect::State state ) +{ + setHint( aspect | QskAspect::Flag, hint, state ); +} + +void QskSkinHintTableEditor::setMetricHint( QskAspect aspect, const QVariant& hint, QskAspect::State state ) +{ + setHint( aspect | QskAspect::Metric, hint, state ); +} + +void QskSkinHintTableEditor::setColorHint( QskAspect aspect, const QVariant& hint, QskAspect::State state ) +{ + setHint( aspect | QskAspect::Color, hint, state ); +} + void QskSkinHintTableEditor::setFlag( QskAspect aspect, int flag ) { setFlagHint( aspect, flag ); @@ -132,6 +159,11 @@ void QskSkinHintTableEditor::setGradient( QskAspect aspect, const QskGradient& g setColorHint( aspect, gradient ); } +void QskSkinHintTableEditor::setGradient( QskAspect aspect, const QskGradient& gradient, QskAspect::State state ) +{ + setColorHint( aspect, QVariant::fromValue( gradient ), state ); +} + QskGradient QskSkinHintTableEditor::gradient( QskAspect aspect ) const { return colorHint< QskGradient >( aspect ); @@ -279,6 +311,12 @@ void QskSkinHintTableEditor::setBoxShape( setMetricHint( aspectShape( aspect ), shape ); } +void QskSkinHintTableEditor::setBoxShape( + QskAspect aspect, const QskBoxShapeMetrics& shape, QskAspect::State state ) +{ + setMetricHint( aspectShape( aspect ), QVariant::fromValue( shape ), state ); +} + void QskSkinHintTableEditor::removeBoxShape( QskAspect aspect ) { return removeMetricHint( aspectShape( aspect ) ); @@ -309,6 +347,12 @@ void QskSkinHintTableEditor::setBoxBorderMetrics( setMetricHint( aspectBorder( aspect ), borderMetrics ); } +void QskSkinHintTableEditor::setBoxBorderMetrics( + QskAspect aspect, const QskBoxBorderMetrics& borderMetrics, QskAspect::State state ) +{ + setMetricHint( aspectBorder( aspect ), QVariant::fromValue( borderMetrics ), state ); +} + void QskSkinHintTableEditor::removeBoxBorderMetric( QskAspect aspect ) { return removeMetricHint( aspectBorder( aspect ) ); @@ -325,6 +369,12 @@ void QskSkinHintTableEditor::setBoxBorderColors( setColorHint( aspectBorder( aspect ), borderColors ); } +void QskSkinHintTableEditor::setBoxBorderColors( + QskAspect aspect, const QskBoxBorderColors& borderColors, QskAspect::State state ) +{ + setColorHint( aspectBorder( aspect ), QVariant::fromValue( borderColors ), state ); +} + void QskSkinHintTableEditor::setBoxBorderColors( QskAspect aspect, const QColor& left, const QColor& top, const QColor& right, const QColor& bottom ) { @@ -341,3 +391,104 @@ QskBoxBorderColors QskSkinHintTableEditor::boxBorderColors( QskAspect aspect ) c { return colorHint< QskBoxBorderColors >( aspectBorder( aspect ) ); } + +void QskSkinHintTableEditor::forAllCombinationsSetHint( QskAspect::State state, + QskAspect aspect, const QVariant& hint ) +{ + uint population = qPopulationCount( quint16( state ) ); + + // first find out which bits are set: + quint16 s = state; + uint i; + std::vector< quint16 > positions; + positions.reserve( population ); + + // use this instead of calling reserve(), trading execution time against space: + // ### even better: don't store positions, but calculate pairs of indices +// quint16 bla[16]; + + while( s != 0 ) // O(population) + { + i = 15 - qCountLeadingZeroBits( s ); + quint16 testBit = ( 1 << i ); + positions.push_back( i ); + s = s ^ testBit; + } + + for( uint i = 1; i <= population; ++i ) // O(population) + { + calculateCombinationsSetHint( positions, 0, positions.size() - 1, 0, i, QskAspect::NoState, aspect, hint ); + } +} + +void QskSkinHintTableEditor::calculateCombinationsSetHint( const std::vector< quint16 >& arr, + int start, int end, int index, int r, + QskAspect::State state, QskAspect aspect, + const QVariant& hint ) +{ + if( index == r ) + { + setHint( aspect | state, hint ); + } + + for( int i = start; i <= end && end - i + 1 >= r - index; i++ ) + { + auto currentState = state | static_cast< QskAspect::State >( 1 << arr[i] ); + calculateCombinationsSetHint( arr, i + 1, end, index + 1, r, currentState, aspect, hint ); + } +} + +bool QskSkinHintTableEditor::forAllCombinationsRemoveHint( QskAspect::State state, + QskAspect aspect ) +{ + uint population = qPopulationCount( quint16( state ) ); + + // first find out which bits are set: + quint16 s = state; + uint i; + std::vector< quint16 > positions; + positions.reserve( population ); + + // use this instead of calling reserve(), trading execution time against space: + // ### even better: don't store positions, but calculate pairs of indices +// quint16 bla[16]; + + while( s != 0 ) // O(population) + { + i = 15 - qCountLeadingZeroBits( s ); + quint16 testBit = ( 1 << i ); + positions.push_back( i ); + s = s ^ testBit; + } + + bool ret = false; + + for( uint i = 1; i <= population; ++i ) // O(population) + { + bool result = calculateCombinationsRemoveHint( positions, 0, positions.size() - 1, 0, i, QskAspect::NoState, aspect ); + ret = ret || result; + } + + return ret; +} + +bool QskSkinHintTableEditor::calculateCombinationsRemoveHint( const std::vector< quint16 >& arr, + int start, int end, int index, int r, + QskAspect::State state, QskAspect aspect ) +{ + if( index == r ) + { + return removeHint( aspect | state ); + } + + bool ret = false; + + for( int i = start; i <= end && end - i + 1 >= r - index; i++ ) + { + auto currentState = state | static_cast< QskAspect::State >( 1 << arr[i] ); + bool result = calculateCombinationsRemoveHint( arr, i + 1, end, index + 1, r, currentState, aspect ); + ret = ret || result; + } + + return ret; +} diff --git a/src/controls/QskSkinHintTableEditor.h b/src/controls/QskSkinHintTableEditor.h index 175b410a..c478610c 100644 --- a/src/controls/QskSkinHintTableEditor.h +++ b/src/controls/QskSkinHintTableEditor.h @@ -30,9 +30,11 @@ class QSK_EXPORT QskSkinHintTableEditor // generic access void setHint( QskAspect, const QVariant& ); + void setHint( QskAspect, const QVariant&, QskAspect::State ); const QVariant& hint( QskAspect ) const; bool removeHint( QskAspect ); + bool removeHint( QskAspect, QskAspect::State ); QVariant takeHint( QskAspect ); bool hasHint( QskAspect ) const; @@ -48,6 +50,7 @@ class QSK_EXPORT QskSkinHintTableEditor // flag/metric/color void setFlagHint( QskAspect, const QVariant& ); + void setFlagHint( QskAspect, const QVariant&, QskAspect::State ); void removeFlagHint( QskAspect ); QVariant flagHint( QskAspect ) const; @@ -55,6 +58,7 @@ class QSK_EXPORT QskSkinHintTableEditor template< typename T > T flagHint( QskAspect ) const; void setMetricHint( QskAspect, const QVariant& ); + void setMetricHint( QskAspect, const QVariant&, QskAspect::State ); void removeMetricHint( QskAspect ); QVariant metricHint( QskAspect ) const; @@ -62,6 +66,7 @@ class QSK_EXPORT QskSkinHintTableEditor template< typename T > T metricHint( QskAspect ) const; void setColorHint( QskAspect, const QVariant& ); + void setColorHint( QskAspect, const QVariant&, QskAspect::State ); void removeColorHint( QskAspect ); QVariant colorHint( QskAspect ) const; @@ -84,6 +89,7 @@ class QSK_EXPORT QskSkinHintTableEditor void setHGradient( QskAspect, const QColor&, const QColor& ); void setVGradient( QskAspect, const QColor&, const QColor& ); void setGradient( QskAspect, const QskGradient& ); + void setGradient( QskAspect, const QskGradient&, QskAspect::State state ); QskGradient gradient( QskAspect ) const; void setStrutSize( QskAspect, const QSizeF& ); @@ -123,6 +129,7 @@ class QSK_EXPORT QskSkinHintTableEditor void setBoxShape( QskAspect, qreal topLeft, qreal topRight, qreal bottomLeft, qreal bottomRight, Qt::SizeMode = Qt::AbsoluteSize ); void setBoxShape( QskAspect, const QskBoxShapeMetrics& ); + void setBoxShape( QskAspect, const QskBoxShapeMetrics&, QskAspect::State ); void removeBoxShape( QskAspect ); QskBoxShapeMetrics boxShape( QskAspect ) const; @@ -131,16 +138,29 @@ class QSK_EXPORT QskSkinHintTableEditor void setBoxBorderMetrics( QskAspect, qreal left, qreal top, qreal right, qreal bottom, Qt::SizeMode = Qt::AbsoluteSize ); void setBoxBorderMetrics( QskAspect, const QskBoxBorderMetrics& ); + void setBoxBorderMetrics( QskAspect, const QskBoxBorderMetrics&, QskAspect::State ); void removeBoxBorderMetric( QskAspect ); QskBoxBorderMetrics boxBorderMetrics( QskAspect ) const; void setBoxBorderColors( QskAspect, const QskBoxBorderColors& ); + void setBoxBorderColors( QskAspect, const QskBoxBorderColors&, QskAspect::State ); void setBoxBorderColors( QskAspect, const QColor& left, const QColor& top, const QColor& right, const QColor& bottom ); void removeBoxBorderColors( QskAspect ); QskBoxBorderColors boxBorderColors( QskAspect ) const; private: + void forAllCombinationsSetHint( QskAspect::State, QskAspect, + const QVariant& hint ); + void calculateCombinationsSetHint( const std::vector< quint16 >& arr, + int start, int end, int index, int r, + QskAspect::State state, QskAspect aspect, + const QVariant& hint ); + bool forAllCombinationsRemoveHint( QskAspect::State, QskAspect ); + bool calculateCombinationsRemoveHint( const std::vector< quint16 >& arr, + int start, int end, int index, int r, + QskAspect::State state, QskAspect aspect ); + QskSkinHintTable* m_table = nullptr; }; diff --git a/tests/skinhints/SkinHints.cpp b/tests/skinhints/SkinHints.cpp new file mode 100644 index 00000000..a5bfd594 --- /dev/null +++ b/tests/skinhints/SkinHints.cpp @@ -0,0 +1,102 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2021 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include + +class TestSkinHints : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void setHints(); + void removeHints(); + +private: + QskSkin m_skin; + QskSkinHintTableEditor ed = QskSkinHintTableEditor( &m_skin.hintTable() ); +}; + +void TestSkinHints::setHints() +{ + QskBoxBorderColors colors; + QVERIFY( colors.colorAt( Qt::TopEdge ) == QColor() ); + + ed.setBoxBorderColors( QskBox::Panel, { Qt::red }, + QskControl::Disabled + | QskControl::Hovered + | QskControl::Focused + ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Disabled ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Hovered ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Focused ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Disabled | QskControl::Hovered ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Disabled | QskControl::Focused ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Hovered | QskControl::Focused ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); + + colors = ed.boxBorderColors( QskBox::Panel | QskControl::Disabled | QskControl::Hovered | QskControl::Focused ); + QVERIFY( colors.colorAt( Qt::TopEdge ) == Qt::red ); +} + +void TestSkinHints::removeHints() +{ + bool removed = ed.removeHint( QskBox::Panel | QskAspect::Color, + QskControl::Hovered + | QskControl::Focused + ); + QVERIFY( removed == false ); + + ed.setColorHint( QskBox::Panel, QColor( Qt::green ), + QskControl::Disabled + | QskControl::Hovered + | QskControl::Focused + ); + + removed = ed.removeHint( QskBox::Panel | QskAspect::Color, + QskControl::Disabled + | QskControl::Hovered + | QskControl::Focused + ); + QVERIFY( removed == true ); + + removed = ed.removeHint( QskBox::Panel | QskAspect::Color, + QskControl::Disabled + | QskControl::Hovered + | QskControl::Focused + ); + QVERIFY( removed == false ); + + ed.setColorHint( QskBox::Panel, QColor( Qt::green ), + QskControl::Disabled + | QskControl::Hovered + | QskControl::Focused + ); + + removed = ed.removeHint( QskBox::Panel | QskAspect::Color, + QskControl::Disabled + | QskControl::Hovered + ); + QVERIFY( removed == true ); +} + +QTEST_MAIN(TestSkinHints) +#include "SkinHints.moc" diff --git a/tests/skinhints/skinhints.pro b/tests/skinhints/skinhints.pro new file mode 100644 index 00000000..38f50f53 --- /dev/null +++ b/tests/skinhints/skinhints.pro @@ -0,0 +1,6 @@ +CONFIG += testcase qskinny +TARGET = tst_skinhints +SOURCES += SkinHints.cpp +QT += testlib + +INCLUDEPATH *= $${QSK_DIRS} diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 00000000..24759042 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + skinhints