shadows added to playground
This commit is contained in:
parent
7b59793054
commit
bbbc02f122
|
@ -5,6 +5,7 @@ SUBDIRS += \
|
|||
dialogbuttons \
|
||||
invoker \
|
||||
inputpanel \
|
||||
shadows \
|
||||
images
|
||||
|
||||
qtHaveModule(webengine) {
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
#include "BoxShadowNode.h"
|
||||
#include "QskBoxShapeMetrics.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QSGMaterialShader>
|
||||
#include <QSGMaterial>
|
||||
|
||||
#include <private/qsgnode_p.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class Shader final : public QSGMaterialShader
|
||||
{
|
||||
public:
|
||||
Shader();
|
||||
|
||||
char const *const *attributeNames() const override;
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void updateState( const QSGMaterialShader::RenderState &state,
|
||||
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
|
||||
|
||||
private:
|
||||
int m_matrixLocation = -1;
|
||||
int m_opacityLocation = -1;
|
||||
int m_aspectLocation = -1;
|
||||
int m_extentLocation = -1;
|
||||
int m_radiusLocation = -1;
|
||||
int m_colorLocation = -1;
|
||||
int m_offsetLocation = -1;
|
||||
};
|
||||
|
||||
class Material final : public QSGMaterial
|
||||
{
|
||||
public:
|
||||
Material();
|
||||
|
||||
QSGMaterialShader* createShader() const override;
|
||||
|
||||
QSGMaterialType* type() const override;
|
||||
|
||||
int compare( const QSGMaterial* other ) const override;
|
||||
|
||||
QVector2D aspect = QVector2D{1.0, 1.0};
|
||||
float extent = 0.0;
|
||||
QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0};
|
||||
QColor color = Qt::black;
|
||||
QVector2D offset;
|
||||
};
|
||||
|
||||
Shader::Shader()
|
||||
{
|
||||
const QString root( ":/qskinny/shaders/" );
|
||||
|
||||
setShaderSourceFile( QOpenGLShader::Vertex, root + "boxshadow.vert" );
|
||||
setShaderSourceFile( QOpenGLShader::Fragment, root + "boxshadow.frag" );
|
||||
}
|
||||
|
||||
char const* const* Shader::attributeNames() const
|
||||
{
|
||||
static char const *const names[] = { "in_vertex", "in_coord", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
void Shader::initialize()
|
||||
{
|
||||
QSGMaterialShader::initialize();
|
||||
|
||||
auto p = program();
|
||||
|
||||
m_matrixLocation = p->uniformLocation( "matrix" );
|
||||
m_aspectLocation = p->uniformLocation( "aspect" );
|
||||
m_opacityLocation = p->uniformLocation( "opacity" );
|
||||
m_extentLocation = p->uniformLocation( "extent" );
|
||||
m_offsetLocation = p->uniformLocation( "offset" );
|
||||
m_radiusLocation = p->uniformLocation( "radius" );
|
||||
m_colorLocation = p->uniformLocation( "color" );
|
||||
}
|
||||
|
||||
void Shader::updateState( const QSGMaterialShader::RenderState &state,
|
||||
QSGMaterial *newMaterial, QSGMaterial *oldMaterial )
|
||||
{
|
||||
auto p = program();
|
||||
|
||||
if ( state.isMatrixDirty() )
|
||||
p->setUniformValue(m_matrixLocation, state.combinedMatrix());
|
||||
|
||||
if ( state.isOpacityDirty() )
|
||||
p->setUniformValue(m_opacityLocation, state.opacity());
|
||||
|
||||
if ( oldMaterial == nullptr || newMaterial->compare( oldMaterial ) != 0
|
||||
|| state.isCachedMaterialDataDirty( ))
|
||||
{
|
||||
auto material = static_cast< const Material* >(newMaterial);
|
||||
|
||||
p->setUniformValue( m_aspectLocation, material->aspect );
|
||||
p->setUniformValue( m_extentLocation, material->extent );
|
||||
p->setUniformValue( m_radiusLocation, material->radius );
|
||||
p->setUniformValue( m_colorLocation, material->color );
|
||||
p->setUniformValue( m_offsetLocation, material->offset );
|
||||
}
|
||||
}
|
||||
|
||||
Material::Material()
|
||||
{
|
||||
setFlag( QSGMaterial::Blending, true );
|
||||
}
|
||||
|
||||
QSGMaterialShader* Material::createShader() const
|
||||
{
|
||||
return new Shader();
|
||||
}
|
||||
|
||||
QSGMaterialType* Material::type() const
|
||||
{
|
||||
static QSGMaterialType staticType;
|
||||
return &staticType;
|
||||
}
|
||||
|
||||
int Material::compare( const QSGMaterial* other ) const
|
||||
{
|
||||
auto material = static_cast< const Material* >( other );
|
||||
|
||||
if ( material->color == color
|
||||
&& material->offset == offset
|
||||
&& material->aspect == aspect
|
||||
&& qFuzzyCompare(material->extent, extent)
|
||||
&& qFuzzyCompare(material->radius, radius) )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return QSGMaterial::compare(other);
|
||||
}
|
||||
}
|
||||
|
||||
class BoxShadowNodePrivate final : public QSGGeometryNodePrivate
|
||||
{
|
||||
public:
|
||||
BoxShadowNodePrivate()
|
||||
: geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 )
|
||||
{
|
||||
}
|
||||
|
||||
QSGGeometry geometry;
|
||||
Material material;
|
||||
|
||||
QRectF rect;
|
||||
};
|
||||
|
||||
BoxShadowNode::BoxShadowNode()
|
||||
: QSGGeometryNode( *new BoxShadowNodePrivate )
|
||||
{
|
||||
Q_D( BoxShadowNode );
|
||||
|
||||
setGeometry( &d->geometry );
|
||||
setMaterial( &d->material );
|
||||
}
|
||||
|
||||
BoxShadowNode::~BoxShadowNode()
|
||||
{
|
||||
}
|
||||
|
||||
void BoxShadowNode::setRect( const QRectF& rect )
|
||||
{
|
||||
Q_D( BoxShadowNode );
|
||||
|
||||
if ( rect == d->rect)
|
||||
return;
|
||||
|
||||
d->rect = rect;
|
||||
|
||||
QVector2D newAspect( 1.0, 1.0 );
|
||||
|
||||
if ( rect.width() >= rect.height() )
|
||||
newAspect.setX( rect.width() / rect.height());
|
||||
else
|
||||
newAspect.setY( rect.height() / rect.width() );
|
||||
|
||||
if ( d->material.aspect != newAspect)
|
||||
{
|
||||
d->material.aspect = newAspect;
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadowNode::setShape( const QskBoxShapeMetrics& shape )
|
||||
{
|
||||
Q_D( BoxShadowNode );
|
||||
|
||||
const float t = 0.5 * std::min( d->rect.width(), d->rect.height());
|
||||
|
||||
const float r1 = shape.radius( Qt::BottomRightCorner ).width();
|
||||
const float r2 = shape.radius( Qt::TopRightCorner ).width();
|
||||
const float r3 = shape.radius( Qt::BottomLeftCorner ).width();
|
||||
const float r4 = shape.radius( Qt::TopLeftCorner ).width();
|
||||
|
||||
const auto uniformRadius = QVector4D(
|
||||
std::min( r1 / t, 1.0f ), std::min( r2 / t, 1.0f ),
|
||||
std::min( r3 / t, 1.0f ), std::min( r4 / t, 1.0f ) );
|
||||
|
||||
if ( d->material.radius != uniformRadius )
|
||||
{
|
||||
d->material.radius = uniformRadius;
|
||||
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadowNode::setColor( const QColor& color )
|
||||
{
|
||||
Q_D( BoxShadowNode );
|
||||
|
||||
const auto a = color.alphaF();
|
||||
|
||||
const auto c = QColor::fromRgbF(
|
||||
color.redF() * a, color.greenF() * a, color.blueF() * a, a );
|
||||
|
||||
if ( d->material.color != c )
|
||||
{
|
||||
d->material.color = c;
|
||||
markDirty(QSGNode::DirtyMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadowNode::setShadow( qreal extent, qreal dx, qreal dy )
|
||||
{
|
||||
Q_D( BoxShadowNode );
|
||||
|
||||
if ( extent <= 0.0 )
|
||||
extent = 0.0;
|
||||
|
||||
const auto minDimension = std::min( d->rect.width(), d->rect.height() );
|
||||
|
||||
const float uniformExtent = ( extent / minDimension ) * 2.0;
|
||||
|
||||
if ( !qFuzzyCompare( d->material.extent, uniformExtent ) )
|
||||
{
|
||||
d->material.extent = uniformExtent;
|
||||
markDirty(QSGNode::DirtyMaterial);
|
||||
}
|
||||
|
||||
const auto uniformOffset = QVector2D( dx, dy ) / minDimension;
|
||||
|
||||
if ( d->material.offset != uniformOffset)
|
||||
{
|
||||
d->material.offset = uniformOffset;
|
||||
markDirty( QSGNode::DirtyMaterial );
|
||||
}
|
||||
}
|
||||
|
||||
void BoxShadowNode::updateGeometry()
|
||||
{
|
||||
Q_D( BoxShadowNode );
|
||||
|
||||
const auto sz = d->material.extent;
|
||||
const auto aspect = d->material.aspect;
|
||||
|
||||
auto rect = d->rect.adjusted(
|
||||
-sz * aspect.x(), -sz * aspect.y(),
|
||||
sz * aspect.x(), sz * aspect.y()
|
||||
);
|
||||
|
||||
auto offsetLength = d->material.offset.length();
|
||||
|
||||
rect = rect.adjusted(
|
||||
-offsetLength * aspect.x(), -offsetLength * aspect.y(),
|
||||
offsetLength * aspect.x(), offsetLength * aspect.y() );
|
||||
|
||||
QSGGeometry::updateTexturedRectGeometry(
|
||||
&d->geometry, rect, QRectF( 0.0, 0.0, 1.0, 1.0 ) );
|
||||
|
||||
markDirty( QSGNode::DirtyGeometry );
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef BOX_SHADOW_NODE_H
|
||||
#define BOX_SHADOW_NODE_H
|
||||
|
||||
#include <QSGGeometryNode>
|
||||
|
||||
class QColor;
|
||||
class QskBoxShapeMetrics;
|
||||
|
||||
class BoxShadowNodePrivate;
|
||||
|
||||
class BoxShadowNode : public QSGGeometryNode
|
||||
{
|
||||
public:
|
||||
BoxShadowNode();
|
||||
~BoxShadowNode() override;
|
||||
|
||||
void setRect( const QRectF& );
|
||||
void setShape( const QskBoxShapeMetrics& );
|
||||
void setColor( const QColor& );
|
||||
void setShadow( qreal extent, qreal dx, qreal dy );
|
||||
|
||||
void updateGeometry();
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE( BoxShadowNode )
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,138 @@
|
|||
#include "ShadowedBox.h"
|
||||
#include "BoxShadowNode.h"
|
||||
|
||||
#include <QskBoxNode.h>
|
||||
#include <QskBoxBorderMetrics.h>
|
||||
#include <QskBoxBorderColors.h>
|
||||
#include <QskGradient.h>
|
||||
#include <QskSkinlet.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class Skinlet : public QskSkinlet
|
||||
{
|
||||
public:
|
||||
enum NodeRole { ShadowRole, PanelRole };
|
||||
|
||||
Skinlet()
|
||||
{
|
||||
setNodeRoles( { ShadowRole, PanelRole } );
|
||||
}
|
||||
|
||||
QRectF subControlRect( const QskSkinnable*,
|
||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const override
|
||||
{
|
||||
if ( subControl == ShadowedBox::Panel )
|
||||
{
|
||||
return contentsRect;
|
||||
}
|
||||
|
||||
return QRectF();
|
||||
}
|
||||
|
||||
QSGNode* updateSubNode( const QskSkinnable* skinnable,
|
||||
quint8 nodeRole, QSGNode* node ) const override
|
||||
{
|
||||
const auto box = static_cast< const ShadowedBox* >( skinnable );
|
||||
|
||||
switch ( nodeRole )
|
||||
{
|
||||
case ShadowRole:
|
||||
{
|
||||
auto shadowNode = static_cast< BoxShadowNode* >( node );
|
||||
if ( shadowNode == nullptr )
|
||||
shadowNode = new BoxShadowNode();
|
||||
|
||||
const auto& s = box->shadow();
|
||||
const qreal dx = s.extent + s.xOffset;
|
||||
const qreal dy = s.extent + s.yOffset;
|
||||
|
||||
auto r = box->subControlRect( ShadowedBox::Panel );
|
||||
r.adjust( -dx, -dy, dx, dy );
|
||||
|
||||
shadowNode->setRect( r );
|
||||
shadowNode->setShape( box->shape() );
|
||||
shadowNode->setShadow( s.extent, s.xOffset, s.yOffset );
|
||||
shadowNode->setColor( box->shadowColor() );
|
||||
shadowNode->updateGeometry();
|
||||
|
||||
return shadowNode;
|
||||
}
|
||||
case PanelRole:
|
||||
{
|
||||
auto boxNode = static_cast< QskBoxNode* >( node );
|
||||
if ( boxNode == nullptr )
|
||||
boxNode = new QskBoxNode();
|
||||
|
||||
const QRectF r = box->subControlRect( ShadowedBox::Panel );
|
||||
|
||||
boxNode->setBoxData( r, box->shape(), QskBoxBorderMetrics(),
|
||||
QskBoxBorderColors(), box->gradient() );
|
||||
|
||||
return boxNode;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QSK_SUBCONTROL( ShadowedBox, Panel )
|
||||
|
||||
ShadowedBox::ShadowedBox(QQuickItem *parentItem)
|
||||
: QskControl( parentItem )
|
||||
{
|
||||
setFlag( QQuickItem::ItemHasContents, true );
|
||||
setSkinlet( new Skinlet() );
|
||||
}
|
||||
|
||||
ShadowedBox::~ShadowedBox()
|
||||
{
|
||||
}
|
||||
|
||||
void ShadowedBox::setShadow( const Shadow& shadow )
|
||||
{
|
||||
m_shadow = shadow;
|
||||
update();
|
||||
}
|
||||
|
||||
const ShadowedBox::Shadow& ShadowedBox::shadow() const
|
||||
{
|
||||
return m_shadow;
|
||||
}
|
||||
|
||||
void ShadowedBox::setShadowColor( const QColor& color )
|
||||
{
|
||||
m_shadowColor = color;
|
||||
update();
|
||||
}
|
||||
|
||||
QColor ShadowedBox::shadowColor() const
|
||||
{
|
||||
return m_shadowColor;
|
||||
}
|
||||
|
||||
void ShadowedBox::setGradient( const QskGradient& gradient )
|
||||
{
|
||||
m_gradient = gradient;
|
||||
update();
|
||||
}
|
||||
|
||||
const QskGradient& ShadowedBox::gradient() const
|
||||
{
|
||||
return m_gradient;
|
||||
}
|
||||
|
||||
void ShadowedBox::setShape( const QskBoxShapeMetrics& shape )
|
||||
{
|
||||
m_shape = shape;
|
||||
update();
|
||||
}
|
||||
|
||||
const QskBoxShapeMetrics& ShadowedBox::shape() const
|
||||
{
|
||||
return m_shape;
|
||||
}
|
||||
|
||||
#include "moc_ShadowedBox.cpp"
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef SHADOWED_BOX_H
|
||||
#define SHADOWED_BOX_H
|
||||
|
||||
#include <QskControl.h>
|
||||
#include <QskBoxShapeMetrics.h>
|
||||
|
||||
class QskGradient;
|
||||
|
||||
class ShadowedBox : public QskControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel )
|
||||
|
||||
class Shadow
|
||||
{
|
||||
public:
|
||||
qreal extent = 0.0;
|
||||
qreal xOffset = 0.0;
|
||||
qreal yOffset = 0.0;
|
||||
};
|
||||
|
||||
ShadowedBox(QQuickItem *parent = nullptr);
|
||||
~ShadowedBox() override;
|
||||
|
||||
void setShadow( const Shadow& );
|
||||
const Shadow& shadow() const;
|
||||
|
||||
void setGradient( const QskGradient& );
|
||||
const QskGradient& gradient() const;
|
||||
|
||||
void setShadowColor( const QColor& );
|
||||
QColor shadowColor() const;
|
||||
|
||||
void setShape( const QskBoxShapeMetrics& );
|
||||
const QskBoxShapeMetrics& shape() const;
|
||||
|
||||
private:
|
||||
Shadow m_shadow;
|
||||
QColor m_shadowColor = Qt::black;
|
||||
QskGradient m_gradient;
|
||||
QskBoxShapeMetrics m_shape;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Heavily inspired by code from the kirigami project:
|
||||
Copyright 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
License: LGPL-2.0-or-later
|
||||
|
||||
https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
|
||||
Copyright: 2017 Inigo Quilez
|
||||
License: MIT
|
||||
*/
|
||||
|
||||
uniform lowp float opacity;
|
||||
uniform lowp float extent;
|
||||
uniform lowp vec4 radius;
|
||||
uniform lowp vec4 color;
|
||||
uniform lowp vec2 offset;
|
||||
uniform lowp vec2 aspect;
|
||||
|
||||
varying lowp vec2 coord;
|
||||
|
||||
lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point )
|
||||
{
|
||||
if ( point.x > 0.0 )
|
||||
return ( point.y > 0.0) ? radii.x : radii.y;
|
||||
else
|
||||
return ( point.y > 0.0) ? radii.z : radii.w;
|
||||
}
|
||||
|
||||
bool isInside( in lowp vec2 point, in lowp vec2 size, in lowp vec4 radii )
|
||||
{
|
||||
lowp float r = effectiveRadius( radii, point );
|
||||
lowp vec2 d = abs(point) - size + r;
|
||||
|
||||
lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ) - r;
|
||||
return l <= 0.0;
|
||||
}
|
||||
|
||||
lowp float shadowAt(
|
||||
in lowp vec2 point, in lowp vec2 size, in lowp vec4 radii )
|
||||
{
|
||||
lowp float r = effectiveRadius( radii, point );
|
||||
lowp vec2 d = abs(point) - size + r;
|
||||
|
||||
return min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ) - r;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 col = vec4(0.0);
|
||||
|
||||
if ( extent > 0.0 && opacity > 0.0 )
|
||||
{
|
||||
lowp float t = 1.0 + 2.0 * length( offset ) + extent;
|
||||
|
||||
if ( !isInside( coord, aspect / t, radius / t) )
|
||||
{
|
||||
const lowp float minRadius = 0.05;
|
||||
lowp float e2 = 0.5 * extent;
|
||||
|
||||
lowp vec4 f = minRadius / max( radius, minRadius );
|
||||
lowp vec4 r = radius + e2 * f;
|
||||
|
||||
lowp float shadow = shadowAt(
|
||||
coord - 2.0 * offset / t, aspect / t, r / t);
|
||||
|
||||
lowp float v = smoothstep( -e2, e2, shadow );
|
||||
|
||||
col = mix( color, vec4(0.0), v ) * opacity;
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = col;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
uniform highp mat4 matrix;
|
||||
uniform lowp vec2 aspect;
|
||||
|
||||
attribute highp vec4 in_vertex;
|
||||
attribute mediump vec2 in_coord;
|
||||
|
||||
varying mediump vec2 coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
coord = ( -1.0 + 2.0 * in_coord ) * aspect;
|
||||
gl_Position = matrix * in_vertex;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
*****************************************************************************/
|
||||
|
||||
#include "ShadowedBox.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QskObjectCounter.h>
|
||||
#include <QskWindow.h>
|
||||
#include <QskLinearBox.h>
|
||||
#include <QskBoxShapeMetrics.h>
|
||||
#include <QskRgbValue.h>
|
||||
|
||||
class Box : public ShadowedBox
|
||||
{
|
||||
public:
|
||||
Box( QQuickItem* parent = nullptr )
|
||||
: ShadowedBox( parent )
|
||||
{
|
||||
Shadow shadow;
|
||||
shadow.extent = 10;
|
||||
shadow.xOffset = 20.0;
|
||||
shadow.yOffset = 20.0;
|
||||
|
||||
setShadow( shadow );
|
||||
setShadowColor( Qt::black );
|
||||
setGradient( Qt::darkRed );
|
||||
setShape( QskBoxShapeMetrics( 5, 10, 15, 20 ) );
|
||||
}
|
||||
};
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
#ifdef ITEM_STATISTICS
|
||||
QskObjectCounter counter( true );
|
||||
#endif
|
||||
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
auto layout = new QskLinearBox();
|
||||
layout->setPanel( true );
|
||||
#if 1
|
||||
layout->setGradientHint( QskBox::Panel,
|
||||
QskGradient( Qt::Vertical, QskRgb::WhiteSmoke, QskRgb::MistyRose ) );
|
||||
#else
|
||||
layout->setGradientHint( QskBox::Panel, Qt::white );
|
||||
#endif
|
||||
layout->setPadding( 60 );
|
||||
(void ) new Box( layout );
|
||||
|
||||
QskWindow window;
|
||||
window.setColor( QskRgb::PapayaWhip );
|
||||
window.addItem( layout );
|
||||
window.resize( 600, 600 );
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/qskinny/shaders">
|
||||
<file>boxshadow.vert</file>
|
||||
<file>boxshadow.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,14 @@
|
|||
CONFIG += qskexample
|
||||
QT += quick_private
|
||||
|
||||
RESOURCES += \
|
||||
shaders.qrc
|
||||
|
||||
HEADERS += \
|
||||
ShadowedBox.h \
|
||||
BoxShadowNode.h
|
||||
|
||||
SOURCES += \
|
||||
ShadowedBox.cpp \
|
||||
BoxShadowNode.cpp \
|
||||
main.cpp
|
Loading…
Reference in New Issue