diff --git a/examples/gallery/Page.cpp b/examples/gallery/Page.cpp index 0dbbfcf3..078d96b3 100644 --- a/examples/gallery/Page.cpp +++ b/examples/gallery/Page.cpp @@ -4,29 +4,11 @@ *****************************************************************************/ #include "Page.h" -#include Page::Page( Qt::Orientation orientation, QQuickItem* parent ) : QskLinearBox( orientation, parent ) - , m_gradient( QskRgb::GhostWhite ) { setMargins( 20 ); setPadding( 10 ); setSpacing( 10 ); } - -void Page::setGradient( const QskGradient& gradient ) -{ - if ( gradient != m_gradient ) - { - m_gradient = gradient; - - if ( parentItem() && isVisibleToParent() ) - parentItem()->update(); - } -} - -QskGradient Page::gradient() const -{ - return m_gradient; -} diff --git a/examples/gallery/Page.h b/examples/gallery/Page.h index f0404294..f8f8bf83 100644 --- a/examples/gallery/Page.h +++ b/examples/gallery/Page.h @@ -6,16 +6,9 @@ #pragma once #include -#include class Page : public QskLinearBox { public: Page( Qt::Orientation, QQuickItem* parent = nullptr ); - - void setGradient( const QskGradient& ); - QskGradient gradient() const; - - private: - QskGradient m_gradient; }; diff --git a/examples/gallery/label/LabelPage.cpp b/examples/gallery/label/LabelPage.cpp index 84f2c438..8e4a2813 100644 --- a/examples/gallery/label/LabelPage.cpp +++ b/examples/gallery/label/LabelPage.cpp @@ -67,7 +67,6 @@ namespace LabelPage::LabelPage( QQuickItem* parent ) : Page( Qt::Vertical, parent ) { - setGradient( QskRgb::AliceBlue ); setSpacing( 40 ); (void) new TextBox( this ); diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index 58e52f7a..838d9106 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -30,27 +30,6 @@ namespace setMargins( 10 ); setTabPosition( Qsk::Left ); setAutoFitTabs( true ); - - connect( this, &QskTabView::currentIndexChanged, - this, &TabView::updateViewPanel ); - } - - protected: - void aboutToShow() override - { - updateViewPanel(); - } - - private: - void updateViewPanel() - { - /* - We should have a better way to set individual colors - for each tab page background - */ - - if ( auto page = dynamic_cast< const ::Page* >( currentItem() ) ) - setGradientHint( QskTabView::Page, page->gradient() ); } }; } diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index 75fad19e..18985ecf 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -45,9 +45,7 @@ namespace ProgressBarPage::ProgressBarPage( QQuickItem* parent ) : Page( Qt::Horizontal, parent ) { - setGradient( QskRgb::AliceBlue ); setSpacing( 40 ); - populate(); } diff --git a/examples/gallery/slider/SliderPage.cpp b/examples/gallery/slider/SliderPage.cpp index 4de7c647..8e164a5c 100644 --- a/examples/gallery/slider/SliderPage.cpp +++ b/examples/gallery/slider/SliderPage.cpp @@ -13,8 +13,6 @@ SliderPage::SliderPage( QQuickItem* parentItem ) : Page( Qt::Vertical, parentItem ) { - setGradient( QskRgb::PeachPuff ); - setMargins( 10 ); setSpacing( 20 ); diff --git a/examples/gallery/switchbutton/SwitchButtonPage.cpp b/examples/gallery/switchbutton/SwitchButtonPage.cpp index 169dac95..1fab35b1 100644 --- a/examples/gallery/switchbutton/SwitchButtonPage.cpp +++ b/examples/gallery/switchbutton/SwitchButtonPage.cpp @@ -15,9 +15,7 @@ SwitchButtonPage::SwitchButtonPage( QQuickItem* parent ) : Page( Qt::Horizontal, parent ) { - setGradient( QskRgb::AliceBlue ); setSpacing( 40 ); - populate(); } diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index e3a5eb1c..a8341aea 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -47,20 +47,12 @@ static const int ButtonFontRole = QskSkin::HugeFont + 77; static const int qskDuration = 150; -static inline QColor qskShadedColor( const QColor color, qreal opacity ) -{ - QColor c = color; - c.setAlphaF( opacity ); - - return c; -} - namespace { class Editor : private QskSkinHintTableEditor { public: - Editor( QskSkinHintTable* table, const ColorPalette& palette ) + Editor( QskSkinHintTable* table, const QskMaterialPalette& palette ) : QskSkinHintTableEditor( table ) , m_pal( palette ) { @@ -93,7 +85,7 @@ namespace void setupTextInput(); void setupTextLabel(); - const ColorPalette& m_pal; + const QskMaterialPalette& m_pal; const uint rippleSize = 30; }; } @@ -135,7 +127,7 @@ void Editor::setupControl() setGradient( A::Control, m_pal.background ); setColor( A::Control | A::StyleColor, m_pal.onBackground ); setColor( A::Control | A::StyleColor | Q::Disabled, - qskShadedColor( m_pal.onBackground, 0.6 ) ); + QskRgb::toTransparentF( m_pal.onBackground, 0.6 ) ); } void Editor::setupBox() @@ -155,8 +147,8 @@ void Editor::setupPopup() setFlagHint( Q::Overlay | A::Style, true ); const QskGradient gradient( QskGradient::Vertical, - qskShadedColor( m_pal.secondary, 0.45 ), - qskShadedColor( m_pal.secondary, 0.7 ) ); + QskRgb::toTransparentF( m_pal.secondary, 0.45 ), + QskRgb::toTransparentF( m_pal.secondary, 0.7 ) ); setGradient( Q::Overlay, gradient ); } @@ -196,20 +188,16 @@ void Editor::setupTextInput() setBoxBorderColors( Q::Panel | Q::Focused, m_pal.primary ); - setColor( Q::Panel, - m_pal.elevated( m_pal.background, 1 ) ); - setColor( Q::Panel | Q::Hovered, - m_pal.elevated( m_pal.background, 2 ) ); - setColor( Q::Panel | Q::Focused, - m_pal.elevated( m_pal.background, 3 ) ); - setColor( Q::Panel | Q::Editing, - m_pal.elevated( m_pal.background, 4 ) ); + setColor( Q::Panel, m_pal.elevated( m_pal.background, 1 ) ); + setColor( Q::Panel | Q::Hovered, m_pal.elevated( m_pal.background, 2 ) ); + setColor( Q::Panel | Q::Focused, m_pal.elevated( m_pal.background, 3 ) ); + setColor( Q::Panel | Q::Editing, m_pal.elevated( m_pal.background, 4 ) ); setColor( Q::Panel | Q::Disabled, - qskShadedColor( m_pal.secondaryVariantNoSaturation, m_pal.disabled ) ); - setColor( Q::Text | Q::Disabled, qskShadedColor( m_pal.onBackground, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation, m_pal.disabled ) ); + setColor( Q::Text | Q::Disabled, QskRgb::toTransparentF( m_pal.onBackground, m_pal.disabled ) ); setBoxBorderColors( Q::Panel, - qskShadedColor( m_pal.onBackground, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.onBackground, m_pal.disabled ) ); } void Editor::setupProgressBar() @@ -233,9 +221,9 @@ void Editor::setupProgressBar() setGradient( Q::Bar, m_pal.secondary ); setGradient( Q::Groove | Q::Disabled, - qskShadedColor( m_pal.secondaryNoSaturation, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondaryNoSaturation, m_pal.disabled ) ); setGradient( Q::Bar | Q::Disabled, - qskShadedColor( m_pal.secondary, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.disabled ) ); } void Editor::setupFocusIndicator() @@ -275,9 +263,9 @@ void Editor::setupPageIndicator() setGradient( Q::Bullet | Q::Selected, m_pal.secondary ); setGradient( Q::Bullet | Q::Disabled, - qskShadedColor( m_pal.secondaryNoSaturation, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondaryNoSaturation, m_pal.disabled ) ); setGradient( Q::Bullet | Q::Selected | Q::Disabled, - qskShadedColor( m_pal.secondary, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.disabled ) ); setSpacing( Q::Panel, qskDpiScaled( 3 ) ); setPadding( Q::Panel, 0 ); @@ -304,19 +292,19 @@ void Editor::setupPushButton() setGradient( Q::Panel | Q::Flat, White & ColorMask ); setColor( Q::Text, m_pal.primary ); - setColor( Q::Text | Q::Disabled, qskShadedColor( m_pal.primary, 0.6 ) ); + setColor( Q::Text | Q::Disabled, QskRgb::toTransparentF( m_pal.primary, 0.6 ) ); setFontRole( Q::Text, ButtonFontRole ); setAlignment( Q::Text, Qt::AlignCenter ); setBoxBorderMetrics( Q::Panel, 1 ); setBoxBorderColors( Q::Panel, m_pal.primary ); - setBoxBorderColors( Q::Panel | Q::Disabled, qskShadedColor( m_pal.onBackground, m_pal.disabled ) ); - setColor( Q::Text | Q::Disabled, qskShadedColor( m_pal.onBackground, m_pal.disabled ) ); + setBoxBorderColors( Q::Panel | Q::Disabled, QskRgb::toTransparentF( m_pal.onBackground, m_pal.disabled ) ); + setColor( Q::Text | Q::Disabled, QskRgb::toTransparentF( m_pal.onBackground, m_pal.disabled ) ); - setColor( Q::Panel | Q::Hovered, qskShadedColor( m_pal.primary, m_pal.hover ) ); - setColor( Q::Panel | Q::Focused, qskShadedColor( m_pal.primary, m_pal.focused ) ); - setColor( Q::Panel | Q::Pressed, qskShadedColor( m_pal.primary, m_pal.pressed ) ); + setColor( Q::Panel | Q::Hovered, QskRgb::toTransparentF( m_pal.primary, m_pal.hover ) ); + setColor( Q::Panel | Q::Focused, QskRgb::toTransparentF( m_pal.primary, m_pal.focused ) ); + setColor( Q::Panel | Q::Pressed, QskRgb::toTransparentF( m_pal.primary, m_pal.pressed ) ); setAnimation( Q::Panel | A::Color, qskDuration ); setAnimation( Q::Panel | A::Metric, qskDuration ); @@ -339,7 +327,7 @@ void Editor::setupDialogButton() setGradient( Q::Panel, m_pal.primary ); setColor( Q::Text, m_pal.onBackground ); - setColor( Q::Text | Q::Disabled, qskShadedColor( m_pal.onPrimary, 0.6 ) ); + setColor( Q::Text | Q::Disabled, QskRgb::toTransparentF( m_pal.onPrimary, 0.6 ) ); setFontRole( Q::Text, ButtonFontRole ); setAlignment( Q::Text, Qt::AlignCenter ); @@ -385,7 +373,7 @@ void Editor::setupSlider() setMetric( Q::Panel | A::Size, extent ); setBoxShape( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 0 ); - setGradient( Q::Panel, m_pal.background ); + setGradient( Q::Panel, QskGradient() ); setPadding( Q::Panel | A::Horizontal, QskMargins( 0.5 * extent, 0 ) ); setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) ); @@ -399,17 +387,17 @@ void Editor::setupSlider() setBoxShape( subControl, 0 ); setBoxBorderMetrics( subControl, 0 ); } + setMetric( Q::Groove | A::Size, qskDpiScaled( 4 ) ); setMetric( Q::Fill | A::Size, qskDpiScaled( 6 ) ); - - setGradient( Q::Groove, qskShadedColor( m_pal.secondary, .38 ) ); + setGradient( Q::Groove, QskRgb::toTransparentF( m_pal.secondary, .38 ) ); setGradient( Q::Groove | Q::Disabled, - qskShadedColor( m_pal.secondaryNoSaturation, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondaryNoSaturation, m_pal.disabled ) ); setGradient( Q::Fill, m_pal.secondary ); setGradient( Q::Fill | Q::Disabled, - qskShadedColor( m_pal.secondaryNoSaturation, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondaryNoSaturation, m_pal.disabled ) ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); setBoxBorderMetrics( Q::Handle, 0 ); @@ -425,11 +413,13 @@ void Editor::setupSlider() setBoxBorderMetrics( Q::Handle, qskDpiScaled( rippleSize / 2 ) ); setBoxBorderColors( Q::Handle | Q::Hovered, - qskShadedColor( m_pal.secondary, m_pal.hover ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.hover ) ); + setBoxBorderColors( Q::Handle | Q::Focused, - qskShadedColor( m_pal.secondary, m_pal.focused ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.focused ) ); + setBoxBorderColors( Q::Handle | Q::Pressed, - qskShadedColor( m_pal.secondary, m_pal.pressed ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.pressed ) ); // move the handle smoothly, when using keys setAnimation( Q::Handle | A::Metric | A::Position, 2 * qskDuration ); @@ -451,17 +441,17 @@ void Editor::setupSwitchButton() setColor( Q::Groove, m_pal.secondaryNoSaturation ); setGradient( Q::Groove | Q::Disabled, - qskShadedColor( m_pal.secondaryNoSaturation, m_pal.disabled ) ); + QskRgb::toTransparentF( m_pal.secondaryNoSaturation, m_pal.disabled ) ); setGradient( Q::Groove | Q::Checked, m_pal.secondaryVariant ); setGradient( Q::Groove | Q::Checked | Q::Disabled, - qskShadedColor( m_pal.secondaryVariant, m_pal.disabledOccupancy ) ); + QskRgb::toTransparentF( m_pal.secondaryVariant, m_pal.disabledOccupancy ) ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); setStrutSize( Q::Handle, qskDpiScaled( 2 * radius + rippleSize ), qskDpiScaled( 2 * radius + rippleSize ) ); - setGradient( Q::Handle, m_pal.background.lighter( 900 ) ); + setGradient( Q::Handle, QskRgb::lighter( m_pal.background, 900 ) ); setGradient( Q::Handle | Q::Checked, m_pal.secondary ); @@ -474,20 +464,20 @@ void Editor::setupSwitchButton() setBoxBorderMetrics( Q::Handle, qskDpiScaled( rippleSize / 2 ) ); setBoxBorderColors( Q::Handle | Q::Checked | Q::Hovered, - qskShadedColor( m_pal.secondary, m_pal.hover ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.hover ) ); setBoxBorderColors( Q::Handle | Q::Checked | Q::Focused, - qskShadedColor( m_pal.secondary, m_pal.focused ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.focused ) ); setBoxBorderColors( Q::Handle | Q::Checked | Q::Pressed, - qskShadedColor( m_pal.secondary, m_pal.pressed ) ); + QskRgb::toTransparentF( m_pal.secondary, m_pal.pressed ) ); setBoxBorderColors( Q::Handle | Q::Hovered, - qskShadedColor( m_pal.secondaryVariantNoSaturation, + QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation, m_pal.hover ) ); setBoxBorderColors( Q::Handle | Q::Focused, - qskShadedColor( m_pal.secondaryVariantNoSaturation, + QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation, m_pal.focused ) ); setBoxBorderColors( Q::Handle | Q::Pressed, - qskShadedColor( m_pal.secondaryVariantNoSaturation, + QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation, m_pal.pressed ) ); for ( auto state : { A::NoState, Q::Disabled } ) @@ -556,15 +546,15 @@ void Editor::setupTabButton() setColor( Q::Text, m_pal.onBackground ); setColor( Q::Text | Q::Disabled, - qskShadedColor( m_pal.onBackground, + QskRgb::toTransparentF( m_pal.onBackground, m_pal.widgetBackgroundDisabled ) ); setColor( Q::Text | Q::Checked, m_pal.primary ); setColor( Q::Text | Q::Hovered, m_pal.primary ); setColor( Q::Panel, m_pal.elevated( m_pal.background ) ); - setColor( Q::Panel | Q::Hovered, qskShadedColor( m_pal.primary, m_pal.hover ) ); - setColor( Q::Panel | Q::Focused, qskShadedColor( m_pal.primary, m_pal.focused ) ); - setColor( Q::Panel | Q::Pressed, qskShadedColor( m_pal.primary, m_pal.pressed ) ); + setColor( Q::Panel | Q::Hovered, QskRgb::toTransparentF( m_pal.primary, m_pal.hover ) ); + setColor( Q::Panel | Q::Focused, QskRgb::toTransparentF( m_pal.primary, m_pal.focused ) ); + setColor( Q::Panel | Q::Pressed, QskRgb::toTransparentF( m_pal.primary, m_pal.pressed ) ); setAnimation( Q::Panel | A::Color, qskDuration ); @@ -587,9 +577,11 @@ void Editor::setupTabBar() setAnimation( Q::Panel | A::Metric, QskAnimationHint( 200, QEasingCurve::InCubic ) ); } -void Editor::setupTabView() { +void Editor::setupTabView() +{ using Q = QskTabView; + setGradient( Q::Page, m_pal.background ); setAnimation( Q::Page, qskDuration ); } @@ -654,7 +646,7 @@ void Editor::setupScrollView() { setBoxShape( subControl, 3 ); setBoxBorderMetrics( subControl, 0 ); - setColor( subControl, qskShadedColor( m_pal.onBackground, m_pal.hover ) ); + setColor( subControl, QskRgb::toTransparentF( m_pal.onBackground, m_pal.hover ) ); setAnimation( subControl | A::Color, qskDuration ); } @@ -663,7 +655,7 @@ void Editor::setupScrollView() Q::VerticalScrollHandle | Q::VerticalHandlePressed } ) { setColor( subControl, - qskShadedColor( m_pal.onBackground, m_pal.pressed ) ); + QskRgb::toTransparentF( m_pal.onBackground, m_pal.pressed ) ); } // when changing the position by QskScrollView::scrollTo @@ -679,7 +671,7 @@ void Editor::setupListView() setColor( Q::Cell, m_pal.background ); setColor( Q::Text, m_pal.onBackground ); - setColor( Q::Cell | Q::Selected, qskShadedColor( m_pal.onBackground, m_pal.focused ) ); + setColor( Q::Cell | Q::Selected, QskRgb::toTransparentF( m_pal.onBackground, m_pal.focused ) ); setColor( Q::Text | Q::Selected, m_pal.onBackground ); } @@ -713,15 +705,8 @@ void Editor::setupSubWindow() } -class QskMaterialSkin::PrivateData -{ - public: - ColorPalette palette; -}; - -QskMaterialSkin::QskMaterialSkin( ColorPalette colors, QObject* parent ) +QskMaterialSkin::QskMaterialSkin( const QskMaterialPalette& palette, QObject* parent ) : Inherited( parent ) - , m_data( new PrivateData { colors } ) { // Default theme colors setupFonts( QStringLiteral( "Roboto" ) ); @@ -730,7 +715,7 @@ QskMaterialSkin::QskMaterialSkin( ColorPalette colors, QObject* parent ) buttonFont.setCapitalization( QFont::AllUppercase ); setFont( ButtonFontRole, buttonFont ); - Editor editor( &hintTable(), m_data->palette ); + Editor editor( &hintTable(), palette ); editor.setup(); } diff --git a/skins/material/QskMaterialSkin.h b/skins/material/QskMaterialSkin.h index 7c7a80a2..a6f041b0 100644 --- a/skins/material/QskMaterialSkin.h +++ b/skins/material/QskMaterialSkin.h @@ -9,86 +9,56 @@ #include "QskMaterialGlobal.h" #include #include -#include -struct ColorPalette +class QSK_MATERIAL_EXPORT QskMaterialPalette { - enum Lightness { light, dark } lightness; - - QColor primary; - QColor primaryVariant; - QColor onPrimary; - - QColor secondary; - QColor secondaryVariant; - QColor onSecondary; - - QColor background; - QColor onBackground; - - QColor error; - QColor onError; - - QColor primaryNoSaturation = QColor::fromHsl( primary.hslHue(), 0, - primary.lightness() ); - - QColor secondaryNoSaturation = - QColor::fromHsl( secondary.hslHue(), 0, - secondary.lightness() ); - - QColor secondaryVariantNoSaturation = - QColor::fromHsl( secondaryVariant.hslHue(), 0, - secondaryVariant.lightness() + - secondaryVariant.hslSaturation() ); - - qreal disabledOccupancy = 0.2; - qreal widgetBackgroundDisabled = 0.6; - - qreal hover = 0.1; - qreal focused = 0.4; - qreal pressed = 0.5; - qreal disabled = 0.3; - - ColorPalette( - Lightness lightness = light, - QColor primary = QColor::fromRgb( 0x6200EE ), - QColor primaryVariant = QColor::fromRgb( 0x3700B3 ), - QColor onPrimary = Qt::white, - QColor secondary = QColor::fromRgb( 0x03DAC6 ), - QColor secondaryVariant = QColor::fromRgb( 0x018786 ), - QColor onSecondary = Qt::white, - QColor background = QColor::fromRgba( QskRgb::Grey100 ), - QColor onBackground = Qt::black, - QColor error = QColor::fromRgb( 0xB00020 ), - QColor onError = Qt::white ): - lightness( lightness ), - primary( primary ), - primaryVariant( primaryVariant ), - onPrimary( onPrimary ), - secondary( secondary ), - secondaryVariant( secondaryVariant ), - onSecondary( onSecondary ), - background( background ), - onBackground( onBackground ), - error( error ), - onError( onError ) + public: + enum Lightness { - primaryNoSaturation = QColor::fromHsl( primary.hslHue(), 0, - primary.lightness() ); + Light, + Dark + }; - secondaryNoSaturation = QColor::fromHsl( secondary.hslHue(), - 0, - secondary.lightness() ); - - secondaryVariantNoSaturation = - QColor::fromHsl( secondaryVariant.hslHue(), 0, - secondaryVariant.lightness() ); + QskMaterialPalette( Lightness lightness ) + : m_lightness( lightness ) + { } - inline QColor elevated( const QColor target, const float level = 1 ) const { - return ( lightness == light ) ? target.darker( 100 + level * 15 ) - : target.lighter( 130 + level * 30 ); + inline QRgb elevated( const QRgb rgb, const float level = 1 ) const + { + return ( m_lightness == Light ) + ? QskRgb::darker( rgb, 100 + level * 15 ) + : QskRgb::lighter( rgb, 130 + level * 30 ); } + + public: + QRgb primary; + QRgb primaryVariant; + QRgb onPrimary; + + QRgb secondary; + QRgb secondaryVariant; + QRgb onSecondary; + + QRgb background; + QRgb onBackground; + + QRgb error; + QRgb onError; + + QRgb primaryNoSaturation; + QRgb secondaryNoSaturation; + QRgb secondaryVariantNoSaturation; + + const qreal disabledOccupancy = 0.2; + const qreal widgetBackgroundDisabled = 0.6; + + const qreal hover = 0.1; + const qreal focused = 0.4; + const qreal pressed = 0.5; + const qreal disabled = 0.3; + + const Lightness m_lightness; }; class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin @@ -98,12 +68,8 @@ class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin using Inherited = QskSkin; public: - QskMaterialSkin( ColorPalette, QObject* parent = nullptr ); + QskMaterialSkin( const QskMaterialPalette&, QObject* parent = nullptr ); ~QskMaterialSkin() override; - - private: - class PrivateData; - std::unique_ptr< PrivateData > m_data; }; #endif diff --git a/skins/material/QskMaterialSkinFactory.cpp b/skins/material/QskMaterialSkinFactory.cpp index 04fb1bc7..038103d1 100644 --- a/skins/material/QskMaterialSkinFactory.cpp +++ b/skins/material/QskMaterialSkinFactory.cpp @@ -9,6 +9,42 @@ static const QString materialLightSkinName = QStringLiteral( "materialLight" ); static const QString materialDarkSkinName = QStringLiteral( "materialDark" ); +namespace +{ + inline int lightnessRgb( QRgb rgb ) + { + const int red = qRed( rgb ); + const int green = qGreen( rgb ); + const int blue = qBlue( rgb ); + + int min, max; + + if ( red > green ) + { + max = qMax( red, blue ); + min = qMin( green, blue ); + } + else + { + max = qMax( green, blue ); + min = qMin( red, blue ); + } + + return ( max + min ) / 2; + } + + inline QRgb toUnsaturated( QRgb rgb ) + { + /* + a saturation of 0 results in having the lightness as r,g,b + Is this intended ? + */ + + const auto l = lightnessRgb( rgb ); + return qRgba( l, l, l, qAlpha( rgb ) ); + } +} + QskMaterialSkinFactory::QskMaterialSkinFactory( QObject* parent ) : QskSkinFactory( parent ) { @@ -25,24 +61,48 @@ QStringList QskMaterialSkinFactory::skinNames() const QskSkin* QskMaterialSkinFactory::createSkin( const QString& skinName ) { - if ( QString::compare( skinName, materialLightSkinName, Qt::CaseInsensitive ) ) - return new QskMaterialSkin( ColorPalette() ); - - if ( QString::compare( skinName, materialDarkSkinName, Qt::CaseInsensitive ) ) + if ( QString::compare( skinName, materialLightSkinName, Qt::CaseInsensitive ) == 0 ) { - return new QskMaterialSkin( ColorPalette( - ColorPalette::dark, // lightness - QColor::fromRgb( 0xBB86FC ), // primary - QColor::fromRgb( 0x3700B3 ), // primaryVariant - Qt::black, // onPrimary - QColor::fromRgb( 0x03DAC6 ), // secondary - QColor::fromRgb( 0x018786 ), // secondaryVariant - Qt::black, // onSecondary - QColor::fromRgb( 0x121212 ), // background - Qt::white, // onBackground - QColor::fromRgb( 0xCF6679 ), // error - Qt::black // onError - ) ); + QskMaterialPalette pal( QskMaterialPalette::Light );; + + pal.primary = 0xff6200ee; + pal.primaryVariant = 0xff3700b3; + pal.onPrimary = QskRgb::White; + pal.secondary = 0xff03dac6; + pal.secondaryVariant = 0xff018786; + pal.onSecondary = QskRgb::White; + pal.background = QskRgb::Grey100; + pal.onBackground = QskRgb::Black; + pal.error = 0xffb00020; + pal.onError = QskRgb::White; + + pal.primaryNoSaturation = toUnsaturated( pal.primary ); + pal.secondaryNoSaturation = toUnsaturated( pal.secondary ); + pal.secondaryVariantNoSaturation = toUnsaturated( pal.secondaryVariant ); + + return new QskMaterialSkin( pal ); + } + + if ( QString::compare( skinName, materialDarkSkinName, Qt::CaseInsensitive ) == 0 ) + { + QskMaterialPalette pal( QskMaterialPalette::Dark ); + + pal.primary = 0xffbb86fc; + pal.primaryVariant = 0xff3700b3; + pal.onPrimary = QskRgb::Black; + pal.secondary = 0xff03dac6; + pal.secondaryVariant = 0xff018786; + pal.onSecondary = QskRgb::Black; + pal.background = 0xff121212; + pal.onBackground = QskRgb::White; + pal.error = 0xffcf6679; + pal.onError = QskRgb::Black; + + pal.primaryNoSaturation = toUnsaturated( pal.primary ); + pal.secondaryNoSaturation = toUnsaturated( pal.secondary ); + pal.secondaryVariantNoSaturation = toUnsaturated( pal.secondaryVariant ); + + return new QskMaterialSkin( pal ); } return nullptr; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index f7031ae1..55ce1c0c 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -647,51 +647,38 @@ void Editor::setupSlider() // Panel - for ( auto placement : { A::Horizontal, A::Vertical } ) - { - const auto aspect = Q::Panel | placement; - - setMetric( aspect | A::Size, extent ); - setBoxBorderMetrics( aspect, 0 ); - setBoxShape( aspect, 0 ); - setGradient( aspect, QskGradient() ); - } + setMetric( Q::Panel | A::Size, extent ); + setBoxBorderMetrics( Q::Panel, 0 ); + setBoxShape( Q::Panel, 0 ); + setGradient( Q::Panel, QskGradient() ); setPadding( Q::Panel | A::Horizontal, QskMargins( 0.5 * extent, 0 ) ); setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) ); // Groove, Fill - for ( auto placement : { A::Horizontal, A::Vertical } ) + for ( auto subControl : { Q::Groove, Q::Fill } ) { - for ( auto subControl : { Q::Groove, Q::Fill } ) - { - const auto aspect = subControl | placement; + const auto aspect = subControl; - setMetric( aspect | A::Size, 0.3 * extent ); - setPadding( aspect, 0 ); + setMetric( aspect | A::Size, 0.3 * extent ); + setPadding( aspect, 0 ); - setBoxBorderMetrics( aspect, 0 ); - setBoxShape( aspect, 0.1 * extent ); - } - - setGradient( Q::Groove | placement, m_pal.darker200 ); - setGradient( Q::Fill | placement, QskGradient() ); // no filling + setBoxBorderMetrics( aspect, 0 ); + setBoxShape( aspect, 0.1 * extent ); } + setGradient( Q::Groove, m_pal.darker200 ); + setGradient( Q::Fill, QskGradient() ); // no filling + // Handle - for ( auto placement : { A::Horizontal, A::Vertical } ) - { - const auto aspect = Q::Handle | placement; + setButton( Q::Handle, Raised, 1 ); + setBoxShape( Q::Handle, 20.0, Qt::RelativeSize ); + setButton( Q::Handle | Q::Pressed, Sunken, 1 ); - setButton( aspect, Raised, 1 ); - setBoxShape( aspect, 20.0, Qt::RelativeSize ); - setButton( aspect | Q::Pressed, Sunken, 1 ); - - const qreal sz = 0.75 * extent; - setStrutSize( aspect, sz, sz ); - } + const qreal sz = 0.75 * extent; + setStrutSize( Q::Handle, sz, sz ); setAnimation( Q::Handle | A::Color, qskDuration ); } diff --git a/skins/squiek/QskSquiekSkinFactory.cpp b/skins/squiek/QskSquiekSkinFactory.cpp index 16c27524..39074d24 100644 --- a/skins/squiek/QskSquiekSkinFactory.cpp +++ b/skins/squiek/QskSquiekSkinFactory.cpp @@ -24,7 +24,7 @@ QStringList QskSquiekSkinFactory::skinNames() const QskSkin* QskSquiekSkinFactory::createSkin( const QString& skinName ) { - if ( skinName.toLower() == squiekSkinName ) + if ( QString::compare( skinName, squiekSkinName, Qt::CaseInsensitive ) == 0 ) return new QskSquiekSkin(); return nullptr; diff --git a/src/common/QskArcMetrics.cpp b/src/common/QskArcMetrics.cpp index 991b075d..44d90fde 100644 --- a/src/common/QskArcMetrics.cpp +++ b/src/common/QskArcMetrics.cpp @@ -11,6 +11,10 @@ static void qskRegisterArcMetrics() { qRegisterMetaType< QskArcMetrics >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskArcMetrics >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics ) diff --git a/src/common/QskAspect.h b/src/common/QskAspect.h index 94e5bd42..4b5f03bc 100644 --- a/src/common/QskAspect.h +++ b/src/common/QskAspect.h @@ -109,8 +109,18 @@ class QSK_EXPORT QskAspect constexpr QskAspect operator|( Type ) const noexcept; constexpr QskAspect operator|( Primitive ) const noexcept; constexpr QskAspect operator|( Placement ) const noexcept; + constexpr QskAspect operator|( State ) const noexcept; + QskAspect& operator|=( State ) noexcept; + + constexpr QskAspect operator&( State ) const noexcept; + QskAspect& operator&=( State ) noexcept; + constexpr QskAspect operator|( States ) const noexcept; + QskAspect& operator|=( States ) noexcept; + + constexpr QskAspect operator&( States ) const noexcept; + QskAspect& operator&=( States ) noexcept; constexpr QskAspect stateless() const noexcept; constexpr QskAspect trunk() const noexcept; @@ -283,12 +293,48 @@ inline constexpr QskAspect QskAspect::operator|( State state ) const noexcept m_bits.primitive, m_bits.placement, m_bits.states | state ); } +inline QskAspect& QskAspect::operator|=( State state ) noexcept +{ + m_bits.states |= state; + return *this; +} + +inline constexpr QskAspect QskAspect::operator&( State state ) const noexcept +{ + return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, + m_bits.primitive, m_bits.placement, m_bits.states & state ); +} + +inline QskAspect& QskAspect::operator&=( State state ) noexcept +{ + m_bits.states &= state; + return *this; +} + inline constexpr QskAspect QskAspect::operator|( States states ) const noexcept { return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, m_bits.primitive, m_bits.placement, m_bits.states | states ); } +inline QskAspect& QskAspect::operator|=( States states ) noexcept +{ + m_bits.states |= states; + return *this; +} + +inline constexpr QskAspect QskAspect::operator&( States states ) const noexcept +{ + return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, + m_bits.primitive, m_bits.placement, m_bits.states & states ); +} + +inline QskAspect& QskAspect::operator&=( States states ) noexcept +{ + m_bits.states &= states; + return *this; +} + inline constexpr QskAspect QskAspect::stateless() const noexcept { return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, @@ -538,6 +584,11 @@ namespace std }; } +inline QskHashValue qHash( const QskAspect aspect, QskHashValue seed = 0 ) noexcept +{ + return qHash( aspect.value(), seed ); +} + #ifndef QT_NO_DEBUG_STREAM class QDebug; diff --git a/src/common/QskBoxBorderColors.cpp b/src/common/QskBoxBorderColors.cpp index 054696dd..80493b20 100644 --- a/src/common/QskBoxBorderColors.cpp +++ b/src/common/QskBoxBorderColors.cpp @@ -13,6 +13,10 @@ static void qskRegisterBoxBorderColors() { qRegisterMetaType< QskBoxBorderColors >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskBoxBorderColors >(); +#endif + QMetaType::registerConverter< QColor, QskBoxBorderColors >( []( const QColor& color ) { return QskBoxBorderColors( color ); } ); @@ -110,6 +114,26 @@ void QskBoxBorderColors::setGradientAt( Qt::Edges edges, const QskGradient& grad m_gradients[ Qsk::Bottom ] = gradient; } +void QskBoxBorderColors::setLeft( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Left ] = gradient; +} + +void QskBoxBorderColors::setTop( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Top ] = gradient; +} + +void QskBoxBorderColors::setRight( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Right ] = gradient; +} + +void QskBoxBorderColors::setBottom( const QskGradient& gradient ) +{ + m_gradients[ Qsk::Bottom ] = gradient; +} + const QskGradient& QskBoxBorderColors::gradientAt( Qt::Edge edge ) const { switch ( edge ) @@ -165,6 +189,14 @@ bool QskBoxBorderColors::isMonochrome() const && m_gradients[ 3 ].isMonochrome(); } +bool QskBoxBorderColors::isValid() const +{ + return m_gradients[ 0 ].isValid() + || m_gradients[ 1 ].isValid() + || m_gradients[ 2 ].isValid() + || m_gradients[ 3 ].isValid(); +} + QskBoxBorderColors QskBoxBorderColors::interpolated( const QskBoxBorderColors& to, qreal ratio ) const { @@ -172,8 +204,14 @@ QskBoxBorderColors QskBoxBorderColors::interpolated( for ( size_t i = 0; i < 4; i++ ) { - colors.m_gradients[ i ] = colors.m_gradients[ i ].interpolated( - to.m_gradients[ i ], ratio ); +#if 1 + /* + When one border has a width of 0 we would prefer to ignore + the color and use always use the other color. TODO ... + */ +#endif + auto& gradient = colors.m_gradients[ i ]; + gradient = gradient.interpolated( to.m_gradients[ i ], ratio ); } return colors; @@ -204,14 +242,44 @@ QDebug operator<<( QDebug debug, const QskBoxBorderColors& colors ) QDebugStateSaver saver( debug ); debug.nospace(); - debug << "BoxBorderColors" << '('; + debug << "BoxBorderColors"; - debug << " L" << colors.gradient( Qsk::Left ); - debug << ", T" << colors.gradient( Qsk::Top ); - debug << ", R" << colors.gradient( Qsk::Right ); - debug << ", B" << colors.gradient( Qsk::Bottom ); + if ( !colors.isValid() ) + { + debug << "()"; + } + else + { + debug << "( "; - debug << " )"; + if ( colors.isMonochrome() ) + { + const auto& gradient = colors.gradient( Qsk::Left ); + QskRgb::debugColor( debug, gradient.startColor() ); + } + else + { + const char prompts[] = { 'L', 'T', 'R', 'B' }; + + for ( int i = 0; i <= Qsk::Bottom; i++ ) + { + if ( i != 0 ) + debug << ", "; + + const auto& gradient = colors.gradient( + static_cast< Qsk::Position >( i ) ); + + debug << prompts[ i ] << ": "; + + if ( gradient.isValid() && gradient.isMonochrome() ) + QskRgb::debugColor( debug, gradient.startColor() ); + else + debug << gradient; + } + } + + debug << " )"; + } return debug; } diff --git a/src/common/QskBoxBorderColors.h b/src/common/QskBoxBorderColors.h index b6ca4b68..6011c2d9 100644 --- a/src/common/QskBoxBorderColors.h +++ b/src/common/QskBoxBorderColors.h @@ -12,10 +12,15 @@ #include #include -class QDebug; - class QSK_EXPORT QskBoxBorderColors { + Q_GADGET + + Q_PROPERTY( QskGradient left READ left WRITE setLeft ) + Q_PROPERTY( QskGradient top READ top WRITE setTop ) + Q_PROPERTY( QskGradient right READ right WRITE setRight ) + Q_PROPERTY( QskGradient bottom READ bottom WRITE setBottom ) + public: QskBoxBorderColors(); @@ -44,6 +49,18 @@ class QSK_EXPORT QskBoxBorderColors void setGradientAt( Qt::Edges, const QskGradient& ); const QskGradient& gradientAt( Qt::Edge ) const; + void setLeft( const QskGradient& ); + const QskGradient& left() const; + + void setTop( const QskGradient& ); + const QskGradient& top() const; + + void setRight( const QskGradient& ); + const QskGradient& right() const; + + void setBottom( const QskGradient& ); + const QskGradient& bottom() const; + QskBoxBorderColors interpolated( const QskBoxBorderColors&, qreal value ) const; static QVariant interpolate( const QskBoxBorderColors&, @@ -53,6 +70,7 @@ class QSK_EXPORT QskBoxBorderColors bool isMonochrome() const; bool isVisible() const; + bool isValid() const; private: QskGradient m_gradients[ 4 ]; @@ -78,8 +96,29 @@ inline const QskGradient& QskBoxBorderColors::gradient( Qsk::Position position ) return m_gradients[ position ]; } +inline const QskGradient& QskBoxBorderColors::left() const +{ + return m_gradients[ Qsk::Left ]; +} + +inline const QskGradient& QskBoxBorderColors::top() const +{ + return m_gradients[ Qsk::Top ]; +} + +inline const QskGradient& QskBoxBorderColors::right() const +{ + return m_gradients[ Qsk::Right ]; +} + +inline const QskGradient& QskBoxBorderColors::bottom() const +{ + return m_gradients[ Qsk::Bottom ]; +} + #ifndef QT_NO_DEBUG_STREAM +class QDebug; QSK_EXPORT QDebug operator<<( QDebug, const QskBoxBorderColors& ); #endif diff --git a/src/common/QskBoxBorderMetrics.cpp b/src/common/QskBoxBorderMetrics.cpp index 25de9fe6..18178062 100644 --- a/src/common/QskBoxBorderMetrics.cpp +++ b/src/common/QskBoxBorderMetrics.cpp @@ -12,6 +12,10 @@ static void qskRegisterBoxBorderMetrics() { qRegisterMetaType< QskBoxBorderMetrics >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskBoxBorderMetrics >(); +#endif + QMetaType::registerConverter< QskMargins, QskBoxBorderMetrics >( []( const QskMargins& margins ) { return QskBoxBorderMetrics( margins ); } ); @@ -109,9 +113,24 @@ QDebug operator<<( QDebug debug, const QskBoxBorderMetrics& metrics ) QDebugStateSaver saver( debug ); debug.nospace(); - debug << "BoxBorder" << '('; - debug << metrics.sizeMode() << ',' << metrics.widths(); - debug << ')'; + debug << "BoxBorder" << "( "; + + if ( metrics.sizeMode() != Qt::AbsoluteSize ) + debug << metrics.sizeMode() << ", "; + + const auto& w = metrics.widths(); + + if ( metrics.isEquidistant() ) + { + debug << w.left(); + } + else + { + const char s[] = ", "; + debug << w.left() << s << w.top() << s << w.right() << s << w.bottom(); + } + + debug << " )"; return debug; } diff --git a/src/common/QskBoxBorderMetrics.h b/src/common/QskBoxBorderMetrics.h index 10b7961c..802d2c75 100644 --- a/src/common/QskBoxBorderMetrics.h +++ b/src/common/QskBoxBorderMetrics.h @@ -60,6 +60,8 @@ class QSK_EXPORT QskBoxBorderMetrics static QVariant interpolate( const QskBoxBorderMetrics&, const QskBoxBorderMetrics&, qreal progress ); + constexpr bool isEquidistant() const noexcept; + private: QskMargins m_widths; Qt::SizeMode m_sizeMode; @@ -115,6 +117,11 @@ inline constexpr bool QskBoxBorderMetrics::isNull() const noexcept return m_widths.isNull(); } +inline constexpr bool QskBoxBorderMetrics::isEquidistant() const noexcept +{ + return m_widths.isEquidistant(); +} + inline constexpr const QskMargins& QskBoxBorderMetrics::widths() const noexcept { return m_widths; diff --git a/src/common/QskBoxShapeMetrics.cpp b/src/common/QskBoxShapeMetrics.cpp index 09e9cc70..17c21be1 100644 --- a/src/common/QskBoxShapeMetrics.cpp +++ b/src/common/QskBoxShapeMetrics.cpp @@ -14,6 +14,10 @@ static void qskRegisterBoxShapeMetrics() { qRegisterMetaType< QskBoxShapeMetrics >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskBoxShapeMetrics >(); +#endif + QMetaType::registerConverter< int, QskBoxShapeMetrics >( []( int radius ) { return QskBoxShapeMetrics( radius ); } ); diff --git a/src/common/QskGradient.cpp b/src/common/QskGradient.cpp index 4da8eff8..82661437 100644 --- a/src/common/QskGradient.cpp +++ b/src/common/QskGradient.cpp @@ -15,6 +15,10 @@ static void qskRegisterGradient() { qRegisterMetaType< QskGradient >(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskGradient >(); +#endif + QMetaType::registerConverter< QColor, QskGradient >( []( const QColor& color ) { return QskGradient( color ); } ); } @@ -229,6 +233,11 @@ QskGradient::QskGradient( Orientation orientation, const QskGradientStops& stops setStops( stops ); } +QskGradient::QskGradient( Qt::Orientation orientation, QGradient::Preset preset ) + : QskGradient( qskOrientation( orientation ), preset ) +{ +} + QskGradient::QskGradient( Orientation orientation, QGradient::Preset preset ) : QskGradient( orientation ) { @@ -499,6 +508,14 @@ QskGradient QskGradient::interpolated( return QskGradient( gradient->orientation(), stops ); } + if ( isMonochrome() && to.isMonochrome() ) + { + const auto c = QskRgb::interpolated( + m_stops[ 0 ].color(), to.m_stops[ 0 ].color(), value ); + + return QskGradient( to.orientation(), c, c ); + } + if ( isMonochrome() ) { // we can ignore our stops @@ -624,7 +641,49 @@ void QskGradient::updateStatusBits() const QDebug operator<<( QDebug debug, const QskGradient& gradient ) { - debug << "GR:" << gradient.orientation() << gradient.stops().count(); + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << "Gradient"; + + if ( !gradient.isValid() ) + { + debug << "()"; + } + else + { + debug << "( "; + + if ( gradient.isMonochrome() ) + { + QskRgb::debugColor( debug, gradient.startColor() ); + } + else + { + const char o[] = { 'H', 'V', 'D' }; + debug << o[ gradient.orientation() ] << ", "; + + if ( gradient.stops().count() == 2 ) + { + QskRgb::debugColor( debug, gradient.startColor() ); + debug << ", "; + QskRgb::debugColor( debug, gradient.endColor() ); + } + else + { + const auto& s = gradient.stops(); + for ( int i = 0; i < s.count(); i++ ) + { + if ( i != 0 ) + debug << ", "; + + debug << s[i]; + } + } + } + debug << " )"; + } + return debug; } diff --git a/src/common/QskGradient.h b/src/common/QskGradient.h index 75005926..be90ad5a 100644 --- a/src/common/QskGradient.h +++ b/src/common/QskGradient.h @@ -52,6 +52,7 @@ class QSK_EXPORT QskGradient QskGradient( Qt::Orientation, const QVector< QskGradientStop >& ); QskGradient( Qt::Orientation, const QColor&, const QColor& ); + QskGradient( Qt::Orientation, QGradient::Preset ); QskGradient( Orientation, const QVector< QskGradientStop >& ); QskGradient( Orientation, const QColor&, const QColor& ); diff --git a/src/common/QskGradientStop.cpp b/src/common/QskGradientStop.cpp index 530a2a1d..48dbdac2 100644 --- a/src/common/QskGradientStop.cpp +++ b/src/common/QskGradientStop.cpp @@ -14,6 +14,10 @@ static void qskRegisterGradientStop() { qRegisterMetaType< QskGradientStop >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskGradientStop >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop ) @@ -78,7 +82,12 @@ QColor QskGradientStop::interpolated( QDebug operator<<( QDebug debug, const QskGradientStop& stop ) { - debug << stop.position() << ": " << stop.color(); + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << stop.position() << ": "; + QskRgb::debugColor( debug, stop.color() ); + return debug; } diff --git a/src/common/QskIntervalF.cpp b/src/common/QskIntervalF.cpp index a27a83c8..f4b19d38 100644 --- a/src/common/QskIntervalF.cpp +++ b/src/common/QskIntervalF.cpp @@ -12,6 +12,10 @@ static void qskRegisterIntervalF() { qRegisterMetaType< QskIntervalF >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskIntervalF >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterIntervalF ) diff --git a/src/common/QskMargins.h b/src/common/QskMargins.h index e90c9338..3a3bb962 100644 --- a/src/common/QskMargins.h +++ b/src/common/QskMargins.h @@ -61,6 +61,7 @@ class QSK_EXPORT QskMargins : public QMarginsF QskMargins interpolated( const QskMargins&, qreal progress ) const noexcept; constexpr bool isExpanding() const noexcept; + constexpr bool isEquidistant() const noexcept; static QVariant interpolate( const QskMargins&, const QskMargins&, qreal progress ) noexcept; @@ -181,6 +182,11 @@ constexpr inline qreal QskMargins::height() const noexcept return top() + bottom(); } +inline constexpr bool QskMargins::isEquidistant() const noexcept +{ + return ( left() == top() ) && ( left() == right() ) && ( left() == bottom() ); +} + Q_DECLARE_TYPEINFO( QskMargins, Q_MOVABLE_TYPE ); Q_DECLARE_METATYPE( QskMargins ) diff --git a/src/common/QskRgbValue.cpp b/src/common/QskRgbValue.cpp index 49a87f96..7b4c0b8c 100644 --- a/src/common/QskRgbValue.cpp +++ b/src/common/QskRgbValue.cpp @@ -172,3 +172,29 @@ QRgb QskRgb::darker( QRgb rgb, int factor ) noexcept return QColor::fromRgba( rgb ).darker( factor ).rgba(); } +#ifndef QT_NO_DEBUG_STREAM + +#include + +void QskRgb::debugColor( QDebug debug, const QColor& color ) +{ + debugColor( debug, color.rgba() ); +} + +void QskRgb::debugColor( QDebug debug, QRgb rgb ) +{ + QDebugStateSaver saver( debug ); + debug.nospace(); + + debug << '['; + + debug << qRed( rgb ) << "r," << qGreen( rgb ) << "g," + << qBlue( rgb ) << 'b'; + + if ( qAlpha( rgb ) != 255 ) + debug << ',' << qAlpha( rgb ) << 'a'; + + debug << ']'; +} + +#endif diff --git a/src/common/QskRgbValue.h b/src/common/QskRgbValue.h index 6fea8d95..8a6059de 100644 --- a/src/common/QskRgbValue.h +++ b/src/common/QskRgbValue.h @@ -461,23 +461,35 @@ namespace QskRgb return ( rgb & ColorMask ) | ( ( static_cast< uint >( alpha ) & 0xffu ) << 24 ); } - inline QColor toTransparentF( const QColor& color, qreal alpha ) + inline QColor toTransparentF( const QColor& color, qreal opacity ) { - return toTransparent( color, qRound( alpha * 255 ) ); + return toTransparent( color, qRound( opacity * 255 ) ); } - inline QColor toTransparentF( Qt::GlobalColor color, qreal alpha ) + inline QColor toTransparentF( Qt::GlobalColor color, qreal opacity ) { - return toTransparent( QColor( color ), qRound( alpha * 255 ) ); + return toTransparent( QColor( color ), qRound( opacity * 255 ) ); } - inline constexpr QRgb toTransparentF( QRgb rgb, qreal alpha ) noexcept + inline constexpr QRgb toTransparentF( QRgb rgb, qreal opacity ) noexcept { - return toTransparent( rgb, qRound( alpha * 255 ) ); + return toTransparent( rgb, qRound( opacity * 255 ) ); } QSK_EXPORT QRgb lighter( QRgb, int factor = 150 ) noexcept; QSK_EXPORT QRgb darker( QRgb, int factor = 200 ) noexcept; } +#ifndef QT_NO_DEBUG_STREAM + +class QDebug; + +namespace QskRgb +{ + QSK_EXPORT void debugColor( QDebug, const QColor& ); + QSK_EXPORT void debugColor( QDebug, QRgb ); +} + +#endif + #endif diff --git a/src/common/QskScaleTickmarks.cpp b/src/common/QskScaleTickmarks.cpp index 836fc474..65f67089 100644 --- a/src/common/QskScaleTickmarks.cpp +++ b/src/common/QskScaleTickmarks.cpp @@ -9,6 +9,10 @@ static void qskRegisterTickmarks() { qRegisterMetaType< QskScaleTickmarks >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskScaleTickmarks >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterTickmarks ) diff --git a/src/common/QskShadowMetrics.cpp b/src/common/QskShadowMetrics.cpp index 37b951b0..384104d3 100644 --- a/src/common/QskShadowMetrics.cpp +++ b/src/common/QskShadowMetrics.cpp @@ -12,6 +12,10 @@ static void qskRegisterShadowMetrics() { qRegisterMetaType< QskShadowMetrics >(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QMetaType::registerEqualsComparator< QskShadowMetrics >(); +#endif } Q_CONSTRUCTOR_FUNCTION( qskRegisterShadowMetrics ) diff --git a/src/common/QskStateCombination.cpp b/src/common/QskStateCombination.cpp deleted file mode 100644 index 07a7b481..00000000 --- a/src/common/QskStateCombination.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the QSkinny License, Version 1.0 - *****************************************************************************/ - -#include "QskStateCombination.h" - -static void qskRegisterStateCombination() -{ - qRegisterMetaType< QskStateCombination >(); -} - -Q_CONSTRUCTOR_FUNCTION( qskRegisterStateCombination ) - diff --git a/src/common/QskStateCombination.h b/src/common/QskStateCombination.h index 8393c90b..13d64e22 100644 --- a/src/common/QskStateCombination.h +++ b/src/common/QskStateCombination.h @@ -21,6 +21,9 @@ class QSK_EXPORT QskStateCombination constexpr QskStateCombination( QskAspect::States = QskAspect::States() ) noexcept; constexpr QskStateCombination( Type, QskAspect::States = QskAspect::States() ) noexcept; + constexpr bool operator==( QskStateCombination ) const noexcept; + constexpr bool operator!=( QskStateCombination ) const noexcept; + constexpr bool isNull() const noexcept; void setType( Type ) noexcept; @@ -36,7 +39,6 @@ class QSK_EXPORT QskStateCombination }; Q_DECLARE_TYPEINFO( QskStateCombination, Q_MOVABLE_TYPE ); -Q_DECLARE_METATYPE( QskStateCombination ) constexpr inline QskStateCombination::QskStateCombination( QskAspect::State state ) noexcept @@ -90,4 +92,14 @@ constexpr inline QskAspect::States QskStateCombination::states() const noexcept return m_states; } +constexpr bool QskStateCombination::operator==( QskStateCombination other ) const noexcept +{ + return ( m_type == other.m_type ) && ( m_states == other.m_states ); +} + +constexpr bool QskStateCombination::operator!=( QskStateCombination other ) const noexcept +{ + return !( *this == other ); +} + #endif diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index f99dc41a..85215d98 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -128,7 +128,6 @@ class QskSkin::PrivateData std::unordered_map< const QMetaObject*, SkinletData > skinletMap; QskSkinHintTable hintTable; - QskAspect::States stateMask = QskAspect::AllStates; std::unordered_map< int, QFont > fonts; std::unordered_map< int, QskColorFilter > graphicFilters; @@ -345,22 +344,6 @@ const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const return QPlatformDialogHelper::buttonLayout( orientation, policy ); } -void QskSkin::setStateMask( QskAspect::States mask ) -{ - for ( auto state : { QskControl::Disabled, QskControl::Hovered, QskControl::Focused } ) - { - if ( mask & state ) - m_data->stateMask |= state; - else - m_data->stateMask &= ~state; - } -} - -QskAspect::States QskSkin::stateMask() const -{ - return m_data->stateMask; -} - QskSkinlet* QskSkin::skinlet( const QMetaObject* metaObject ) { while ( metaObject ) diff --git a/src/controls/QskSkin.h b/src/controls/QskSkin.h index adeb2c54..723b0115 100644 --- a/src/controls/QskSkin.h +++ b/src/controls/QskSkin.h @@ -77,9 +77,6 @@ class QSK_EXPORT QskSkin : public QObject virtual const int* dialogButtonLayout( Qt::Orientation ) const; virtual QString dialogButtonText( int button ) const; - void setStateMask( QskAspect::States ); - QskAspect::States stateMask() const; - QskSkinlet* skinlet( const QMetaObject* ); const QskSkinHintTable& hintTable() const; diff --git a/src/controls/QskSkinHintTable.cpp b/src/controls/QskSkinHintTable.cpp index 9bac96f0..7b654c65 100644 --- a/src/controls/QskSkinHintTable.cpp +++ b/src/controls/QskSkinHintTable.cpp @@ -64,41 +64,11 @@ QskSkinHintTable::QskSkinHintTable() { } -QskSkinHintTable::QskSkinHintTable( const QskSkinHintTable& other ) - : m_hints( nullptr ) - , m_animatorCount( other.m_animatorCount ) - , m_statefulCount( other.m_statefulCount ) -{ - if ( other.m_hints ) - m_hints = new HintMap( *( other.m_hints ) ); -} - QskSkinHintTable::~QskSkinHintTable() { delete m_hints; } -QskSkinHintTable& QskSkinHintTable::operator=( const QskSkinHintTable& other ) -{ - m_animatorCount = other.m_animatorCount; - m_statefulCount = other.m_statefulCount; - - if ( other.m_hints ) - { - if ( m_hints == nullptr ) - m_hints = new HintMap(); - - *m_hints = *other.m_hints; - } - else - { - delete m_hints; - m_hints = nullptr; - } - - return *this; -} - const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const { if ( m_hints ) @@ -126,11 +96,7 @@ bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint ) QSK_ASSERT_COUNTER( m_animatorCount ); } - if ( aspect.hasStates() ) - { - m_statefulCount++; - QSK_ASSERT_COUNTER( m_statefulCount ); - } + m_states |= aspect.states(); return true; } @@ -158,8 +124,7 @@ bool QskSkinHintTable::removeHint( QskAspect aspect ) if ( aspect.isAnimator() ) m_animatorCount--; - if ( aspect.hasStates() ) - m_statefulCount--; + // how to clear m_states ? TODO ... if ( m_hints->empty() ) { @@ -184,8 +149,7 @@ QVariant QskSkinHintTable::takeHint( QskAspect aspect ) if ( aspect.isAnimator() ) m_animatorCount--; - if ( aspect.hasStates() ) - m_statefulCount--; + // how to clear m_states ? TODO ... if ( m_hints->empty() ) { @@ -206,14 +170,14 @@ void QskSkinHintTable::clear() m_hints = nullptr; m_animatorCount = 0; - m_statefulCount = 0; + m_states = QskAspect::NoState; } const QVariant* QskSkinHintTable::resolvedHint( QskAspect aspect, QskAspect* resolvedAspect ) const { if ( m_hints != nullptr ) - return qskResolvedHint( aspect, *m_hints, resolvedAspect ); + return qskResolvedHint( aspect & m_states, *m_hints, resolvedAspect ); return nullptr; } @@ -223,7 +187,7 @@ QskAspect QskSkinHintTable::resolvedAspect( QskAspect aspect ) const QskAspect a; if ( m_hints != nullptr ) - qskResolvedHint( aspect, *m_hints, &a ); + qskResolvedHint( aspect & m_states, *m_hints, &a ); return a; } @@ -233,6 +197,8 @@ QskAspect QskSkinHintTable::resolvedAnimator( { if ( m_hints && m_animatorCount > 0 ) { + aspect &= m_states; + Q_FOREVER { auto it = m_hints->find( aspect ); @@ -268,15 +234,16 @@ bool QskSkinHintTable::setAnimation( bool QskSkinHintTable::isResolutionMatching( QskAspect aspect1, QskAspect aspect2 ) const { + // remove states we do not have early + aspect1 &= m_states; + aspect2 &= m_states; + if ( aspect1 == aspect2 ) return true; if ( aspect1.trunk() != aspect2.trunk() ) return false; - if ( !hasStates() ) - return false; - const auto a1 = aspect1; const auto a2 = aspect2; diff --git a/src/controls/QskSkinHintTable.h b/src/controls/QskSkinHintTable.h index c1e6a647..9ae56be9 100644 --- a/src/controls/QskSkinHintTable.h +++ b/src/controls/QskSkinHintTable.h @@ -17,12 +17,8 @@ class QSK_EXPORT QskSkinHintTable { public: QskSkinHintTable(); - QskSkinHintTable( const QskSkinHintTable& other ); - ~QskSkinHintTable(); - QskSkinHintTable& operator=( const QskSkinHintTable& ); - bool setAnimation( QskAspect, QskAnimationHint ); QskAnimationHint animation( QskAspect ) const; @@ -40,9 +36,10 @@ class QSK_EXPORT QskSkinHintTable const std::unordered_map< QskAspect, QVariant >& hints() const; bool hasAnimators() const; - bool hasStates() const; bool hasHints() const; + QskAspect::States states() const; + void clear(); const QVariant* resolvedHint( QskAspect, @@ -56,13 +53,15 @@ class QSK_EXPORT QskSkinHintTable bool isResolutionMatching( QskAspect, QskAspect ) const; private: + Q_DISABLE_COPY( QskSkinHintTable ) + static const QVariant invalidHint; typedef std::unordered_map< QskAspect, QVariant > HintMap; HintMap* m_hints = nullptr; unsigned short m_animatorCount = 0; - unsigned short m_statefulCount = 0; + QskAspect::States m_states; }; inline bool QskSkinHintTable::hasHints() const @@ -70,9 +69,9 @@ inline bool QskSkinHintTable::hasHints() const return m_hints != nullptr; } -inline bool QskSkinHintTable::hasStates() const +inline QskAspect::States QskSkinHintTable::states() const { - return m_statefulCount > 0; + return m_states; } inline bool QskSkinHintTable::hasAnimators() const diff --git a/src/controls/QskSkinTransition.cpp b/src/controls/QskSkinTransition.cpp index 9b769199..b96b6753 100644 --- a/src/controls/QskSkinTransition.cpp +++ b/src/controls/QskSkinTransition.cpp @@ -20,6 +20,51 @@ #include #include +static void qskAddCandidates( const QskSkinTransition::Type mask, + const QskSkin* skin, QSet< QskAspect >& candidates ) +{ + for ( const auto& entry : skin->hintTable().hints() ) + { + const auto aspect = entry.first.trunk(); + + if ( aspect.isAnimator() ) + continue; + + bool isCandidate = false; + + switch( aspect.type() ) + { + case QskAspect::Flag: + { + if ( aspect.flagPrimitive() == QskAspect::GraphicRole ) + { + isCandidate = mask & QskSkinTransition::Color; + } +#if 0 + else if ( aspect.flagPrimitive() == QskAspect::FontRole ) + { + isCandidate = mask & QskSkinTransition::Metric; + } +#endif + break; + } + case QskAspect::Color: + { + isCandidate = mask & QskSkinTransition::Color; + break; + } + case QskAspect::Metric: + { + isCandidate = mask & QskSkinTransition::Metric; + break; + } + } + + if ( isCandidate ) + candidates += aspect; + } +} + namespace { class UpdateInfo @@ -40,475 +85,85 @@ namespace int updateModes; }; - class AnimatorCandidate + class HintAnimator : public QskHintAnimator { public: - AnimatorCandidate() = default; - - inline AnimatorCandidate( QskAspect aspect, - const QVariant& from, const QVariant& to ) - : aspect( aspect ) - , from( from ) - , to( to ) + inline HintAnimator( const QskControl* control, const QskAspect aspect, + const QVariant& value1, const QVariant& value2, QskAnimationHint hint ) { - } + setAspect( aspect ); + setStartValue( value1 ); + setEndValue( value2 ); - QskAspect aspect; - QVariant from; - QVariant to; + setDuration( hint.duration ); + setEasingCurve( hint.type ); + setUpdateFlags( hint.updateFlags ); + + setWindow( control->window() ); + } }; -} -static QVector< AnimatorCandidate > qskAnimatorCandidates( - QskSkinTransition::Type mask, - const QskSkinHintTable& oldTable, - const std::unordered_map< int, QskColorFilter >& oldFilters, - const QskSkinHintTable& newTable, - const std::unordered_map< int, QskColorFilter >& newFilters ) -{ - // building a list of candidates for animations by comparing - // the old/new set of skin hints - - const QskColorFilter noFilter; - QVector< AnimatorCandidate > candidates; - - if ( !oldTable.hasHints() ) - return candidates; - - for ( const auto& entry : newTable.hints() ) - { - const auto aspect = entry.first; - - if ( aspect.isAnimator() ) - continue; - - const auto type = aspect.type(); - - if ( type == QskAspect::Flag ) - { - switch ( aspect.flagPrimitive() ) - { - case QskAspect::GraphicRole: - { - if ( !( mask & QskSkinTransition::Color ) ) - continue; - - int role1 = 0; - - const auto value = oldTable.resolvedHint( aspect ); - if ( value ) - role1 = value->toInt(); - - const int role2 = entry.second.toInt(); - - /* - When the role is the same we already have the animators - for the graphic filter table running - */ - if ( role1 != role2 ) - { - const auto it1 = oldFilters.find( role1 ); - const auto it2 = newFilters.find( role2 ); - - if ( it1 != oldFilters.end() || it2 != newFilters.end() ) - { - const auto& f1 = ( it1 != oldFilters.end() ) ? it1->second : noFilter; - const auto& f2 = ( it2 != newFilters.end() ) ? it2->second : noFilter; - - if ( f1 != f2 ) - { - candidates += AnimatorCandidate( aspect, - QVariant::fromValue( f1 ), QVariant::fromValue( f2 ) ); - } - } - } - break; - } - case QskAspect::FontRole: - { - if ( !( mask & QskSkinTransition::Metric ) ) - continue; - - break; - } - default: - ; - } - } - else - { - if ( ( ( type == QskAspect::Color ) && ( mask & QskSkinTransition::Color ) ) || - ( ( type == QskAspect::Metric ) && ( mask & QskSkinTransition::Metric ) ) ) - { - auto value = oldTable.resolvedHint( aspect ); - if ( value == nullptr && aspect.subControl() != QskAspect::Control ) - { - auto a = aspect; - a.setSubControl( QskAspect::Control ); - a.clearStates(); - value = oldTable.resolvedHint( a ); - } - - /* - We are missing transitions, when a hint in newTable - gets resolved from QskControl. TODO ... - */ - if ( value && *value != entry.second ) - candidates += AnimatorCandidate( aspect, *value, entry.second ); - } - } - } - - return candidates; -} - -namespace -{ - class AnimatorGroup + class WindowAnimator { public: - AnimatorGroup( QQuickWindow* window = nullptr ) - : m_window( window ) - { - } + WindowAnimator( QQuickWindow* = nullptr ); - inline const QQuickWindow* window() const - { - return m_window; - } + const QQuickWindow* window() const; - void start() - { - for ( auto& it : m_hintAnimatorMap ) - it.second.start(); + void start(); + bool isRunning() const; - for ( auto& it : m_graphicFilterAnimatorMap ) - it.second.start(); - } + QVariant animatedHint( QskAspect ) const; + QVariant animatedGraphicFilter( int graphicRole ) const; - bool isRunning() const - { - if ( !m_hintAnimatorMap.empty() ) - { - const auto& animator = m_hintAnimatorMap.begin()->second; - if ( animator.isRunning() ) - return true; - } + void addGraphicFilterAnimators( const QskAnimationHint&, + const QskSkin*, const QskSkin* ); - if ( !m_graphicFilterAnimatorMap.empty() ) - { - const auto& animator = m_graphicFilterAnimatorMap.begin()->second; - if ( animator.isRunning() ) - return true; - } + void addItemAspects( QQuickItem*, + const QskAnimationHint&, const QSet< QskAspect >&, + const QskSkin*, const QskSkin* ); - return false; - } - - inline QVariant animatedHint( QskAspect aspect ) const - { - auto it = m_hintAnimatorMap.find( aspect ); - if ( it != m_hintAnimatorMap.cend() ) - { - const auto& animator = it->second; - if ( animator.isRunning() ) - return animator.currentValue(); - } - - return QVariant(); - } - - inline QVariant animatedGraphicFilter( int graphicRole ) const - { - auto it = m_graphicFilterAnimatorMap.find( graphicRole ); - if ( it != m_graphicFilterAnimatorMap.cend() ) - { - const auto& animator = it->second; - if ( animator.isRunning() ) - return animator.currentValue(); - } - - return QVariant(); - } - - void addGraphicFilterAnimators( - const QskAnimationHint& animatorHint, - const std::unordered_map< int, QskColorFilter >& oldFilters, - const std::unordered_map< int, QskColorFilter >& newFilters ) - { - const QskColorFilter noFilter; - - for ( auto it2 = newFilters.begin(); it2 != newFilters.end(); ++it2 ) - { - auto it1 = oldFilters.find( it2->first ); - if ( it1 == oldFilters.cend() ) - it1 = oldFilters.find( 0 ); - - const auto& f1 = ( it1 != oldFilters.cend() ) ? it1->second : noFilter; - const auto& f2 = it2->second; - - if ( f1 != f2 ) - { - QskVariantAnimator animator; - animator.setWindow( m_window ); - animator.setDuration( animatorHint.duration ); - animator.setEasingCurve( animatorHint.type ); - animator.setStartValue( QVariant::fromValue( f1 ) ); - animator.setEndValue( QVariant::fromValue( f2 ) ); - - m_graphicFilterAnimatorMap.emplace( it2->first, animator ); - } - } - } - - void addAnimators( QQuickItem* item, const QskAnimationHint& animatorHint, - const QVector< AnimatorCandidate >& candidates, QskSkin* skin ) - { - if ( !item->isVisible() ) - return; - - if ( auto control = qskControlCast( item ) ) - { - if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) ) - { - addControlAnimators( control, animatorHint, candidates ); -#if 1 - /* - As it is hard to identify which controls depend on the animated - graphic filters we schedule an initial update and let the - controls do the rest: see QskSkinnable::effectiveGraphicFilter - */ - control->update(); -#endif - } - } - - const auto children = item->childItems(); - for ( auto child : children ) - addAnimators( child, animatorHint, candidates, skin ); - } - - void update() - { - for ( auto& info : m_updateInfos ) - { - if ( auto control = info.control ) - { - if ( info.updateModes & UpdateInfo::Polish ) - { - control->resetImplicitSize(); - control->polish(); - } - - if ( info.updateModes & UpdateInfo::Update ) - control->update(); - } - } - } + void update(); private: - void addControlAnimators( QskControl* control, const QskAnimationHint& animatorHint, - const QVector< AnimatorCandidate >& candidates ) - { - const auto subControls = control->subControls(); + bool isControlAffected( const QskControl*, + const QVector< QskAspect::Subcontrol >&, QskAspect ) const; - for ( const auto& candidate : candidates ) - { - if ( !candidate.aspect.isMetric() ) - { - if ( !( control->flags() & QQuickItem::ItemHasContents ) ) - { - // while metrics might have an effect on layouts, we - // can safely ignore others for controls without content - continue; - } - } + void addHints( const QskControl*, + const QskAnimationHint&, const QSet< QskAspect >& candidates, + const QskSkin* skin1, const QskSkin* skin2 ); - const auto subControl = candidate.aspect.subControl(); - if ( subControl != control->effectiveSubcontrol( subControl ) ) - { - // The control uses subcontrol redirection, so we can assume it - // is not interested in this subcontrol. - continue; - } + void storeAnimator( const QskControl*, const QskAspect, + const QVariant&, const QVariant&, QskAnimationHint ); - if ( subControl != QskAspect::Control ) - { - if ( !subControls.contains( subControl ) ) - { - // the control is not interested in the aspect - continue; - } - } - else - { - if ( !control->autoFillBackground() ) - { - // no need to animate the background unless we show it - continue; - } - } - - auto a = candidate.aspect; - a.setStates( control->skinStates() ); - - const auto requestState = control->hintStatus( a ); - - if ( requestState.source != QskSkinHintStatus::Skin ) - { - // The control does not resolve the aspect from the skin. - continue; - } - - if ( candidate.aspect != requestState.aspect ) - { - // the aspect was resolved to something else - continue; - } - - addAnimator( control->window(), candidate, animatorHint ); - storeUpdateInfo( control, candidate.aspect ); - } - } - - void addAnimator( QQuickWindow* window, - const AnimatorCandidate& candidate, QskAnimationHint animationHint ) - { - auto it = m_hintAnimatorMap.find( candidate.aspect ); - if ( it != m_hintAnimatorMap.end() ) - return; // already there - - it = m_hintAnimatorMap.emplace( candidate.aspect, QskHintAnimator() ).first; - auto& animator = it->second; - - animator.setAspect( candidate.aspect ); - animator.setStartValue( candidate.from ); - animator.setEndValue( candidate.to ); - - animator.setDuration( animationHint.duration ); - animator.setEasingCurve( animationHint.type ); - animator.setUpdateFlags( animationHint.updateFlags ); - - animator.setControl( nullptr ); - animator.setWindow( window ); - } - - inline void storeUpdateInfo( QskControl* control, QskAspect aspect ) - { - UpdateInfo info; - info.control = control; - - info.updateModes = UpdateInfo::Update; - if ( aspect.isMetric() ) - info.updateModes |= UpdateInfo::Polish; - - auto it = std::lower_bound( - m_updateInfos.begin(), m_updateInfos.end(), info, UpdateInfo::compare ); - - if ( ( it != m_updateInfos.end() ) && ( it->control == info.control ) ) - it->updateModes |= info.updateModes; - else - m_updateInfos.insert( it, info ); - } + void storeUpdateInfo( const QskControl*, QskAspect ); QQuickWindow* m_window; - std::unordered_map< QskAspect, QskHintAnimator > m_hintAnimatorMap; + std::unordered_map< QskAspect, HintAnimator > m_animatorMap; std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap; std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration }; - class AnimatorGroups : public QObject + class ApplicationAnimator : public QObject { Q_OBJECT public: - ~AnimatorGroups() - { - reset(); - } + ~ApplicationAnimator(); - inline AnimatorGroup* animatorGroup( const QQuickWindow* window ) - { - if ( !m_animatorGroups.empty() && window ) - { - for ( auto group : m_animatorGroups ) - { - if ( group->window() == window ) - return group; - } - } + WindowAnimator* windowAnimator( const QQuickWindow* ); - return nullptr; - } + void add( WindowAnimator* ); - void add( AnimatorGroup* group ) - { - m_animatorGroups.push_back( group ); - } - - void start() - { - m_connections[0] = QskAnimator::addAdvanceHandler( - this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection ); - - m_connections[1] = QskAnimator::addCleanupHandler( - this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection ); - - for ( auto& group : m_animatorGroups ) - group->start(); - } - - void reset() - { - qDeleteAll( m_animatorGroups ); - m_animatorGroups.clear(); - - disconnect( m_connections[0] ); - disconnect( m_connections[1] ); - } - - inline bool isRunning() const - { - return !m_animatorGroups.empty(); - } + void start(); + void reset(); + bool isRunning() const; private Q_SLOTS: - void notify( QQuickWindow* window ) - { - for ( auto& group : m_animatorGroups ) - { - if ( group->window() == window ) - { - group->update(); - return; - } - } - } - - void cleanup( QQuickWindow* window ) - { - for ( auto it = m_animatorGroups.begin(); - it != m_animatorGroups.end(); ++it ) - { - auto group = *it; - if ( group->window() == window ) - { - if ( !group->isRunning() ) - { - // The notification might be for other animators - - m_animatorGroups.erase( it ); - delete group; - } - - break; - } - } - - if ( m_animatorGroups.empty() ) - reset(); - } + // using functor slots ? + void notify( QQuickWindow* ); + void cleanup( QQuickWindow* ); private: /* @@ -518,12 +173,372 @@ namespace But as skin transitions are no operations, that happen often, we can accept the overhaed of the current implementation and do the finetuning later. */ - std::vector< AnimatorGroup* > m_animatorGroups; + std::vector< WindowAnimator* > m_windowAnimators; QMetaObject::Connection m_connections[2]; }; } -Q_GLOBAL_STATIC( AnimatorGroups, qskSkinAnimator ) +Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator ) + +WindowAnimator::WindowAnimator( QQuickWindow* window ) + : m_window( window ) +{ +} + +inline const QQuickWindow* WindowAnimator::window() const +{ + return m_window; +} + +void WindowAnimator::start() +{ + for ( auto& it : m_animatorMap ) + it.second.start(); + + for ( auto& it : m_graphicFilterAnimatorMap ) + it.second.start(); +} + +bool WindowAnimator::isRunning() const +{ + if ( !m_animatorMap.empty() ) + { + const auto& animator = m_animatorMap.begin()->second; + if ( animator.isRunning() ) + return true; + } + + if ( !m_graphicFilterAnimatorMap.empty() ) + { + const auto& animator = m_graphicFilterAnimatorMap.begin()->second; + if ( animator.isRunning() ) + return true; + } + + return false; +} + +inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const +{ + auto it = m_animatorMap.find( aspect ); + if ( it != m_animatorMap.cend() ) + { + const auto& animator = it->second; + if ( animator.isRunning() ) + return animator.currentValue(); + } + + return QVariant(); +} + +inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const +{ + auto it = m_graphicFilterAnimatorMap.find( graphicRole ); + if ( it != m_graphicFilterAnimatorMap.cend() ) + { + const auto& animator = it->second; + if ( animator.isRunning() ) + return animator.currentValue(); + } + + return QVariant(); +} + +void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint, + const QskSkin* skin1, const QskSkin* skin2 ) +{ + const QskColorFilter noFilter; + + const auto& filter1 = skin1->graphicFilters(); + const auto& filter2 = skin2->graphicFilters(); + + for ( auto it2 = filter2.begin(); it2 != filter2.end(); ++it2 ) + { + auto it1 = filter1.find( it2->first ); + if ( it1 == filter1.cend() ) + it1 = filter1.find( 0 ); + + const auto& f1 = ( it1 != filter1.cend() ) ? it1->second : noFilter; + const auto& f2 = it2->second; + + if ( f1 != f2 ) + { + QskVariantAnimator animator; + animator.setWindow( m_window ); + animator.setDuration( animatorHint.duration ); + animator.setEasingCurve( animatorHint.type ); + animator.setStartValue( QVariant::fromValue( f1 ) ); + animator.setEndValue( QVariant::fromValue( f2 ) ); + + m_graphicFilterAnimatorMap.emplace( it2->first, animator ); + } + } +} + +void WindowAnimator::addItemAspects( QQuickItem* item, + const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, + const QskSkin* skin1, const QskSkin* skin2 ) +{ + if ( !item->isVisible() ) + return; + + if ( auto control = qskControlCast( item ) ) + { + if ( control->isInitiallyPainted() && ( control->effectiveSkin() == skin2 ) ) + { + addHints( control, animatorHint, candidates, skin1, skin2 ); +#if 1 + /* + As it is hard to identify which controls depend on the animated + graphic filters we schedule an initial update and let the + controls do the rest: see QskSkinnable::effectiveGraphicFilter + */ + control->update(); +#endif + } + } + + const auto children = item->childItems(); + for ( auto child : children ) + addItemAspects( child, animatorHint, candidates, skin1, skin2 ); +} + +void WindowAnimator::update() +{ + for ( auto& info : m_updateInfos ) + { + if ( auto control = info.control ) + { + if ( info.updateModes & UpdateInfo::Polish ) + { + control->resetImplicitSize(); + control->polish(); + } + + if ( info.updateModes & UpdateInfo::Update ) + control->update(); + } + } +} + +void WindowAnimator::addHints( const QskControl* control, + const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates, + const QskSkin* skin1, const QskSkin* skin2 ) +{ + const auto subControls = control->subControls(); + + const auto& localTable = control->hintTable(); + + const auto& table1 = skin1->hintTable(); + const auto& table2 = skin2->hintTable(); + + for ( auto aspect : candidates ) + { + if ( !isControlAffected( control, subControls, aspect ) ) + continue; + + aspect.setPlacement( control->effectivePlacement() ); + aspect.setStates( control->skinStates() ); + + if ( localTable.resolvedHint( aspect ) ) + { + // value is not from the skin - ignored + continue; + } + + QskAspect r1, r2; + + const auto v1 = table1.resolvedHint( aspect, &r1 ); + const auto v2 = table2.resolvedHint( aspect, &r2 ); + + if ( v1 && v2 ) + { + if ( QskVariantAnimator::maybeInterpolate( *v1, *v2 ) ) + { + if ( r1.placement() == r2.placement() ) + aspect.setPlacement( r2.placement() ); + + if ( r1.states() == r2.states() ) + aspect.setStates( r2.states() ); + + storeAnimator( control, aspect, *v1, *v2, animatorHint ); + storeUpdateInfo( control, aspect ); + } + } + else if ( v1 ) + { + aspect.setPlacement( r1.placement() ); + aspect.setStates( r1.states() ); + + storeAnimator( control, aspect, *v1, QVariant(), animatorHint ); + storeUpdateInfo( control, aspect ); + } + else if ( v2 ) + { + aspect.setPlacement( r1.placement() ); + aspect.setStates( r1.states() ); + + storeAnimator( control, aspect, QVariant(), *v2, animatorHint ); + storeUpdateInfo( control, aspect ); + } + } +} + +inline bool WindowAnimator::isControlAffected( const QskControl* control, + const QVector< QskAspect::Subcontrol >& subControls, const QskAspect aspect ) const +{ + if ( !aspect.isMetric() ) + { + if ( !( control->flags() & QQuickItem::ItemHasContents ) ) + { + // while metrics might have an effect on layouts, we + // ignore all others for controls without content + return false; + } + } + + const auto subControl = aspect.subControl(); + if ( subControl != control->effectiveSubcontrol( subControl ) ) + { + // The control uses subcontrol redirection, so we can assume it + // is not interested in this subcontrol. + + return false; + } + + if ( subControl == QskAspect::Control ) + { + if ( !control->autoFillBackground() ) + { + // no need to animate the background unless we show it + return false; + } + } + else + { + if ( !subControls.contains( subControl ) ) + { + // the control is not interested in the aspect + return false; + } + } + + return true; +} + +inline void WindowAnimator::storeAnimator( const QskControl* control, const QskAspect aspect, + const QVariant& value1, const QVariant& value2, QskAnimationHint hint ) +{ + if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() ) + { + m_animatorMap.emplace( aspect, + HintAnimator( control, aspect, value1, value2, hint ) ); + } +} + +inline void WindowAnimator::storeUpdateInfo( const QskControl* control, QskAspect aspect ) +{ + UpdateInfo info; + info.control = const_cast< QskControl* >( control ); + + info.updateModes = UpdateInfo::Update; + if ( aspect.isMetric() ) + info.updateModes |= UpdateInfo::Polish; + + auto it = std::lower_bound( + m_updateInfos.begin(), m_updateInfos.end(), info, UpdateInfo::compare ); + + if ( ( it != m_updateInfos.end() ) && ( it->control == info.control ) ) + it->updateModes |= info.updateModes; + else + m_updateInfos.insert( it, info ); +} + +ApplicationAnimator::~ApplicationAnimator() +{ + reset(); +} + +inline WindowAnimator* ApplicationAnimator::windowAnimator( const QQuickWindow* window ) +{ + if ( window ) + { + for ( auto animator : m_windowAnimators ) + { + if ( animator->window() == window ) + return animator; + } + } + + return nullptr; +} + +void ApplicationAnimator::add( WindowAnimator* animator ) +{ + m_windowAnimators.push_back( animator ); +} + +void ApplicationAnimator::start() +{ + m_connections[0] = QskAnimator::addAdvanceHandler( + this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection ); + + m_connections[1] = QskAnimator::addCleanupHandler( + this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection ); + + for ( auto& animator : m_windowAnimators ) + animator->start(); +} + +void ApplicationAnimator::reset() +{ + qDeleteAll( m_windowAnimators ); + m_windowAnimators.clear(); + + disconnect( m_connections[0] ); + disconnect( m_connections[1] ); +} + +inline bool ApplicationAnimator::isRunning() const +{ + return !m_windowAnimators.empty(); +} + +void ApplicationAnimator::notify( QQuickWindow* window ) +{ + for ( auto& animator : m_windowAnimators ) + { + if ( animator->window() == window ) + { + animator->update(); + return; + } + } +} + +void ApplicationAnimator::cleanup( QQuickWindow* window ) +{ + for ( auto it = m_windowAnimators.begin(); + it != m_windowAnimators.end(); ++it ) + { + auto animator = *it; + if ( animator->window() == window ) + { + if ( !animator->isRunning() ) + { + // The notification might be for other animators + + m_windowAnimators.erase( it ); + delete animator; + } + + break; + } + } + + if ( m_windowAnimators.empty() ) + reset(); +} class QskSkinTransition::PrivateData { @@ -589,38 +604,20 @@ void QskSkinTransition::updateSkin( QskSkin*, QskSkin* ) void QskSkinTransition::process() { - auto skinFrom = m_data->skins[ 0 ]; - auto skinTo = m_data->skins[ 1 ]; + qskApplicationAnimator->reset(); - if ( ( skinFrom == nullptr ) || ( skinTo == nullptr ) ) + auto skin1 = m_data->skins[ 0 ]; + auto skin2 = m_data->skins[ 1 ]; + + QSet< QskAspect > candidates; + + if ( skin1 && skin2 ) { - // do nothing - return; - } - - qskSkinAnimator->reset(); - - if ( ( m_data->animationHint.duration <= 0 ) || ( m_data->mask == 0 ) ) - { - // no animations, we can apply the changes - updateSkin( skinFrom, skinTo ); - return; - } - - QVector< AnimatorCandidate > candidates; - const auto oldFilters = skinFrom->graphicFilters(); - - { - // copy out all hints before updating the skin - // - would be good to have Copy on Write here - - const auto oldTable = skinFrom->hintTable(); - - // apply the changes - updateSkin( skinFrom, skinTo ); - - candidates = qskAnimatorCandidates( m_data->mask, oldTable, oldFilters, - skinTo->hintTable(), skinTo->graphicFilters() ); + if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) ) + { + qskAddCandidates( m_data->mask, skin1, candidates ); + qskAddCandidates( m_data->mask, skin2, candidates ); + } } if ( !candidates.isEmpty() ) @@ -631,21 +628,19 @@ void QskSkinTransition::process() for ( const auto window : windows ) { - if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) ) + if ( auto w = qobject_cast< QQuickWindow* >( window ) ) { - if ( !quickWindow->isVisible() || - ( qskEffectiveSkin( quickWindow ) != skinTo ) ) + if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) ) { continue; } - auto group = new AnimatorGroup( quickWindow ); + auto animator = new WindowAnimator( w ); if ( doGraphicFilter ) { - group->addGraphicFilterAnimators( - m_data->animationHint, oldFilters, - skinTo->graphicFilters() ); + animator->addGraphicFilterAnimators( + m_data->animationHint, skin1, skin2 ); doGraphicFilter = false; } @@ -655,21 +650,24 @@ void QskSkinTransition::process() over the the item trees. */ - group->addAnimators( quickWindow->contentItem(), - m_data->animationHint, candidates, skinTo ); + animator->addItemAspects( w->contentItem(), + m_data->animationHint, candidates, skin1, skin2 ); - qskSkinAnimator->add( group ); + qskApplicationAnimator->add( animator ); } } - qskSkinAnimator->start(); + qskApplicationAnimator->start(); } + + // apply the changes + updateSkin( skin1, skin2 ); } bool QskSkinTransition::isRunning() { - if ( qskSkinAnimator.exists() ) - return qskSkinAnimator->isRunning(); + if ( qskApplicationAnimator.exists() ) + return qskApplicationAnimator->isRunning(); return false; } @@ -677,10 +675,10 @@ bool QskSkinTransition::isRunning() QVariant QskSkinTransition::animatedHint( const QQuickWindow* window, QskAspect aspect ) { - if ( qskSkinAnimator.exists() ) + if ( qskApplicationAnimator.exists() ) { - if ( const auto group = qskSkinAnimator->animatorGroup( window ) ) - return group->animatedHint( aspect ); + if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) ) + return animator->animatedHint( aspect ); } return QVariant(); @@ -689,10 +687,10 @@ QVariant QskSkinTransition::animatedHint( QVariant QskSkinTransition::animatedGraphicFilter( const QQuickWindow* window, int graphicRole ) { - if ( qskSkinAnimator.exists() ) + if ( qskApplicationAnimator.exists() ) { - if ( const auto group = qskSkinAnimator->animatorGroup( window ) ) - return group->animatedGraphicFilter( graphicRole ); + if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) ) + return animator->animatedGraphicFilter( graphicRole ); } return QVariant(); diff --git a/src/controls/QskSkinnable.cpp b/src/controls/QskSkinnable.cpp index 1268fcde..15d8b0b5 100644 --- a/src/controls/QskSkinnable.cpp +++ b/src/controls/QskSkinnable.cpp @@ -38,17 +38,6 @@ static inline bool qskIsControl( const QskSkinnable* skinnable ) return skinnable->metaObject()->inherits( &QskControl::staticMetaObject ); } -static inline QVariant qskTypedNullValue( const QVariant& value ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - const auto vType = static_cast< QMetaType >( value.userType() ); -#else - const auto vType = value.userType(); -#endif - - return QVariant( vType, nullptr ); -} - static inline bool qskSetFlag( QskSkinnable* skinnable, const QskAspect aspect, int flag ) { @@ -940,23 +929,12 @@ const QVariant& QskSkinnable::storedHint( { const auto skin = effectiveSkin(); - // clearing all state bits not being handled from the skin - aspect.clearStates( ~skin->stateMask() ); - QskAspect resolvedAspect; const auto& localTable = m_data->hintTable; if ( localTable.hasHints() ) { - auto a = aspect; - - if ( !localTable.hasStates() ) - { - // we don't need to clear the state bits stepwise - a.clearStates(); - } - - if ( const QVariant* value = localTable.resolvedHint( a, &resolvedAspect ) ) + if ( const auto value = localTable.resolvedHint( aspect, &resolvedAspect ) ) { if ( status ) { @@ -972,10 +950,7 @@ const QVariant& QskSkinnable::storedHint( const auto& skinTable = skin->hintTable(); if ( skinTable.hasHints() ) { - auto a = aspect; - - const QVariant* value = skinTable.resolvedHint( a, &resolvedAspect ); - if ( value ) + if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) ) { if ( status ) { @@ -993,8 +968,7 @@ const QVariant& QskSkinnable::storedHint( aspect.setSubControl( QskAspect::Control ); aspect.clearStates(); - value = skinTable.resolvedHint( aspect, &resolvedAspect ); - if ( value ) + if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) ) { if ( status ) { @@ -1171,27 +1145,11 @@ void QskSkinnable::startHintTransition( QskAspect aspect, if ( control->window() == nullptr || !isTransitionAccepted( aspect ) ) return; - /* - We might be invalid for one of the values, when an aspect - has not been defined for all states ( f.e. metrics are expected - to fallback to 0.0 ). In this case we create a default one. - */ - auto v1 = from; auto v2 = to; - if ( !v1.isValid() ) - { - v1 = qskTypedNullValue( v2 ); - } - else if ( !v2.isValid() ) - { - v2 = qskTypedNullValue( v1 ); - } - else if ( v1.userType() != v2.userType() ) - { + if ( !QskVariantAnimator::convertValues( v1, v2 ) ) return; - } if ( aspect.flagPrimitive() == QskAspect::GraphicRole ) { @@ -1257,7 +1215,8 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates ) if ( skin ) { - const auto mask = skin->stateMask(); + const auto mask = skin->hintTable().states() | m_data->hintTable.states(); + if ( ( newStates & mask ) == ( m_data->skinStates & mask ) ) { // the modified bits are not handled by the skin @@ -1297,24 +1256,13 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates ) const auto primitive = static_cast< QskAspect::Primitive >( i ); aspect.setPrimitive( type, primitive ); - auto a1 = aspect | m_data->skinStates; - auto a2 = aspect | newStates; + const auto a1 = aspect | m_data->skinStates; + const auto a2 = aspect | newStates; bool doTransition = true; - if ( !m_data->hintTable.hasStates() ) - { - /* - The hints are found by stripping the state bits one by - one until a lookup into the hint table is successful. - So for deciding whether two aspects lead to the same hint - we can stop as soon as the aspects have the same state bits. - This way we can reduce the number of lookups significantly - for skinnables with many state bits. - - */ + if ( m_data->hintTable.states() == QskAspect::NoState ) doTransition = !skinTable.isResolutionMatching( a1, a2 ); - } if ( doTransition ) { @@ -1343,12 +1291,7 @@ QskSkin* QskSkinnable::effectiveSkin() const if ( skin == nullptr ) { if ( const auto control = owningControl() ) - { - if ( auto window = qobject_cast< const QskWindow* >( control->window() ) ) - { - skin = window->skin(); - } - } + skin = qskEffectiveSkin( control->window() ); } return skin ? skin : qskSetup->skin(); diff --git a/src/controls/QskSkinnable.h b/src/controls/QskSkinnable.h index b73ea956..97324e2c 100644 --- a/src/controls/QskSkinnable.h +++ b/src/controls/QskSkinnable.h @@ -230,6 +230,8 @@ class QSK_EXPORT QskSkinnable bool resetGraphicRoleHint( QskAspect ); int graphicRoleHint( QskAspect, QskSkinHintStatus* = nullptr ) const; + const QskSkinHintTable& hintTable() const; + protected: virtual void updateNode( QSGNode* ); virtual bool isTransitionAccepted( QskAspect ) const; @@ -237,7 +239,6 @@ class QSK_EXPORT QskSkinnable virtual QskAspect::Subcontrol substitutedSubcontrol( QskAspect::Subcontrol ) const; QskSkinHintTable& hintTable(); - const QskSkinHintTable& hintTable() const; private: Q_DISABLE_COPY( QskSkinnable ) diff --git a/src/controls/QskVariantAnimator.cpp b/src/controls/QskVariantAnimator.cpp index 6372fbf0..a23fd356 100644 --- a/src/controls/QskVariantAnimator.cpp +++ b/src/controls/QskVariantAnimator.cpp @@ -76,6 +76,31 @@ QSK_DECL_INSANE static inline QVariant qskInterpolate( return f( from.constData(), to.constData(), progress ); } +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + +using QskMetaType = int; +static inline QskMetaType qskMetaType( const QVariant& v ) { return v.userType(); } + +#else + +using QskMetaType = QMetaType; +static inline QskMetaType qskMetaType( const QVariant& v ) { return v.metaType(); } + +#endif + +static inline QVariant qskDefaultVariant( QskMetaType type ) +{ + return QVariant( type, nullptr ); +} + +static inline QVariant qskConvertedVariant( const QVariant& from, QskMetaType type ) +{ + auto v = from; + v.convert( type ); + + return v; +} + QskVariantAnimator::QskVariantAnimator() : m_interpolator( nullptr ) { @@ -100,16 +125,60 @@ void QskVariantAnimator::setCurrentValue( const QVariant& value ) m_currentValue = value; } +bool QskVariantAnimator::convertValues( QVariant& v1, QVariant& v2 ) +{ + if ( !v1.isValid() && !v2.isValid() ) + return false; + + const auto type1 = qskMetaType( v1 ); + const auto type2 = qskMetaType( v2 ); + + if ( !v1.isValid() ) + { + v1 = qskDefaultVariant( type2 ); + return true; + } + + if ( !v2.isValid() ) + { + v2 = qskDefaultVariant( type1 ); + return true; + } + + if ( type1 != type2 ) + { + if ( v1.canConvert( type2 ) ) + { + v1.convert( type2 ); + return true; + } + + if ( v2.canConvert( type1 ) ) + { + v2.convert( type1 ); + return true; + } + + return false; + } + + return true; +} + void QskVariantAnimator::setup() { m_interpolator = nullptr; - const auto type = m_startValue.userType(); - if ( type == m_endValue.userType() ) + if ( convertValues( m_startValue, m_endValue ) ) { - // all what has been registered by qRegisterAnimationInterpolator - m_interpolator = reinterpret_cast< void ( * )() >( - QVariantAnimationPrivate::getInterpolator( type ) ); + if ( m_startValue != m_endValue ) + { + const auto id = m_startValue.userType(); + + // all what has been registered by qRegisterAnimationInterpolator + m_interpolator = reinterpret_cast< void ( * )() >( + QVariantAnimationPrivate::getInterpolator( id ) ); + } } m_currentValue = m_interpolator ? m_startValue : m_endValue; @@ -131,3 +200,32 @@ void QskVariantAnimator::done() { m_interpolator = nullptr; } + +bool QskVariantAnimator::maybeInterpolate( + const QVariant& value1, const QVariant& value2 ) +{ + if ( !value1.isValid() && !value2.isValid() ) + return false; + + const auto type1 = qskMetaType( value1 ); + const auto type2 = qskMetaType( value2 ); + + if ( !value1.isValid() ) + return value2 != qskDefaultVariant( type2 ); + + if ( !value2.isValid() ) + return value1 != qskDefaultVariant( type1 ); + + if ( type1 != type2 ) + { + if ( value1.canConvert( type2 ) ) + return value2 != qskConvertedVariant( value1, type2 ); + + if ( value2.canConvert( type1 ) ) + return value1 != qskConvertedVariant( value2, type1 ); + + return false; + } + + return value1 != value2; +} diff --git a/src/controls/QskVariantAnimator.h b/src/controls/QskVariantAnimator.h index 1507670d..9e036b06 100644 --- a/src/controls/QskVariantAnimator.h +++ b/src/controls/QskVariantAnimator.h @@ -24,6 +24,9 @@ class QSK_EXPORT QskVariantAnimator : public QskAnimator void setEndValue( const QVariant& ); QVariant endValue() const; + static bool maybeInterpolate( const QVariant&, const QVariant& ); + static bool convertValues( QVariant&, QVariant& ); + protected: void setup() override; void advance( qreal value ) override; diff --git a/src/src.pro b/src/src.pro index a25be7da..0c00a0ec 100644 --- a/src/src.pro +++ b/src/src.pro @@ -63,7 +63,6 @@ SOURCES += \ common/QskScaleTickmarks.cpp \ common/QskShadowMetrics.cpp \ common/QskSizePolicy.cpp \ - common/QskStateCombination.cpp \ common/QskTextColors.cpp \ common/QskTextOptions.cpp diff --git a/support/SkinnyShortcut.cpp b/support/SkinnyShortcut.cpp index 670405ca..582827fb 100644 --- a/support/SkinnyShortcut.cpp +++ b/support/SkinnyShortcut.cpp @@ -77,30 +77,31 @@ void SkinnyShortcut::enable( Types types ) void SkinnyShortcut::rotateSkin() { - const QStringList names = qskSkinManager->skinNames(); + const auto names = qskSkinManager->skinNames(); if ( names.size() <= 1 ) return; int index = names.indexOf( qskSetup->skinName() ); index = ( index + 1 ) % names.size(); - QskSkin* oldSkin = qskSetup->skin(); + auto oldSkin = qskSetup->skin(); if ( oldSkin->parent() == qskSetup ) oldSkin->setParent( nullptr ); // otherwise setSkin deletes it - QskSkin* newSkin = qskSetup->setSkin( names[ index ] ); + if ( auto newSkin = qskSetup->setSkin( names[ index ] ) ) + { + QskSkinTransition transition; - QskSkinTransition transition; + //transition.setMask( QskAspect::Color ); // Metrics are flickering -> TODO + transition.setSourceSkin( oldSkin ); + transition.setTargetSkin( newSkin ); + transition.setAnimation( 500 ); - //transition.setMask( QskAspect::Color ); // Metrics are flickering -> TODO - transition.setSourceSkin( oldSkin ); - transition.setTargetSkin( newSkin ); - transition.setAnimation( 500 ); + transition.process(); - transition.process(); - - if ( oldSkin->parent() == nullptr ) - delete oldSkin; + if ( oldSkin->parent() == nullptr ) + delete oldSkin; + } } void SkinnyShortcut::showBackground()