Uing RGB values instead of colors. QskSkinTransition reimplemented.

Ongoing work
This commit is contained in:
Uwe Rathmann 2022-03-31 18:29:39 +02:00
parent 5284880eaa
commit 570d3d2d51
41 changed files with 1178 additions and 929 deletions

View File

@ -4,29 +4,11 @@
*****************************************************************************/ *****************************************************************************/
#include "Page.h" #include "Page.h"
#include <QskRgbValue.h>
Page::Page( Qt::Orientation orientation, QQuickItem* parent ) Page::Page( Qt::Orientation orientation, QQuickItem* parent )
: QskLinearBox( orientation, parent ) : QskLinearBox( orientation, parent )
, m_gradient( QskRgb::GhostWhite )
{ {
setMargins( 20 ); setMargins( 20 );
setPadding( 10 ); setPadding( 10 );
setSpacing( 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;
}

View File

@ -6,16 +6,9 @@
#pragma once #pragma once
#include <QskLinearBox.h> #include <QskLinearBox.h>
#include <QskGradient.h>
class Page : public QskLinearBox class Page : public QskLinearBox
{ {
public: public:
Page( Qt::Orientation, QQuickItem* parent = nullptr ); Page( Qt::Orientation, QQuickItem* parent = nullptr );
void setGradient( const QskGradient& );
QskGradient gradient() const;
private:
QskGradient m_gradient;
}; };

View File

@ -67,7 +67,6 @@ namespace
LabelPage::LabelPage( QQuickItem* parent ) LabelPage::LabelPage( QQuickItem* parent )
: Page( Qt::Vertical, parent ) : Page( Qt::Vertical, parent )
{ {
setGradient( QskRgb::AliceBlue );
setSpacing( 40 ); setSpacing( 40 );
(void) new TextBox( this ); (void) new TextBox( this );

View File

@ -30,27 +30,6 @@ namespace
setMargins( 10 ); setMargins( 10 );
setTabPosition( Qsk::Left ); setTabPosition( Qsk::Left );
setAutoFitTabs( true ); 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() );
} }
}; };
} }

View File

@ -45,9 +45,7 @@ namespace
ProgressBarPage::ProgressBarPage( QQuickItem* parent ) ProgressBarPage::ProgressBarPage( QQuickItem* parent )
: Page( Qt::Horizontal, parent ) : Page( Qt::Horizontal, parent )
{ {
setGradient( QskRgb::AliceBlue );
setSpacing( 40 ); setSpacing( 40 );
populate(); populate();
} }

View File

@ -13,8 +13,6 @@
SliderPage::SliderPage( QQuickItem* parentItem ) SliderPage::SliderPage( QQuickItem* parentItem )
: Page( Qt::Vertical, parentItem ) : Page( Qt::Vertical, parentItem )
{ {
setGradient( QskRgb::PeachPuff );
setMargins( 10 ); setMargins( 10 );
setSpacing( 20 ); setSpacing( 20 );

View File

@ -15,9 +15,7 @@
SwitchButtonPage::SwitchButtonPage( QQuickItem* parent ) SwitchButtonPage::SwitchButtonPage( QQuickItem* parent )
: Page( Qt::Horizontal, parent ) : Page( Qt::Horizontal, parent )
{ {
setGradient( QskRgb::AliceBlue );
setSpacing( 40 ); setSpacing( 40 );
populate(); populate();
} }

View File

@ -47,20 +47,12 @@ static const int ButtonFontRole = QskSkin::HugeFont + 77;
static const int qskDuration = 150; static const int qskDuration = 150;
static inline QColor qskShadedColor( const QColor color, qreal opacity )
{
QColor c = color;
c.setAlphaF( opacity );
return c;
}
namespace namespace
{ {
class Editor : private QskSkinHintTableEditor class Editor : private QskSkinHintTableEditor
{ {
public: public:
Editor( QskSkinHintTable* table, const ColorPalette& palette ) Editor( QskSkinHintTable* table, const QskMaterialPalette& palette )
: QskSkinHintTableEditor( table ) : QskSkinHintTableEditor( table )
, m_pal( palette ) , m_pal( palette )
{ {
@ -93,7 +85,7 @@ namespace
void setupTextInput(); void setupTextInput();
void setupTextLabel(); void setupTextLabel();
const ColorPalette& m_pal; const QskMaterialPalette& m_pal;
const uint rippleSize = 30; const uint rippleSize = 30;
}; };
} }
@ -135,7 +127,7 @@ void Editor::setupControl()
setGradient( A::Control, m_pal.background ); setGradient( A::Control, m_pal.background );
setColor( A::Control | A::StyleColor, m_pal.onBackground ); setColor( A::Control | A::StyleColor, m_pal.onBackground );
setColor( A::Control | A::StyleColor | Q::Disabled, setColor( A::Control | A::StyleColor | Q::Disabled,
qskShadedColor( m_pal.onBackground, 0.6 ) ); QskRgb::toTransparentF( m_pal.onBackground, 0.6 ) );
} }
void Editor::setupBox() void Editor::setupBox()
@ -155,8 +147,8 @@ void Editor::setupPopup()
setFlagHint( Q::Overlay | A::Style, true ); setFlagHint( Q::Overlay | A::Style, true );
const QskGradient gradient( QskGradient::Vertical, const QskGradient gradient( QskGradient::Vertical,
qskShadedColor( m_pal.secondary, 0.45 ), QskRgb::toTransparentF( m_pal.secondary, 0.45 ),
qskShadedColor( m_pal.secondary, 0.7 ) ); QskRgb::toTransparentF( m_pal.secondary, 0.7 ) );
setGradient( Q::Overlay, gradient ); setGradient( Q::Overlay, gradient );
} }
@ -196,20 +188,16 @@ void Editor::setupTextInput()
setBoxBorderColors( Q::Panel | Q::Focused, m_pal.primary ); setBoxBorderColors( Q::Panel | Q::Focused, m_pal.primary );
setColor( Q::Panel, setColor( Q::Panel, m_pal.elevated( m_pal.background, 1 ) );
m_pal.elevated( m_pal.background, 1 ) ); setColor( Q::Panel | Q::Hovered, m_pal.elevated( m_pal.background, 2 ) );
setColor( Q::Panel | Q::Hovered, setColor( Q::Panel | Q::Focused, m_pal.elevated( m_pal.background, 3 ) );
m_pal.elevated( m_pal.background, 2 ) ); setColor( Q::Panel | Q::Editing, m_pal.elevated( m_pal.background, 4 ) );
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, setColor( Q::Panel | Q::Disabled,
qskShadedColor( m_pal.secondaryVariantNoSaturation, m_pal.disabled ) ); QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation, m_pal.disabled ) );
setColor( Q::Text | Q::Disabled, qskShadedColor( m_pal.onBackground, m_pal.disabled ) ); setColor( Q::Text | Q::Disabled, QskRgb::toTransparentF( m_pal.onBackground, m_pal.disabled ) );
setBoxBorderColors( Q::Panel, setBoxBorderColors( Q::Panel,
qskShadedColor( m_pal.onBackground, m_pal.disabled ) ); QskRgb::toTransparentF( m_pal.onBackground, m_pal.disabled ) );
} }
void Editor::setupProgressBar() void Editor::setupProgressBar()
@ -233,9 +221,9 @@ void Editor::setupProgressBar()
setGradient( Q::Bar, m_pal.secondary ); setGradient( Q::Bar, m_pal.secondary );
setGradient( Q::Groove | Q::Disabled, 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, setGradient( Q::Bar | Q::Disabled,
qskShadedColor( m_pal.secondary, m_pal.disabled ) ); QskRgb::toTransparentF( m_pal.secondary, m_pal.disabled ) );
} }
void Editor::setupFocusIndicator() void Editor::setupFocusIndicator()
@ -275,9 +263,9 @@ void Editor::setupPageIndicator()
setGradient( Q::Bullet | Q::Selected, m_pal.secondary ); setGradient( Q::Bullet | Q::Selected, m_pal.secondary );
setGradient( Q::Bullet | Q::Disabled, 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, 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 ) ); setSpacing( Q::Panel, qskDpiScaled( 3 ) );
setPadding( Q::Panel, 0 ); setPadding( Q::Panel, 0 );
@ -304,19 +292,19 @@ void Editor::setupPushButton()
setGradient( Q::Panel | Q::Flat, White & ColorMask ); setGradient( Q::Panel | Q::Flat, White & ColorMask );
setColor( Q::Text, m_pal.primary ); 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 ); setFontRole( Q::Text, ButtonFontRole );
setAlignment( Q::Text, Qt::AlignCenter ); setAlignment( Q::Text, Qt::AlignCenter );
setBoxBorderMetrics( Q::Panel, 1 ); setBoxBorderMetrics( Q::Panel, 1 );
setBoxBorderColors( Q::Panel, m_pal.primary ); setBoxBorderColors( Q::Panel, m_pal.primary );
setBoxBorderColors( Q::Panel | 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, qskShadedColor( 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::Hovered, QskRgb::toTransparentF( m_pal.primary, m_pal.hover ) );
setColor( Q::Panel | Q::Focused, qskShadedColor( m_pal.primary, m_pal.focused ) ); setColor( Q::Panel | Q::Focused, QskRgb::toTransparentF( m_pal.primary, m_pal.focused ) );
setColor( Q::Panel | Q::Pressed, qskShadedColor( m_pal.primary, m_pal.pressed ) ); setColor( Q::Panel | Q::Pressed, QskRgb::toTransparentF( m_pal.primary, m_pal.pressed ) );
setAnimation( Q::Panel | A::Color, qskDuration ); setAnimation( Q::Panel | A::Color, qskDuration );
setAnimation( Q::Panel | A::Metric, qskDuration ); setAnimation( Q::Panel | A::Metric, qskDuration );
@ -339,7 +327,7 @@ void Editor::setupDialogButton()
setGradient( Q::Panel, m_pal.primary ); setGradient( Q::Panel, m_pal.primary );
setColor( Q::Text, m_pal.onBackground ); 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 ); setFontRole( Q::Text, ButtonFontRole );
setAlignment( Q::Text, Qt::AlignCenter ); setAlignment( Q::Text, Qt::AlignCenter );
@ -385,7 +373,7 @@ void Editor::setupSlider()
setMetric( Q::Panel | A::Size, extent ); setMetric( Q::Panel | A::Size, extent );
setBoxShape( Q::Panel, 0 ); setBoxShape( Q::Panel, 0 );
setBoxBorderMetrics( 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::Horizontal, QskMargins( 0.5 * extent, 0 ) );
setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) ); setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) );
@ -399,17 +387,17 @@ void Editor::setupSlider()
setBoxShape( subControl, 0 ); setBoxShape( subControl, 0 );
setBoxBorderMetrics( subControl, 0 ); setBoxBorderMetrics( subControl, 0 );
} }
setMetric( Q::Groove | A::Size, qskDpiScaled( 4 ) ); setMetric( Q::Groove | A::Size, qskDpiScaled( 4 ) );
setMetric( Q::Fill | A::Size, qskDpiScaled( 6 ) ); setMetric( Q::Fill | A::Size, qskDpiScaled( 6 ) );
setGradient( Q::Groove, QskRgb::toTransparentF( m_pal.secondary, .38 ) );
setGradient( Q::Groove, qskShadedColor( m_pal.secondary, .38 ) );
setGradient( Q::Groove | Q::Disabled, 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, m_pal.secondary );
setGradient( Q::Fill | Q::Disabled, 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 ); setBoxShape( Q::Handle, 100, Qt::RelativeSize );
setBoxBorderMetrics( Q::Handle, 0 ); setBoxBorderMetrics( Q::Handle, 0 );
@ -425,11 +413,13 @@ void Editor::setupSlider()
setBoxBorderMetrics( Q::Handle, qskDpiScaled( rippleSize / 2 ) ); setBoxBorderMetrics( Q::Handle, qskDpiScaled( rippleSize / 2 ) );
setBoxBorderColors( Q::Handle | Q::Hovered, 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, 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, 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 // move the handle smoothly, when using keys
setAnimation( Q::Handle | A::Metric | A::Position, 2 * qskDuration ); setAnimation( Q::Handle | A::Metric | A::Position, 2 * qskDuration );
@ -451,17 +441,17 @@ void Editor::setupSwitchButton()
setColor( Q::Groove, m_pal.secondaryNoSaturation ); setColor( Q::Groove, m_pal.secondaryNoSaturation );
setGradient( Q::Groove | Q::Disabled, 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, setGradient( Q::Groove | Q::Checked,
m_pal.secondaryVariant ); m_pal.secondaryVariant );
setGradient( Q::Groove | Q::Checked | Q::Disabled, 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 ); setBoxShape( Q::Handle, 100, Qt::RelativeSize );
setStrutSize( Q::Handle, qskDpiScaled( 2 * radius + rippleSize ), setStrutSize( Q::Handle, qskDpiScaled( 2 * radius + rippleSize ),
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 ); setGradient( Q::Handle | Q::Checked, m_pal.secondary );
@ -474,20 +464,20 @@ void Editor::setupSwitchButton()
setBoxBorderMetrics( Q::Handle, qskDpiScaled( rippleSize / 2 ) ); setBoxBorderMetrics( Q::Handle, qskDpiScaled( rippleSize / 2 ) );
setBoxBorderColors( Q::Handle | Q::Checked | Q::Hovered, 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, 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, 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, setBoxBorderColors( Q::Handle | Q::Hovered,
qskShadedColor( m_pal.secondaryVariantNoSaturation, QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation,
m_pal.hover ) ); m_pal.hover ) );
setBoxBorderColors( Q::Handle | Q::Focused, setBoxBorderColors( Q::Handle | Q::Focused,
qskShadedColor( m_pal.secondaryVariantNoSaturation, QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation,
m_pal.focused ) ); m_pal.focused ) );
setBoxBorderColors( Q::Handle | Q::Pressed, setBoxBorderColors( Q::Handle | Q::Pressed,
qskShadedColor( m_pal.secondaryVariantNoSaturation, QskRgb::toTransparentF( m_pal.secondaryVariantNoSaturation,
m_pal.pressed ) ); m_pal.pressed ) );
for ( auto state : { A::NoState, Q::Disabled } ) for ( auto state : { A::NoState, Q::Disabled } )
@ -556,15 +546,15 @@ void Editor::setupTabButton()
setColor( Q::Text, m_pal.onBackground ); setColor( Q::Text, m_pal.onBackground );
setColor( Q::Text | Q::Disabled, setColor( Q::Text | Q::Disabled,
qskShadedColor( m_pal.onBackground, QskRgb::toTransparentF( m_pal.onBackground,
m_pal.widgetBackgroundDisabled ) ); m_pal.widgetBackgroundDisabled ) );
setColor( Q::Text | Q::Checked, m_pal.primary ); setColor( Q::Text | Q::Checked, m_pal.primary );
setColor( Q::Text | Q::Hovered, m_pal.primary ); setColor( Q::Text | Q::Hovered, m_pal.primary );
setColor( Q::Panel, m_pal.elevated( m_pal.background ) ); 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::Hovered, QskRgb::toTransparentF( m_pal.primary, m_pal.hover ) );
setColor( Q::Panel | Q::Focused, qskShadedColor( m_pal.primary, m_pal.focused ) ); setColor( Q::Panel | Q::Focused, QskRgb::toTransparentF( m_pal.primary, m_pal.focused ) );
setColor( Q::Panel | Q::Pressed, qskShadedColor( m_pal.primary, m_pal.pressed ) ); setColor( Q::Panel | Q::Pressed, QskRgb::toTransparentF( m_pal.primary, m_pal.pressed ) );
setAnimation( Q::Panel | A::Color, qskDuration ); setAnimation( Q::Panel | A::Color, qskDuration );
@ -587,9 +577,11 @@ void Editor::setupTabBar()
setAnimation( Q::Panel | A::Metric, QskAnimationHint( 200, QEasingCurve::InCubic ) ); setAnimation( Q::Panel | A::Metric, QskAnimationHint( 200, QEasingCurve::InCubic ) );
} }
void Editor::setupTabView() { void Editor::setupTabView()
{
using Q = QskTabView; using Q = QskTabView;
setGradient( Q::Page, m_pal.background );
setAnimation( Q::Page, qskDuration ); setAnimation( Q::Page, qskDuration );
} }
@ -654,7 +646,7 @@ void Editor::setupScrollView()
{ {
setBoxShape( subControl, 3 ); setBoxShape( subControl, 3 );
setBoxBorderMetrics( subControl, 0 ); 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 ); setAnimation( subControl | A::Color, qskDuration );
} }
@ -663,7 +655,7 @@ void Editor::setupScrollView()
Q::VerticalScrollHandle | Q::VerticalHandlePressed } ) Q::VerticalScrollHandle | Q::VerticalHandlePressed } )
{ {
setColor( subControl, setColor( subControl,
qskShadedColor( m_pal.onBackground, m_pal.pressed ) ); QskRgb::toTransparentF( m_pal.onBackground, m_pal.pressed ) );
} }
// when changing the position by QskScrollView::scrollTo // when changing the position by QskScrollView::scrollTo
@ -679,7 +671,7 @@ void Editor::setupListView()
setColor( Q::Cell, m_pal.background ); setColor( Q::Cell, m_pal.background );
setColor( Q::Text, m_pal.onBackground ); 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 ); setColor( Q::Text | Q::Selected, m_pal.onBackground );
} }
@ -713,15 +705,8 @@ void Editor::setupSubWindow()
} }
class QskMaterialSkin::PrivateData QskMaterialSkin::QskMaterialSkin( const QskMaterialPalette& palette, QObject* parent )
{
public:
ColorPalette palette;
};
QskMaterialSkin::QskMaterialSkin( ColorPalette colors, QObject* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData { colors } )
{ {
// Default theme colors // Default theme colors
setupFonts( QStringLiteral( "Roboto" ) ); setupFonts( QStringLiteral( "Roboto" ) );
@ -730,7 +715,7 @@ QskMaterialSkin::QskMaterialSkin( ColorPalette colors, QObject* parent )
buttonFont.setCapitalization( QFont::AllUppercase ); buttonFont.setCapitalization( QFont::AllUppercase );
setFont( ButtonFontRole, buttonFont ); setFont( ButtonFontRole, buttonFont );
Editor editor( &hintTable(), m_data->palette ); Editor editor( &hintTable(), palette );
editor.setup(); editor.setup();
} }

View File

@ -9,86 +9,56 @@
#include "QskMaterialGlobal.h" #include "QskMaterialGlobal.h"
#include <QskSkin.h> #include <QskSkin.h>
#include <QskRgbValue.h> #include <QskRgbValue.h>
#include <memory>
struct ColorPalette class QSK_MATERIAL_EXPORT QskMaterialPalette
{ {
enum Lightness { light, dark } lightness; public:
enum 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 )
{ {
primaryNoSaturation = QColor::fromHsl( primary.hslHue(), 0, Light,
primary.lightness() ); Dark
};
secondaryNoSaturation = QColor::fromHsl( secondary.hslHue(), QskMaterialPalette( Lightness lightness )
0, : m_lightness( lightness )
secondary.lightness() ); {
secondaryVariantNoSaturation =
QColor::fromHsl( secondaryVariant.hslHue(), 0,
secondaryVariant.lightness() );
} }
inline QColor elevated( const QColor target, const float level = 1 ) const { inline QRgb elevated( const QRgb rgb, const float level = 1 ) const
return ( lightness == light ) ? target.darker( 100 + level * 15 ) {
: target.lighter( 130 + level * 30 ); 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 class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin
@ -98,12 +68,8 @@ class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin
using Inherited = QskSkin; using Inherited = QskSkin;
public: public:
QskMaterialSkin( ColorPalette, QObject* parent = nullptr ); QskMaterialSkin( const QskMaterialPalette&, QObject* parent = nullptr );
~QskMaterialSkin() override; ~QskMaterialSkin() override;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
}; };
#endif #endif

View File

@ -9,6 +9,42 @@
static const QString materialLightSkinName = QStringLiteral( "materialLight" ); static const QString materialLightSkinName = QStringLiteral( "materialLight" );
static const QString materialDarkSkinName = QStringLiteral( "materialDark" ); 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 ) QskMaterialSkinFactory::QskMaterialSkinFactory( QObject* parent )
: QskSkinFactory( parent ) : QskSkinFactory( parent )
{ {
@ -25,24 +61,48 @@ QStringList QskMaterialSkinFactory::skinNames() const
QskSkin* QskMaterialSkinFactory::createSkin( const QString& skinName ) QskSkin* QskMaterialSkinFactory::createSkin( const QString& skinName )
{ {
if ( QString::compare( skinName, materialLightSkinName, Qt::CaseInsensitive ) ) if ( QString::compare( skinName, materialLightSkinName, Qt::CaseInsensitive ) == 0 )
return new QskMaterialSkin( ColorPalette() );
if ( QString::compare( skinName, materialDarkSkinName, Qt::CaseInsensitive ) )
{ {
return new QskMaterialSkin( ColorPalette( QskMaterialPalette pal( QskMaterialPalette::Light );;
ColorPalette::dark, // lightness
QColor::fromRgb( 0xBB86FC ), // primary pal.primary = 0xff6200ee;
QColor::fromRgb( 0x3700B3 ), // primaryVariant pal.primaryVariant = 0xff3700b3;
Qt::black, // onPrimary pal.onPrimary = QskRgb::White;
QColor::fromRgb( 0x03DAC6 ), // secondary pal.secondary = 0xff03dac6;
QColor::fromRgb( 0x018786 ), // secondaryVariant pal.secondaryVariant = 0xff018786;
Qt::black, // onSecondary pal.onSecondary = QskRgb::White;
QColor::fromRgb( 0x121212 ), // background pal.background = QskRgb::Grey100;
Qt::white, // onBackground pal.onBackground = QskRgb::Black;
QColor::fromRgb( 0xCF6679 ), // error pal.error = 0xffb00020;
Qt::black // onError 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; return nullptr;

View File

@ -647,26 +647,19 @@ void Editor::setupSlider()
// Panel // Panel
for ( auto placement : { A::Horizontal, A::Vertical } ) setMetric( Q::Panel | A::Size, extent );
{ setBoxBorderMetrics( Q::Panel, 0 );
const auto aspect = Q::Panel | placement; setBoxShape( Q::Panel, 0 );
setGradient( Q::Panel, QskGradient() );
setMetric( aspect | A::Size, extent );
setBoxBorderMetrics( aspect, 0 );
setBoxShape( aspect, 0 );
setGradient( aspect, QskGradient() );
}
setPadding( Q::Panel | A::Horizontal, QskMargins( 0.5 * extent, 0 ) ); setPadding( Q::Panel | A::Horizontal, QskMargins( 0.5 * extent, 0 ) );
setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) ); setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) );
// Groove, Fill // 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 ); setMetric( aspect | A::Size, 0.3 * extent );
setPadding( aspect, 0 ); setPadding( aspect, 0 );
@ -675,23 +668,17 @@ void Editor::setupSlider()
setBoxShape( aspect, 0.1 * extent ); setBoxShape( aspect, 0.1 * extent );
} }
setGradient( Q::Groove | placement, m_pal.darker200 ); setGradient( Q::Groove, m_pal.darker200 );
setGradient( Q::Fill | placement, QskGradient() ); // no filling setGradient( Q::Fill, QskGradient() ); // no filling
}
// Handle // Handle
for ( auto placement : { A::Horizontal, A::Vertical } ) setButton( Q::Handle, Raised, 1 );
{ setBoxShape( Q::Handle, 20.0, Qt::RelativeSize );
const auto aspect = Q::Handle | placement; 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; const qreal sz = 0.75 * extent;
setStrutSize( aspect, sz, sz ); setStrutSize( Q::Handle, sz, sz );
}
setAnimation( Q::Handle | A::Color, qskDuration ); setAnimation( Q::Handle | A::Color, qskDuration );
} }

View File

@ -24,7 +24,7 @@ QStringList QskSquiekSkinFactory::skinNames() const
QskSkin* QskSquiekSkinFactory::createSkin( const QString& skinName ) QskSkin* QskSquiekSkinFactory::createSkin( const QString& skinName )
{ {
if ( skinName.toLower() == squiekSkinName ) if ( QString::compare( skinName, squiekSkinName, Qt::CaseInsensitive ) == 0 )
return new QskSquiekSkin(); return new QskSquiekSkin();
return nullptr; return nullptr;

View File

@ -11,6 +11,10 @@
static void qskRegisterArcMetrics() static void qskRegisterArcMetrics()
{ {
qRegisterMetaType< QskArcMetrics >(); qRegisterMetaType< QskArcMetrics >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskArcMetrics >();
#endif
} }
Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics ) Q_CONSTRUCTOR_FUNCTION( qskRegisterArcMetrics )

View File

@ -109,8 +109,18 @@ class QSK_EXPORT QskAspect
constexpr QskAspect operator|( Type ) const noexcept; constexpr QskAspect operator|( Type ) const noexcept;
constexpr QskAspect operator|( Primitive ) const noexcept; constexpr QskAspect operator|( Primitive ) const noexcept;
constexpr QskAspect operator|( Placement ) const noexcept; constexpr QskAspect operator|( Placement ) const noexcept;
constexpr QskAspect operator|( State ) 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; 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 stateless() const noexcept;
constexpr QskAspect trunk() 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 ); 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 inline constexpr QskAspect QskAspect::operator|( States states ) const noexcept
{ {
return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator,
m_bits.primitive, m_bits.placement, m_bits.states | states ); 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 inline constexpr QskAspect QskAspect::stateless() const noexcept
{ {
return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator, 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 #ifndef QT_NO_DEBUG_STREAM
class QDebug; class QDebug;

View File

@ -13,6 +13,10 @@ static void qskRegisterBoxBorderColors()
{ {
qRegisterMetaType< QskBoxBorderColors >(); qRegisterMetaType< QskBoxBorderColors >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskBoxBorderColors >();
#endif
QMetaType::registerConverter< QColor, QskBoxBorderColors >( QMetaType::registerConverter< QColor, QskBoxBorderColors >(
[]( const QColor& color ) { return QskBoxBorderColors( color ); } ); []( const QColor& color ) { return QskBoxBorderColors( color ); } );
@ -110,6 +114,26 @@ void QskBoxBorderColors::setGradientAt( Qt::Edges edges, const QskGradient& grad
m_gradients[ Qsk::Bottom ] = gradient; 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 const QskGradient& QskBoxBorderColors::gradientAt( Qt::Edge edge ) const
{ {
switch ( edge ) switch ( edge )
@ -165,6 +189,14 @@ bool QskBoxBorderColors::isMonochrome() const
&& m_gradients[ 3 ].isMonochrome(); && 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( QskBoxBorderColors QskBoxBorderColors::interpolated(
const QskBoxBorderColors& to, qreal ratio ) const const QskBoxBorderColors& to, qreal ratio ) const
{ {
@ -172,8 +204,14 @@ QskBoxBorderColors QskBoxBorderColors::interpolated(
for ( size_t i = 0; i < 4; i++ ) for ( size_t i = 0; i < 4; i++ )
{ {
colors.m_gradients[ i ] = colors.m_gradients[ i ].interpolated( #if 1
to.m_gradients[ i ], ratio ); /*
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; return colors;
@ -204,14 +242,44 @@ QDebug operator<<( QDebug debug, const QskBoxBorderColors& colors )
QDebugStateSaver saver( debug ); QDebugStateSaver saver( debug );
debug.nospace(); debug.nospace();
debug << "BoxBorderColors" << '('; debug << "BoxBorderColors";
debug << " L" << colors.gradient( Qsk::Left ); if ( !colors.isValid() )
debug << ", T" << colors.gradient( Qsk::Top ); {
debug << ", R" << colors.gradient( Qsk::Right ); debug << "()";
debug << ", B" << colors.gradient( Qsk::Bottom ); }
else
{
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 << " )"; debug << " )";
}
return debug; return debug;
} }

View File

@ -12,10 +12,15 @@
#include <qcolor.h> #include <qcolor.h>
#include <qmetatype.h> #include <qmetatype.h>
class QDebug;
class QSK_EXPORT QskBoxBorderColors 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: public:
QskBoxBorderColors(); QskBoxBorderColors();
@ -44,6 +49,18 @@ class QSK_EXPORT QskBoxBorderColors
void setGradientAt( Qt::Edges, const QskGradient& ); void setGradientAt( Qt::Edges, const QskGradient& );
const QskGradient& gradientAt( Qt::Edge ) const; 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; QskBoxBorderColors interpolated( const QskBoxBorderColors&, qreal value ) const;
static QVariant interpolate( const QskBoxBorderColors&, static QVariant interpolate( const QskBoxBorderColors&,
@ -53,6 +70,7 @@ class QSK_EXPORT QskBoxBorderColors
bool isMonochrome() const; bool isMonochrome() const;
bool isVisible() const; bool isVisible() const;
bool isValid() const;
private: private:
QskGradient m_gradients[ 4 ]; QskGradient m_gradients[ 4 ];
@ -78,8 +96,29 @@ inline const QskGradient& QskBoxBorderColors::gradient( Qsk::Position position )
return m_gradients[ 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 #ifndef QT_NO_DEBUG_STREAM
class QDebug;
QSK_EXPORT QDebug operator<<( QDebug, const QskBoxBorderColors& ); QSK_EXPORT QDebug operator<<( QDebug, const QskBoxBorderColors& );
#endif #endif

View File

@ -12,6 +12,10 @@ static void qskRegisterBoxBorderMetrics()
{ {
qRegisterMetaType< QskBoxBorderMetrics >(); qRegisterMetaType< QskBoxBorderMetrics >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskBoxBorderMetrics >();
#endif
QMetaType::registerConverter< QskMargins, QskBoxBorderMetrics >( QMetaType::registerConverter< QskMargins, QskBoxBorderMetrics >(
[]( const QskMargins& margins ) { return QskBoxBorderMetrics( margins ); } ); []( const QskMargins& margins ) { return QskBoxBorderMetrics( margins ); } );
@ -109,9 +113,24 @@ QDebug operator<<( QDebug debug, const QskBoxBorderMetrics& metrics )
QDebugStateSaver saver( debug ); QDebugStateSaver saver( debug );
debug.nospace(); debug.nospace();
debug << "BoxBorder" << '('; debug << "BoxBorder" << "( ";
debug << metrics.sizeMode() << ',' << metrics.widths();
debug << ')'; 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; return debug;
} }

View File

@ -60,6 +60,8 @@ class QSK_EXPORT QskBoxBorderMetrics
static QVariant interpolate( const QskBoxBorderMetrics&, static QVariant interpolate( const QskBoxBorderMetrics&,
const QskBoxBorderMetrics&, qreal progress ); const QskBoxBorderMetrics&, qreal progress );
constexpr bool isEquidistant() const noexcept;
private: private:
QskMargins m_widths; QskMargins m_widths;
Qt::SizeMode m_sizeMode; Qt::SizeMode m_sizeMode;
@ -115,6 +117,11 @@ inline constexpr bool QskBoxBorderMetrics::isNull() const noexcept
return m_widths.isNull(); return m_widths.isNull();
} }
inline constexpr bool QskBoxBorderMetrics::isEquidistant() const noexcept
{
return m_widths.isEquidistant();
}
inline constexpr const QskMargins& QskBoxBorderMetrics::widths() const noexcept inline constexpr const QskMargins& QskBoxBorderMetrics::widths() const noexcept
{ {
return m_widths; return m_widths;

View File

@ -14,6 +14,10 @@ static void qskRegisterBoxShapeMetrics()
{ {
qRegisterMetaType< QskBoxShapeMetrics >(); qRegisterMetaType< QskBoxShapeMetrics >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskBoxShapeMetrics >();
#endif
QMetaType::registerConverter< int, QskBoxShapeMetrics >( QMetaType::registerConverter< int, QskBoxShapeMetrics >(
[]( int radius ) { return QskBoxShapeMetrics( radius ); } ); []( int radius ) { return QskBoxShapeMetrics( radius ); } );

View File

@ -15,6 +15,10 @@ static void qskRegisterGradient()
{ {
qRegisterMetaType< QskGradient >(); qRegisterMetaType< QskGradient >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskGradient >();
#endif
QMetaType::registerConverter< QColor, QskGradient >( QMetaType::registerConverter< QColor, QskGradient >(
[]( const QColor& color ) { return QskGradient( color ); } ); []( const QColor& color ) { return QskGradient( color ); } );
} }
@ -229,6 +233,11 @@ QskGradient::QskGradient( Orientation orientation, const QskGradientStops& stops
setStops( stops ); setStops( stops );
} }
QskGradient::QskGradient( Qt::Orientation orientation, QGradient::Preset preset )
: QskGradient( qskOrientation( orientation ), preset )
{
}
QskGradient::QskGradient( Orientation orientation, QGradient::Preset preset ) QskGradient::QskGradient( Orientation orientation, QGradient::Preset preset )
: QskGradient( orientation ) : QskGradient( orientation )
{ {
@ -499,6 +508,14 @@ QskGradient QskGradient::interpolated(
return QskGradient( gradient->orientation(), stops ); 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() ) if ( isMonochrome() )
{ {
// we can ignore our stops // we can ignore our stops
@ -624,7 +641,49 @@ void QskGradient::updateStatusBits() const
QDebug operator<<( QDebug debug, const QskGradient& gradient ) 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; return debug;
} }

View File

@ -52,6 +52,7 @@ class QSK_EXPORT QskGradient
QskGradient( Qt::Orientation, const QVector< QskGradientStop >& ); QskGradient( Qt::Orientation, const QVector< QskGradientStop >& );
QskGradient( Qt::Orientation, const QColor&, const QColor& ); QskGradient( Qt::Orientation, const QColor&, const QColor& );
QskGradient( Qt::Orientation, QGradient::Preset );
QskGradient( Orientation, const QVector< QskGradientStop >& ); QskGradient( Orientation, const QVector< QskGradientStop >& );
QskGradient( Orientation, const QColor&, const QColor& ); QskGradient( Orientation, const QColor&, const QColor& );

View File

@ -14,6 +14,10 @@
static void qskRegisterGradientStop() static void qskRegisterGradientStop()
{ {
qRegisterMetaType< QskGradientStop >(); qRegisterMetaType< QskGradientStop >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskGradientStop >();
#endif
} }
Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop ) Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop )
@ -78,7 +82,12 @@ QColor QskGradientStop::interpolated(
QDebug operator<<( QDebug debug, const QskGradientStop& stop ) 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; return debug;
} }

View File

@ -12,6 +12,10 @@
static void qskRegisterIntervalF() static void qskRegisterIntervalF()
{ {
qRegisterMetaType< QskIntervalF >(); qRegisterMetaType< QskIntervalF >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskIntervalF >();
#endif
} }
Q_CONSTRUCTOR_FUNCTION( qskRegisterIntervalF ) Q_CONSTRUCTOR_FUNCTION( qskRegisterIntervalF )

View File

@ -61,6 +61,7 @@ class QSK_EXPORT QskMargins : public QMarginsF
QskMargins interpolated( const QskMargins&, qreal progress ) const noexcept; QskMargins interpolated( const QskMargins&, qreal progress ) const noexcept;
constexpr bool isExpanding() const noexcept; constexpr bool isExpanding() const noexcept;
constexpr bool isEquidistant() const noexcept;
static QVariant interpolate( const QskMargins&, static QVariant interpolate( const QskMargins&,
const QskMargins&, qreal progress ) noexcept; const QskMargins&, qreal progress ) noexcept;
@ -181,6 +182,11 @@ constexpr inline qreal QskMargins::height() const noexcept
return top() + bottom(); 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_TYPEINFO( QskMargins, Q_MOVABLE_TYPE );
Q_DECLARE_METATYPE( QskMargins ) Q_DECLARE_METATYPE( QskMargins )

View File

@ -172,3 +172,29 @@ QRgb QskRgb::darker( QRgb rgb, int factor ) noexcept
return QColor::fromRgba( rgb ).darker( factor ).rgba(); return QColor::fromRgba( rgb ).darker( factor ).rgba();
} }
#ifndef QT_NO_DEBUG_STREAM
#include <qdebug.h>
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

View File

@ -461,23 +461,35 @@ namespace QskRgb
return ( rgb & ColorMask ) | ( ( static_cast< uint >( alpha ) & 0xffu ) << 24 ); 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 lighter( QRgb, int factor = 150 ) noexcept;
QSK_EXPORT QRgb darker( QRgb, int factor = 200 ) 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 #endif

View File

@ -9,6 +9,10 @@
static void qskRegisterTickmarks() static void qskRegisterTickmarks()
{ {
qRegisterMetaType< QskScaleTickmarks >(); qRegisterMetaType< QskScaleTickmarks >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskScaleTickmarks >();
#endif
} }
Q_CONSTRUCTOR_FUNCTION( qskRegisterTickmarks ) Q_CONSTRUCTOR_FUNCTION( qskRegisterTickmarks )

View File

@ -12,6 +12,10 @@
static void qskRegisterShadowMetrics() static void qskRegisterShadowMetrics()
{ {
qRegisterMetaType< QskShadowMetrics >(); qRegisterMetaType< QskShadowMetrics >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskShadowMetrics >();
#endif
} }
Q_CONSTRUCTOR_FUNCTION( qskRegisterShadowMetrics ) Q_CONSTRUCTOR_FUNCTION( qskRegisterShadowMetrics )

View File

@ -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 )

View File

@ -21,6 +21,9 @@ class QSK_EXPORT QskStateCombination
constexpr QskStateCombination( QskAspect::States = QskAspect::States() ) noexcept; constexpr QskStateCombination( QskAspect::States = QskAspect::States() ) noexcept;
constexpr QskStateCombination( Type, 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; constexpr bool isNull() const noexcept;
void setType( Type ) noexcept; void setType( Type ) noexcept;
@ -36,7 +39,6 @@ class QSK_EXPORT QskStateCombination
}; };
Q_DECLARE_TYPEINFO( QskStateCombination, Q_MOVABLE_TYPE ); Q_DECLARE_TYPEINFO( QskStateCombination, Q_MOVABLE_TYPE );
Q_DECLARE_METATYPE( QskStateCombination )
constexpr inline QskStateCombination::QskStateCombination( constexpr inline QskStateCombination::QskStateCombination(
QskAspect::State state ) noexcept QskAspect::State state ) noexcept
@ -90,4 +92,14 @@ constexpr inline QskAspect::States QskStateCombination::states() const noexcept
return m_states; 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 #endif

View File

@ -128,7 +128,6 @@ class QskSkin::PrivateData
std::unordered_map< const QMetaObject*, SkinletData > skinletMap; std::unordered_map< const QMetaObject*, SkinletData > skinletMap;
QskSkinHintTable hintTable; QskSkinHintTable hintTable;
QskAspect::States stateMask = QskAspect::AllStates;
std::unordered_map< int, QFont > fonts; std::unordered_map< int, QFont > fonts;
std::unordered_map< int, QskColorFilter > graphicFilters; std::unordered_map< int, QskColorFilter > graphicFilters;
@ -345,22 +344,6 @@ const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const
return QPlatformDialogHelper::buttonLayout( orientation, policy ); 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 ) QskSkinlet* QskSkin::skinlet( const QMetaObject* metaObject )
{ {
while ( metaObject ) while ( metaObject )

View File

@ -77,9 +77,6 @@ class QSK_EXPORT QskSkin : public QObject
virtual const int* dialogButtonLayout( Qt::Orientation ) const; virtual const int* dialogButtonLayout( Qt::Orientation ) const;
virtual QString dialogButtonText( int button ) const; virtual QString dialogButtonText( int button ) const;
void setStateMask( QskAspect::States );
QskAspect::States stateMask() const;
QskSkinlet* skinlet( const QMetaObject* ); QskSkinlet* skinlet( const QMetaObject* );
const QskSkinHintTable& hintTable() const; const QskSkinHintTable& hintTable() const;

View File

@ -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() QskSkinHintTable::~QskSkinHintTable()
{ {
delete m_hints; 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 const std::unordered_map< QskAspect, QVariant >& QskSkinHintTable::hints() const
{ {
if ( m_hints ) if ( m_hints )
@ -126,11 +96,7 @@ bool QskSkinHintTable::setHint( QskAspect aspect, const QVariant& skinHint )
QSK_ASSERT_COUNTER( m_animatorCount ); QSK_ASSERT_COUNTER( m_animatorCount );
} }
if ( aspect.hasStates() ) m_states |= aspect.states();
{
m_statefulCount++;
QSK_ASSERT_COUNTER( m_statefulCount );
}
return true; return true;
} }
@ -158,8 +124,7 @@ bool QskSkinHintTable::removeHint( QskAspect aspect )
if ( aspect.isAnimator() ) if ( aspect.isAnimator() )
m_animatorCount--; m_animatorCount--;
if ( aspect.hasStates() ) // how to clear m_states ? TODO ...
m_statefulCount--;
if ( m_hints->empty() ) if ( m_hints->empty() )
{ {
@ -184,8 +149,7 @@ QVariant QskSkinHintTable::takeHint( QskAspect aspect )
if ( aspect.isAnimator() ) if ( aspect.isAnimator() )
m_animatorCount--; m_animatorCount--;
if ( aspect.hasStates() ) // how to clear m_states ? TODO ...
m_statefulCount--;
if ( m_hints->empty() ) if ( m_hints->empty() )
{ {
@ -206,14 +170,14 @@ void QskSkinHintTable::clear()
m_hints = nullptr; m_hints = nullptr;
m_animatorCount = 0; m_animatorCount = 0;
m_statefulCount = 0; m_states = QskAspect::NoState;
} }
const QVariant* QskSkinHintTable::resolvedHint( const QVariant* QskSkinHintTable::resolvedHint(
QskAspect aspect, QskAspect* resolvedAspect ) const QskAspect aspect, QskAspect* resolvedAspect ) const
{ {
if ( m_hints != nullptr ) if ( m_hints != nullptr )
return qskResolvedHint( aspect, *m_hints, resolvedAspect ); return qskResolvedHint( aspect & m_states, *m_hints, resolvedAspect );
return nullptr; return nullptr;
} }
@ -223,7 +187,7 @@ QskAspect QskSkinHintTable::resolvedAspect( QskAspect aspect ) const
QskAspect a; QskAspect a;
if ( m_hints != nullptr ) if ( m_hints != nullptr )
qskResolvedHint( aspect, *m_hints, &a ); qskResolvedHint( aspect & m_states, *m_hints, &a );
return a; return a;
} }
@ -233,6 +197,8 @@ QskAspect QskSkinHintTable::resolvedAnimator(
{ {
if ( m_hints && m_animatorCount > 0 ) if ( m_hints && m_animatorCount > 0 )
{ {
aspect &= m_states;
Q_FOREVER Q_FOREVER
{ {
auto it = m_hints->find( aspect ); auto it = m_hints->find( aspect );
@ -268,15 +234,16 @@ bool QskSkinHintTable::setAnimation(
bool QskSkinHintTable::isResolutionMatching( bool QskSkinHintTable::isResolutionMatching(
QskAspect aspect1, QskAspect aspect2 ) const QskAspect aspect1, QskAspect aspect2 ) const
{ {
// remove states we do not have early
aspect1 &= m_states;
aspect2 &= m_states;
if ( aspect1 == aspect2 ) if ( aspect1 == aspect2 )
return true; return true;
if ( aspect1.trunk() != aspect2.trunk() ) if ( aspect1.trunk() != aspect2.trunk() )
return false; return false;
if ( !hasStates() )
return false;
const auto a1 = aspect1; const auto a1 = aspect1;
const auto a2 = aspect2; const auto a2 = aspect2;

View File

@ -17,12 +17,8 @@ class QSK_EXPORT QskSkinHintTable
{ {
public: public:
QskSkinHintTable(); QskSkinHintTable();
QskSkinHintTable( const QskSkinHintTable& other );
~QskSkinHintTable(); ~QskSkinHintTable();
QskSkinHintTable& operator=( const QskSkinHintTable& );
bool setAnimation( QskAspect, QskAnimationHint ); bool setAnimation( QskAspect, QskAnimationHint );
QskAnimationHint animation( QskAspect ) const; QskAnimationHint animation( QskAspect ) const;
@ -40,9 +36,10 @@ class QSK_EXPORT QskSkinHintTable
const std::unordered_map< QskAspect, QVariant >& hints() const; const std::unordered_map< QskAspect, QVariant >& hints() const;
bool hasAnimators() const; bool hasAnimators() const;
bool hasStates() const;
bool hasHints() const; bool hasHints() const;
QskAspect::States states() const;
void clear(); void clear();
const QVariant* resolvedHint( QskAspect, const QVariant* resolvedHint( QskAspect,
@ -56,13 +53,15 @@ class QSK_EXPORT QskSkinHintTable
bool isResolutionMatching( QskAspect, QskAspect ) const; bool isResolutionMatching( QskAspect, QskAspect ) const;
private: private:
Q_DISABLE_COPY( QskSkinHintTable )
static const QVariant invalidHint; static const QVariant invalidHint;
typedef std::unordered_map< QskAspect, QVariant > HintMap; typedef std::unordered_map< QskAspect, QVariant > HintMap;
HintMap* m_hints = nullptr; HintMap* m_hints = nullptr;
unsigned short m_animatorCount = 0; unsigned short m_animatorCount = 0;
unsigned short m_statefulCount = 0; QskAspect::States m_states;
}; };
inline bool QskSkinHintTable::hasHints() const inline bool QskSkinHintTable::hasHints() const
@ -70,9 +69,9 @@ inline bool QskSkinHintTable::hasHints() const
return m_hints != nullptr; 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 inline bool QskSkinHintTable::hasAnimators() const

View File

@ -20,6 +20,51 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
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 namespace
{ {
class UpdateInfo class UpdateInfo
@ -40,157 +85,125 @@ namespace
int updateModes; int updateModes;
}; };
class AnimatorCandidate class HintAnimator : public QskHintAnimator
{ {
public: public:
AnimatorCandidate() = default; inline HintAnimator( const QskControl* control, const QskAspect aspect,
const QVariant& value1, const QVariant& value2, QskAnimationHint hint )
inline AnimatorCandidate( QskAspect aspect,
const QVariant& from, const QVariant& to )
: aspect( aspect )
, from( from )
, to( to )
{ {
} setAspect( aspect );
setStartValue( value1 );
setEndValue( value2 );
QskAspect aspect; setDuration( hint.duration );
QVariant from; setEasingCurve( hint.type );
QVariant to; setUpdateFlags( hint.updateFlags );
setWindow( control->window() );
}
};
class WindowAnimator
{
public:
WindowAnimator( QQuickWindow* = nullptr );
const QQuickWindow* window() const;
void start();
bool isRunning() const;
QVariant animatedHint( QskAspect ) const;
QVariant animatedGraphicFilter( int graphicRole ) const;
void addGraphicFilterAnimators( const QskAnimationHint&,
const QskSkin*, const QskSkin* );
void addItemAspects( QQuickItem*,
const QskAnimationHint&, const QSet< QskAspect >&,
const QskSkin*, const QskSkin* );
void update();
private:
bool isControlAffected( const QskControl*,
const QVector< QskAspect::Subcontrol >&, QskAspect ) const;
void addHints( const QskControl*,
const QskAnimationHint&, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 );
void storeAnimator( const QskControl*, const QskAspect,
const QVariant&, const QVariant&, QskAnimationHint );
void storeUpdateInfo( const QskControl*, QskAspect );
QQuickWindow* m_window;
std::unordered_map< QskAspect, HintAnimator > m_animatorMap;
std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration
};
class ApplicationAnimator : public QObject
{
Q_OBJECT
public:
~ApplicationAnimator();
WindowAnimator* windowAnimator( const QQuickWindow* );
void add( WindowAnimator* );
void start();
void reset();
bool isRunning() const;
private Q_SLOTS:
// using functor slots ?
void notify( QQuickWindow* );
void cleanup( QQuickWindow* );
private:
/*
It should be possible to find an implementation, that interpolates
a skin hint only once for all windows. But as our animtors are driven by
QQuickWindow::afterAnimating the code will have to be somehow tricky.
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< WindowAnimator* > m_windowAnimators;
QMetaObject::Connection m_connections[2];
}; };
} }
static QVector< AnimatorCandidate > qskAnimatorCandidates( Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator )
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; WindowAnimator::WindowAnimator( QQuickWindow* window )
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
{
public:
AnimatorGroup( QQuickWindow* window = nullptr )
: m_window( window ) : m_window( window )
{ {
} }
inline const QQuickWindow* window() const inline const QQuickWindow* WindowAnimator::window() const
{ {
return m_window; return m_window;
} }
void start() void WindowAnimator::start()
{ {
for ( auto& it : m_hintAnimatorMap ) for ( auto& it : m_animatorMap )
it.second.start(); it.second.start();
for ( auto& it : m_graphicFilterAnimatorMap ) for ( auto& it : m_graphicFilterAnimatorMap )
it.second.start(); it.second.start();
} }
bool isRunning() const bool WindowAnimator::isRunning() const
{ {
if ( !m_hintAnimatorMap.empty() ) if ( !m_animatorMap.empty() )
{ {
const auto& animator = m_hintAnimatorMap.begin()->second; const auto& animator = m_animatorMap.begin()->second;
if ( animator.isRunning() ) if ( animator.isRunning() )
return true; return true;
} }
@ -205,10 +218,10 @@ namespace
return false; return false;
} }
inline QVariant animatedHint( QskAspect aspect ) const inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const
{ {
auto it = m_hintAnimatorMap.find( aspect ); auto it = m_animatorMap.find( aspect );
if ( it != m_hintAnimatorMap.cend() ) if ( it != m_animatorMap.cend() )
{ {
const auto& animator = it->second; const auto& animator = it->second;
if ( animator.isRunning() ) if ( animator.isRunning() )
@ -218,7 +231,7 @@ namespace
return QVariant(); return QVariant();
} }
inline QVariant animatedGraphicFilter( int graphicRole ) const inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const
{ {
auto it = m_graphicFilterAnimatorMap.find( graphicRole ); auto it = m_graphicFilterAnimatorMap.find( graphicRole );
if ( it != m_graphicFilterAnimatorMap.cend() ) if ( it != m_graphicFilterAnimatorMap.cend() )
@ -231,20 +244,21 @@ namespace
return QVariant(); return QVariant();
} }
void addGraphicFilterAnimators( void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint,
const QskAnimationHint& animatorHint, const QskSkin* skin1, const QskSkin* skin2 )
const std::unordered_map< int, QskColorFilter >& oldFilters,
const std::unordered_map< int, QskColorFilter >& newFilters )
{ {
const QskColorFilter noFilter; const QskColorFilter noFilter;
for ( auto it2 = newFilters.begin(); it2 != newFilters.end(); ++it2 ) const auto& filter1 = skin1->graphicFilters();
{ const auto& filter2 = skin2->graphicFilters();
auto it1 = oldFilters.find( it2->first );
if ( it1 == oldFilters.cend() )
it1 = oldFilters.find( 0 );
const auto& f1 = ( it1 != oldFilters.cend() ) ? it1->second : noFilter; 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; const auto& f2 = it2->second;
if ( f1 != f2 ) if ( f1 != f2 )
@ -261,17 +275,18 @@ namespace
} }
} }
void addAnimators( QQuickItem* item, const QskAnimationHint& animatorHint, void WindowAnimator::addItemAspects( QQuickItem* item,
const QVector< AnimatorCandidate >& candidates, QskSkin* skin ) const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 )
{ {
if ( !item->isVisible() ) if ( !item->isVisible() )
return; return;
if ( auto control = qskControlCast( item ) ) if ( auto control = qskControlCast( item ) )
{ {
if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) ) if ( control->isInitiallyPainted() && ( control->effectiveSkin() == skin2 ) )
{ {
addControlAnimators( control, animatorHint, candidates ); addHints( control, animatorHint, candidates, skin1, skin2 );
#if 1 #if 1
/* /*
As it is hard to identify which controls depend on the animated As it is hard to identify which controls depend on the animated
@ -285,10 +300,10 @@ namespace
const auto children = item->childItems(); const auto children = item->childItems();
for ( auto child : children ) for ( auto child : children )
addAnimators( child, animatorHint, candidates, skin ); addItemAspects( child, animatorHint, candidates, skin1, skin2 );
} }
void update() void WindowAnimator::update()
{ {
for ( auto& info : m_updateInfos ) for ( auto& info : m_updateInfos )
{ {
@ -306,98 +321,125 @@ namespace
} }
} }
private: void WindowAnimator::addHints( const QskControl* control,
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
void addControlAnimators( QskControl* control, const QskAnimationHint& animatorHint, const QskSkin* skin1, const QskSkin* skin2 )
const QVector< AnimatorCandidate >& candidates )
{ {
const auto subControls = control->subControls(); const auto subControls = control->subControls();
for ( const auto& candidate : candidates ) const auto& localTable = control->hintTable();
const auto& table1 = skin1->hintTable();
const auto& table2 = skin2->hintTable();
for ( auto aspect : candidates )
{ {
if ( !candidate.aspect.isMetric() ) 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 ) ) if ( !( control->flags() & QQuickItem::ItemHasContents ) )
{ {
// while metrics might have an effect on layouts, we // while metrics might have an effect on layouts, we
// can safely ignore others for controls without content // ignore all others for controls without content
continue; return false;
} }
} }
const auto subControl = candidate.aspect.subControl(); const auto subControl = aspect.subControl();
if ( subControl != control->effectiveSubcontrol( subControl ) ) if ( subControl != control->effectiveSubcontrol( subControl ) )
{ {
// The control uses subcontrol redirection, so we can assume it // The control uses subcontrol redirection, so we can assume it
// is not interested in this subcontrol. // is not interested in this subcontrol.
continue;
return false;
} }
if ( subControl != QskAspect::Control ) if ( subControl == QskAspect::Control )
{
if ( !subControls.contains( subControl ) )
{
// the control is not interested in the aspect
continue;
}
}
else
{ {
if ( !control->autoFillBackground() ) if ( !control->autoFillBackground() )
{ {
// no need to animate the background unless we show it // no need to animate the background unless we show it
continue; return false;
} }
} }
else
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. if ( !subControls.contains( subControl ) )
continue;
}
if ( candidate.aspect != requestState.aspect )
{ {
// the aspect was resolved to something else // the control is not interested in the aspect
continue; return false;
}
addAnimator( control->window(), candidate, animatorHint );
storeUpdateInfo( control, candidate.aspect );
} }
} }
void addAnimator( QQuickWindow* window, return true;
const AnimatorCandidate& candidate, QskAnimationHint animationHint ) }
inline void WindowAnimator::storeAnimator( const QskControl* control, const QskAspect aspect,
const QVariant& value1, const QVariant& value2, QskAnimationHint hint )
{ {
auto it = m_hintAnimatorMap.find( candidate.aspect ); if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() )
if ( it != m_hintAnimatorMap.end() ) {
return; // already there m_animatorMap.emplace( aspect,
HintAnimator( control, aspect, value1, value2, hint ) );
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 ) inline void WindowAnimator::storeUpdateInfo( const QskControl* control, QskAspect aspect )
{ {
UpdateInfo info; UpdateInfo info;
info.control = control; info.control = const_cast< QskControl* >( control );
info.updateModes = UpdateInfo::Update; info.updateModes = UpdateInfo::Update;
if ( aspect.isMetric() ) if ( aspect.isMetric() )
@ -412,42 +454,31 @@ namespace
m_updateInfos.insert( it, info ); m_updateInfos.insert( it, info );
} }
QQuickWindow* m_window; ApplicationAnimator::~ApplicationAnimator()
std::unordered_map< QskAspect, QskHintAnimator > m_hintAnimatorMap;
std::unordered_map< int, QskVariantAnimator > m_graphicFilterAnimatorMap;
std::vector< UpdateInfo > m_updateInfos; // vector: for fast iteration
};
class AnimatorGroups : public QObject
{
Q_OBJECT
public:
~AnimatorGroups()
{ {
reset(); reset();
} }
inline AnimatorGroup* animatorGroup( const QQuickWindow* window ) inline WindowAnimator* ApplicationAnimator::windowAnimator( const QQuickWindow* window )
{ {
if ( !m_animatorGroups.empty() && window ) if ( window )
{ {
for ( auto group : m_animatorGroups ) for ( auto animator : m_windowAnimators )
{ {
if ( group->window() == window ) if ( animator->window() == window )
return group; return animator;
} }
} }
return nullptr; return nullptr;
} }
void add( AnimatorGroup* group ) void ApplicationAnimator::add( WindowAnimator* animator )
{ {
m_animatorGroups.push_back( group ); m_windowAnimators.push_back( animator );
} }
void start() void ApplicationAnimator::start()
{ {
m_connections[0] = QskAnimator::addAdvanceHandler( m_connections[0] = QskAnimator::addAdvanceHandler(
this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection ); this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection );
@ -455,76 +486,60 @@ namespace
m_connections[1] = QskAnimator::addCleanupHandler( m_connections[1] = QskAnimator::addCleanupHandler(
this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection ); this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection );
for ( auto& group : m_animatorGroups ) for ( auto& animator : m_windowAnimators )
group->start(); animator->start();
} }
void reset() void ApplicationAnimator::reset()
{ {
qDeleteAll( m_animatorGroups ); qDeleteAll( m_windowAnimators );
m_animatorGroups.clear(); m_windowAnimators.clear();
disconnect( m_connections[0] ); disconnect( m_connections[0] );
disconnect( m_connections[1] ); disconnect( m_connections[1] );
} }
inline bool isRunning() const inline bool ApplicationAnimator::isRunning() const
{ {
return !m_animatorGroups.empty(); return !m_windowAnimators.empty();
} }
private Q_SLOTS: void ApplicationAnimator::notify( QQuickWindow* window )
void notify( QQuickWindow* window )
{ {
for ( auto& group : m_animatorGroups ) for ( auto& animator : m_windowAnimators )
{ {
if ( group->window() == window ) if ( animator->window() == window )
{ {
group->update(); animator->update();
return; return;
} }
} }
} }
void cleanup( QQuickWindow* window ) void ApplicationAnimator::cleanup( QQuickWindow* window )
{ {
for ( auto it = m_animatorGroups.begin(); for ( auto it = m_windowAnimators.begin();
it != m_animatorGroups.end(); ++it ) it != m_windowAnimators.end(); ++it )
{ {
auto group = *it; auto animator = *it;
if ( group->window() == window ) if ( animator->window() == window )
{ {
if ( !group->isRunning() ) if ( !animator->isRunning() )
{ {
// The notification might be for other animators // The notification might be for other animators
m_animatorGroups.erase( it ); m_windowAnimators.erase( it );
delete group; delete animator;
} }
break; break;
} }
} }
if ( m_animatorGroups.empty() ) if ( m_windowAnimators.empty() )
reset(); reset();
} }
private:
/*
It should be possible to find an implementation, that interpolates
a skin hint only once for all windows. But as our animtors are driven by
QQuickWindow::afterAnimating the code will have to be somehow tricky.
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;
QMetaObject::Connection m_connections[2];
};
}
Q_GLOBAL_STATIC( AnimatorGroups, qskSkinAnimator )
class QskSkinTransition::PrivateData class QskSkinTransition::PrivateData
{ {
public: public:
@ -589,38 +604,20 @@ void QskSkinTransition::updateSkin( QskSkin*, QskSkin* )
void QskSkinTransition::process() void QskSkinTransition::process()
{ {
auto skinFrom = m_data->skins[ 0 ]; qskApplicationAnimator->reset();
auto skinTo = m_data->skins[ 1 ];
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 if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) )
return; {
qskAddCandidates( m_data->mask, skin1, candidates );
qskAddCandidates( m_data->mask, skin2, candidates );
} }
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 ( !candidates.isEmpty() ) if ( !candidates.isEmpty() )
@ -631,21 +628,19 @@ void QskSkinTransition::process()
for ( const auto window : windows ) for ( const auto window : windows )
{ {
if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) ) if ( auto w = qobject_cast< QQuickWindow* >( window ) )
{ {
if ( !quickWindow->isVisible() || if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) )
( qskEffectiveSkin( quickWindow ) != skinTo ) )
{ {
continue; continue;
} }
auto group = new AnimatorGroup( quickWindow ); auto animator = new WindowAnimator( w );
if ( doGraphicFilter ) if ( doGraphicFilter )
{ {
group->addGraphicFilterAnimators( animator->addGraphicFilterAnimators(
m_data->animationHint, oldFilters, m_data->animationHint, skin1, skin2 );
skinTo->graphicFilters() );
doGraphicFilter = false; doGraphicFilter = false;
} }
@ -655,21 +650,24 @@ void QskSkinTransition::process()
over the the item trees. over the the item trees.
*/ */
group->addAnimators( quickWindow->contentItem(), animator->addItemAspects( w->contentItem(),
m_data->animationHint, candidates, skinTo ); 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() bool QskSkinTransition::isRunning()
{ {
if ( qskSkinAnimator.exists() ) if ( qskApplicationAnimator.exists() )
return qskSkinAnimator->isRunning(); return qskApplicationAnimator->isRunning();
return false; return false;
} }
@ -677,10 +675,10 @@ bool QskSkinTransition::isRunning()
QVariant QskSkinTransition::animatedHint( QVariant QskSkinTransition::animatedHint(
const QQuickWindow* window, QskAspect aspect ) const QQuickWindow* window, QskAspect aspect )
{ {
if ( qskSkinAnimator.exists() ) if ( qskApplicationAnimator.exists() )
{ {
if ( const auto group = qskSkinAnimator->animatorGroup( window ) ) if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
return group->animatedHint( aspect ); return animator->animatedHint( aspect );
} }
return QVariant(); return QVariant();
@ -689,10 +687,10 @@ QVariant QskSkinTransition::animatedHint(
QVariant QskSkinTransition::animatedGraphicFilter( QVariant QskSkinTransition::animatedGraphicFilter(
const QQuickWindow* window, int graphicRole ) const QQuickWindow* window, int graphicRole )
{ {
if ( qskSkinAnimator.exists() ) if ( qskApplicationAnimator.exists() )
{ {
if ( const auto group = qskSkinAnimator->animatorGroup( window ) ) if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
return group->animatedGraphicFilter( graphicRole ); return animator->animatedGraphicFilter( graphicRole );
} }
return QVariant(); return QVariant();

View File

@ -38,17 +38,6 @@ static inline bool qskIsControl( const QskSkinnable* skinnable )
return skinnable->metaObject()->inherits( &QskControl::staticMetaObject ); 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, static inline bool qskSetFlag( QskSkinnable* skinnable,
const QskAspect aspect, int flag ) const QskAspect aspect, int flag )
{ {
@ -940,23 +929,12 @@ const QVariant& QskSkinnable::storedHint(
{ {
const auto skin = effectiveSkin(); const auto skin = effectiveSkin();
// clearing all state bits not being handled from the skin
aspect.clearStates( ~skin->stateMask() );
QskAspect resolvedAspect; QskAspect resolvedAspect;
const auto& localTable = m_data->hintTable; const auto& localTable = m_data->hintTable;
if ( localTable.hasHints() ) if ( localTable.hasHints() )
{ {
auto a = aspect; if ( const auto value = localTable.resolvedHint( aspect, &resolvedAspect ) )
if ( !localTable.hasStates() )
{
// we don't need to clear the state bits stepwise
a.clearStates();
}
if ( const QVariant* value = localTable.resolvedHint( a, &resolvedAspect ) )
{ {
if ( status ) if ( status )
{ {
@ -972,10 +950,7 @@ const QVariant& QskSkinnable::storedHint(
const auto& skinTable = skin->hintTable(); const auto& skinTable = skin->hintTable();
if ( skinTable.hasHints() ) if ( skinTable.hasHints() )
{ {
auto a = aspect; if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) )
const QVariant* value = skinTable.resolvedHint( a, &resolvedAspect );
if ( value )
{ {
if ( status ) if ( status )
{ {
@ -993,8 +968,7 @@ const QVariant& QskSkinnable::storedHint(
aspect.setSubControl( QskAspect::Control ); aspect.setSubControl( QskAspect::Control );
aspect.clearStates(); aspect.clearStates();
value = skinTable.resolvedHint( aspect, &resolvedAspect ); if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) )
if ( value )
{ {
if ( status ) if ( status )
{ {
@ -1171,27 +1145,11 @@ void QskSkinnable::startHintTransition( QskAspect aspect,
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) ) if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
return; 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 v1 = from;
auto v2 = to; auto v2 = to;
if ( !v1.isValid() ) if ( !QskVariantAnimator::convertValues( v1, v2 ) )
{
v1 = qskTypedNullValue( v2 );
}
else if ( !v2.isValid() )
{
v2 = qskTypedNullValue( v1 );
}
else if ( v1.userType() != v2.userType() )
{
return; return;
}
if ( aspect.flagPrimitive() == QskAspect::GraphicRole ) if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
{ {
@ -1257,7 +1215,8 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates )
if ( skin ) if ( skin )
{ {
const auto mask = skin->stateMask(); const auto mask = skin->hintTable().states() | m_data->hintTable.states();
if ( ( newStates & mask ) == ( m_data->skinStates & mask ) ) if ( ( newStates & mask ) == ( m_data->skinStates & mask ) )
{ {
// the modified bits are not handled by the skin // 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 ); const auto primitive = static_cast< QskAspect::Primitive >( i );
aspect.setPrimitive( type, primitive ); aspect.setPrimitive( type, primitive );
auto a1 = aspect | m_data->skinStates; const auto a1 = aspect | m_data->skinStates;
auto a2 = aspect | newStates; const auto a2 = aspect | newStates;
bool doTransition = true; bool doTransition = true;
if ( !m_data->hintTable.hasStates() ) if ( m_data->hintTable.states() == QskAspect::NoState )
{
/*
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.
*/
doTransition = !skinTable.isResolutionMatching( a1, a2 ); doTransition = !skinTable.isResolutionMatching( a1, a2 );
}
if ( doTransition ) if ( doTransition )
{ {
@ -1343,12 +1291,7 @@ QskSkin* QskSkinnable::effectiveSkin() const
if ( skin == nullptr ) if ( skin == nullptr )
{ {
if ( const auto control = owningControl() ) if ( const auto control = owningControl() )
{ skin = qskEffectiveSkin( control->window() );
if ( auto window = qobject_cast< const QskWindow* >( control->window() ) )
{
skin = window->skin();
}
}
} }
return skin ? skin : qskSetup->skin(); return skin ? skin : qskSetup->skin();

View File

@ -230,6 +230,8 @@ class QSK_EXPORT QskSkinnable
bool resetGraphicRoleHint( QskAspect ); bool resetGraphicRoleHint( QskAspect );
int graphicRoleHint( QskAspect, QskSkinHintStatus* = nullptr ) const; int graphicRoleHint( QskAspect, QskSkinHintStatus* = nullptr ) const;
const QskSkinHintTable& hintTable() const;
protected: protected:
virtual void updateNode( QSGNode* ); virtual void updateNode( QSGNode* );
virtual bool isTransitionAccepted( QskAspect ) const; virtual bool isTransitionAccepted( QskAspect ) const;
@ -237,7 +239,6 @@ class QSK_EXPORT QskSkinnable
virtual QskAspect::Subcontrol substitutedSubcontrol( QskAspect::Subcontrol ) const; virtual QskAspect::Subcontrol substitutedSubcontrol( QskAspect::Subcontrol ) const;
QskSkinHintTable& hintTable(); QskSkinHintTable& hintTable();
const QskSkinHintTable& hintTable() const;
private: private:
Q_DISABLE_COPY( QskSkinnable ) Q_DISABLE_COPY( QskSkinnable )

View File

@ -76,6 +76,31 @@ QSK_DECL_INSANE static inline QVariant qskInterpolate(
return f( from.constData(), to.constData(), progress ); 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() QskVariantAnimator::QskVariantAnimator()
: m_interpolator( nullptr ) : m_interpolator( nullptr )
{ {
@ -100,16 +125,60 @@ void QskVariantAnimator::setCurrentValue( const QVariant& value )
m_currentValue = 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() void QskVariantAnimator::setup()
{ {
m_interpolator = nullptr; m_interpolator = nullptr;
const auto type = m_startValue.userType(); if ( convertValues( m_startValue, m_endValue ) )
if ( type == m_endValue.userType() )
{ {
if ( m_startValue != m_endValue )
{
const auto id = m_startValue.userType();
// all what has been registered by qRegisterAnimationInterpolator // all what has been registered by qRegisterAnimationInterpolator
m_interpolator = reinterpret_cast< void ( * )() >( m_interpolator = reinterpret_cast< void ( * )() >(
QVariantAnimationPrivate::getInterpolator( type ) ); QVariantAnimationPrivate::getInterpolator( id ) );
}
} }
m_currentValue = m_interpolator ? m_startValue : m_endValue; m_currentValue = m_interpolator ? m_startValue : m_endValue;
@ -131,3 +200,32 @@ void QskVariantAnimator::done()
{ {
m_interpolator = nullptr; 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;
}

View File

@ -24,6 +24,9 @@ class QSK_EXPORT QskVariantAnimator : public QskAnimator
void setEndValue( const QVariant& ); void setEndValue( const QVariant& );
QVariant endValue() const; QVariant endValue() const;
static bool maybeInterpolate( const QVariant&, const QVariant& );
static bool convertValues( QVariant&, QVariant& );
protected: protected:
void setup() override; void setup() override;
void advance( qreal value ) override; void advance( qreal value ) override;

View File

@ -63,7 +63,6 @@ SOURCES += \
common/QskScaleTickmarks.cpp \ common/QskScaleTickmarks.cpp \
common/QskShadowMetrics.cpp \ common/QskShadowMetrics.cpp \
common/QskSizePolicy.cpp \ common/QskSizePolicy.cpp \
common/QskStateCombination.cpp \
common/QskTextColors.cpp \ common/QskTextColors.cpp \
common/QskTextOptions.cpp common/QskTextOptions.cpp

View File

@ -77,19 +77,19 @@ void SkinnyShortcut::enable( Types types )
void SkinnyShortcut::rotateSkin() void SkinnyShortcut::rotateSkin()
{ {
const QStringList names = qskSkinManager->skinNames(); const auto names = qskSkinManager->skinNames();
if ( names.size() <= 1 ) if ( names.size() <= 1 )
return; return;
int index = names.indexOf( qskSetup->skinName() ); int index = names.indexOf( qskSetup->skinName() );
index = ( index + 1 ) % names.size(); index = ( index + 1 ) % names.size();
QskSkin* oldSkin = qskSetup->skin(); auto oldSkin = qskSetup->skin();
if ( oldSkin->parent() == qskSetup ) if ( oldSkin->parent() == qskSetup )
oldSkin->setParent( nullptr ); // otherwise setSkin deletes it 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.setMask( QskAspect::Color ); // Metrics are flickering -> TODO
@ -102,6 +102,7 @@ void SkinnyShortcut::rotateSkin()
if ( oldSkin->parent() == nullptr ) if ( oldSkin->parent() == nullptr )
delete oldSkin; delete oldSkin;
} }
}
void SkinnyShortcut::showBackground() void SkinnyShortcut::showBackground()
{ {