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 <QskRgbValue.h>
Page::Page( Qt::Orientation orientation, QQuickItem* parent )
: QskLinearBox( orientation, parent )
, m_gradient( QskRgb::GhostWhite )
{
setMargins( 20 );
setPadding( 10 );
setSpacing( 10 );
}
void Page::setGradient( const QskGradient& gradient )
{
if ( gradient != m_gradient )
{
m_gradient = gradient;
if ( parentItem() && isVisibleToParent() )
parentItem()->update();
}
}
QskGradient Page::gradient() const
{
return m_gradient;
}

View File

@ -6,16 +6,9 @@
#pragma once
#include <QskLinearBox.h>
#include <QskGradient.h>
class Page : public QskLinearBox
{
public:
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 )
: Page( Qt::Vertical, parent )
{
setGradient( QskRgb::AliceBlue );
setSpacing( 40 );
(void) new TextBox( this );

View File

@ -30,27 +30,6 @@ namespace
setMargins( 10 );
setTabPosition( Qsk::Left );
setAutoFitTabs( true );
connect( this, &QskTabView::currentIndexChanged,
this, &TabView::updateViewPanel );
}
protected:
void aboutToShow() override
{
updateViewPanel();
}
private:
void updateViewPanel()
{
/*
We should have a better way to set individual colors
for each tab page background
*/
if ( auto page = dynamic_cast< const ::Page* >( currentItem() ) )
setGradientHint( QskTabView::Page, page->gradient() );
}
};
}

View File

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

View File

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

View File

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

View File

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

View File

@ -9,86 +9,56 @@
#include "QskMaterialGlobal.h"
#include <QskSkin.h>
#include <QskRgbValue.h>
#include <memory>
struct ColorPalette
class QSK_MATERIAL_EXPORT QskMaterialPalette
{
enum Lightness { light, dark } lightness;
QColor primary;
QColor primaryVariant;
QColor onPrimary;
QColor secondary;
QColor secondaryVariant;
QColor onSecondary;
QColor background;
QColor onBackground;
QColor error;
QColor onError;
QColor primaryNoSaturation = QColor::fromHsl( primary.hslHue(), 0,
primary.lightness() );
QColor secondaryNoSaturation =
QColor::fromHsl( secondary.hslHue(), 0,
secondary.lightness() );
QColor secondaryVariantNoSaturation =
QColor::fromHsl( secondaryVariant.hslHue(), 0,
secondaryVariant.lightness() +
secondaryVariant.hslSaturation() );
qreal disabledOccupancy = 0.2;
qreal widgetBackgroundDisabled = 0.6;
qreal hover = 0.1;
qreal focused = 0.4;
qreal pressed = 0.5;
qreal disabled = 0.3;
ColorPalette(
Lightness lightness = light,
QColor primary = QColor::fromRgb( 0x6200EE ),
QColor primaryVariant = QColor::fromRgb( 0x3700B3 ),
QColor onPrimary = Qt::white,
QColor secondary = QColor::fromRgb( 0x03DAC6 ),
QColor secondaryVariant = QColor::fromRgb( 0x018786 ),
QColor onSecondary = Qt::white,
QColor background = QColor::fromRgba( QskRgb::Grey100 ),
QColor onBackground = Qt::black,
QColor error = QColor::fromRgb( 0xB00020 ),
QColor onError = Qt::white ):
lightness( lightness ),
primary( primary ),
primaryVariant( primaryVariant ),
onPrimary( onPrimary ),
secondary( secondary ),
secondaryVariant( secondaryVariant ),
onSecondary( onSecondary ),
background( background ),
onBackground( onBackground ),
error( error ),
onError( onError )
public:
enum Lightness
{
primaryNoSaturation = QColor::fromHsl( primary.hslHue(), 0,
primary.lightness() );
Light,
Dark
};
secondaryNoSaturation = QColor::fromHsl( secondary.hslHue(),
0,
secondary.lightness() );
secondaryVariantNoSaturation =
QColor::fromHsl( secondaryVariant.hslHue(), 0,
secondaryVariant.lightness() );
QskMaterialPalette( Lightness lightness )
: m_lightness( lightness )
{
}
inline QColor elevated( const QColor target, const float level = 1 ) const {
return ( lightness == light ) ? target.darker( 100 + level * 15 )
: target.lighter( 130 + level * 30 );
inline QRgb elevated( const QRgb rgb, const float level = 1 ) const
{
return ( m_lightness == Light )
? QskRgb::darker( rgb, 100 + level * 15 )
: QskRgb::lighter( rgb, 130 + level * 30 );
}
public:
QRgb primary;
QRgb primaryVariant;
QRgb onPrimary;
QRgb secondary;
QRgb secondaryVariant;
QRgb onSecondary;
QRgb background;
QRgb onBackground;
QRgb error;
QRgb onError;
QRgb primaryNoSaturation;
QRgb secondaryNoSaturation;
QRgb secondaryVariantNoSaturation;
const qreal disabledOccupancy = 0.2;
const qreal widgetBackgroundDisabled = 0.6;
const qreal hover = 0.1;
const qreal focused = 0.4;
const qreal pressed = 0.5;
const qreal disabled = 0.3;
const Lightness m_lightness;
};
class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin
@ -98,12 +68,8 @@ class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin
using Inherited = QskSkin;
public:
QskMaterialSkin( ColorPalette, QObject* parent = nullptr );
QskMaterialSkin( const QskMaterialPalette&, QObject* parent = nullptr );
~QskMaterialSkin() override;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -9,6 +9,42 @@
static const QString materialLightSkinName = QStringLiteral( "materialLight" );
static const QString materialDarkSkinName = QStringLiteral( "materialDark" );
namespace
{
inline int lightnessRgb( QRgb rgb )
{
const int red = qRed( rgb );
const int green = qGreen( rgb );
const int blue = qBlue( rgb );
int min, max;
if ( red > green )
{
max = qMax( red, blue );
min = qMin( green, blue );
}
else
{
max = qMax( green, blue );
min = qMin( red, blue );
}
return ( max + min ) / 2;
}
inline QRgb toUnsaturated( QRgb rgb )
{
/*
a saturation of 0 results in having the lightness as r,g,b
Is this intended ?
*/
const auto l = lightnessRgb( rgb );
return qRgba( l, l, l, qAlpha( rgb ) );
}
}
QskMaterialSkinFactory::QskMaterialSkinFactory( QObject* parent )
: QskSkinFactory( parent )
{
@ -25,24 +61,48 @@ QStringList QskMaterialSkinFactory::skinNames() const
QskSkin* QskMaterialSkinFactory::createSkin( const QString& skinName )
{
if ( QString::compare( skinName, materialLightSkinName, Qt::CaseInsensitive ) )
return new QskMaterialSkin( ColorPalette() );
if ( QString::compare( skinName, materialDarkSkinName, Qt::CaseInsensitive ) )
if ( QString::compare( skinName, materialLightSkinName, Qt::CaseInsensitive ) == 0 )
{
return new QskMaterialSkin( ColorPalette(
ColorPalette::dark, // lightness
QColor::fromRgb( 0xBB86FC ), // primary
QColor::fromRgb( 0x3700B3 ), // primaryVariant
Qt::black, // onPrimary
QColor::fromRgb( 0x03DAC6 ), // secondary
QColor::fromRgb( 0x018786 ), // secondaryVariant
Qt::black, // onSecondary
QColor::fromRgb( 0x121212 ), // background
Qt::white, // onBackground
QColor::fromRgb( 0xCF6679 ), // error
Qt::black // onError
) );
QskMaterialPalette pal( QskMaterialPalette::Light );;
pal.primary = 0xff6200ee;
pal.primaryVariant = 0xff3700b3;
pal.onPrimary = QskRgb::White;
pal.secondary = 0xff03dac6;
pal.secondaryVariant = 0xff018786;
pal.onSecondary = QskRgb::White;
pal.background = QskRgb::Grey100;
pal.onBackground = QskRgb::Black;
pal.error = 0xffb00020;
pal.onError = QskRgb::White;
pal.primaryNoSaturation = toUnsaturated( pal.primary );
pal.secondaryNoSaturation = toUnsaturated( pal.secondary );
pal.secondaryVariantNoSaturation = toUnsaturated( pal.secondaryVariant );
return new QskMaterialSkin( pal );
}
if ( QString::compare( skinName, materialDarkSkinName, Qt::CaseInsensitive ) == 0 )
{
QskMaterialPalette pal( QskMaterialPalette::Dark );
pal.primary = 0xffbb86fc;
pal.primaryVariant = 0xff3700b3;
pal.onPrimary = QskRgb::Black;
pal.secondary = 0xff03dac6;
pal.secondaryVariant = 0xff018786;
pal.onSecondary = QskRgb::Black;
pal.background = 0xff121212;
pal.onBackground = QskRgb::White;
pal.error = 0xffcf6679;
pal.onError = QskRgb::Black;
pal.primaryNoSaturation = toUnsaturated( pal.primary );
pal.secondaryNoSaturation = toUnsaturated( pal.secondary );
pal.secondaryVariantNoSaturation = toUnsaturated( pal.secondaryVariant );
return new QskMaterialSkin( pal );
}
return nullptr;

View File

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

View File

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

View File

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

View File

@ -109,8 +109,18 @@ class QSK_EXPORT QskAspect
constexpr QskAspect operator|( Type ) const noexcept;
constexpr QskAspect operator|( Primitive ) const noexcept;
constexpr QskAspect operator|( Placement ) const noexcept;
constexpr QskAspect operator|( State ) const noexcept;
QskAspect& operator|=( State ) noexcept;
constexpr QskAspect operator&( State ) const noexcept;
QskAspect& operator&=( State ) noexcept;
constexpr QskAspect operator|( States ) const noexcept;
QskAspect& operator|=( States ) noexcept;
constexpr QskAspect operator&( States ) const noexcept;
QskAspect& operator&=( States ) noexcept;
constexpr QskAspect stateless() const noexcept;
constexpr QskAspect trunk() const noexcept;
@ -283,12 +293,48 @@ inline constexpr QskAspect QskAspect::operator|( State state ) const noexcept
m_bits.primitive, m_bits.placement, m_bits.states | state );
}
inline QskAspect& QskAspect::operator|=( State state ) noexcept
{
m_bits.states |= state;
return *this;
}
inline constexpr QskAspect QskAspect::operator&( State state ) const noexcept
{
return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator,
m_bits.primitive, m_bits.placement, m_bits.states & state );
}
inline QskAspect& QskAspect::operator&=( State state ) noexcept
{
m_bits.states &= state;
return *this;
}
inline constexpr QskAspect QskAspect::operator|( States states ) const noexcept
{
return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator,
m_bits.primitive, m_bits.placement, m_bits.states | states );
}
inline QskAspect& QskAspect::operator|=( States states ) noexcept
{
m_bits.states |= states;
return *this;
}
inline constexpr QskAspect QskAspect::operator&( States states ) const noexcept
{
return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator,
m_bits.primitive, m_bits.placement, m_bits.states & states );
}
inline QskAspect& QskAspect::operator&=( States states ) noexcept
{
m_bits.states &= states;
return *this;
}
inline constexpr QskAspect QskAspect::stateless() const noexcept
{
return QskAspect( m_bits.subControl, m_bits.type, m_bits.isAnimator,
@ -538,6 +584,11 @@ namespace std
};
}
inline QskHashValue qHash( const QskAspect aspect, QskHashValue seed = 0 ) noexcept
{
return qHash( aspect.value(), seed );
}
#ifndef QT_NO_DEBUG_STREAM
class QDebug;

View File

@ -13,6 +13,10 @@ static void qskRegisterBoxBorderColors()
{
qRegisterMetaType< QskBoxBorderColors >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskBoxBorderColors >();
#endif
QMetaType::registerConverter< QColor, QskBoxBorderColors >(
[]( const QColor& color ) { return QskBoxBorderColors( color ); } );
@ -110,6 +114,26 @@ void QskBoxBorderColors::setGradientAt( Qt::Edges edges, const QskGradient& grad
m_gradients[ Qsk::Bottom ] = gradient;
}
void QskBoxBorderColors::setLeft( const QskGradient& gradient )
{
m_gradients[ Qsk::Left ] = gradient;
}
void QskBoxBorderColors::setTop( const QskGradient& gradient )
{
m_gradients[ Qsk::Top ] = gradient;
}
void QskBoxBorderColors::setRight( const QskGradient& gradient )
{
m_gradients[ Qsk::Right ] = gradient;
}
void QskBoxBorderColors::setBottom( const QskGradient& gradient )
{
m_gradients[ Qsk::Bottom ] = gradient;
}
const QskGradient& QskBoxBorderColors::gradientAt( Qt::Edge edge ) const
{
switch ( edge )
@ -165,6 +189,14 @@ bool QskBoxBorderColors::isMonochrome() const
&& m_gradients[ 3 ].isMonochrome();
}
bool QskBoxBorderColors::isValid() const
{
return m_gradients[ 0 ].isValid()
|| m_gradients[ 1 ].isValid()
|| m_gradients[ 2 ].isValid()
|| m_gradients[ 3 ].isValid();
}
QskBoxBorderColors QskBoxBorderColors::interpolated(
const QskBoxBorderColors& to, qreal ratio ) const
{
@ -172,8 +204,14 @@ QskBoxBorderColors QskBoxBorderColors::interpolated(
for ( size_t i = 0; i < 4; i++ )
{
colors.m_gradients[ i ] = colors.m_gradients[ i ].interpolated(
to.m_gradients[ i ], ratio );
#if 1
/*
When one border has a width of 0 we would prefer to ignore
the color and use always use the other color. TODO ...
*/
#endif
auto& gradient = colors.m_gradients[ i ];
gradient = gradient.interpolated( to.m_gradients[ i ], ratio );
}
return colors;
@ -204,14 +242,44 @@ QDebug operator<<( QDebug debug, const QskBoxBorderColors& colors )
QDebugStateSaver saver( debug );
debug.nospace();
debug << "BoxBorderColors" << '(';
debug << "BoxBorderColors";
debug << " L" << colors.gradient( Qsk::Left );
debug << ", T" << colors.gradient( Qsk::Top );
debug << ", R" << colors.gradient( Qsk::Right );
debug << ", B" << colors.gradient( Qsk::Bottom );
if ( !colors.isValid() )
{
debug << "()";
}
else
{
debug << "( ";
if ( colors.isMonochrome() )
{
const auto& gradient = colors.gradient( Qsk::Left );
QskRgb::debugColor( debug, gradient.startColor() );
}
else
{
const char prompts[] = { 'L', 'T', 'R', 'B' };
for ( int i = 0; i <= Qsk::Bottom; i++ )
{
if ( i != 0 )
debug << ", ";
const auto& gradient = colors.gradient(
static_cast< Qsk::Position >( i ) );
debug << prompts[ i ] << ": ";
if ( gradient.isValid() && gradient.isMonochrome() )
QskRgb::debugColor( debug, gradient.startColor() );
else
debug << gradient;
}
}
debug << " )";
}
return debug;
}

View File

@ -12,10 +12,15 @@
#include <qcolor.h>
#include <qmetatype.h>
class QDebug;
class QSK_EXPORT QskBoxBorderColors
{
Q_GADGET
Q_PROPERTY( QskGradient left READ left WRITE setLeft )
Q_PROPERTY( QskGradient top READ top WRITE setTop )
Q_PROPERTY( QskGradient right READ right WRITE setRight )
Q_PROPERTY( QskGradient bottom READ bottom WRITE setBottom )
public:
QskBoxBorderColors();
@ -44,6 +49,18 @@ class QSK_EXPORT QskBoxBorderColors
void setGradientAt( Qt::Edges, const QskGradient& );
const QskGradient& gradientAt( Qt::Edge ) const;
void setLeft( const QskGradient& );
const QskGradient& left() const;
void setTop( const QskGradient& );
const QskGradient& top() const;
void setRight( const QskGradient& );
const QskGradient& right() const;
void setBottom( const QskGradient& );
const QskGradient& bottom() const;
QskBoxBorderColors interpolated( const QskBoxBorderColors&, qreal value ) const;
static QVariant interpolate( const QskBoxBorderColors&,
@ -53,6 +70,7 @@ class QSK_EXPORT QskBoxBorderColors
bool isMonochrome() const;
bool isVisible() const;
bool isValid() const;
private:
QskGradient m_gradients[ 4 ];
@ -78,8 +96,29 @@ inline const QskGradient& QskBoxBorderColors::gradient( Qsk::Position position )
return m_gradients[ position ];
}
inline const QskGradient& QskBoxBorderColors::left() const
{
return m_gradients[ Qsk::Left ];
}
inline const QskGradient& QskBoxBorderColors::top() const
{
return m_gradients[ Qsk::Top ];
}
inline const QskGradient& QskBoxBorderColors::right() const
{
return m_gradients[ Qsk::Right ];
}
inline const QskGradient& QskBoxBorderColors::bottom() const
{
return m_gradients[ Qsk::Bottom ];
}
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
QSK_EXPORT QDebug operator<<( QDebug, const QskBoxBorderColors& );
#endif

View File

@ -12,6 +12,10 @@ static void qskRegisterBoxBorderMetrics()
{
qRegisterMetaType< QskBoxBorderMetrics >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskBoxBorderMetrics >();
#endif
QMetaType::registerConverter< QskMargins, QskBoxBorderMetrics >(
[]( const QskMargins& margins ) { return QskBoxBorderMetrics( margins ); } );
@ -109,9 +113,24 @@ QDebug operator<<( QDebug debug, const QskBoxBorderMetrics& metrics )
QDebugStateSaver saver( debug );
debug.nospace();
debug << "BoxBorder" << '(';
debug << metrics.sizeMode() << ',' << metrics.widths();
debug << ')';
debug << "BoxBorder" << "( ";
if ( metrics.sizeMode() != Qt::AbsoluteSize )
debug << metrics.sizeMode() << ", ";
const auto& w = metrics.widths();
if ( metrics.isEquidistant() )
{
debug << w.left();
}
else
{
const char s[] = ", ";
debug << w.left() << s << w.top() << s << w.right() << s << w.bottom();
}
debug << " )";
return debug;
}

View File

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

View File

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

View File

@ -15,6 +15,10 @@ static void qskRegisterGradient()
{
qRegisterMetaType< QskGradient >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskGradient >();
#endif
QMetaType::registerConverter< QColor, QskGradient >(
[]( const QColor& color ) { return QskGradient( color ); } );
}
@ -229,6 +233,11 @@ QskGradient::QskGradient( Orientation orientation, const QskGradientStops& stops
setStops( stops );
}
QskGradient::QskGradient( Qt::Orientation orientation, QGradient::Preset preset )
: QskGradient( qskOrientation( orientation ), preset )
{
}
QskGradient::QskGradient( Orientation orientation, QGradient::Preset preset )
: QskGradient( orientation )
{
@ -499,6 +508,14 @@ QskGradient QskGradient::interpolated(
return QskGradient( gradient->orientation(), stops );
}
if ( isMonochrome() && to.isMonochrome() )
{
const auto c = QskRgb::interpolated(
m_stops[ 0 ].color(), to.m_stops[ 0 ].color(), value );
return QskGradient( to.orientation(), c, c );
}
if ( isMonochrome() )
{
// we can ignore our stops
@ -624,7 +641,49 @@ void QskGradient::updateStatusBits() const
QDebug operator<<( QDebug debug, const QskGradient& gradient )
{
debug << "GR:" << gradient.orientation() << gradient.stops().count();
QDebugStateSaver saver( debug );
debug.nospace();
debug << "Gradient";
if ( !gradient.isValid() )
{
debug << "()";
}
else
{
debug << "( ";
if ( gradient.isMonochrome() )
{
QskRgb::debugColor( debug, gradient.startColor() );
}
else
{
const char o[] = { 'H', 'V', 'D' };
debug << o[ gradient.orientation() ] << ", ";
if ( gradient.stops().count() == 2 )
{
QskRgb::debugColor( debug, gradient.startColor() );
debug << ", ";
QskRgb::debugColor( debug, gradient.endColor() );
}
else
{
const auto& s = gradient.stops();
for ( int i = 0; i < s.count(); i++ )
{
if ( i != 0 )
debug << ", ";
debug << s[i];
}
}
}
debug << " )";
}
return debug;
}

View File

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

View File

@ -14,6 +14,10 @@
static void qskRegisterGradientStop()
{
qRegisterMetaType< QskGradientStop >();
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QMetaType::registerEqualsComparator< QskGradientStop >();
#endif
}
Q_CONSTRUCTOR_FUNCTION( qskRegisterGradientStop )
@ -78,7 +82,12 @@ QColor QskGradientStop::interpolated(
QDebug operator<<( QDebug debug, const QskGradientStop& stop )
{
debug << stop.position() << ": " << stop.color();
QDebugStateSaver saver( debug );
debug.nospace();
debug << stop.position() << ": ";
QskRgb::debugColor( debug, stop.color() );
return debug;
}

View File

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

View File

@ -61,6 +61,7 @@ class QSK_EXPORT QskMargins : public QMarginsF
QskMargins interpolated( const QskMargins&, qreal progress ) const noexcept;
constexpr bool isExpanding() const noexcept;
constexpr bool isEquidistant() const noexcept;
static QVariant interpolate( const QskMargins&,
const QskMargins&, qreal progress ) noexcept;
@ -181,6 +182,11 @@ constexpr inline qreal QskMargins::height() const noexcept
return top() + bottom();
}
inline constexpr bool QskMargins::isEquidistant() const noexcept
{
return ( left() == top() ) && ( left() == right() ) && ( left() == bottom() );
}
Q_DECLARE_TYPEINFO( QskMargins, Q_MOVABLE_TYPE );
Q_DECLARE_METATYPE( QskMargins )

View File

@ -172,3 +172,29 @@ QRgb QskRgb::darker( QRgb rgb, int factor ) noexcept
return QColor::fromRgba( rgb ).darker( factor ).rgba();
}
#ifndef QT_NO_DEBUG_STREAM
#include <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 );
}
inline QColor toTransparentF( const QColor& color, qreal alpha )
inline QColor toTransparentF( const QColor& color, qreal opacity )
{
return toTransparent( color, qRound( alpha * 255 ) );
return toTransparent( color, qRound( opacity * 255 ) );
}
inline QColor toTransparentF( Qt::GlobalColor color, qreal alpha )
inline QColor toTransparentF( Qt::GlobalColor color, qreal opacity )
{
return toTransparent( QColor( color ), qRound( alpha * 255 ) );
return toTransparent( QColor( color ), qRound( opacity * 255 ) );
}
inline constexpr QRgb toTransparentF( QRgb rgb, qreal alpha ) noexcept
inline constexpr QRgb toTransparentF( QRgb rgb, qreal opacity ) noexcept
{
return toTransparent( rgb, qRound( alpha * 255 ) );
return toTransparent( rgb, qRound( opacity * 255 ) );
}
QSK_EXPORT QRgb lighter( QRgb, int factor = 150 ) noexcept;
QSK_EXPORT QRgb darker( QRgb, int factor = 200 ) noexcept;
}
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
namespace QskRgb
{
QSK_EXPORT void debugColor( QDebug, const QColor& );
QSK_EXPORT void debugColor( QDebug, QRgb );
}
#endif
#endif

View File

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

View File

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

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

View File

@ -128,7 +128,6 @@ class QskSkin::PrivateData
std::unordered_map< const QMetaObject*, SkinletData > skinletMap;
QskSkinHintTable hintTable;
QskAspect::States stateMask = QskAspect::AllStates;
std::unordered_map< int, QFont > fonts;
std::unordered_map< int, QskColorFilter > graphicFilters;
@ -345,22 +344,6 @@ const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const
return QPlatformDialogHelper::buttonLayout( orientation, policy );
}
void QskSkin::setStateMask( QskAspect::States mask )
{
for ( auto state : { QskControl::Disabled, QskControl::Hovered, QskControl::Focused } )
{
if ( mask & state )
m_data->stateMask |= state;
else
m_data->stateMask &= ~state;
}
}
QskAspect::States QskSkin::stateMask() const
{
return m_data->stateMask;
}
QskSkinlet* QskSkin::skinlet( const QMetaObject* metaObject )
{
while ( metaObject )

View File

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

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

View File

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

View File

@ -20,6 +20,51 @@
#include <unordered_map>
#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
{
class UpdateInfo
@ -40,157 +85,125 @@ namespace
int updateModes;
};
class AnimatorCandidate
class HintAnimator : public QskHintAnimator
{
public:
AnimatorCandidate() = default;
inline AnimatorCandidate( QskAspect aspect,
const QVariant& from, const QVariant& to )
: aspect( aspect )
, from( from )
, to( to )
inline HintAnimator( const QskControl* control, const QskAspect aspect,
const QVariant& value1, const QVariant& value2, QskAnimationHint hint )
{
}
setAspect( aspect );
setStartValue( value1 );
setEndValue( value2 );
QskAspect aspect;
QVariant from;
QVariant to;
setDuration( hint.duration );
setEasingCurve( hint.type );
setUpdateFlags( hint.updateFlags );
setWindow( control->window() );
}
};
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(
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
Q_GLOBAL_STATIC( ApplicationAnimator, qskApplicationAnimator )
const QskColorFilter noFilter;
QVector< AnimatorCandidate > candidates;
if ( !oldTable.hasHints() )
return candidates;
for ( const auto& entry : newTable.hints() )
{
const auto aspect = entry.first;
if ( aspect.isAnimator() )
continue;
const auto type = aspect.type();
if ( type == QskAspect::Flag )
{
switch ( aspect.flagPrimitive() )
{
case QskAspect::GraphicRole:
{
if ( !( mask & QskSkinTransition::Color ) )
continue;
int role1 = 0;
const auto value = oldTable.resolvedHint( aspect );
if ( value )
role1 = value->toInt();
const int role2 = entry.second.toInt();
/*
When the role is the same we already have the animators
for the graphic filter table running
*/
if ( role1 != role2 )
{
const auto it1 = oldFilters.find( role1 );
const auto it2 = newFilters.find( role2 );
if ( it1 != oldFilters.end() || it2 != newFilters.end() )
{
const auto& f1 = ( it1 != oldFilters.end() ) ? it1->second : noFilter;
const auto& f2 = ( it2 != newFilters.end() ) ? it2->second : noFilter;
if ( f1 != f2 )
{
candidates += AnimatorCandidate( aspect,
QVariant::fromValue( f1 ), QVariant::fromValue( f2 ) );
}
}
}
break;
}
case QskAspect::FontRole:
{
if ( !( mask & QskSkinTransition::Metric ) )
continue;
break;
}
default:
;
}
}
else
{
if ( ( ( type == QskAspect::Color ) && ( mask & QskSkinTransition::Color ) ) ||
( ( type == QskAspect::Metric ) && ( mask & QskSkinTransition::Metric ) ) )
{
auto value = oldTable.resolvedHint( aspect );
if ( value == nullptr && aspect.subControl() != QskAspect::Control )
{
auto a = aspect;
a.setSubControl( QskAspect::Control );
a.clearStates();
value = oldTable.resolvedHint( a );
}
/*
We are missing transitions, when a hint in newTable
gets resolved from QskControl. TODO ...
*/
if ( value && *value != entry.second )
candidates += AnimatorCandidate( aspect, *value, entry.second );
}
}
}
return candidates;
}
namespace
{
class AnimatorGroup
{
public:
AnimatorGroup( QQuickWindow* window = nullptr )
WindowAnimator::WindowAnimator( QQuickWindow* window )
: m_window( window )
{
}
inline const QQuickWindow* window() const
inline const QQuickWindow* WindowAnimator::window() const
{
return m_window;
}
void start()
void WindowAnimator::start()
{
for ( auto& it : m_hintAnimatorMap )
for ( auto& it : m_animatorMap )
it.second.start();
for ( auto& it : m_graphicFilterAnimatorMap )
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() )
return true;
}
@ -205,10 +218,10 @@ namespace
return false;
}
inline QVariant animatedHint( QskAspect aspect ) const
inline QVariant WindowAnimator::animatedHint( QskAspect aspect ) const
{
auto it = m_hintAnimatorMap.find( aspect );
if ( it != m_hintAnimatorMap.cend() )
auto it = m_animatorMap.find( aspect );
if ( it != m_animatorMap.cend() )
{
const auto& animator = it->second;
if ( animator.isRunning() )
@ -218,7 +231,7 @@ namespace
return QVariant();
}
inline QVariant animatedGraphicFilter( int graphicRole ) const
inline QVariant WindowAnimator::animatedGraphicFilter( int graphicRole ) const
{
auto it = m_graphicFilterAnimatorMap.find( graphicRole );
if ( it != m_graphicFilterAnimatorMap.cend() )
@ -231,20 +244,21 @@ namespace
return QVariant();
}
void addGraphicFilterAnimators(
const QskAnimationHint& animatorHint,
const std::unordered_map< int, QskColorFilter >& oldFilters,
const std::unordered_map< int, QskColorFilter >& newFilters )
void WindowAnimator::addGraphicFilterAnimators( const QskAnimationHint& animatorHint,
const QskSkin* skin1, const QskSkin* skin2 )
{
const QskColorFilter noFilter;
for ( auto it2 = newFilters.begin(); it2 != newFilters.end(); ++it2 )
{
auto it1 = oldFilters.find( it2->first );
if ( it1 == oldFilters.cend() )
it1 = oldFilters.find( 0 );
const auto& filter1 = skin1->graphicFilters();
const auto& filter2 = skin2->graphicFilters();
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;
if ( f1 != f2 )
@ -261,17 +275,18 @@ namespace
}
}
void addAnimators( QQuickItem* item, const QskAnimationHint& animatorHint,
const QVector< AnimatorCandidate >& candidates, QskSkin* skin )
void WindowAnimator::addItemAspects( QQuickItem* item,
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 )
{
if ( !item->isVisible() )
return;
if ( auto control = qskControlCast( item ) )
{
if ( control->isInitiallyPainted() && ( skin == control->effectiveSkin() ) )
if ( control->isInitiallyPainted() && ( control->effectiveSkin() == skin2 ) )
{
addControlAnimators( control, animatorHint, candidates );
addHints( control, animatorHint, candidates, skin1, skin2 );
#if 1
/*
As it is hard to identify which controls depend on the animated
@ -285,10 +300,10 @@ namespace
const auto children = item->childItems();
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 )
{
@ -306,98 +321,125 @@ namespace
}
}
private:
void addControlAnimators( QskControl* control, const QskAnimationHint& animatorHint,
const QVector< AnimatorCandidate >& candidates )
void WindowAnimator::addHints( const QskControl* control,
const QskAnimationHint& animatorHint, const QSet< QskAspect >& candidates,
const QskSkin* skin1, const QskSkin* skin2 )
{
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 ) )
{
// while metrics might have an effect on layouts, we
// can safely ignore others for controls without content
continue;
// ignore all others for controls without content
return false;
}
}
const auto subControl = candidate.aspect.subControl();
const auto subControl = aspect.subControl();
if ( subControl != control->effectiveSubcontrol( subControl ) )
{
// The control uses subcontrol redirection, so we can assume it
// is not interested in this subcontrol.
continue;
return false;
}
if ( subControl != QskAspect::Control )
{
if ( !subControls.contains( subControl ) )
{
// the control is not interested in the aspect
continue;
}
}
else
if ( subControl == QskAspect::Control )
{
if ( !control->autoFillBackground() )
{
// no need to animate the background unless we show it
continue;
return false;
}
}
auto a = candidate.aspect;
a.setStates( control->skinStates() );
const auto requestState = control->hintStatus( a );
if ( requestState.source != QskSkinHintStatus::Skin )
else
{
// The control does not resolve the aspect from the skin.
continue;
}
if ( candidate.aspect != requestState.aspect )
if ( !subControls.contains( subControl ) )
{
// the aspect was resolved to something else
continue;
}
addAnimator( control->window(), candidate, animatorHint );
storeUpdateInfo( control, candidate.aspect );
// the control is not interested in the aspect
return false;
}
}
void addAnimator( QQuickWindow* window,
const AnimatorCandidate& candidate, QskAnimationHint animationHint )
return true;
}
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 ( it != m_hintAnimatorMap.end() )
return; // already there
it = m_hintAnimatorMap.emplace( candidate.aspect, QskHintAnimator() ).first;
auto& animator = it->second;
animator.setAspect( candidate.aspect );
animator.setStartValue( candidate.from );
animator.setEndValue( candidate.to );
animator.setDuration( animationHint.duration );
animator.setEasingCurve( animationHint.type );
animator.setUpdateFlags( animationHint.updateFlags );
animator.setControl( nullptr );
animator.setWindow( window );
if ( m_animatorMap.find( aspect ) == m_animatorMap.cend() )
{
m_animatorMap.emplace( aspect,
HintAnimator( control, aspect, value1, value2, hint ) );
}
}
inline void storeUpdateInfo( QskControl* control, QskAspect aspect )
inline void WindowAnimator::storeUpdateInfo( const QskControl* control, QskAspect aspect )
{
UpdateInfo info;
info.control = control;
info.control = const_cast< QskControl* >( control );
info.updateModes = UpdateInfo::Update;
if ( aspect.isMetric() )
@ -412,42 +454,31 @@ namespace
m_updateInfos.insert( it, info );
}
QQuickWindow* m_window;
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()
ApplicationAnimator::~ApplicationAnimator()
{
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 )
return group;
if ( animator->window() == window )
return animator;
}
}
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(
this, SLOT(notify(QQuickWindow*)), Qt::UniqueConnection );
@ -455,76 +486,60 @@ namespace
m_connections[1] = QskAnimator::addCleanupHandler(
this, SLOT(cleanup(QQuickWindow*)), Qt::UniqueConnection );
for ( auto& group : m_animatorGroups )
group->start();
for ( auto& animator : m_windowAnimators )
animator->start();
}
void reset()
void ApplicationAnimator::reset()
{
qDeleteAll( m_animatorGroups );
m_animatorGroups.clear();
qDeleteAll( m_windowAnimators );
m_windowAnimators.clear();
disconnect( m_connections[0] );
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 notify( QQuickWindow* window )
void ApplicationAnimator::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;
}
}
}
void cleanup( QQuickWindow* window )
void ApplicationAnimator::cleanup( QQuickWindow* window )
{
for ( auto it = m_animatorGroups.begin();
it != m_animatorGroups.end(); ++it )
for ( auto it = m_windowAnimators.begin();
it != m_windowAnimators.end(); ++it )
{
auto group = *it;
if ( group->window() == window )
auto animator = *it;
if ( animator->window() == window )
{
if ( !group->isRunning() )
if ( !animator->isRunning() )
{
// The notification might be for other animators
m_animatorGroups.erase( it );
delete group;
m_windowAnimators.erase( it );
delete animator;
}
break;
}
}
if ( m_animatorGroups.empty() )
if ( m_windowAnimators.empty() )
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
{
public:
@ -589,38 +604,20 @@ void QskSkinTransition::updateSkin( QskSkin*, QskSkin* )
void QskSkinTransition::process()
{
auto skinFrom = m_data->skins[ 0 ];
auto skinTo = m_data->skins[ 1 ];
qskApplicationAnimator->reset();
if ( ( skinFrom == nullptr ) || ( skinTo == nullptr ) )
auto skin1 = m_data->skins[ 0 ];
auto skin2 = m_data->skins[ 1 ];
QSet< QskAspect > candidates;
if ( skin1 && skin2 )
{
// do nothing
return;
if ( ( m_data->animationHint.duration > 0 ) && ( m_data->mask != 0 ) )
{
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() )
@ -631,21 +628,19 @@ void QskSkinTransition::process()
for ( const auto window : windows )
{
if ( auto quickWindow = qobject_cast< QQuickWindow* >( window ) )
if ( auto w = qobject_cast< QQuickWindow* >( window ) )
{
if ( !quickWindow->isVisible() ||
( qskEffectiveSkin( quickWindow ) != skinTo ) )
if ( !w->isVisible() || ( qskEffectiveSkin( w ) != skin2 ) )
{
continue;
}
auto group = new AnimatorGroup( quickWindow );
auto animator = new WindowAnimator( w );
if ( doGraphicFilter )
{
group->addGraphicFilterAnimators(
m_data->animationHint, oldFilters,
skinTo->graphicFilters() );
animator->addGraphicFilterAnimators(
m_data->animationHint, skin1, skin2 );
doGraphicFilter = false;
}
@ -655,21 +650,24 @@ void QskSkinTransition::process()
over the the item trees.
*/
group->addAnimators( quickWindow->contentItem(),
m_data->animationHint, candidates, skinTo );
animator->addItemAspects( w->contentItem(),
m_data->animationHint, candidates, skin1, skin2 );
qskSkinAnimator->add( group );
qskApplicationAnimator->add( animator );
}
}
qskSkinAnimator->start();
qskApplicationAnimator->start();
}
// apply the changes
updateSkin( skin1, skin2 );
}
bool QskSkinTransition::isRunning()
{
if ( qskSkinAnimator.exists() )
return qskSkinAnimator->isRunning();
if ( qskApplicationAnimator.exists() )
return qskApplicationAnimator->isRunning();
return false;
}
@ -677,10 +675,10 @@ bool QskSkinTransition::isRunning()
QVariant QskSkinTransition::animatedHint(
const QQuickWindow* window, QskAspect aspect )
{
if ( qskSkinAnimator.exists() )
if ( qskApplicationAnimator.exists() )
{
if ( const auto group = qskSkinAnimator->animatorGroup( window ) )
return group->animatedHint( aspect );
if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
return animator->animatedHint( aspect );
}
return QVariant();
@ -689,10 +687,10 @@ QVariant QskSkinTransition::animatedHint(
QVariant QskSkinTransition::animatedGraphicFilter(
const QQuickWindow* window, int graphicRole )
{
if ( qskSkinAnimator.exists() )
if ( qskApplicationAnimator.exists() )
{
if ( const auto group = qskSkinAnimator->animatorGroup( window ) )
return group->animatedGraphicFilter( graphicRole );
if ( const auto animator = qskApplicationAnimator->windowAnimator( window ) )
return animator->animatedGraphicFilter( graphicRole );
}
return QVariant();

View File

@ -38,17 +38,6 @@ static inline bool qskIsControl( const QskSkinnable* skinnable )
return skinnable->metaObject()->inherits( &QskControl::staticMetaObject );
}
static inline QVariant qskTypedNullValue( const QVariant& value )
{
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
const auto vType = static_cast< QMetaType >( value.userType() );
#else
const auto vType = value.userType();
#endif
return QVariant( vType, nullptr );
}
static inline bool qskSetFlag( QskSkinnable* skinnable,
const QskAspect aspect, int flag )
{
@ -940,23 +929,12 @@ const QVariant& QskSkinnable::storedHint(
{
const auto skin = effectiveSkin();
// clearing all state bits not being handled from the skin
aspect.clearStates( ~skin->stateMask() );
QskAspect resolvedAspect;
const auto& localTable = m_data->hintTable;
if ( localTable.hasHints() )
{
auto a = aspect;
if ( !localTable.hasStates() )
{
// we don't need to clear the state bits stepwise
a.clearStates();
}
if ( const QVariant* value = localTable.resolvedHint( a, &resolvedAspect ) )
if ( const auto value = localTable.resolvedHint( aspect, &resolvedAspect ) )
{
if ( status )
{
@ -972,10 +950,7 @@ const QVariant& QskSkinnable::storedHint(
const auto& skinTable = skin->hintTable();
if ( skinTable.hasHints() )
{
auto a = aspect;
const QVariant* value = skinTable.resolvedHint( a, &resolvedAspect );
if ( value )
if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) )
{
if ( status )
{
@ -993,8 +968,7 @@ const QVariant& QskSkinnable::storedHint(
aspect.setSubControl( QskAspect::Control );
aspect.clearStates();
value = skinTable.resolvedHint( aspect, &resolvedAspect );
if ( value )
if ( const auto value = skinTable.resolvedHint( aspect, &resolvedAspect ) )
{
if ( status )
{
@ -1171,27 +1145,11 @@ void QskSkinnable::startHintTransition( QskAspect aspect,
if ( control->window() == nullptr || !isTransitionAccepted( aspect ) )
return;
/*
We might be invalid for one of the values, when an aspect
has not been defined for all states ( f.e. metrics are expected
to fallback to 0.0 ). In this case we create a default one.
*/
auto v1 = from;
auto v2 = to;
if ( !v1.isValid() )
{
v1 = qskTypedNullValue( v2 );
}
else if ( !v2.isValid() )
{
v2 = qskTypedNullValue( v1 );
}
else if ( v1.userType() != v2.userType() )
{
if ( !QskVariantAnimator::convertValues( v1, v2 ) )
return;
}
if ( aspect.flagPrimitive() == QskAspect::GraphicRole )
{
@ -1257,7 +1215,8 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates )
if ( skin )
{
const auto mask = skin->stateMask();
const auto mask = skin->hintTable().states() | m_data->hintTable.states();
if ( ( newStates & mask ) == ( m_data->skinStates & mask ) )
{
// the modified bits are not handled by the skin
@ -1297,24 +1256,13 @@ void QskSkinnable::setSkinStates( QskAspect::States newStates )
const auto primitive = static_cast< QskAspect::Primitive >( i );
aspect.setPrimitive( type, primitive );
auto a1 = aspect | m_data->skinStates;
auto a2 = aspect | newStates;
const auto a1 = aspect | m_data->skinStates;
const auto a2 = aspect | newStates;
bool doTransition = true;
if ( !m_data->hintTable.hasStates() )
{
/*
The hints are found by stripping the state bits one by
one until a lookup into the hint table is successful.
So for deciding whether two aspects lead to the same hint
we can stop as soon as the aspects have the same state bits.
This way we can reduce the number of lookups significantly
for skinnables with many state bits.
*/
if ( m_data->hintTable.states() == QskAspect::NoState )
doTransition = !skinTable.isResolutionMatching( a1, a2 );
}
if ( doTransition )
{
@ -1343,12 +1291,7 @@ QskSkin* QskSkinnable::effectiveSkin() const
if ( skin == nullptr )
{
if ( const auto control = owningControl() )
{
if ( auto window = qobject_cast< const QskWindow* >( control->window() ) )
{
skin = window->skin();
}
}
skin = qskEffectiveSkin( control->window() );
}
return skin ? skin : qskSetup->skin();

View File

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

View File

@ -76,6 +76,31 @@ QSK_DECL_INSANE static inline QVariant qskInterpolate(
return f( from.constData(), to.constData(), progress );
}
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
using QskMetaType = int;
static inline QskMetaType qskMetaType( const QVariant& v ) { return v.userType(); }
#else
using QskMetaType = QMetaType;
static inline QskMetaType qskMetaType( const QVariant& v ) { return v.metaType(); }
#endif
static inline QVariant qskDefaultVariant( QskMetaType type )
{
return QVariant( type, nullptr );
}
static inline QVariant qskConvertedVariant( const QVariant& from, QskMetaType type )
{
auto v = from;
v.convert( type );
return v;
}
QskVariantAnimator::QskVariantAnimator()
: m_interpolator( nullptr )
{
@ -100,16 +125,60 @@ void QskVariantAnimator::setCurrentValue( const QVariant& value )
m_currentValue = value;
}
bool QskVariantAnimator::convertValues( QVariant& v1, QVariant& v2 )
{
if ( !v1.isValid() && !v2.isValid() )
return false;
const auto type1 = qskMetaType( v1 );
const auto type2 = qskMetaType( v2 );
if ( !v1.isValid() )
{
v1 = qskDefaultVariant( type2 );
return true;
}
if ( !v2.isValid() )
{
v2 = qskDefaultVariant( type1 );
return true;
}
if ( type1 != type2 )
{
if ( v1.canConvert( type2 ) )
{
v1.convert( type2 );
return true;
}
if ( v2.canConvert( type1 ) )
{
v2.convert( type1 );
return true;
}
return false;
}
return true;
}
void QskVariantAnimator::setup()
{
m_interpolator = nullptr;
const auto type = m_startValue.userType();
if ( type == m_endValue.userType() )
if ( convertValues( m_startValue, m_endValue ) )
{
if ( m_startValue != m_endValue )
{
const auto id = m_startValue.userType();
// all what has been registered by qRegisterAnimationInterpolator
m_interpolator = reinterpret_cast< void ( * )() >(
QVariantAnimationPrivate::getInterpolator( type ) );
QVariantAnimationPrivate::getInterpolator( id ) );
}
}
m_currentValue = m_interpolator ? m_startValue : m_endValue;
@ -131,3 +200,32 @@ void QskVariantAnimator::done()
{
m_interpolator = nullptr;
}
bool QskVariantAnimator::maybeInterpolate(
const QVariant& value1, const QVariant& value2 )
{
if ( !value1.isValid() && !value2.isValid() )
return false;
const auto type1 = qskMetaType( value1 );
const auto type2 = qskMetaType( value2 );
if ( !value1.isValid() )
return value2 != qskDefaultVariant( type2 );
if ( !value2.isValid() )
return value1 != qskDefaultVariant( type1 );
if ( type1 != type2 )
{
if ( value1.canConvert( type2 ) )
return value2 != qskConvertedVariant( value1, type2 );
if ( value2.canConvert( type1 ) )
return value1 != qskConvertedVariant( value2, type1 );
return false;
}
return value1 != value2;
}

View File

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

View File

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

View File

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