starting to configure box subcontrols/nodes with

QskBoxColors/QskBorderMetrics
This commit is contained in:
Uwe Rathmann 2017-08-28 17:42:11 +02:00
parent 42981403e7
commit ff9e9ab63c
16 changed files with 931 additions and 205 deletions

View File

@ -0,0 +1,167 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskBorderMetrics.h"
#include <QDebug>
static void qskRegisterBorderMetrics()
{
qRegisterMetaType< QskBorderMetrics >();
}
Q_CONSTRUCTOR_FUNCTION( qskRegisterBorderMetrics )
static inline QSizeF qskInterpolatedSize(
const QSizeF& from, const QSizeF& to, qreal ratio )
{
return from + ( to - from ) * ratio;
}
QskBorderMetrics::QskBorderMetrics( qreal width, qreal radiusX, qreal radiusY ):
m_widths( ( width >= 0.0 ) ? width : 0.0 ),
m_widthIsRelative( false ),
m_radiusIsRelative( false )
{
setRadius( radiusX, radiusY );
}
QskBorderMetrics::~QskBorderMetrics()
{
}
bool QskBorderMetrics::operator==( const QskBorderMetrics& other ) const
{
if ( m_widthIsRelative != other.m_widthIsRelative )
return false;
if ( m_radiusIsRelative != other.m_radiusIsRelative )
return false;
if ( m_widths != other.m_widths )
return false;
for ( size_t i = 0; i < 4; i++ )
{
if ( m_radii[i] != other.m_radii[i] )
return false;
}
return true;
}
void QskBorderMetrics::setWidthSizeMode( Qt::SizeMode sizeMode )
{
m_widthIsRelative = ( sizeMode == Qt::RelativeSize );
}
void QskBorderMetrics::setWidths( const QskMargins& widths )
{
m_widths = widths;
}
void QskBorderMetrics::setWidthAt( Qt::Edges edges, qreal width )
{
m_widths.setMarginsAt( edges, width );
}
void QskBorderMetrics::setRadiusSizeMode( Qt::SizeMode sizeMode )
{
m_radiusIsRelative = ( sizeMode == Qt::RelativeSize );
}
void QskBorderMetrics::setRadius(
qreal topLeftX, qreal topLeftY, qreal topRightX, qreal topRightY,
qreal bottomLeftX, qreal bottomLeftY, qreal bottomRightX, qreal bottomRightY )
{
m_radii[ Qt::TopLeftCorner ].setWidth( qMax( topLeftX, 0.0 ) );
m_radii[ Qt::TopLeftCorner ].setHeight( qMax( topLeftY, 0.0 ) );
m_radii[ Qt::TopRightCorner ].setWidth( qMax( topRightX, 0.0 ) );
m_radii[ Qt::TopRightCorner ].setHeight( qMax( topRightY, 0.0 ) );
m_radii[ Qt::BottomLeftCorner ].setWidth( qMax( bottomLeftX, 0.0 ) );
m_radii[ Qt::BottomLeftCorner ].setHeight( qMax( bottomLeftY, 0.0 ) );
m_radii[ Qt::BottomRightCorner ].setWidth( qMax( bottomRightX, 0.0 ) );
m_radii[ Qt::BottomRightCorner ].setHeight( qMax( bottomRightY, 0.0 ) );
}
void QskBorderMetrics::setRadius( Qt::Corner corner, qreal radiusX, qreal radiusY )
{
if ( ( corner >= Qt::TopLeftCorner ) && ( corner <= Qt::BottomRightCorner ) )
{
m_radii[ corner ].setWidth( qMax( radiusX, 0.0 ) );
m_radii[ corner ].setHeight( qMax( radiusY, 0.0 ) );
}
}
QskBorderMetrics QskBorderMetrics::interpolated(
const QskBorderMetrics& to, qreal ratio ) const
{
if ( *this == to )
return to;
if ( ( m_widthIsRelative != to.m_widthIsRelative )
&& ( m_radiusIsRelative != to.m_radiusIsRelative ) )
{
return to;
}
QskMargins widths( to.m_widths );
if ( m_widthIsRelative == to.m_widthIsRelative )
{
widths = m_widths.interpolated( to.m_widths, ratio );
}
if ( m_radiusIsRelative == to.m_radiusIsRelative )
{
QSizeF radii[4];
radii[0] = qskInterpolatedSize( m_radii[0], to.m_radii[0], ratio );
radii[1] = qskInterpolatedSize( m_radii[1], to.m_radii[1], ratio );
radii[2] = qskInterpolatedSize( m_radii[2], to.m_radii[2], ratio );
radii[3] = qskInterpolatedSize( m_radii[3], to.m_radii[3], ratio );
return QskBorderMetrics( to.m_widthIsRelative, widths,
to.m_radiusIsRelative, radii );
}
else
{
return QskBorderMetrics( to.m_widthIsRelative, widths,
to.m_radiusIsRelative, to.m_radii );
}
}
QVariant QskBorderMetrics::interpolate( const QskBorderMetrics& from,
const QskBorderMetrics& to, qreal progress )
{
return QVariant::fromValue( from.interpolated( to, progress ) );
}
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<( QDebug debug, const QskBorderMetrics& metrics )
{
QDebugStateSaver saver( debug );
debug.nospace();
debug << "BorderMetrics" << '(';
debug << metrics.widthSizeMode() << metrics.widths();
debug << metrics.radiusSizeMode();
for ( int i = Qt::TopLeftCorner; i <= Qt::BottomRightCorner; i++ )
{
const QSizeF r = metrics.radius( static_cast< Qt::Corner >( i ) );
debug << "(" << r.width() << r.height() << ")";
}
debug << ')';
return debug;
}
#endif

View File

@ -0,0 +1,183 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_BORDER_METRICS_H
#define QSK_BORDER_HINTS_H
#include "QskGlobal.h"
#include "QskMargins.h"
#include <Qt>
#include <QMetaType>
#include <QSizeF>
class QDebug;
class QVariant;
class QSK_EXPORT QskBorderMetrics
{
public:
QskBorderMetrics();
QskBorderMetrics( qreal width, qreal radius = 0.0 );
QskBorderMetrics( qreal width, qreal radiusX, qreal radiusY );
~QskBorderMetrics();
bool operator==( const QskBorderMetrics& ) const;
bool operator!=( const QskBorderMetrics& ) const;
void setRadius( qreal radius );
void setRadius( qreal radius, Qt::SizeMode );
void setRadius( Qt::Corner, qreal radius );
void setRadius( qreal radiusX, qreal radiusY );
void setRadius( const QSizeF& );
void setRadius( Qt::Corner, qreal radiusX, qreal radiusY );
void setRadius( Qt::Corner, const QSizeF& radius );
void setRadius( qreal topLeft, qreal topRight,
qreal bottomLeft, qreal bottomRight );
void setRadius( const QSizeF& topLeft, const QSizeF& topRight,
const QSizeF& bottomLeft, const QSizeF& bottomRight );
void setRadius(
qreal topLeftX, qreal topLeftY,
qreal topRightX, qreal topRightY,
qreal bottomLeftX, qreal bottomLeftY,
qreal bottomRightX, qreal bottomRightY );
QSizeF radius( Qt::Corner ) const;
void setRadiusSizeMode( Qt::SizeMode );
Qt::SizeMode radiusSizeMode() const;
void setWidthAt( Qt::Edges, qreal width );
qreal widthAt( Qt::Edge ) const;
void setWidths( const QskMargins& );
const QskMargins& widths() const;
void setWidthSizeMode( Qt::SizeMode );
Qt::SizeMode widthSizeMode() const;
QskBorderMetrics interpolated( const QskBorderMetrics&, qreal value ) const;
static QVariant interpolate( const QskBorderMetrics&,
const QskBorderMetrics&, qreal progress );
private:
QskBorderMetrics( bool widthIsRelative, const QskMargins& widths,
bool radiusIsRelative, const QSizeF radii[4] ):
m_widths( widths ),
m_radii( { radii[0], radii[1], radii[2], radii[3] } ),
m_widthIsRelative( widthIsRelative ),
m_radiusIsRelative( radiusIsRelative )
{
}
QskMargins m_widths;
QSizeF m_radii[ 4 ];
bool m_widthIsRelative;
bool m_radiusIsRelative;
};
inline QskBorderMetrics::QskBorderMetrics():
m_widthIsRelative( false ),
m_radiusIsRelative( false )
{
}
inline QskBorderMetrics::QskBorderMetrics( qreal width, qreal radius ):
QskBorderMetrics( width, radius, radius )
{
}
inline bool QskBorderMetrics::operator!=( const QskBorderMetrics& other ) const
{
return !( *this == other );
}
inline void QskBorderMetrics::setRadius( qreal radius )
{
setRadius( radius, radius );
}
inline void QskBorderMetrics::setRadius( qreal radius, Qt::SizeMode sizeMode )
{
setRadius( radius );
setRadiusSizeMode( sizeMode );
}
inline void QskBorderMetrics::setRadius( qreal radiusX, qreal radiusY )
{
setRadius( radiusX, radiusY, radiusX, radiusY,
radiusX, radiusY, radiusX, radiusY );
}
inline void QskBorderMetrics::setRadius( const QSizeF& radius )
{
setRadius( radius.width(), radius.height() );
}
inline void QskBorderMetrics::setRadius( Qt::Corner corner, qreal radius )
{
setRadius( corner, radius, radius );
}
inline void QskBorderMetrics::setRadius( Qt::Corner corner, const QSizeF& radius )
{
setRadius( corner, radius.width(), radius.height() );
}
inline void QskBorderMetrics::setRadius(
const QSizeF& topLeft, const QSizeF& topRight,
const QSizeF& bottomLeft, const QSizeF& bottomRight )
{
setRadius( topLeft.width(), topLeft.height(),
topRight.width(), topRight.height(),
bottomLeft.width(), bottomLeft.height(),
bottomRight.width(), bottomRight.height() );
}
inline QSizeF QskBorderMetrics::radius( Qt::Corner corner ) const
{
if ( ( corner >= Qt::TopLeftCorner ) && ( corner <= Qt::BottomRightCorner ) )
return m_radii[ corner ];
return QSizeF();
}
inline Qt::SizeMode QskBorderMetrics::radiusSizeMode() const
{
return m_radiusIsRelative ? Qt::RelativeSize : Qt::AbsoluteSize;
}
inline const QskMargins& QskBorderMetrics::widths() const
{
return m_widths;
}
inline qreal QskBorderMetrics::widthAt( Qt::Edge edge ) const
{
return m_widths.marginAt( edge );
}
inline Qt::SizeMode QskBorderMetrics::widthSizeMode() const
{
return m_widthIsRelative ? Qt::RelativeSize : Qt::AbsoluteSize;
}
#ifndef QT_NO_DEBUG_STREAM
QSK_EXPORT QDebug operator<<( QDebug, const QskBorderMetrics& );
#endif
Q_DECLARE_TYPEINFO( QskBorderMetrics, Q_MOVABLE_TYPE );
Q_DECLARE_METATYPE( QskBorderMetrics )
#endif

295
src/common/QskBoxColors.cpp Normal file
View File

@ -0,0 +1,295 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskBoxColors.h"
#include "QskRgbValue.h"
#include "QskNamespace.h"
#include <QDebug>
static void qskRegisterBoxColors()
{
qRegisterMetaType< QskBoxColors >();
}
Q_CONSTRUCTOR_FUNCTION( qskRegisterBoxColors )
static inline void qskMirrorEdges(
Qt::Orientations orientations, const QColor* from, QColor* to )
{
if ( orientations == Qt::Vertical )
{
to[ Qt::TopLeftCorner ] = from[ Qt::BottomLeftCorner ];
to[ Qt::BottomLeftCorner ] = from[ Qt::TopLeftCorner ];
to[ Qt::TopRightCorner ] = from[ Qt::BottomRightCorner ];
to[ Qt::BottomRightCorner ] = from[ Qt::TopRightCorner ];
}
else if ( orientations == Qt::Horizontal )
{
to[ Qt::TopLeftCorner ] = from[ Qt::TopRightCorner ];
to[ Qt::BottomLeftCorner ] = from[ Qt::BottomRightCorner ];
to[ Qt::TopRightCorner ] = from[ Qt::TopLeftCorner ];
to[ Qt::BottomRightCorner ] = from[ Qt::BottomLeftCorner ];
}
else if ( orientations == ( Qt::Vertical | Qt::Horizontal ) )
{
to[ Qt::TopLeftCorner ] = from[ Qt::BottomRightCorner ];
to[ Qt::BottomLeftCorner ] = from[ Qt::TopRightCorner ];
to[ Qt::TopRightCorner ] = from[ Qt::BottomLeftCorner ];
to[ Qt::BottomRightCorner ] = from[ Qt::TopLeftCorner ];
}
else
{
to[ Qt::TopLeftCorner ] = from[ Qt::TopLeftCorner ];
to[ Qt::BottomLeftCorner ] = from[ Qt::BottomLeftCorner ];
to[ Qt::TopRightCorner ] = from[ Qt::TopRightCorner ];
to[ Qt::BottomRightCorner ] = from[ Qt::BottomRightCorner ];
}
}
QskBoxColors::QskBoxColors()
{
}
QskBoxColors::QskBoxColors( const QColor& border, const QColor& fill )
{
setBorderColor( border );
setFillColor( fill );
}
QskBoxColors::~QskBoxColors()
{
}
bool QskBoxColors::operator==( const QskBoxColors& other ) const
{
for ( size_t i = 0; i < 4; i++ )
{
if ( ( m_border[i] != other.m_border[i] )
|| ( m_fill[i] != other.m_fill[i] ) )
{
return false;
}
}
return true;
}
void QskBoxColors::setBorderColor( const QColor& color )
{
m_border[ 0 ] = m_border[ 1 ] = m_border[ 2 ] = m_border[ 3 ] = color.toRgb();
}
void QskBoxColors::setBorderColor( const QColor& left, const QColor& top,
const QColor& right, const QColor& bottom )
{
m_border[ Qsk::Left ] = left.toRgb();
m_border[ Qsk::Top ] = top.toRgb();
m_border[ Qsk::Right ] = right.toRgb();
m_border[ Qsk::Bottom ] = bottom.toRgb();
}
void QskBoxColors::setBorderColor( Qt::Edges edges, const QColor& color )
{
const QColor c = color.toRgb();
if ( edges & Qt::TopEdge )
m_border[ Qsk::Top ] = c;
if ( edges & Qt::LeftEdge )
m_border[ Qsk::Left ] = c;
if ( edges & Qt::RightEdge )
m_border[ Qsk::Right ] = c;
if ( edges & Qt::BottomEdge )
m_border[ Qsk::Bottom ] = c;
}
QColor QskBoxColors::borderColor( Qt::Edge edge ) const
{
switch( edge )
{
case Qt::TopEdge:
return m_border[ Qsk::Top ];
case Qt::LeftEdge:
return m_border[ Qsk::Left ];
case Qt::RightEdge:
return m_border[ Qsk::Right ];
case Qt::BottomEdge:
return m_border[ Qsk::Bottom ];
}
return QColor();
}
void QskBoxColors::setFillColor( const QColor& color )
{
m_fill[ 0 ] = m_fill[ 1 ] = m_fill[ 2 ] = m_fill[ 3 ] = color.toRgb();
}
void QskBoxColors::setFillColor(
const QColor& topLeft, const QColor& topRight,
const QColor& bottomLeft, const QColor& bottomRight )
{
m_fill[ Qt::TopLeftCorner ] = topLeft.toRgb();
m_fill[ Qt::TopRightCorner ] = topRight.toRgb();
m_fill[ Qt::BottomLeftCorner ] = bottomLeft.toRgb();
m_fill[ Qt::BottomRightCorner ] = bottomRight.toRgb();
}
void QskBoxColors::setFillColor( Qt::Corner corner, const QColor& color )
{
if ( corner >= 0 && corner < 4 )
m_fill[ corner ] = color.toRgb();
}
void QskBoxColors::setFillColor( Qt::Edge edge, const QColor& color )
{
switch( edge )
{
case Qt::TopEdge:
{
m_fill[ Qt::TopLeftCorner ] = color;
m_fill[ Qt::TopRightCorner ] = color;
break;
}
case Qt::LeftEdge:
{
m_fill[ Qt::TopLeftCorner ] = color;
m_fill[ Qt::BottomLeftCorner ] = color;
break;
}
case Qt::RightEdge:
{
m_fill[ Qt::TopRightCorner ] = color;
m_fill[ Qt::BottomRightCorner ] = color;
break;
}
case Qt::BottomEdge:
{
m_fill[ Qt::BottomLeftCorner ] = color;
m_fill[ Qt::BottomRightCorner ] = color;
break;
}
}
}
QColor QskBoxColors::fillColor( Qt::Corner corner ) const
{
if ( corner >= 0 && corner < 4 )
return m_fill[ corner ];
return QColor();
}
QskBoxColors QskBoxColors::shaded( uint alpha ) const
{
QskBoxColors colors = *this;
for ( int i = 0; i < 4; i++ )
{
colors.m_border[i].setAlpha( alpha );
colors.m_fill[i].setAlpha( alpha );
}
return colors;
}
QskBoxColors QskBoxColors::mirrored( Qt::Orientations orientations ) const
{
if ( orientations == 0 )
return *this;
QskBoxColors c;
qskMirrorEdges( orientations, m_border, c.m_border );
qskMirrorEdges( orientations, m_fill, c.m_fill );
return c;
}
QskBoxColors QskBoxColors::interpolated( const QskBoxColors& to, qreal ratio ) const
{
QskBoxColors colors;
for ( size_t i = 0; i < 4; i++ )
{
colors.m_border[i] = QskRgbValue::interpolated( m_border[i], to.m_border[i], ratio );
colors.m_fill[i] = QskRgbValue::interpolated( m_fill[i], to.m_fill[i], ratio );
}
return colors;
}
QVariant QskBoxColors::interpolate(
const QskBoxColors& from, const QskBoxColors& to, qreal ratio )
{
return QVariant::fromValue( from.interpolated( to, ratio ) );
}
#ifndef QT_NO_DEBUG_STREAM
static inline void qskDebugColor( QDebug debug, const QColor& c )
{
debug << '('
<< c.red() << ','
<< c.green() << ','
<< c.blue() << ','
<< c.alpha() << ')';
}
QDebug operator<<( QDebug debug, const QskBoxColors& colors )
{
QDebugStateSaver saver( debug );
debug.nospace();
debug << "BoxColors" << '(';
debug << "\n Border" << '(';
debug << " L";
qskDebugColor( debug, colors.borderColor( Qt::LeftEdge ) );
debug << ", T";
qskDebugColor( debug, colors.borderColor( Qt::TopEdge ) );
debug << ", R";
qskDebugColor( debug, colors.borderColor( Qt::RightEdge ) );
debug << ", B";
qskDebugColor( debug, colors.borderColor( Qt::BottomEdge ) );
debug << " )";
debug << "\n Fill" << '(';
debug << " TL";
qskDebugColor( debug, colors.fillColor( Qt::TopLeftCorner ) );
debug << ", TR";
qskDebugColor( debug, colors.fillColor( Qt::TopRightCorner ) );
debug << ", BL";
qskDebugColor( debug, colors.fillColor( Qt::BottomLeftCorner ) );
debug << ", BR";
qskDebugColor( debug, colors.fillColor( Qt::BottomRightCorner ) );
debug << " )";
debug << "\n)";
return debug;
}
#endif

72
src/common/QskBoxColors.h Normal file
View File

@ -0,0 +1,72 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_BOX_COLORS_H
#define QSK_BOX_COLORS_H
#include "QskGlobal.h"
#include <QMarginsF>
#include <QColor>
#include <Qt>
#include <QMetaType>
class QDebug;
class QSK_EXPORT QskBoxColors
{
public:
QskBoxColors();
QskBoxColors( const QColor& border, const QColor& fill );
~QskBoxColors();
bool operator==( const QskBoxColors& ) const;
bool operator!=( const QskBoxColors& ) const;
void setBorderColor( const QColor& );
void setBorderColor( Qt::Edges, const QColor& );
void setBorderColor( const QColor& left, const QColor& top,
const QColor& right, const QColor& bottom );
QColor borderColor( Qt::Edge ) const;
// will be a QskGradient later
void setFillColor( const QColor& );
void setFillColor( Qt::Corner, const QColor& );
void setFillColor( Qt::Edge, const QColor& );
void setFillColor( const QColor& topLeft, const QColor& topRight,
const QColor& bottomLeft, const QColor& bottomRight );
QColor fillColor( Qt::Corner ) const;
QskBoxColors interpolated( const QskBoxColors&, qreal value ) const;
QskBoxColors mirrored( Qt::Orientations ) const;
QskBoxColors shaded( uint alpha ) const;
static QVariant interpolate( const QskBoxColors&,
const QskBoxColors&, qreal ratio );
private:
QColor m_border[ 4 ];
QColor m_fill[ 4 ]; // should be QskGradient later
};
inline bool QskBoxColors::operator!=( const QskBoxColors& other ) const
{
return !( *this == other );
}
#ifndef QT_NO_DEBUG_STREAM
QSK_EXPORT QDebug operator<<( QDebug, const QskBoxColors& );
#endif
Q_DECLARE_METATYPE( QskBoxColors )
#endif

View File

@ -4,84 +4,29 @@
*****************************************************************************/
#include "QskBoxOptions.h"
#include <qhashfunctions.h>
QskBoxOptions::QskBoxOptions():
radius( { 0, 0, 0, 0, 0, 0, 0, 0 } ),
color( { 0, 0, 0, 0, 0, 0, 0, 0 } )
static inline bool qskIsVisible( const QColor& color )
{
return color.isValid() && color.alpha() > 0;
}
uint QskBoxOptions::metricsHash( uint seed ) const
QskBoxOptions::QskBoxOptions()
{
return qHashBits( this, 2 * sizeof( QMarginsF ) + 8 * sizeof( qreal ), seed );
}
uint QskBoxOptions::colorsHash( uint seed ) const
{
return qHashBits( &color, 8 * sizeof( QRgb ), seed );
}
void QskBoxOptions::setBorder( qreal width )
{
borders = QMarginsF( width, width, width, width );
}
void QskBoxOptions::setBorder( qreal left, qreal top, qreal right, qreal bottom )
{
borders = QMarginsF( left, top, right, bottom );
}
void QskBoxOptions::setRadius( qreal radius )
{
this->radius = { radius, radius, radius, radius, radius, radius, radius, radius };
}
void QskBoxOptions::setBorderRgb( QRgb rgb )
{
color.borderLeft = color.borderTop =
color.borderRight = color.borderBottom = rgb;
}
void QskBoxOptions::setBorderRgb( QRgb rgbLeft, QRgb rgbTop,
QRgb rgbRight, QRgb rgbBottom )
{
color.borderLeft = rgbLeft;
color.borderTop = rgbTop;
color.borderRight = rgbRight;
color.borderBottom = rgbBottom;
}
void QskBoxOptions::setFillRgb( QRgb rgb )
{
color.fillTopLeft = color.fillTopRight =
color.fillBottomRight = color.fillBottomLeft = rgb;
}
void QskBoxOptions::setFillRgb( QRgb rgbTopLeft, QRgb rgbTopRight,
QRgb rgbBottomRight, QRgb rgbBottomLeft )
{
color.fillTopLeft = rgbTopLeft;
color.fillTopRight = rgbTopRight;
color.fillBottomRight = rgbBottomRight;
color.fillBottomLeft = rgbBottomLeft;
}
bool QskBoxOptions::isVisible() const
{
if ( qAlpha( color.fillTopLeft ) ||
qAlpha( color.fillTopRight ) ||
qAlpha( color.fillBottomRight ) ||
qAlpha( color.fillBottomLeft ) )
if ( qskIsVisible( colors.fillColor( Qt::TopLeftCorner ) )
|| qskIsVisible( colors.fillColor( Qt::TopRightCorner ) )
|| qskIsVisible( colors.fillColor( Qt::BottomLeftCorner ) )
|| qskIsVisible( colors.fillColor( Qt::BottomRightCorner ) ) )
{
return true;
}
if ( ( borders.left() && qAlpha( color.borderLeft ) ) ||
( borders.top() && qAlpha( color.borderTop ) ) ||
( borders.right() && qAlpha( color.borderRight ) ) ||
( borders.bottom() && qAlpha( color.borderBottom ) ) )
for ( auto edge : { Qt::LeftEdge, Qt::TopEdge, Qt::RightEdge, Qt::BottomEdge } )
{
if ( metrics.widthAt( edge ) && qskIsVisible( colors.borderColor( edge ) ) )
return true;
}
@ -90,13 +35,16 @@ bool QskBoxOptions::isVisible() const
QMarginsF QskBoxOptions::padding() const
{
const qreal left = std::max( radius.topLeftX, radius.bottomLeftX );
const qreal top = std::max( radius.topLeftY, radius.topRightY );
const qreal right = std::max( radius.topRightX, radius.bottomRightX );
const qreal bottom = std::max( radius.bottomRightY, radius.bottomLeftY );
const QSizeF topLeft = metrics.radius( Qt::TopLeftCorner );
const QSizeF topRight = metrics.radius( Qt::TopRightCorner );
const QSizeF bottomLeft = metrics.radius( Qt::BottomLeftCorner );
const QSizeF bottomRight = metrics.radius( Qt::BottomRightCorner );
return QMarginsF( std::max( 0.0, left ), std::max( 0.0, top ),
std::max( 0.0, right ), std::max( 0.0, bottom ) );
return QMarginsF(
std::max( topLeft.width(), bottomLeft.width() ),
std::max( topLeft.height(), topRight.height() ),
std::max( topRight.width(), bottomRight.width() ),
std::max( bottomLeft.height(), bottomRight.height() ) );
}
QMarginsF QskBoxOptions::unitedMargins() const
@ -111,5 +59,5 @@ QMarginsF QskBoxOptions::unitedMargins() const
std::max( 0.0, pad.bottom() - shadows.bottom() ) + 0.5
);
return borders + shadows + pad + extra;
return metrics.widths() + shadows + pad + extra;
}

View File

@ -7,70 +7,26 @@
#define QSK_BOX_OPTIONS_H
#include "QskGlobal.h"
#include "QskBoxColors.h"
#include "QskBorderMetrics.h"
#include <QMarginsF>
#include <QColor>
class QSK_EXPORT QskBoxOptions
{
public:
QskBoxOptions();
void setBorder( qreal width );
void setBorder( qreal left, qreal top, qreal right, qreal bottom );
void setRadius( qreal radius );
void setBorderRgb( QRgb rgb );
void setBorderRgb( QRgb rgbLeft, QRgb rgbTop, QRgb rgbRight, QRgb rgbBottom );
void setFillRgb( QRgb rgb );
void setFillRgb( QRgb rgbTopLeft, QRgb rgbTopRight,
QRgb rgbBottomRight, QRgb rgbBottomLeft );
bool isVisible() const;
QMarginsF unitedMargins() const;
QMarginsF padding() const;
uint metricsHash( uint seed = 0 ) const;
uint colorsHash( uint seed = 0 ) const;
public:
QMarginsF shadows;
QMarginsF borders;
QskMargins shadows;
struct Radius
{
qreal topLeftX;
qreal topLeftY;
qreal topRightX;
qreal topRightY;
qreal bottomRightX;
qreal bottomRightY;
qreal bottomLeftX;
qreal bottomLeftY;
} radius;
struct Colors
{
// borders
QRgb borderLeft;
QRgb borderTop;
QRgb borderRight;
QRgb borderBottom;
// background
QRgb fillTopLeft;
QRgb fillTopRight;
QRgb fillBottomRight;
QRgb fillBottomLeft;
} color;
QskBorderMetrics metrics;
QskBoxColors colors;
};
#endif

View File

@ -7,12 +7,61 @@
namespace
{
inline int value( int from, int to, qreal progress )
inline int value( int from, int to, qreal ratio )
{
return int( from + ( to - from ) * progress );
return int( from + ( to - from ) * ratio );
}
}
static inline QColor qskInterpolatedColor(
const QColor& c1, const QColor& c2, qreal ratio )
{
switch( c1.spec() )
{
case QColor::Rgb:
{
const int r = value( c1.red(), c2.red(), ratio );
const int g = value( c1.green(), c2.green(), ratio );
const int b = value( c1.blue(), c2.blue(), ratio );
const int a = value( c1.alpha(), c2.alpha(), ratio );
return QColor::fromRgb( r, g, b, a );
}
case QColor::Hsv:
{
const int h = value( c1.hue(), c2.hue(), ratio );
const int s = value( c1.saturation(), c2.saturation(), ratio );
const int v = value( c1.value(), c2.value(), ratio );
const int a = value( c1.alpha(), c2.alpha(), ratio );
return QColor::fromHsv( h, s, v, a );
}
case QColor::Cmyk:
{
const int c = value( c1.cyan(), c2.cyan(), ratio );
const int m = value( c1.magenta(), c2.magenta(), ratio );
const int y = value( c1.yellow(), c2.yellow(), ratio );
const int k = value( c1.black(), c2.black(), ratio );
const int a = value( c1.alpha(), c2.alpha(), ratio );
return QColor::fromCmykF( c, m, y, k, a );
}
case QColor::Hsl:
{
const int h = value( c1.hue(), c2.hue(), ratio );
const int s = value( c1.saturation(), c2.saturation(), ratio );
const int l = value( c1.lightness(), c2.lightness(), ratio );
const int a = value( c1.alpha(), c2.alpha(), ratio );
return QColor::fromHsl( h, s, l, a );
}
case QColor::Invalid:
break;
}
return c2;
}
QRgb QskRgbValue::interpolated( QRgb rgb1, QRgb rgb2, qreal ratio )
{
// interpolating in HSV usually provides better results !!
@ -20,8 +69,6 @@ QRgb QskRgbValue::interpolated( QRgb rgb1, QRgb rgb2, qreal ratio )
if ( rgb1 == rgb2 )
return rgb1;
ratio = qBound( 0.0, ratio, 1.0 );
const int r = value( qRed( rgb1 ), qRed( rgb2 ), ratio );
const int g = value( qGreen( rgb1 ), qGreen( rgb2 ), ratio );
const int b = value( qBlue( rgb1 ), qBlue( rgb2 ), ratio );
@ -29,3 +76,16 @@ QRgb QskRgbValue::interpolated( QRgb rgb1, QRgb rgb2, qreal ratio )
return qRgba( r, g, b, a );
}
QColor QskRgbValue::interpolated( const QColor& c1, const QColor& c2, qreal ratio )
{
if ( c1 != c2 && c1.isValid() && c2.isValid() )
{
if ( c1.spec() == c2.spec() )
return qskInterpolatedColor( c1, c2, ratio );
else
return qskInterpolatedColor( c1.convertTo( c2.spec() ), c2, ratio );
}
return c2;
}

View File

@ -434,6 +434,7 @@ QSK_RGB_VALUES
#undef RGB
QRgb interpolated( QRgb rgb1, QRgb rgb2, qreal ratio );
QColor interpolated( const QColor& c1, const QColor& c2, qreal ratio );
}
#endif

View File

@ -249,8 +249,8 @@ QskBoxOptions QskSkinRenderer::boxOptions( const QskSkinnable* skinnable,
QskBoxOptions options;
options.borders = qskRotatedMargins(
skinnable->borderMetrics( subControl ), rotation );
options.metrics.setWidths( qskRotatedMargins(
skinnable->borderMetrics( subControl ), rotation ) );
options.shadows = qskRotatedMargins(
skinnable->marginsHint( subControl | Shadow ), rotation );
@ -267,26 +267,29 @@ QskBoxOptions QskSkinRenderer::boxOptions( const QskSkinnable* skinnable,
const auto bottomLeft = static_cast<Corner>( bottomEdge );
// corner radii
options.radius.topLeftX = qskRadius( skinnable, rect, subControl | RadiusX | topLeft );
options.radius.topLeftY = qskRadius( skinnable, rect, subControl | RadiusY | topLeft );
options.radius.topRightX = qskRadius( skinnable, rect, subControl | RadiusX | topRight );
options.radius.topRightY = qskRadius( skinnable, rect, subControl | RadiusY | topRight );
options.radius.bottomRightX = qskRadius( skinnable, rect, subControl | RadiusX | bottomRight );
options.radius.bottomRightY = qskRadius( skinnable, rect, subControl | RadiusY | bottomRight );
options.radius.bottomLeftX = qskRadius( skinnable, rect, subControl | RadiusX | bottomLeft );
options.radius.bottomLeftY = qskRadius( skinnable, rect, subControl | RadiusY | bottomLeft );
options.metrics.setRadius(
qskRadius( skinnable, rect, subControl | RadiusX | topLeft ),
qskRadius( skinnable, rect, subControl | RadiusY | topLeft ),
qskRadius( skinnable, rect, subControl | RadiusX | topRight ),
qskRadius( skinnable, rect, subControl | RadiusY | topRight ),
qskRadius( skinnable, rect, subControl | RadiusX | bottomLeft ),
qskRadius( skinnable, rect, subControl | RadiusY | bottomLeft ),
qskRadius( skinnable, rect, subControl | RadiusX | bottomRight ),
qskRadius( skinnable, rect, subControl | RadiusY | bottomRight ) );
// border colors
options.color.borderLeft = skinnable->color( subControl | Border | leftEdge ).rgba();
options.color.borderTop = skinnable->color( subControl | Border | topEdge ).rgba();
options.color.borderRight = skinnable->color( subControl | Border | rightEdge ).rgba();
options.color.borderBottom = skinnable->color( subControl | Border | bottomEdge ).rgba();
options.colors.setBorderColor(
skinnable->color( subControl | Border | leftEdge ),
skinnable->color( subControl | Border | topEdge ),
skinnable->color( subControl | Border | rightEdge ),
skinnable->color( subControl | Border | bottomEdge ) );
// background colors
options.color.fillTopLeft = skinnable->color( subControl | leftEdge ).rgba();
options.color.fillTopRight = skinnable->color( subControl | topEdge ).rgba();
options.color.fillBottomRight = skinnable->color( subControl | rightEdge ).rgba();
options.color.fillBottomLeft = skinnable->color( subControl | bottomEdge ).rgba();
options.colors.setFillColor(
skinnable->color( subControl | topLeft ),
skinnable->color( subControl | topRight ),
skinnable->color( subControl | bottomLeft ),
skinnable->color( subControl | bottomRight ) );
return options;
}

View File

@ -6,6 +6,8 @@
#include "QskVariantAnimator.h"
#include "QskColorFilter.h"
#include "QskMargins.h"
#include "QskBorderMetrics.h"
#include "QskBoxColors.h"
// Even if we don't use the standard Qt animation system we
// use its registry of interpolators: why adding our own ...
@ -18,6 +20,8 @@ static void qskRegisterInterpolator()
{
qRegisterAnimationInterpolator<QskColorFilter>( QskColorFilter::interpolate );
qRegisterAnimationInterpolator<QskMargins>( QskMargins::interpolate );
qRegisterAnimationInterpolator<QskBorderMetrics>( QskBorderMetrics::interpolate );
qRegisterAnimationInterpolator<QskBoxColors>( QskBoxColors::interpolate );
}
Q_CONSTRUCTOR_FUNCTION( qskRegisterInterpolator )

View File

@ -164,14 +164,19 @@ namespace
static QPainterPath qskBorderPath( QMarginsF borders, QMarginsF marginsExtra,
const QskBoxOptions& options, bool inner )
{
qreal topLeftX = options.radius.topLeftX;
qreal topRightX = options.radius.topRightX;
qreal bottomRightX = options.radius.bottomRightX;
qreal bottomLeftX = options.radius.bottomLeftX;
qreal topLeftY = options.radius.topLeftY;
qreal topRightY = options.radius.topRightY;
qreal bottomRightY = options.radius.bottomRightY;
qreal bottomLeftY = options.radius.bottomLeftY;
const auto& metrics = options.metrics;
qreal topLeftX = metrics.radius( Qt::TopLeftCorner ).width();
qreal topRightX = metrics.radius( Qt::TopRightCorner ).width();
qreal bottomRightX = metrics.radius( Qt::BottomRightCorner ).width();
qreal bottomLeftX = metrics.radius( Qt::BottomLeftCorner ).width();
qreal topLeftY = metrics.radius( Qt::TopLeftCorner ).height();
qreal topRightY = metrics.radius( Qt::TopRightCorner ).height();
qreal bottomRightY = metrics.radius( Qt::BottomRightCorner ).height();
qreal bottomLeftY = metrics.radius( Qt::BottomLeftCorner).height();
const auto spacingX = marginsExtra.left() + marginsExtra.right();
const auto spacingY = marginsExtra.top() + marginsExtra.bottom();
@ -288,8 +293,9 @@ namespace
const QMarginsF allMargins = options.unitedMargins();
const QMarginsF padding = options.padding();
const QMarginsF borders = options.metrics.widths();
const QMarginsF extra = allMargins - options.borders - options.shadows - padding;
const QMarginsF extra = allMargins - borders - options.shadows - padding;
const auto width = allMargins.left() + allMargins.right();
const auto height = allMargins.top() + allMargins.bottom();
@ -299,15 +305,15 @@ namespace
const auto translateY = options.shadows.top() + ( size.height() - height ) * 0.5f;
{
auto outerPath = qskBorderPath( options.borders, extra, options, false );
auto outerPath = qskBorderPath( borders, extra, options, false );
outerPath = outerPath.translated( translateX, translateY );
paths.append( outerPath );
}
if ( !options.borders.isNull() )
if ( !borders.isNull() )
{
auto innerPath = qskBorderPath( options.borders, extra, options, true );
auto innerPath = qskBorderPath( borders, extra, options, true );
innerPath = innerPath.translated( translateX, translateY );
paths.append( innerPath );
@ -320,7 +326,7 @@ namespace
if ( !options.shadows.isNull() )
{
auto shadowPath = qskBorderPath(
options.borders + options.shadows, extra, options, false );
borders + options.shadows, extra, options, false );
shadowPath = shadowPath.translated( translateX - options.shadows.left(),
translateY - options.shadows.top() );
@ -440,10 +446,8 @@ bool QskBoxMaterial::isValid() const
return m_data != nullptr;
}
void QskBoxMaterial::setBoxOptions( const QskBoxOptions& options )
void QskBoxMaterial::setBoxOptions( const QskBoxOptions& options, uint key )
{
const auto key = options.metricsHash();
auto it = qskTextureCache.find( key );
if ( it != qskTextureCache.cend() )
{

View File

@ -22,7 +22,7 @@ public:
QskBoxMaterial();
virtual ~QskBoxMaterial();
void setBoxOptions( const QskBoxOptions& );
void setBoxOptions( const QskBoxOptions&, uint key );
QSizeF textureSize() const;
QRectF textureCoordinates() const;

View File

@ -76,14 +76,19 @@ namespace
static QPainterPath qskBorderPath( QMarginsF borders, QMarginsF marginsExtra,
const QskBoxOptions& options, bool inner )
{
qreal topLeftX = options.radius.topLeftX;
qreal topRightX = options.radius.topRightX;
qreal bottomRightX = options.radius.bottomRightX;
qreal bottomLeftX = options.radius.bottomLeftX;
qreal topLeftY = options.radius.topLeftY;
qreal topRightY = options.radius.topRightY;
qreal bottomRightY = options.radius.bottomRightY;
qreal bottomLeftY = options.radius.bottomLeftY;
const auto& metrics = options.metrics;
qreal topLeftX = metrics.radius( Qt::TopLeftCorner ).width();
qreal topRightX = metrics.radius( Qt::TopRightCorner ).width();
qreal bottomRightX = metrics.radius( Qt::BottomRightCorner ).width();
qreal bottomLeftX = metrics.radius( Qt::BottomLeftCorner ).width();
qreal topLeftY = metrics.radius( Qt::TopLeftCorner ).height();
qreal topRightY = metrics.radius( Qt::TopRightCorner ).height();
qreal bottomRightY = metrics.radius( Qt::BottomRightCorner ).height();
qreal bottomLeftY = metrics.radius( Qt::BottomLeftCorner).height();
const auto spacingX = marginsExtra.left() + marginsExtra.right();
const auto spacingY = marginsExtra.top() + marginsExtra.bottom();
@ -200,8 +205,9 @@ namespace
const QMarginsF allMargins = options.unitedMargins();
const QMarginsF padding = options.padding();
const QMarginsF borders = options.metrics.widths();
const QMarginsF extra = allMargins - options.borders - options.shadows - padding;
const QMarginsF extra = allMargins - borders - options.shadows - padding;
const auto width = allMargins.left() + allMargins.right();
const auto height = allMargins.top() + allMargins.bottom();
@ -211,15 +217,15 @@ namespace
const auto translateY = options.shadows.top() + ( size.height() - height ) * 0.5f;
{
auto outerPath = qskBorderPath( options.borders, extra, options, false );
auto outerPath = qskBorderPath( borders, extra, options, false );
outerPath = outerPath.translated( translateX, translateY );
paths.append( outerPath );
}
if ( !options.borders.isNull() )
if ( !borders.isNull() )
{
auto innerPath = qskBorderPath( options.borders, extra, options, true );
auto innerPath = qskBorderPath( borders, extra, options, true );
innerPath = innerPath.translated( translateX, translateY );
paths.append( innerPath );
@ -232,7 +238,7 @@ namespace
if ( !options.shadows.isNull() )
{
auto shadowPath = qskBorderPath(
options.borders + options.shadows, extra, options, false );
borders + options.shadows, extra, options, false );
shadowPath = shadowPath.translated( translateX - options.shadows.left(),
translateY - options.shadows.top() );
@ -356,11 +362,8 @@ QskBoxMaterialVM::~QskBoxMaterialVM()
releaseTextures();
}
void QskBoxMaterialVM::setBoxOptions( const QskBoxOptions& options )
void QskBoxMaterialVM::setBoxOptions( const QskBoxOptions& options, uint key )
{
auto key = options.metricsHash();
key = qHash( QOpenGLContext::currentContext(), key );
auto it = qskTextureCache.find( key );
if ( it != qskTextureCache.cend() )
{

View File

@ -25,7 +25,7 @@ public:
QskBoxMaterialVM();
virtual ~QskBoxMaterialVM();
void setBoxOptions( const QskBoxOptions& );
void setBoxOptions( const QskBoxOptions&, uint key );
QSizeF textureSize() const;
QRectF textureCoordinates() const;

View File

@ -7,28 +7,48 @@
#include "QskBoxOptions.h"
#include "QskAspect.h"
#include <QOpenGLContext>
#include <qhashfunctions.h>
static inline Qt::Edge qskAspectToEdge( QskAspect::Edge edge )
// hashes based on CRC32 -> not necessarily unique: TODO
static inline uint qskMetricsHash( const QskBoxOptions& options )
{
switch ( edge )
uint hash = qHash( QOpenGLContext::currentContext() );
hash = qHashBits( &options.shadows, sizeof( options.shadows ), hash );
const auto& m = options.metrics;
hash = qHashBits( &m.widths(), sizeof( QskMargins ), hash );
QSizeF radius[4];
for ( int i = 0; i < 4; i++ )
radius[i] = m.radius( static_cast<Qt::Corner>( i ) );
hash = qHashBits( radius, sizeof( radius ), hash );
uint flags[2];
flags[0] = m.radiusSizeMode();
flags[1] = m.widthSizeMode();
return qHashBits( flags, sizeof( flags ), hash );
}
static inline uint qskColorsHash( const QskBoxOptions& options )
{
const auto& colors = options.colors;
QRgb rgb[8];
for ( int i = 0; i < 4; i++ )
{
case QskAspect::LeftEdge:
return Qt::LeftEdge;
const QColor c1 = colors.borderColor( static_cast< Qt::Edge >( i ) );
rgb[i] = c1.isValid() ? c1.rgba() : 0;
case QskAspect::TopEdge:
return Qt::TopEdge;
case QskAspect::RightEdge:
return Qt::RightEdge;
case QskAspect::BottomEdge:
return Qt::BottomEdge;
default:
break;
const QColor c2 = colors.fillColor( static_cast< Qt::Corner >( i ) );
rgb[2*i] = c2.isValid() ? c2.rgba() : 0;
}
return static_cast< Qt::Edge >( 0 );
return qHashBits( rgb, sizeof( rgb ), 17000 );
}
QskBoxNode::QskBoxNode():
@ -51,8 +71,8 @@ void QskBoxNode::setBoxData( const QRectF& rect, const QskBoxOptions& options )
{
using namespace QskAspect;
const uint metricsHash = options.metricsHash();
const uint colorsHash = options.colorsHash();
const uint metricsHash = qskMetricsHash( options );
const uint colorsHash = qskColorsHash( options );
QSGNode::DirtyState dirtyState = 0;
@ -62,7 +82,7 @@ void QskBoxNode::setBoxData( const QRectF& rect, const QskBoxOptions& options )
if ( m_metricsHash > 0 )
m_material.release( m_metricsHash );
#endif
m_material.setBoxOptions( options );
m_material.setBoxOptions( options, metricsHash );
dirtyState |= QSGNode::DirtyMaterial;
}
@ -79,22 +99,28 @@ void QskBoxNode::setBoxData( const QRectF& rect, const QskBoxOptions& options )
if ( colorsHash != m_colorsHash )
{
const auto& c = options.colors;
m_geometry.setEdgeBackground( Qt::LeftEdge,
options.color.fillTopLeft, options.color.fillBottomLeft );
c.fillColor( Qt::TopLeftCorner ).rgba(),
c.fillColor( Qt::BottomLeftCorner ).rgba() );
m_geometry.setEdgeBackground( Qt::TopEdge,
options.color.fillTopLeft, options.color.fillTopRight );
c.fillColor( Qt::TopLeftCorner ).rgba(),
c.fillColor( Qt::TopRightCorner ).rgba() );
m_geometry.setEdgeBackground( Qt::RightEdge,
options.color.fillTopRight, options.color.fillBottomRight );
c.fillColor( Qt::TopRightCorner ).rgba(),
c.fillColor( Qt::BottomRightCorner ).rgba() );
m_geometry.setEdgeBackground( Qt::BottomEdge,
options.color.fillBottomLeft, options.color.fillBottomRight );
c.fillColor( Qt::BottomLeftCorner ).rgba(),
c.fillColor( Qt::BottomRightCorner ).rgba() );
m_geometry.setEdgeForeground( qskAspectToEdge( LeftEdge ), options.color.borderLeft );
m_geometry.setEdgeForeground( qskAspectToEdge( TopEdge ), options.color.borderTop );
m_geometry.setEdgeForeground( qskAspectToEdge( RightEdge ), options.color.borderRight );
m_geometry.setEdgeForeground( qskAspectToEdge( BottomEdge ), options.color.borderBottom );
m_geometry.setEdgeForeground( Qt::LeftEdge, c.borderColor( Qt::LeftEdge ).rgba() );
m_geometry.setEdgeForeground( Qt::TopEdge, c.borderColor( Qt::TopEdge ).rgba() );
m_geometry.setEdgeForeground( Qt::RightEdge, c.borderColor( Qt::RightEdge ).rgba() );
m_geometry.setEdgeForeground( Qt::BottomEdge, c.borderColor( Qt::BottomEdge ).rgba() );
dirtyState |= QSGNode::DirtyGeometry;
m_colorsHash = colorsHash;

View File

@ -31,6 +31,8 @@ DEPENDPATH *= $${QSK_SUBDIRS}
HEADERS += \
common/QskAspect.h \
common/QskBorderMetrics.h \
common/QskBoxColors.h \
common/QskBoxOptions.h \
common/QskCorner.h \
common/QskFlags.h \
@ -46,6 +48,8 @@ HEADERS += \
SOURCES += \
common/QskAspect.cpp \
common/QskBorderMetrics.cpp \
common/QskBoxColors.cpp \
common/QskBoxOptions.cpp \
common/QskCorner.cpp \
common/QskFunctions.cpp \