add shadow metrics

This commit is contained in:
Vogel, Rick 2023-12-05 15:13:31 +01:00
parent af149c0e84
commit ded6523249
8 changed files with 226 additions and 56 deletions

View File

@ -17,11 +17,75 @@
#include <QskTextLabel.h>
#include <QskGraphicLabel.h>
#include <QskSlider.h>
#include <QskShadowMetrics.h>
#include <QskBoxBorderColors.h>
#include <QskBoxBorderMetrics.h>
#include <qpainter.h>
QSK_SUBCONTROL(ArcControl, Arc)
namespace
{
class LinearGradientSlider : public QskSlider
{
Q_OBJECT
Q_PROPERTY(
QColor selectedColor READ selectedColor NOTIFY selectedColorChanged )
using Inherited = QskSlider;
public:
explicit LinearGradientSlider( QQuickItem* parent = nullptr );
explicit LinearGradientSlider( Qt::Orientation orientation, QQuickItem* parent = nullptr );
QColor selectedColor() const;
Q_SIGNALS:
void selectedColorChanged();
};
LinearGradientSlider::LinearGradientSlider( QQuickItem* parent )
: LinearGradientSlider( Qt::Horizontal, parent )
{
}
LinearGradientSlider::LinearGradientSlider( Qt::Orientation orientation, QQuickItem* parent )
: Inherited( orientation, parent )
{
static const QVector< QskGradientStop > gradientStops = {
{ 0.0000, QColor::fromRgb( 255, 0, 0 ) },
{ 0.1667, QColor::fromRgb( 255, 255, 0 ) },
{ 0.3333, QColor::fromRgb( 0, 255, 0 ) },
{ 0.5000, QColor::fromRgb( 0, 255, 255 ) },
{ 0.6667, QColor::fromRgb( 0, 0, 255 ) },
{ 0.8333, QColor::fromRgb( 255, 0, 255 ) },
{ 1.0000, QColor::fromRgb( 255, 0, 0 ) },
};
QskGradient gradient( gradientStops );
gradient.setLinearDirection( orientation );
setGradientHint(Groove, {gradientStops});
setColor( Inherited::Fill, Qt::transparent );
setGradientHint( Inherited::Groove, gradient );
setBoxBorderColorsHint( Inherited::Handle, Qt::white );
setBoxBorderMetricsHint( Inherited::Handle, 2 );
connect( this, &QskSlider::valueChanged, this, [ this, gradient ]( qreal value ) {
value = this->orientation() == Qt::Horizontal ? value : 1.0 - value;
const auto selectedColor = gradient.extracted( value, value ).startColor();
setColor( Inherited::Handle, selectedColor );
setColor( Inherited::Ripple, selectedColor );
} );
valueChanged(0.0);
}
QColor LinearGradientSlider::selectedColor() const
{
const auto gradient = gradientHint(Groove);
return gradient.extracted( value(), value() ).startColor();
}
class ChartBox : public QskControl
{
Q_OBJECT
@ -83,18 +147,21 @@ namespace
}
namespace
{
{
class SliderBox : public QskLinearBox
{
Q_OBJECT
public:
SliderBox( const QString& label, qreal min, qreal max, qreal value )
SliderBox(
const QString& label, qreal min, qreal max, qreal value,
std::function< QskSlider*( QQuickItem* ) > allocator =
[]( QQuickItem* parent = nullptr ) { return new QskSlider( parent ); } )
{
auto textLabel = new QskTextLabel( label, this );
textLabel->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed );
auto slider = new QskSlider( this );
auto slider = allocator( this );
slider->setBoundaries( min, max );
slider->setValue( value );
slider->setStepSize( 1.0 );
@ -110,7 +177,7 @@ namespace
}
namespace
{
{
class ControlPanel : public QskGridBox
{
Q_OBJECT
@ -126,8 +193,11 @@ namespace
auto sliderStart = new SliderBox( "Angle", 0.0, 360.0, metrics.startAngle() );
auto sliderSpan = new SliderBox( "Span", -360.0, 360.0, metrics.spanAngle() );
auto sliderExtent = new SliderBox( "Extent", 10.0, 100.0, metrics.thickness() );
auto sliderOffsetX = new SliderBox( "Offset X", 0.0, 100.0, 0 );
auto sliderOffsetY = new SliderBox( "Offset Y", 0.0, 100.0, 0 );
auto shadowExtent = new SliderBox( "Shadow Extent", 0.0, 100.0, 50 );
auto sliderOffsetX = new SliderBox( "Offset X", -1.0, +1.0, 0 );
auto sliderOffsetY = new SliderBox( "Offset Y", -1.0, +1.0, 0 );
auto sliderFillColor = new SliderBox( "Fill Color", 0.0, 1.0, 0 , []( QQuickItem* parent = nullptr ) { return new LinearGradientSlider( parent ); });
auto sliderShadowColor = new SliderBox( "Shadow Color", 0.0, 1.0, 0, []( QQuickItem* parent = nullptr ) { return new LinearGradientSlider( parent ); } );
connect( sliderStart, &SliderBox::valueChanged,
this, &ControlPanel::startAngleChanged );
@ -138,15 +208,47 @@ namespace
connect( sliderExtent, &SliderBox::valueChanged,
this, &ControlPanel::thicknessChanged );
connect( sliderExtent, &SliderBox::valueChanged,
this, &ControlPanel::thicknessChanged );
connect( sliderOffsetX, &SliderBox::valueChanged,
this, &ControlPanel::offsetXChanged );
connect( sliderOffsetY, &SliderBox::valueChanged,
this, &ControlPanel::offsetYChanged );
connect( shadowExtent, &SliderBox::valueChanged,
this, &ControlPanel::shadowExtendChanged );
connect( sliderFillColor, &SliderBox::valueChanged, this, [=](){
auto* const slider = sliderFillColor->findChild<LinearGradientSlider*>();
Q_EMIT fillColorChanged(slider->selectedColor());
} );
connect( sliderShadowColor, &SliderBox::valueChanged, this, [=](){
auto* const slider = sliderShadowColor->findChild<LinearGradientSlider*>();
Q_EMIT shadowColorChanged(slider->selectedColor());
} );
addItem( sliderStart, 0, 0 );
addItem( sliderExtent, 0, 1 );
addItem( shadowExtent, 0, 2 );
addItem( sliderSpan, 1, 0, 1, 2 );
addItem( sliderOffsetX, 2, 0, 1, 1 );
addItem( sliderOffsetY, 2, 1, 1, 1 );
addItem( sliderFillColor, 3, 0, 1, 1 );
addItem( sliderShadowColor, 3, 1, 1, 1 );
}
Q_SIGNALS:
void thicknessChanged( qreal );
void startAngleChanged( qreal );
void spanAngleChanged( qreal );
void offsetXChanged( qreal );
void offsetYChanged( qreal );
void fillColorChanged( QColor );
void shadowColorChanged( QColor );
void shadowExtendChanged( qreal );
};
class Legend : public QskGridBox
@ -200,7 +302,7 @@ namespace
}
ChartView::ChartView( QskControl* chart, QQuickItem* parent )
ChartView::ChartView( ArcControl* chart, QQuickItem* parent )
: QskMainView( parent )
{
auto hBox = new QskLinearBox( Qt::Horizontal );
@ -215,24 +317,50 @@ ChartView::ChartView( QskControl* chart, QQuickItem* parent )
auto controlPanel = new ControlPanel( chart->arcMetricsHint(QskControl::Background) );
controlPanel->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed );
connect( controlPanel, &ControlPanel::thicknessChanged, chart, [ chart ](qreal v) {
auto m = chart->arcMetricsHint( QskControl::Background );
m.setThickness(v);
chart->setArcMetricsHint( QskControl::Background, m );
const auto subcontrol = ArcControl::Arc;
connect( controlPanel, &ControlPanel::thicknessChanged, chart, [ = ]( qreal v ) {
auto m = chart->arcMetricsHint( subcontrol );
m.setThickness( v ) ;
chart->setArcMetricsHint( subcontrol, m );
} );
connect( controlPanel, &ControlPanel::startAngleChanged, chart, [ = ]( qreal v ) {
auto m = chart->arcMetricsHint( subcontrol );
m.setStartAngle( v );
chart->setArcMetricsHint( subcontrol, m );
} );
connect( controlPanel, &ControlPanel::startAngleChanged, chart, [ chart ](qreal v) {
auto m = chart->arcMetricsHint( QskControl::Background );
m.setStartAngle(v);
chart->setArcMetricsHint( QskControl::Background, m );
connect( controlPanel, &ControlPanel::spanAngleChanged, chart, [ = ]( qreal v ) {
auto m = chart->arcMetricsHint( subcontrol );
m.setSpanAngle( v );
chart->setArcMetricsHint( subcontrol, m );
} );
connect( controlPanel, &ControlPanel::spanAngleChanged, chart, [ chart ](qreal v) {
auto m = chart->arcMetricsHint( QskControl::Background );
m.setSpanAngle(v);
chart->setArcMetricsHint( QskControl::Background, m );
connect( controlPanel, &ControlPanel::offsetXChanged, chart, [ = ]( qreal v ) {
auto h = chart->shadowMetricsHint( subcontrol );
h.setOffsetX( v );
chart->setShadowMetricsHint( subcontrol, h );
} );
connect( controlPanel, &ControlPanel::offsetYChanged, chart, [ = ]( qreal v ) {
auto h = chart->shadowMetricsHint( subcontrol );
h.setOffsetY( v );
chart->setShadowMetricsHint( subcontrol, h );
} );
connect( controlPanel, &ControlPanel::shadowExtendChanged, chart, [ = ]( qreal v ) {
auto h = chart->shadowMetricsHint( subcontrol );
h.setSpreadRadius( v );
chart->setShadowMetricsHint( subcontrol, h );
} );
connect( controlPanel, &ControlPanel::fillColorChanged, chart,
[ = ]( QColor c ) { chart->setColor( subcontrol, c ); } );
connect( controlPanel, &ControlPanel::shadowColorChanged, chart,
[ = ]( QColor c ) { chart->setShadowColorHint( subcontrol, c ); } );
setHeader( controlPanel );
setBody( hBox );
}

View File

@ -7,10 +7,17 @@
#include <QskMainView.h>
class ArcControl : public QskControl
{
public:
QSK_SUBCONTROLS(Arc)
};
class CircularChart;
class ChartView : public QskMainView
{
public:
ChartView( QskControl*, QQuickItem* parent = nullptr );
ChartView( ArcControl*, QQuickItem* parent = nullptr );
};

View File

@ -10,6 +10,7 @@
#include <QskIntervalF.h>
#include <QskArcMetrics.h>
#include <QskArcNode.h>
#include <QskShadowMetrics.h>
#include <qpainterpath.h>
#include <qmath.h>
@ -366,7 +367,7 @@ QSGNode* CircularChartSkinlet::updateArcSegmentNode(
arcNode = new QskArcNode();
arcNode->setArcData( m_data->closedArcRect, metrics,
borderWidth, borderColor, fillGradient );
borderWidth, borderColor, fillGradient, {}, {} );
#endif
return arcNode;

View File

@ -14,6 +14,7 @@
#include <QskSkinlet.h>
#include <QskArcNode.h>
#include <QskArcMetrics.h>
#include <QskShadowMetrics.h>
#include <SkinnyShortcut.h>
#include <QGuiApplication>
@ -64,6 +65,8 @@ namespace
};
}
using Q = ArcControl;
class ArcSkinlet : public QskSkinlet
{
public:
@ -71,6 +74,7 @@ class ArcSkinlet : public QskSkinlet
{
Arc
};
ArcSkinlet( QskSkin* skin = nullptr )
: QskSkinlet( skin )
{
@ -85,14 +89,20 @@ class ArcSkinlet : public QskSkinlet
auto* const arcNode =
( node == nullptr ) ? new QskArcNode : static_cast< QskArcNode* >( node );
const auto* const q = static_cast< const QskControl* >( skinnable );
const auto* const q = static_cast< const Q* >( skinnable );
const auto rect = q->contentsRect();
const auto metrics = skinnable->arcMetricsHint(QskControl::Background);
const auto path = metrics.painterPath(rect);
const auto borderColor = q->color(QskControl::Background | QskAspect::Border);
const auto borderWidth = q->metric(QskControl::Background | QskAspect::Border);
arcNode->setArcData( rect, metrics, borderWidth, borderColor, { Qt::red } );
const auto metrics = skinnable->arcMetricsHint(Q::Arc);
const auto fillGradient = q->gradientHint(Q::Arc);
const auto borderColor = q->color(Q::Arc | QskAspect::Border);
const auto borderWidth = q->metric(Q::Arc | QskAspect::Border);
const auto shadowColor = q->shadowColorHint(Q::Arc);
const auto shadowMetrics = q->shadowMetricsHint(Q::Arc);
arcNode->setArcData( rect, metrics, borderWidth, borderColor, fillGradient, shadowColor, shadowMetrics);
return arcNode;
}
return nullptr;
@ -109,7 +119,7 @@ int main( int argc, char* argv[] )
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
auto* const control = new QskControl;
auto* const control = new ArcControl;
auto* const skinlet = new ArcSkinlet;
control->setSkinlet(skinlet);
skinlet->setOwnedBySkinnable(true);
@ -117,9 +127,18 @@ int main( int argc, char* argv[] )
QskArcMetrics metrics;
metrics.setSpanAngle(270);
metrics.setThickness(10);
control->setArcMetricsHint(QskControl::Background, metrics);
control->setMetric(QskControl::Background | QskAspect::Border, 4);
control->setColor(QskControl::Background | QskAspect::Border, Qt::blue);
QskShadowMetrics shadowMetrics;
shadowMetrics.setSpreadRadius(10);
shadowMetrics.setSizeMode(Qt::SizeMode::RelativeSize);
control->setBackgroundColor(Qt::white);
control->setGradientHint(Q::Arc, {Qt::red});
control->setArcMetricsHint(Q::Arc, metrics);
control->setMetric(Q::Arc | QskAspect::Border, 4);
control->setColor(Q::Arc | QskAspect::Border, Qt::blue);
control->setShadowColorHint(Q::Arc, Qt::blue);
control->setShadowMetricsHint(Q::Arc, shadowMetrics);
QskWindow window;
window.addItem( new ChartView( control ) );

View File

@ -231,7 +231,7 @@ static inline QSGNode* qskUpdateArcNode(
return nullptr;
auto arcNode = QskSGNode::ensureNode< QskArcNode >( node );
arcNode->setArcData( rect, metrics, borderWidth, borderColor, gradient );
arcNode->setArcData( rect, metrics, borderWidth, borderColor, gradient, {}, {} );
return arcNode;
}

View File

@ -10,6 +10,7 @@
#include "QskShapeNode.h"
#include "QskStrokeNode.h"
#include "QskSGNode.h"
#include "QskShadowMetrics.h"
#include <qpen.h>
#include <qpainterpath.h>
@ -25,6 +26,7 @@ namespace
{
QColor color = Qt::red;
QRectF rect;
QPointF offset;
qreal radius = 1.0; // [0.0,1.0]
qreal thickness = 0.2;
qreal startAngle = 0.0; //< degree [0.0,360.0]
@ -61,17 +63,18 @@ namespace
const auto& startAngle = newState->startAngle;
const auto& spanAngle = newState->spanAngle;
const auto& extend = newState->extend;
const auto& offset = newState->offset;
auto& p = *program();
p.setUniformValue("color", color.redF(), color.greenF(), color.blueF(), 1.0f);
p.setUniformValue("rect", rect.x(), rect.y(), rect.width(), rect.height());
p.setUniformValue("radius", (float) radius);
p.setUniformValue("thickness", (float) thickness);
p.setUniformValue("startAngle", (float) startAngle - 90.0f);
p.setUniformValue("spanAngle", (float) spanAngle);
p.setUniformValue("extend", (float) extend);
p.setUniformValue( "color", color.redF(), color.greenF(), color.blueF(), 1.0f );
p.setUniformValue( "rect", rect.x(), rect.y(), rect.width(), rect.height() );
p.setUniformValue( "radius", ( float ) radius );
p.setUniformValue( "thickness", ( float ) thickness );
p.setUniformValue( "startAngle", ( float ) startAngle - 90.0f );
p.setUniformValue( "spanAngle", ( float ) spanAngle );
p.setUniformValue( "extend", ( float ) extend );
p.setUniformValue( "offset", ( float ) offset.x(), ( float ) offset.y() );
}
};
class QskArcShadowNode : public QSGGeometryNode
@ -85,10 +88,11 @@ namespace
material()->setFlag(QSGMaterial::Blending);
}
void update(const QRectF& rect, const QskArcMetrics& metrics, const QColor& color, const qreal extend)
void update(const QRectF& rect, const QskArcMetrics& metrics, const QColor& color, const QskShadowMetrics& shadowMetrics = {}, const qreal borderWidth = 0.0)
{
auto* const vertices = geometry()->vertexDataAsPoint2D();
const auto r = rect.adjusted( 0, -4, +4, 0 );
const auto b = borderWidth / 2;
const auto r = rect.adjusted( -b, -b, +b, +b );
vertices[0].set(r.left(), r.top());
vertices[1].set(r.left(), r.bottom());
vertices[2].set(r.right(), r.top());
@ -100,11 +104,12 @@ namespace
auto& state = *material->state();
state.color = color;
state.rect = r;
state.radius = 1.0 - (metrics.thickness() + extend / 4) / size;
state.radius = 1.0 - (metrics.thickness() + borderWidth) / size;
state.thickness = 2 * metrics.thickness() / size;
state.startAngle = metrics.startAngle();
state.spanAngle = metrics.spanAngle();
state.extend = extend;
state.extend = shadowMetrics.spreadRadius();
state.offset = shadowMetrics.offset();
markDirty( QSGNode::DirtyMaterial );
}
@ -165,11 +170,11 @@ QskArcNode::~QskArcNode()
void QskArcNode::setArcData( const QRectF& rect,
const QskArcMetrics& arcMetrics, const QskGradient& fillGradient )
{
setArcData( rect, arcMetrics, 0.0, QColor(), fillGradient );
setArcData( rect, arcMetrics, 0.0, QColor(), fillGradient, {}, {} );
}
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics,
qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient )
const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient, const QColor& shadowColor, const QskShadowMetrics& shadowMetrics )
{
enum NodeRole
{
@ -201,18 +206,17 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
const auto path = metrics.painterPath( arcRect );
if ( true /* TODO */ )
if ( shadowColor.alpha() > 0.0 )
{
if ( shadowNode == nullptr )
{
shadowNode = new QskArcShadowNode;
QskSGNode::setNodeRole( shadowNode, ShadowRole );
}
const auto extend = 16.0;
shadowNode->update( arcRect, metrics, Qt::black, extend );
shadowNode->update( arcRect, metrics, shadowColor, shadowMetrics, borderWidth );
}
else
else
{
delete shadowNode;
shadowNode = nullptr;
@ -261,7 +265,7 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics
{
removeAllChildNodes();
for ( QSGNode* node : { ( QSGNode* ) shadowNode, ( QSGNode* ) fillNode, ( QSGNode* ) borderNode })
for ( QSGNode* node : { ( QSGNode* ) borderNode, ( QSGNode* ) shadowNode, ( QSGNode* ) fillNode })
{
if ( node != nullptr )
{

View File

@ -10,6 +10,7 @@
class QskArcMetrics;
class QskGradient;
class QskShadowMetrics;
/*
For the moment a QPainterPath/QskShapeNode.
@ -24,7 +25,8 @@ class QSK_EXPORT QskArcNode : public QskShapeNode
void setArcData( const QRectF&, const QskArcMetrics&, const QskGradient& );
void setArcData( const QRectF&, const QskArcMetrics&,
qreal borderWidth, const QColor& borderColor, const QskGradient& );
qreal borderWidth, const QColor& borderColor, const QskGradient&,
const QColor& shadowColor, const QskShadowMetrics& shadowMetrics);
};
#endif

View File

@ -2,6 +2,7 @@ uniform lowp float qt_Opacity;
uniform lowp vec4 color;
uniform lowp vec4 rect;
uniform lowp vec2 offset;
uniform lowp float radius;
uniform lowp float thickness;
uniform lowp float startAngle;
@ -35,10 +36,18 @@ void main()
// rotation
float ra = radians(startAngle + spanAngle / 2.0);
{
{
p = p + offset;
float sin_ra = sin(ra);
float cos_ra = cos(ra);
p = mat2(cos_ra, -sin_ra, sin_ra, cos_ra) * p;
mat2 transform = mat2
(
cos_ra, -sin_ra,
sin_ra, cos_ra
);
p = transform * p;
}
// distance
@ -48,5 +57,5 @@ void main()
// coloring
float v = 1.0 - abs(d) * e;
float a = d >= 0.0 && abs(d) < e ? v : 0.0; // alpha
gl_FragColor = vec4(color.rgb, a) * qt_Opacity;
gl_FragColor = vec4(color.rgb, 1.0) * a * qt_Opacity;
}