This commit is contained in:
Uwe Rathmann 2021-11-02 07:47:49 +01:00
commit 411e9832fd
40 changed files with 598 additions and 2474 deletions

View File

@ -8,14 +8,9 @@
#include <QskTextLabel.h>
QSK_SUBCONTROL( Box, Panel )
Box::Box( const QString& title, QQuickItem* parent )
: QskLinearBox( Qt::Vertical, parent )
: ShadowedBox( Qt::Vertical, parent )
{
setPanel( true );
setSubcontrolProxy( QskBox::Panel, Box::Panel );
if ( !title.isEmpty() )
{
auto label = new QskTextLabel( title, this );

View File

@ -5,16 +5,12 @@
#pragma once
#include <QskLinearBox.h>
#include "ShadowedBox.h"
class QskTextLabel;
class Box : public QskLinearBox
class Box : public ShadowedBox
{
Q_OBJECT
public:
QSK_SUBCONTROLS( Panel )
Box( const QString& title, QQuickItem* parent = nullptr );
};

View File

@ -36,7 +36,6 @@ BoxWithButtons::BoxWithButtons( const QString& title, const QString& value,
bool isBright, QQuickItem* parent )
: Box( QString(), parent )
{
setPanel( true );
setSubcontrolProxy( QskBox::Panel, Panel );
setSizePolicy( Qt::Vertical, QskSizePolicy::Maximum );

View File

@ -23,12 +23,8 @@
#include <QskTextLabel.h>
#include <QskQuick.h>
#include "kirigami/shadowedrectangle.h"
#include <QTimer>
QSK_SUBCONTROL( ShadowPositioner, Panel )
QSK_SUBCONTROL( MainContent, Panel )
QSK_SUBCONTROL( MainContentGridBox, Panel )
@ -63,59 +59,6 @@ namespace
};
}
ShadowPositioner::ShadowPositioner( QQuickItem* parent )
: QskControl( parent )
{
setAutoLayoutChildren( true );
}
void ShadowPositioner::setGridBox( QskGridBox* gridBox )
{
m_gridBox = gridBox;
m_rectangles.reserve( m_gridBox->elementCount() );
for( int i = 0; i < m_gridBox->elementCount(); ++i )
{
auto r = new ShadowedRectangle( this );
r->setZ( 5 );
r->setColor( Qt::transparent );
r->shadow()->setColor( color( ShadowPositioner::Panel ) );
connect( qskSetup, &QskSetup::skinChanged, [this, r]()
{
r->shadow()->setColor( color( ShadowPositioner::Panel ) );
} );
r->shadow()->setSize( metric( ShadowPositioner::Panel | QskAspect::Size ) );
r->setOpacity( 0.1 );
auto shape = boxShapeHint( ShadowPositioner::Panel );
r->corners()->setTopLeft( shape.radius( Qt::TopLeftCorner ).width() );
r->corners()->setTopRight( shape.radius( Qt::TopRightCorner ).width() );
r->corners()->setBottomLeft( shape.radius( Qt::BottomLeftCorner ).width() );
r->corners()->setBottomRight( shape.radius( Qt::BottomRightCorner ).width() );
m_rectangles.append( r );
}
}
void ShadowPositioner::updateLayout()
{
auto mainContent = static_cast< QskLinearBox* >( parentItem() );
QTimer::singleShot( 0, this, [this, mainContent]()
{
const auto pos0 = mainContent->itemAtIndex( 1 )->position();
for( int i = 0; i < m_rectangles.count(); ++i )
{
const auto item = m_gridBox->itemAtIndex( i );
m_rectangles[i]->setPosition( pos0 + item->position() );
m_rectangles[i]->setSize( qskItemSize( item ) );
}
} );
}
MainContent::MainContent( QQuickItem* parent )
: QskLinearBox( Qt::Vertical, parent )
{
@ -146,15 +89,6 @@ MainContent::MainContent( QQuickItem* parent )
addItem( topBar );
addItem( gridBox );
m_shadowPositioner = new ShadowPositioner( this );
m_shadowPositioner->setGridBox( gridBox );
}
void MainContent::geometryChangeEvent( QskGeometryChangeEvent* event )
{
QskLinearBox::geometryChangeEvent( event );
m_shadowPositioner->polish();
}
#include "moc_MainContent.cpp"

View File

@ -8,25 +8,6 @@
#include <QskGridBox.h>
#include <QskLinearBox.h>
class ShadowedRectangle;
class ShadowPositioner : public QskControl
{
Q_OBJECT
public:
QSK_SUBCONTROLS( Panel )
ShadowPositioner( QQuickItem* parent );
void updateLayout() override;
void setGridBox( QskGridBox* gridBox );
private:
QskGridBox* m_gridBox;
QVector< ShadowedRectangle* > m_rectangles;
};
class MainContentGridBox : public QskGridBox
{
Q_OBJECT
@ -50,10 +31,6 @@ class MainContent : public QskLinearBox
MainContent( QQuickItem* parent );
protected:
void geometryChangeEvent( QskGeometryChangeEvent* ) override;
private:
QList< QskLinearBox* > m_columns;
ShadowPositioner* m_shadowPositioner;
};

View File

@ -0,0 +1,179 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "ShadowedBox.h"
#include "nodes/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 );
const auto r = box->subControlRect( ShadowedBox::Panel );
if ( r.isEmpty() )
return nullptr;
switch ( nodeRole )
{
case ShadowRole:
{
auto shadowNode = static_cast< BoxShadowNode* >( node );
if ( shadowNode == nullptr )
shadowNode = new BoxShadowNode();
const auto& shadowMetrics = box->shadow();
shadowNode->setRect( shadowMetrics.shadowRect( r ) );
shadowNode->setShape( box->shape() );
shadowNode->setBlurRadius( shadowMetrics.blurRadius() );
shadowNode->setColor( box->shadowColor() );
shadowNode->setClipRect( r );
shadowNode->updateGeometry();
return shadowNode;
}
case PanelRole:
{
auto boxNode = static_cast< QskBoxNode* >( node );
if ( boxNode == nullptr )
boxNode = new QskBoxNode();
const auto r = box->subControlRect( ShadowedBox::Panel );
boxNode->setBoxData( r, box->shape(), box->borderWidth(),
box->borderColor(), box->gradient() );
return boxNode;
}
}
return nullptr;
}
};
}
QSK_SUBCONTROL( ShadowedBox, Panel )
ShadowedBox::ShadowedBox(Qt::Orientation orientation, QQuickItem* parentItem )
: QskLinearBox( orientation, parentItem )
{
setFlag( QQuickItem::ItemHasContents, true );
setSkinlet( new Skinlet() );
// ### move to Skin:
setGradient( Qt::white );
setShadow( { 0, 10 } );
setShadowColor( 0xe5e5e5 );
setShape( 6 );
}
ShadowedBox::~ShadowedBox()
{
}
void ShadowedBox::setShadow( const QskShadowMetrics& shadow )
{
m_shadow = shadow;
update();
}
const QskShadowMetrics& ShadowedBox::shadow() const
{
return m_shadow;
}
void ShadowedBox::setShadowColor( const QColor& color )
{
m_shadowColor = color;
update();
}
QColor ShadowedBox::shadowColor() const
{
return m_shadowColor;
}
QRectF ShadowedBox::layoutRectForSize( const QSizeF &size ) const
{
auto padding = paddingHint( Panel );
return { padding.left() / 2, padding.top() / 2,
size.width() - padding.right(), size.height() - padding.bottom() };
}
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;
}
void ShadowedBox::setBorderWidth( qreal width )
{
m_borderWidth = qMax( width, 0.0 );
update();
}
qreal ShadowedBox::borderWidth() const
{
return m_borderWidth;
}
void ShadowedBox::setBorderColor( const QColor& color )
{
m_borderColor = color;
update();
}
QColor ShadowedBox::borderColor() const
{
return m_borderColor;
}
#include "moc_ShadowedBox.cpp"

View File

@ -0,0 +1,53 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#pragma once
#include <QskLinearBox.h>
#include <QskBoxShapeMetrics.h>
#include <QskShadowMetrics.h>
class QskGradient;
class ShadowedBox : public QskLinearBox
{
Q_OBJECT
public:
QSK_SUBCONTROLS( Panel )
ShadowedBox( Qt::Orientation orientation, QQuickItem* parent = nullptr );
~ShadowedBox() override;
void setShadow( const QskShadowMetrics& );
const QskShadowMetrics& shadow() const;
void setGradient( const QskGradient& );
const QskGradient& gradient() const;
void setShadowColor( const QColor& );
QColor shadowColor() const;
QRectF layoutRectForSize( const QSizeF& size ) const override;
void setShape( const QskBoxShapeMetrics& );
const QskBoxShapeMetrics& shape() const;
void setBorderWidth( qreal width );
qreal borderWidth() const;
void setBorderColor( const QColor& );
QColor borderColor() const;
private:
QskShadowMetrics m_shadow;
QColor m_shadowColor = Qt::black;
QskGradient m_gradient;
QskBoxShapeMetrics m_shape;
qreal m_borderWidth = 0.0;
QColor m_borderColor = Qt::black;
};

View File

@ -16,8 +16,9 @@
#include "MenuBar.h"
#include "PieChartPainted.h"
#include "RoundedIcon.h"
#include "TopBar.h"
#include "RoundButton.h"
#include "ShadowedBox.h"
#include "TopBar.h"
#include "UsageBox.h"
#include "UsageDiagram.h"
@ -125,13 +126,8 @@ void Skin::initHints( const Palette& palette )
ed.setFontRole( TimeLabel::Text, QskSkin::HugeFont );
ed.setColor( TimeLabel::Text, "#6776FF" );
// boxes (including shadow):
ed.setPadding( Box::Panel, 15 );
ed.setMetric( ShadowPositioner::Panel | QskAspect::Size, 15 );
ed.setBoxShape( ShadowPositioner::Panel, 6 );
ed.setPadding( ShadowedBox::Panel, 15 );
// content in boxes (indoor temperature, humidity etc.):
ed.setFontRole( UsageBox::Separator, QskSkin::SmallFont );
@ -200,6 +196,5 @@ void Skin::initHints( const Palette& palette )
ed.setBoxBorderColors( UsageDiagramBox::DaysBox, palette.weekdayBox );
ed.setColor( QskTextLabel::Text, palette.text );
ed.setColor( UsageDiagramBox::DayText, palette.text );
ed.setColor( ShadowPositioner::Panel, palette.shadow );
ed.setGradient( CircularProgressBar::Groove, palette.circularProgressBarGroove );
}

View File

@ -60,7 +60,7 @@ class DaytimeSkin : public Skin
: Skin(
Skin::Palette( {"#6D7BFB"}, {"#fbfbfb"}, {"#ffffff"},
"#ffffff", {"#f7f7f7"}, {"#f4f4f4"}, Qt::black, Qt::black,
{ QskGradient::Vertical, { { 0.0, 0xc4c4c4 }, { 0.5, 0xf8f8f8 }, { 1.0, 0xc4c4c4 } } } )
{ QskGradient::Vertical, { { 0.0, 0xffc4c4c4 }, { 0.5, 0xfff8f8f8 }, { 1.0, 0xffc4c4c4 } } } )
, parent )
{
}
@ -73,7 +73,7 @@ class NighttimeSkin : public Skin
: Skin(
Skin::Palette( {"#2937A7"}, {"#040404"}, {"#000000"},
"#000000", {"#0a0a0a"}, {"#0c0c0c"}, Qt::white, Qt::white,
{ QskGradient::Vertical, { { 0.0, 0x666666 }, { 0.5, 0x222222 }, { 1.0, 0x333333 } } } )
{ QskGradient::Vertical, { { 0.0, 0xff666666 }, { 0.5, 0xff222222 }, { 1.0, 0xff333333 } } } )
, parent )
{
}

View File

@ -18,6 +18,7 @@ SOURCES += \
PieChartPainted.cpp \
PieChartSkinlet.cpp \
RoundedIcon.cpp \
ShadowedBox.cpp \
Skin.cpp \
TopBar.cpp \
RoundButton.cpp \
@ -28,7 +29,8 @@ SOURCES += \
SOURCES += \
nodes/DiagramDataNode.cpp \
nodes/DiagramSegmentsNode.cpp
nodes/DiagramSegmentsNode.cpp \
nodes/BoxShadowNode.cpp
HEADERS += \
Box.h \
@ -47,6 +49,7 @@ HEADERS += \
PieChartPainted.h \
PieChartSkinlet.h \
RoundedIcon.h \
ShadowedBox.h \
Skin.h \
TopBar.h \
RoundButton.h \
@ -55,23 +58,10 @@ HEADERS += \
HEADERS += \
nodes/DiagramDataNode.h \
nodes/DiagramSegmentsNode.h
HEADERS += \
kirigami/shadowedrectangle.h \
kirigami/scenegraph/paintedrectangleitem.h \
kirigami/scenegraph/shadowedborderrectanglematerial.h \
kirigami/scenegraph/shadowedrectanglematerial.h \
kirigami/scenegraph/shadowedrectanglenode.h
SOURCES += \
kirigami/shadowedrectangle.cpp \
kirigami/scenegraph/paintedrectangleitem.cpp \
kirigami/scenegraph/shadowedborderrectanglematerial.cpp \
kirigami/scenegraph/shadowedrectanglematerial.cpp \
kirigami/scenegraph/shadowedrectanglenode.cpp
nodes/DiagramSegmentsNode.h \
nodes/BoxShadowNode.h
RESOURCES += \
images.qrc \
fonts.qrc \
kirigami/scenegraph/shaders/shaders.qrc
shaders.qrc

View File

@ -1,50 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "paintedrectangleitem.h"
#include <QPainter>
PaintedRectangleItem::PaintedRectangleItem(QQuickItem* parent)
: QQuickPaintedItem(parent)
{
}
void PaintedRectangleItem::setColor(const QColor& color)
{
m_color = color;
update();
}
void PaintedRectangleItem::setRadius(qreal radius)
{
m_radius = radius;
update();
}
void PaintedRectangleItem::setBorderColor(const QColor& color)
{
m_borderColor = color;
update();
}
void PaintedRectangleItem::setBorderWidth(qreal width)
{
m_borderWidth = width;
update();
}
void PaintedRectangleItem::paint(QPainter* painter)
{
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setBrush(m_color);
if (m_borderWidth > 0.0) {
painter->setPen(QPen(m_borderColor, m_borderWidth));
} else {
painter->setPen(Qt::transparent);
}
painter->drawRoundedRect(m_borderWidth / 2, m_borderWidth / 2, width() - m_borderWidth, height() - m_borderWidth, m_radius, m_radius);
}

View File

@ -1,43 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef PAINTEDRECTANGLEITEM_H
#define PAINTEDRECTANGLEITEM_H
#include <QQuickPaintedItem>
/**
* A rectangle with a border and rounded corners, rendered through QPainter.
*
* This is a helper used by ShadowedRectangle as fallback for when software
* rendering is used, which means our shaders cannot be used.
*
* Since we cannot actually use QSGPaintedNode, we need to do some trickery
* using QQuickPaintedItem as a child of ShadowedRectangle.
*
* \warning This item is **not** intended as a general purpose item.
*/
class PaintedRectangleItem : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit PaintedRectangleItem(QQuickItem *parent = nullptr);
void setColor(const QColor &color);
void setRadius(qreal radius);
void setBorderColor(const QColor &color);
void setBorderWidth(qreal width);
void paint(QPainter *painter) override;
private:
QColor m_color;
qreal m_radius = 0.0;
QColor m_borderColor;
qreal m_borderWidth = 0.0;
};
#endif // PAINTEDRECTANGLEITEM_H

View File

@ -1,26 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// This file contains common directives needed for the shaders to work.
// It is included as the very first bit in the shader.
// Important: If a specific GLSL version is needed, it should be set in this
// file.
// This file is intended for desktop OpenGL version 2.1 or greater.
#version 120
#ifndef lowp
#define lowp
#endif
#ifndef mediump
#define mediump
#endif
#ifndef highp
#define highp mediump
#endif

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// This file contains common directives needed for the shaders to work.
// It is included as the very first bit in the shader.
// Important: If a specific GLSL version is needed, it should be set in this
// file.
// This file is intended for desktop OpenGL version 4.5 or greater.
#version 450
#define CORE_PROFILE

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// This file contains common directives needed for the shaders to work.
// It is included as the very first bit in the shader.
// Important: If a specific GLSL version is needed, it should be set in this
// file.
// This file is intended for OpenGLES version 2.0 or greater.
#version 100
#extension GL_OES_standard_derivatives : enable

View File

@ -1,240 +0,0 @@
// SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
// SPDX-FileCopyrightText: 2017 Inigo Quilez
//
// SPDX-License-Identifier: MIT
//
// This file is based on
// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
//if not GLES
// include "desktop_header.glsl"
//else
// include "es_header.glsl"
// A maximum point count to be used for sdf_polygon input arrays.
// Unfortunately even function inputs require a fixed size at declaration time
// for arrays, unless we were to use OpenGL 4.5.
// Since the polygon is most likely to be defined in a uniform, this should be
// at least less than MAX_FRAGMENT_UNIFORM_COMPONENTS / 2 (since we need vec2).
#define SDF_POLYGON_MAX_POINT_COUNT 400
/*********************************
Shapes
*********************************/
// Distance field for a circle.
//
// \param point A point on the distance field.
// \param radius The radius of the circle.
//
// \return The signed distance from point to the circle. If negative, point is
// inside the circle.
lowp float sdf_circle(in lowp vec2 point, in lowp float radius)
{
return length(point) - radius;
}
// Distance field for a triangle.
//
// \param point A point on the distance field.
// \param p0 The first vertex of the triangle.
// \param p0 The second vertex of the triangle.
// \param p0 The third vertex of the triangle.
//
// \note The ordering of the three vertices does not matter.
//
// \return The signed distance from point to triangle. If negative, point is
// inside the triangle.
lowp float sdf_triangle(in lowp vec2 point, in lowp vec2 p0, in lowp vec2 p1, in lowp vec2 p2)
{
lowp vec2 e0 = p1 - p0;
lowp vec2 e1 = p2 - p1;
lowp vec2 e2 = p0 - p2;
lowp vec2 v0 = point - p0;
lowp vec2 v1 = point - p1;
lowp vec2 v2 = point - p2;
lowp vec2 pq0 = v0 - e0 * clamp( dot(v0, e0) / dot(e0, e0), 0.0, 1.0 );
lowp vec2 pq1 = v1 - e1 * clamp( dot(v1, e1) / dot(e1, e1), 0.0, 1.0 );
lowp vec2 pq2 = v2 - e2 * clamp( dot(v2, e2) / dot(e2, e2), 0.0, 1.0 );
lowp float s = sign( e0.x*e2.y - e0.y*e2.x );
lowp vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)),
vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))),
vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x)));
return -sqrt(d.x)*sign(d.y);
}
// Distance field for a rectangle.
//
// \param point A point on the distance field.
// \param rect A vec2 with the size of the rectangle.
//
// \return The signed distance from point to rectangle. If negative, point is
// inside the rectangle.
lowp float sdf_rectangle(in lowp vec2 point, in lowp vec2 rect)
{
lowp vec2 d = abs(point) - rect;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
// Distance field for a rectangle with rounded corners.
//
// \param point The point to calculate the distance of.
// \param rect The rectangle to calculate the distance of.
// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
//
// \return The signed distance from point to rectangle. If negative, point is
// inside the rectangle.
lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec4 radius)
{
radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
radius.x = (point.y > 0.0) ? radius.x : radius.y;
lowp vec2 d = abs(point) - rect + radius.x;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
}
/*********************
Operators
*********************/
// Convert a distance field to an annular (hollow) distance field.
//
// \param sdf The result of an sdf shape to convert.
// \param thickness The thickness of the resulting shape.
//
// \return The value of sdf modified to an annular shape.
lowp float sdf_annular(in lowp float sdf, in lowp float thickness)
{
return abs(sdf) - thickness;
}
// Union two sdf shapes together.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
//
// \return The union of sdf1 and sdf2, that is, the distance to both sdf1 and
// sdf2.
lowp float sdf_union(in lowp float sdf1, in lowp float sdf2)
{
return min(sdf1, sdf2);
}
// Subtract two sdf shapes.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
//
// \return sdf1 with sdf2 subtracted from it.
lowp float sdf_subtract(in lowp float sdf1, in lowp float sdf2)
{
return max(sdf1, -sdf2);
}
// Intersect two sdf shapes.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
//
// \return The intersection between sdf1 and sdf2, that is, the area where both
// sdf1 and sdf2 provide the same distance value.
lowp float sdf_intersect(in lowp float sdf1, in lowp float sdf2)
{
return max(sdf1, sdf2);
}
// Smoothly intersect two sdf shapes.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
// \param smoothing The amount of smoothing to apply.
//
// \return A smoothed version of the intersect operation.
lowp float sdf_intersect_smooth(in lowp float sdf1, in lowp float sdf2, in lowp float smoothing)
{
lowp float h = clamp(0.5 - 0.5 * (sdf1 - sdf2) / smoothing, 0.0, 1.0);
return mix(sdf1, sdf2, h) + smoothing * h * (1.0 - h);
}
// Round an sdf shape.
//
// \param sdf The sdf shape to round.
// \param amount The amount of rounding to apply.
//
// \return The rounded shape of sdf.
// Note that rounding happens by basically selecting an isoline of sdf,
// therefore, the resulting shape may be larger than the input shape.
lowp float sdf_round(in lowp float sdf, in lowp float amount)
{
return sdf - amount;
}
// Convert an sdf shape to an outline of its shape.
//
// \param sdf The sdf shape to turn into an outline.
//
// \return The outline of sdf.
lowp float sdf_outline(in lowp float sdf)
{
return abs(sdf);
}
/********************
Convenience
********************/
// A constant to represent a "null" value of an sdf.
//
// Since 0 is a point exactly on the outline of an sdf shape, and negative
// values are inside the shape, this uses a very large positive constant to
// indicate a value that is really far away from the actual sdf shape.
const lowp float sdf_null = 99999.0;
// A constant for a default level of smoothing when rendering an sdf.
//
// This
const lowp float sdf_default_smoothing = 0.625;
// Render an sdf shape alpha-blended onto an existing color.
//
// This is an overload of sdf_render(float, vec4, vec4) that allows specifying a
// blending amount and a smoothing amount.
//
// \param alpha The alpha to use for blending.
// \param smoothing The amount of smoothing to apply to the sdf.
//
lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float alpha, in lowp float smoothing)
{
lowp float g = fwidth(sdf);
return mix(sourceColor, sdfColor, alpha * (1.0 - smoothstep(-smoothing * g, smoothing * g, sdf)));
}
// Render an sdf shape.
//
// This will render the sdf shape on top of whatever source color is input,
// making sure to apply smoothing if desired.
//
// \param sdf The sdf shape to render.
// \param sourceColor The source color to render on top of.
// \param sdfColor The color to use for rendering the sdf shape.
//
// \return sourceColor with the sdf shape rendered on top.
lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor)
{
return sdf_render(sdf, sourceColor, sdfColor, 1.0, sdf_default_smoothing);
}
// Render an sdf shape.
//
// This is an overload of sdf_render(float, vec4, vec4) that allows specifying a
// smoothing amount.
//
// \param smoothing The amount of smoothing to apply to the sdf.
//
lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float smoothing)
{
return sdf_render(sdf, sourceColor, sdfColor, 1.0, smoothing);
}

View File

@ -1,239 +0,0 @@
// SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
// SPDX-FileCopyrightText: 2017 Inigo Quilez
//
// SPDX-License-Identifier: MIT
//
// This file is based on
// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
//if not GLES
// include "desktop_header.glsl"
//else
// include "es_header.glsl"
// A maximum point count to be used for sdf_polygon input arrays.
// Unfortunately even function inputs require a fixed size at declaration time
// for arrays, unless we were to use OpenGL 4.5.
// Since the polygon is most likely to be defined in a uniform, this should be
// at least less than MAX_FRAGMENT_UNIFORM_COMPONENTS / 2 (since we need vec2).
#define SDF_POLYGON_MAX_POINT_COUNT 400
/*********************************
Shapes
*********************************/
// Distance field for a circle.
//
// \param point A point on the distance field.
// \param radius The radius of the circle.
//
// \return The signed distance from point to the circle. If negative, point is
// inside the circle.
lowp float sdf_circle(in lowp vec2 point, in lowp float radius)
{
return length(point) - radius;
}
// Distance field for a triangle.
//
// \param point A point on the distance field.
// \param p0 The first vertex of the triangle.
// \param p0 The second vertex of the triangle.
// \param p0 The third vertex of the triangle.
//
// \note The ordering of the three vertices does not matter.
//
// \return The signed distance from point to triangle. If negative, point is
// inside the triangle.
lowp float sdf_triangle(in lowp vec2 point, in lowp vec2 p0, in lowp vec2 p1, in lowp vec2 p2)
{
lowp vec2 e0 = p1 - p0;
lowp vec2 e1 = p2 - p1;
lowp vec2 e2 = p0 - p2;
lowp vec2 v0 = point - p0;
lowp vec2 v1 = point - p1;
lowp vec2 v2 = point - p2;
lowp vec2 pq0 = v0 - e0 * clamp( dot(v0, e0) / dot(e0, e0), 0.0, 1.0 );
lowp vec2 pq1 = v1 - e1 * clamp( dot(v1, e1) / dot(e1, e1), 0.0, 1.0 );
lowp vec2 pq2 = v2 - e2 * clamp( dot(v2, e2) / dot(e2, e2), 0.0, 1.0 );
lowp float s = sign( e0.x*e2.y - e0.y*e2.x );
lowp vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)),
vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))),
vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x)));
return -sqrt(d.x)*sign(d.y);
}
// Distance field for a rectangle.
//
// \param point A point on the distance field.
// \param rect A vec2 with the size of the rectangle.
//
// \return The signed distance from point to rectangle. If negative, point is
// inside the rectangle.
lowp float sdf_rectangle(in lowp vec2 point, in lowp vec2 rect)
{
lowp vec2 d = abs(point) - rect;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}
// Distance field for a rectangle with rounded corners.
//
// \param point The point to calculate the distance of.
// \param rect The rectangle to calculate the distance of.
// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
//
// \return The signed distance from point to rectangle. If negative, point is
// inside the rectangle.
lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec4 radius)
{
radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
radius.x = (point.y > 0.0) ? radius.x : radius.y;
lowp vec2 d = abs(point) - rect + radius.x;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
}
/*********************
Operators
*********************/
// Convert a distance field to an annular (hollow) distance field.
//
// \param sdf The result of an sdf shape to convert.
// \param thickness The thickness of the resulting shape.
//
// \return The value of sdf modified to an annular shape.
lowp float sdf_annular(in lowp float sdf, in lowp float thickness)
{
return abs(sdf) - thickness;
}
// Union two sdf shapes together.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
//
// \return The union of sdf1 and sdf2, that is, the distance to both sdf1 and
// sdf2.
lowp float sdf_union(in lowp float sdf1, in lowp float sdf2)
{
return min(sdf1, sdf2);
}
// Subtract two sdf shapes.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
//
// \return sdf1 with sdf2 subtracted from it.
lowp float sdf_subtract(in lowp float sdf1, in lowp float sdf2)
{
return max(sdf1, -sdf2);
}
// Intersect two sdf shapes.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
//
// \return The intersection between sdf1 and sdf2, that is, the area where both
// sdf1 and sdf2 provide the same distance value.
lowp float sdf_intersect(in lowp float sdf1, in lowp float sdf2)
{
return max(sdf1, sdf2);
}
// Smoothly intersect two sdf shapes.
//
// \param sdf1 The first sdf shape.
// \param sdf2 The second sdf shape.
// \param smoothing The amount of smoothing to apply.
//
// \return A smoothed version of the intersect operation.
lowp float sdf_intersect_smooth(in lowp float sdf1, in lowp float sdf2, in lowp float smoothing)
{
lowp float h = clamp(0.5 - 0.5 * (sdf1 - sdf2) / smoothing, 0.0, 1.0);
return mix(sdf1, sdf2, h) + smoothing * h * (1.0 - h);
}
// Round an sdf shape.
//
// \param sdf The sdf shape to round.
// \param amount The amount of rounding to apply.
//
// \return The rounded shape of sdf.
// Note that rounding happens by basically selecting an isoline of sdf,
// therefore, the resulting shape may be larger than the input shape.
lowp float sdf_round(in lowp float sdf, in lowp float amount)
{
return sdf - amount;
}
// Convert an sdf shape to an outline of its shape.
//
// \param sdf The sdf shape to turn into an outline.
//
// \return The outline of sdf.
lowp float sdf_outline(in lowp float sdf)
{
return abs(sdf);
}
/********************
Convenience
********************/
// A constant to represent a "null" value of an sdf.
//
// Since 0 is a point exactly on the outline of an sdf shape, and negative
// values are inside the shape, this uses a very large positive constant to
// indicate a value that is really far away from the actual sdf shape.
const lowp float sdf_null = 99999.0;
// A constant for a default level of smoothing when rendering an sdf.
//
// This
const lowp float sdf_default_smoothing = 0.625;
// Render an sdf shape alpha-blended onto an existing color.
//
// This is an overload of sdf_render(float, vec4, vec4) that allows specifying a
// blending amount and a smoothing amount.
//
// \param alpha The alpha to use for blending.
// \param smoothing The amount of smoothing to apply to the sdf.
//
lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float alpha, in lowp float smoothing)
{
return mix(sourceColor, sdfColor, alpha * (1.0 - clamp(sdf * 300.0, 0.0, 1.0)));
}
// Render an sdf shape.
//
// This will render the sdf shape on top of whatever source color is input,
// making sure to apply smoothing if desired.
//
// \param sdf The sdf shape to render.
// \param sourceColor The source color to render on top of.
// \param sdfColor The color to use for rendering the sdf shape.
//
// \return sourceColor with the sdf shape rendered on top.
lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor)
{
return sdf_render(sdf, sourceColor, sdfColor, 1.0, sdf_default_smoothing);
}
// Render an sdf shape.
//
// This is an overload of sdf_render(float, vec4, vec4) that allows specifying a
// smoothing amount.
//
// \param smoothing The amount of smoothing to apply to the sdf.
//
lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float smoothing)
{
return sdf_render(sdf, sourceColor, sdfColor, 1.0, smoothing);
}

View File

@ -1,26 +0,0 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/org/kde/kirigami/shaders">
<file>header_es.glsl</file>
<file>header_desktop.glsl</file>
<file>header_desktop_core.glsl</file>
<file>sdf.glsl</file>
<file>sdf_lowpower.glsl</file>
<file alias="sdf_core.glsl">sdf.glsl</file>
<file>shadowedrectangle.vert</file>
<file alias="shadowedrectangle_core.vert">shadowedrectangle.vert</file>
<file>shadowedrectangle.frag</file>
<file>shadowedrectangle_lowpower.frag</file>
<file alias="shadowedrectangle_core.frag">shadowedrectangle.frag</file>
<file>shadowedborderrectangle.frag</file>
<file>shadowedborderrectangle_lowpower.frag</file>
<file alias="shadowedborderrectangle_core.frag">shadowedborderrectangle.frag</file>
<file>shadowedtexture.frag</file>
<file>shadowedtexture_lowpower.frag</file>
<file alias="shadowedtexture_core.frag">shadowedtexture.frag</file>
<file>shadowedbordertexture.frag</file>
<file>shadowedbordertexture_lowpower.frag</file>
<file alias="shadowedbordertexture_core.frag">shadowedbordertexture.frag</file>
</qresource>
</RCC>

View File

@ -1,73 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
// In addition it renders a border around it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
uniform lowp float borderWidth;
uniform lowp vec4 borderColor;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#endif
const lowp float minimum_shadow_radius = 0.05;
void main()
{
// Scaling factor that is the inverse of the amount of scaling applied to the geometry.
lowp float inverse_scale = 1.0 / (1.0 + size + length(offset) * 2.0);
// Correction factor to round the corners of a larger shadow.
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp vec4 size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
lowp vec4 shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, shadow_radius * inverse_scale);
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Scale corrected corner radius
lowp vec4 corner_radius = radius * inverse_scale;
// Calculate the outer rectangle distance field.
lowp float outer_rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, corner_radius);
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
col = sdf_render(outer_rect, col, vec4(0.0));
// Then, render it again but this time with the proper color and properly alpha blended.
col = sdf_render(outer_rect, col, borderColor);
// The inner rectangle distance field is the outer reduces by twice the border size.
lowp float inner_rect = outer_rect + (borderWidth * inverse_scale) * 2.0;
// Like above, but this time cut out the inner rectangle.
col = sdf_render(inner_rect, col, vec4(0.0));
// Finally, render the inner rectangle.
col = sdf_render(inner_rect, col, color);
out_color = col * opacity;
}

View File

@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This is a version of shadowedborderrectangle.frag for extremely low powered
// hardware (PinePhone). It does not draw a shadow and also eliminates alpha
// blending.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
uniform lowp float borderWidth;
uniform lowp vec4 borderColor;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#define texture texture2D
#endif
void main()
{
lowp vec4 col = vec4(0.0);
// Calculate the outer rectangle distance field.
lowp float outer_rect = sdf_rounded_rectangle(uv, aspect, radius);
col = sdf_render(outer_rect, col, vec4(borderColor.rgb, 1.0));
// The inner distance field is the outer reduced by border width.
lowp float inner_rect = outer_rect + borderWidth * 2.0;
col = sdf_render(inner_rect, col, vec4(color.rgb, 1.0));
out_color = col * opacity;
}

View File

@ -1,83 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
// In addition it renders a border around it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
uniform lowp float borderWidth;
uniform lowp vec4 borderColor;
uniform sampler2D textureSource;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#define texture texture2D
#endif
const lowp float minimum_shadow_radius = 0.05;
void main()
{
// Scaling factor that is the inverse of the amount of scaling applied to the geometry.
lowp float inverse_scale = 1.0 / (1.0 + size + length(offset) * 2.0);
// Correction factor to round the corners of a larger shadow.
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp vec4 size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
lowp vec4 shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, shadow_radius * inverse_scale);
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Scale corrected corner radius
lowp vec4 corner_radius = radius * inverse_scale;
// Calculate the outer rectangle distance field.
lowp float outer_rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, corner_radius);
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
col = sdf_render(outer_rect, col, vec4(0.0));
// Then, render it again but this time with the proper color and properly alpha blended.
col = sdf_render(outer_rect, col, borderColor);
// The inner rectangle distance field is the outer reduced by twice the border width.
lowp float inner_rect = outer_rect + (borderWidth * inverse_scale) * 2.0;
// Like above, but this time cut out the inner rectangle.
col = sdf_render(inner_rect, col, vec4(0.0));
// Finally, render the inner rectangle.
col = sdf_render(inner_rect, col, color);
// Slightly increase the size of the inner rectangle, to avoid issues with anti-aliasing.
inner_rect = inner_rect - 0.005;
// Sample the texture, then blend it on top of the background color.
lowp vec2 texture_uv = ((uv / aspect) + (1.0 * inverse_scale)) / (2.0 * inverse_scale);
lowp vec4 texture_color = texture(textureSource, texture_uv);
col = sdf_render(inner_rect, col, texture_color, texture_color.a, sdf_default_smoothing / 2.0);
out_color = col * opacity;
}

View File

@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
// In addition it renders a border around it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
uniform lowp float borderWidth;
uniform lowp vec4 borderColor;
uniform sampler2D textureSource;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#define texture texture2D
#endif
const lowp float minimum_shadow_radius = 0.05;
void main()
{
lowp vec4 col = vec4(0.0);
// Calculate the outer rectangle distance field.
lowp float outer_rect = sdf_rounded_rectangle(uv, aspect, radius);
// Render it, cancelling out any alpha components.
col = sdf_render(outer_rect, col, vec4(borderColor.rgb, 1.0));
// Inner rectangle distance field equals outer reduced by twice the border width
lowp float inner_rect = outer_rect + borderWidth * 2.0;
// Sample the texture, then render it on top of the background color.
lowp vec2 texture_uv = ((uv / aspect) + 1.0) / 2.0;
lowp vec4 texture_color = vec4(texture(textureSource, texture_uv).rgb, 1.0);
col = sdf_render(inner_rect, col, texture_color, texture_color.a, sdf_default_smoothing / 2.0);
out_color = col * opacity;
}

View File

@ -1,58 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#endif
const lowp float minimum_shadow_radius = 0.05;
void main()
{
// Scaling factor that is the inverse of the amount of scaling applied to the geometry.
lowp float inverse_scale = 1.0 / (1.0 + size + length(offset) * 2.0);
// Correction factor to round the corners of a larger shadow.
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp vec4 size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
lowp vec4 shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, shadow_radius * inverse_scale);
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Calculate the main rectangle distance field.
lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, radius * inverse_scale);
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
col = sdf_render(rect, col, vec4(0.0));
// Then, render it again but this time with the proper color and properly alpha blended.
col = sdf_render(rect, col, color);
out_color = col * opacity;
}

View File

@ -1,23 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
uniform highp mat4 matrix;
uniform lowp vec2 aspect;
#ifdef CORE_PROFILE
in highp vec4 in_vertex;
in mediump vec2 in_uv;
out mediump vec2 uv;
#else
attribute highp vec4 in_vertex;
attribute mediump vec2 in_uv;
varying mediump vec2 uv;
#endif
void main() {
uv = (-1.0 + 2.0 * in_uv) * aspect;
gl_Position = matrix * in_vertex;
}

View File

@ -1,39 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This is a version of shadowedrectangle.frag meant for very low power hardware
// (PinePhone). It does not render a shadow and does not do alpha blending.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#endif
void main()
{
lowp vec4 col = vec4(0.0);
// Calculate the main rectangle distance field.
lowp float rect = sdf_rounded_rectangle(uv, aspect, radius);
// Render it, cancelling out any alpha component.
col = sdf_render(rect, col, vec4(color.rgb, 1.0));
out_color = col * opacity;
}

View File

@ -1,66 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This shader renders a texture on top of a rectangle with rounded corners and
// a shadow below it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
uniform sampler2D textureSource;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#define texture texture2D
#endif
const lowp float minimum_shadow_radius = 0.05;
void main()
{
// Scaling factor that is the inverse of the amount of scaling applied to the geometry.
lowp float inverse_scale = 1.0 / (1.0 + size + length(offset) * 2.0);
// Correction factor to round the corners of a larger shadow.
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp vec4 size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
lowp vec4 shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, shadow_radius * inverse_scale);
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Calculate the main rectangle distance field.
lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, radius * inverse_scale);
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
col = sdf_render(rect, col, vec4(0.0));
// Then, render it again but this time with the proper color.
col = sdf_render(rect, col, color);
// Sample the texture, then blend it on top of the background color.
lowp vec2 texture_uv = ((uv / aspect) + (1.0 * inverse_scale)) / (2.0 * inverse_scale);
lowp vec4 texture_color = texture(textureSource, texture_uv);
col = sdf_render(rect, col, texture_color, texture_color.a, sdf_default_smoothing);
out_color = col * opacity;
}

View File

@ -1,44 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// See sdf.glsl for the SDF related functions.
// This shader renders a texture on top of a rectangle with rounded corners and
// a shadow below it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
uniform sampler2D textureSource;
#ifdef CORE_PROFILE
in lowp vec2 uv;
out lowp vec4 out_color;
#else
varying lowp vec2 uv;
#define out_color gl_FragColor
#define texture texture2D
#endif
void main()
{
lowp vec4 col = vec4(0.0);
// Calculate the main rectangle distance field.
lowp float rect = sdf_rounded_rectangle(uv, aspect, radius);
// Sample the texture, then render it, without any alpha blending.
lowp vec2 texture_uv = ((uv / aspect) + 1.0) / 2.0;
lowp vec4 texture_color = vec4(texture(textureSource, texture_uv).rgb, 1.0);
col = sdf_render(rect, col, texture_color);
out_color = col * opacity;
}

View File

@ -1,67 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "shadowedborderrectanglematerial.h"
#include <QOpenGLContext>
QSGMaterialType ShadowedBorderRectangleMaterial::staticType;
ShadowedBorderRectangleMaterial::ShadowedBorderRectangleMaterial()
{
setFlag(QSGMaterial::Blending, true);
}
QSGMaterialShader* ShadowedBorderRectangleMaterial::createShader() const
{
return new ShadowedBorderRectangleShader{shaderType};
}
QSGMaterialType* ShadowedBorderRectangleMaterial::type() const
{
return &staticType;
}
int ShadowedBorderRectangleMaterial::compare(const QSGMaterial *other) const
{
auto material = static_cast<const ShadowedBorderRectangleMaterial *>(other);
auto result = ShadowedRectangleMaterial::compare(other);
if (result == 0
&& material->borderColor == borderColor
&& qFuzzyCompare(material->borderWidth, borderWidth)
) {
return 0;
}
return result;
}
ShadowedBorderRectangleShader::ShadowedBorderRectangleShader(ShadowedRectangleMaterial::ShaderType shaderType)
: ShadowedRectangleShader(shaderType)
{
setShader(shaderType, QStringLiteral("shadowedborderrectangle"));
}
void ShadowedBorderRectangleShader::initialize()
{
ShadowedRectangleShader::initialize();
m_borderWidthLocation = program()->uniformLocation("borderWidth");
m_borderColorLocation = program()->uniformLocation("borderColor");
}
void ShadowedBorderRectangleShader::updateState(const QSGMaterialShader::RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial)
{
ShadowedRectangleShader::updateState(state, newMaterial, oldMaterial);
auto p = program();
if (!oldMaterial || newMaterial->compare(oldMaterial) != 0 ) {
auto material = static_cast<ShadowedBorderRectangleMaterial *>(newMaterial);
p->setUniformValue(m_borderWidthLocation, material->borderWidth);
p->setUniformValue(m_borderColorLocation, material->borderColor);
}
}

View File

@ -1,43 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include "shadowedrectanglematerial.h"
/**
* A material rendering a rectangle with a shadow and a border.
*
* This material uses a distance field shader to render a rectangle with a
* shadow below it, optionally with rounded corners and a border.
*/
class ShadowedBorderRectangleMaterial : public ShadowedRectangleMaterial
{
public:
ShadowedBorderRectangleMaterial();
QSGMaterialShader* createShader() const override;
QSGMaterialType* type() const override;
int compare(const QSGMaterial* other) const override;
float borderWidth = 0.0;
QColor borderColor = Qt::black;
static QSGMaterialType staticType;
};
class ShadowedBorderRectangleShader : public ShadowedRectangleShader
{
public:
ShadowedBorderRectangleShader(ShadowedRectangleMaterial::ShaderType shaderType);
void initialize() override;
void updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
int m_borderWidthLocation = -1;
int m_borderColorLocation = -1;
};

View File

@ -1,114 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "shadowedrectanglematerial.h"
#include <QOpenGLContext>
QSGMaterialType ShadowedRectangleMaterial::staticType;
ShadowedRectangleMaterial::ShadowedRectangleMaterial()
{
setFlag(QSGMaterial::Blending, true);
}
QSGMaterialShader* ShadowedRectangleMaterial::createShader() const
{
return new ShadowedRectangleShader{shaderType};
}
QSGMaterialType* ShadowedRectangleMaterial::type() const
{
return &staticType;
}
int ShadowedRectangleMaterial::compare(const QSGMaterial *other) const
{
auto material = static_cast<const ShadowedRectangleMaterial *>(other);
if (material->color == color
&& material->shadowColor == shadowColor
&& material->offset == offset
&& material->aspect == aspect
&& qFuzzyCompare(material->size, size)
&& qFuzzyCompare(material->radius, radius)) {
return 0;
}
return QSGMaterial::compare(other);
}
ShadowedRectangleShader::ShadowedRectangleShader(ShadowedRectangleMaterial::ShaderType shaderType)
{
setShader(shaderType, QStringLiteral("shadowedrectangle"));
}
const char *const * ShadowedRectangleShader::attributeNames() const
{
static char const *const names[] = {"in_vertex", "in_uv", nullptr};
return names;
}
void ShadowedRectangleShader::initialize()
{
QSGMaterialShader::initialize();
m_matrixLocation = program()->uniformLocation("matrix");
m_aspectLocation = program()->uniformLocation("aspect");
m_opacityLocation = program()->uniformLocation("opacity");
m_sizeLocation = program()->uniformLocation("size");
m_radiusLocation = program()->uniformLocation("radius");
m_colorLocation = program()->uniformLocation("color");
m_shadowColorLocation = program()->uniformLocation("shadowColor");
m_offsetLocation = program()->uniformLocation("offset");
}
void ShadowedRectangleShader::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 || newMaterial->compare(oldMaterial) != 0 ) {
auto material = static_cast<ShadowedRectangleMaterial *>(newMaterial);
p->setUniformValue(m_aspectLocation, material->aspect);
p->setUniformValue(m_sizeLocation, material->size);
p->setUniformValue(m_radiusLocation, material->radius);
p->setUniformValue(m_colorLocation, material->color);
p->setUniformValue(m_shadowColorLocation, material->shadowColor);
p->setUniformValue(m_offsetLocation, material->offset);
}
}
void ShadowedRectangleShader::setShader(ShadowedRectangleMaterial::ShaderType shaderType, const QString& shader)
{
auto header = QOpenGLContext::currentContext()->isOpenGLES() ? QStringLiteral("header_es.glsl") : QStringLiteral("header_desktop.glsl");
auto shaderRoot = QStringLiteral(":/org/kde/kirigami/shaders/");
setShaderSourceFiles(QOpenGLShader::Vertex, {
shaderRoot + header,
shaderRoot + QStringLiteral("shadowedrectangle.vert")
});
QString shaderFile = shader + QStringLiteral(".frag");
auto sdfFile = QStringLiteral("sdf.glsl");
if (shaderType == ShadowedRectangleMaterial::ShaderType::LowPower) {
shaderFile = shader + QStringLiteral("_lowpower.frag");
sdfFile = QStringLiteral("sdf_lowpower.glsl");
}
setShaderSourceFiles(QOpenGLShader::Fragment, {
shaderRoot + header,
shaderRoot + sdfFile,
shaderRoot + shaderFile
});
}

View File

@ -1,67 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <QSGMaterial>
#include <QSGMaterialShader>
#include <QColor>
/**
* A material rendering a rectangle with a shadow.
*
* This material uses a distance field shader to render a rectangle with a
* shadow below it, optionally with rounded corners.
*/
class ShadowedRectangleMaterial : public QSGMaterial
{
public:
enum class ShaderType
{
Standard,
LowPower
};
ShadowedRectangleMaterial();
QSGMaterialShader* createShader() const override;
QSGMaterialType* type() const override;
int compare( const QSGMaterial* other ) const override;
QVector2D aspect = QVector2D{1.0, 1.0};
float size = 0.0;
QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0};
QColor color = Qt::white;
QColor shadowColor = Qt::black;
QVector2D offset;
ShaderType shaderType = ShaderType::Standard;
static QSGMaterialType staticType;
};
class ShadowedRectangleShader : public QSGMaterialShader
{
public:
ShadowedRectangleShader( ShadowedRectangleMaterial::ShaderType shaderType );
char const* const* attributeNames() const override;
void initialize() override;
void updateState( const QSGMaterialShader::RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override;
protected:
void setShader( ShadowedRectangleMaterial::ShaderType shaderType, const QString& shader );
private:
int m_matrixLocation = -1;
int m_opacityLocation = -1;
int m_aspectLocation = -1;
int m_sizeLocation = -1;
int m_radiusLocation = -1;
int m_colorLocation = -1;
int m_shadowColorLocation = -1;
int m_offsetLocation = -1;
};

View File

@ -1,209 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "shadowedrectanglenode.h"
#include "shadowedrectanglematerial.h"
#include "shadowedborderrectanglematerial.h"
QColor premultiply(const QColor &color)
{
return QColor::fromRgbF(
color.redF() * color.alphaF(),
color.greenF() * color.alphaF(),
color.blueF() * color.alphaF(),
color.alphaF()
);
}
ShadowedRectangleNode::ShadowedRectangleNode()
{
m_geometry = new QSGGeometry{QSGGeometry::defaultAttributes_TexturedPoint2D(), 4};
setGeometry(m_geometry);
setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
}
void ShadowedRectangleNode::setBorderEnabled(bool enabled)
{
// We can achieve more performant shaders by splitting the two into separate
// shaders. This requires separating the materials as well. So when
// borderWidth is increased to something where the border should be visible,
// switch to the with-border material. Otherwise use the no-border version.
if (enabled) {
if (!m_material || m_material->type() == borderlessMaterialType()) {
auto newMaterial = createBorderMaterial();
newMaterial->shaderType = m_shaderType;
setMaterial(newMaterial);
m_material = newMaterial;
m_rect = QRectF{};
markDirty(QSGNode::DirtyMaterial);
}
} else {
if (!m_material || m_material->type() == borderMaterialType()) {
auto newMaterial = createBorderlessMaterial();
newMaterial->shaderType = m_shaderType;
setMaterial(newMaterial);
m_material = newMaterial;
m_rect = QRectF{};
markDirty(QSGNode::DirtyMaterial);
}
}
}
void ShadowedRectangleNode::setRect(const QRectF& rect)
{
if (rect == m_rect) {
return;
}
m_rect = rect;
QVector2D newAspect{1.0, 1.0};
if (m_rect.width() >= m_rect.height()) {
newAspect.setX(m_rect.width() / m_rect.height());
} else {
newAspect.setY(m_rect.height() / m_rect.width());
}
if (m_material->aspect != newAspect) {
m_material->aspect = newAspect;
markDirty(QSGNode::DirtyMaterial);
m_aspect = newAspect;
}
}
void ShadowedRectangleNode::setSize(qreal size)
{
auto minDimension = std::min(m_rect.width(), m_rect.height());
float uniformSize = (size / minDimension) * 2.0;
if (!qFuzzyCompare(m_material->size, uniformSize)) {
m_material->size = uniformSize;
markDirty(QSGNode::DirtyMaterial);
m_size = size;
}
}
void ShadowedRectangleNode::setRadius(const QVector4D &radius)
{
float minDimension = std::min(m_rect.width(), m_rect.height());
auto uniformRadius = QVector4D{
std::min(radius.x() * 2.0f / minDimension, 1.0f),
std::min(radius.y() * 2.0f / minDimension, 1.0f),
std::min(radius.z() * 2.0f / minDimension, 1.0f),
std::min(radius.w() * 2.0f / minDimension, 1.0f)
};
if (m_material->radius != uniformRadius) {
m_material->radius = uniformRadius;
markDirty(QSGNode::DirtyMaterial);
m_radius = radius;
}
}
void ShadowedRectangleNode::setColor(const QColor &color)
{
auto premultiplied = premultiply(color);
if (m_material->color != premultiplied) {
m_material->color = premultiplied;
markDirty(QSGNode::DirtyMaterial);
}
}
void ShadowedRectangleNode::setShadowColor(const QColor& color)
{
auto premultiplied = premultiply(color);
if (m_material->shadowColor != premultiplied) {
m_material->shadowColor = premultiplied;
markDirty(QSGNode::DirtyMaterial);
}
}
void ShadowedRectangleNode::setOffset(const QVector2D& offset)
{
auto minDimension = std::min(m_rect.width(), m_rect.height());
auto uniformOffset = offset / minDimension;
if (m_material->offset != uniformOffset) {
m_material->offset = uniformOffset;
markDirty(QSGNode::DirtyMaterial);
m_offset = offset;
}
}
void ShadowedRectangleNode::setBorderWidth(qreal width)
{
if (m_material->type() != borderMaterialType()) {
return;
}
auto minDimension = std::min(m_rect.width(), m_rect.height());
float uniformBorderWidth = width / minDimension;
auto borderMaterial = static_cast<ShadowedBorderRectangleMaterial*>(m_material);
if (!qFuzzyCompare(borderMaterial->borderWidth, uniformBorderWidth)) {
borderMaterial->borderWidth = uniformBorderWidth;
markDirty(QSGNode::DirtyMaterial);
m_borderWidth = width;
}
}
void ShadowedRectangleNode::setBorderColor(const QColor& color)
{
if (m_material->type() != borderMaterialType()) {
return;
}
auto borderMaterial = static_cast<ShadowedBorderRectangleMaterial*>(m_material);
auto premultiplied = premultiply(color);
if (borderMaterial->borderColor != premultiplied) {
borderMaterial->borderColor = premultiplied;
markDirty(QSGNode::DirtyMaterial);
}
}
void ShadowedRectangleNode::setShaderType(ShadowedRectangleMaterial::ShaderType type)
{
m_shaderType = type;
}
void ShadowedRectangleNode::updateGeometry()
{
auto rect = m_rect;
if (m_shaderType == ShadowedRectangleMaterial::ShaderType::Standard) {
rect = rect.adjusted(-m_size * m_aspect.x(), -m_size * m_aspect.y(),
m_size * m_aspect.x(), m_size * m_aspect.y());
auto offsetLength = m_offset.length();
rect = rect.adjusted(-offsetLength * m_aspect.x(), -offsetLength * m_aspect.y(),
offsetLength * m_aspect.x(), offsetLength * m_aspect.y());
}
QSGGeometry::updateTexturedRectGeometry(m_geometry, rect, QRectF{0.0, 0.0, 1.0, 1.0});
markDirty(QSGNode::DirtyGeometry);
}
ShadowedRectangleMaterial *ShadowedRectangleNode::createBorderlessMaterial()
{
return new ShadowedRectangleMaterial{};
}
ShadowedBorderRectangleMaterial *ShadowedRectangleNode::createBorderMaterial()
{
return new ShadowedBorderRectangleMaterial{};
}
QSGMaterialType *ShadowedRectangleNode::borderlessMaterialType()
{
return &ShadowedRectangleMaterial::staticType;
}
QSGMaterialType *ShadowedRectangleNode::borderMaterialType()
{
return &ShadowedBorderRectangleMaterial::staticType;
}

View File

@ -1,79 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <QSGGeometryNode>
#include <QColor>
#include <QVector2D>
#include <QVector4D>
#include "shadowedrectanglematerial.h"
struct QSGMaterialType;
class ShadowedBorderRectangleMaterial;
/**
* Scene graph node for a shadowed rectangle.
*
* This node will set up the geometry and materials for a shadowed rectangle,
* optionally with rounded corners.
*
* \note You must call updateGeometry() after setting properties of this node,
* otherwise the node's state will not correctly reflect all the properties.
*
* \sa ShadowedRectangle
*/
class ShadowedRectangleNode : public QSGGeometryNode
{
public:
ShadowedRectangleNode();
/**
* Set whether to draw a border.
*
* Note that this will switch between a material with or without border.
* This means this needs to be called before any other setters.
*/
void setBorderEnabled(bool enabled);
void setRect(const QRectF &rect);
void setSize(qreal size);
void setRadius(const QVector4D &radius);
void setColor(const QColor &color);
void setShadowColor(const QColor &color);
void setOffset(const QVector2D &offset);
void setBorderWidth(qreal width);
void setBorderColor(const QColor &color);
void setShaderType(ShadowedRectangleMaterial::ShaderType type);
/**
* Update the geometry for this node.
*
* This is done as an explicit step to avoid the geometry being recreated
* multiple times while updating properties.
*/
void updateGeometry();
protected:
virtual ShadowedRectangleMaterial *createBorderlessMaterial();
virtual ShadowedBorderRectangleMaterial *createBorderMaterial();
virtual QSGMaterialType* borderMaterialType();
virtual QSGMaterialType* borderlessMaterialType();
QSGGeometry *m_geometry;
ShadowedRectangleMaterial *m_material = nullptr;
ShadowedRectangleMaterial::ShaderType m_shaderType = ShadowedRectangleMaterial::ShaderType::Standard;
private:
QRectF m_rect;
qreal m_size = 0.0;
QVector4D m_radius = QVector4D{0.0, 0.0, 0.0, 0.0};
QVector2D m_offset = QVector2D{0.0, 0.0};
QVector2D m_aspect = QVector2D{1.0, 1.0};
qreal m_borderWidth = 0.0;
QColor m_borderColor;
};

View File

@ -1,368 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "shadowedrectangle.h"
#include <QQuickWindow>
#include "scenegraph/shadowedrectanglenode.h"
#include "scenegraph/paintedrectangleitem.h"
BorderGroup::BorderGroup( QObject* parent )
: QObject( parent )
{
}
qreal BorderGroup::width() const
{
return m_width;
}
void BorderGroup::setWidth( qreal newWidth )
{
if( newWidth == m_width )
{
return;
}
m_width = newWidth;
Q_EMIT changed();
}
QColor BorderGroup::color() const
{
return m_color;
}
void BorderGroup::setColor( const QColor& newColor )
{
if( newColor == m_color )
{
return;
}
m_color = newColor;
Q_EMIT changed();
}
ShadowGroup::ShadowGroup( QObject* parent )
: QObject( parent )
{
}
qreal ShadowGroup::size() const
{
return m_size;
}
void ShadowGroup::setSize( qreal newSize )
{
if( newSize == m_size )
{
return;
}
m_size = newSize;
Q_EMIT changed();
}
qreal ShadowGroup::xOffset() const
{
return m_xOffset;
}
void ShadowGroup::setXOffset( qreal newXOffset )
{
if( newXOffset == m_xOffset )
{
return;
}
m_xOffset = newXOffset;
Q_EMIT changed();
}
qreal ShadowGroup::yOffset() const
{
return m_yOffset;
}
void ShadowGroup::setYOffset( qreal newYOffset )
{
if( newYOffset == m_yOffset )
{
return;
}
m_yOffset = newYOffset;
Q_EMIT changed();
}
QColor ShadowGroup::color() const
{
return m_color;
}
void ShadowGroup::setColor( const QColor& newColor )
{
if( newColor == m_color )
{
return;
}
m_color = newColor;
Q_EMIT changed();
}
CornersGroup::CornersGroup( QObject* parent )
: QObject( parent )
{
}
qreal CornersGroup::topLeft() const
{
return m_topLeft;
}
void CornersGroup::setTopLeft( qreal newTopLeft )
{
if( newTopLeft == m_topLeft )
{
return;
}
m_topLeft = newTopLeft;
Q_EMIT changed();
}
qreal CornersGroup::topRight() const
{
return m_topRight;
}
void CornersGroup::setTopRight( qreal newTopRight )
{
if( newTopRight == m_topRight )
{
return;
}
m_topRight = newTopRight;
Q_EMIT changed();
}
qreal CornersGroup::bottomLeft() const
{
return m_bottomLeft;
}
void CornersGroup::setBottomLeft( qreal newBottomLeft )
{
if( newBottomLeft == m_bottomLeft )
{
return;
}
m_bottomLeft = newBottomLeft;
Q_EMIT changed();
}
qreal CornersGroup::bottomRight() const
{
return m_bottomRight;
}
void CornersGroup::setBottomRight( qreal newBottomRight )
{
if( newBottomRight == m_bottomRight )
{
return;
}
m_bottomRight = newBottomRight;
Q_EMIT changed();
}
QVector4D CornersGroup::toVector4D( float all ) const
{
return QVector4D
{
m_bottomRight < 0.0 ? all : m_bottomRight,
m_topRight < 0.0 ? all : m_topRight,
m_bottomLeft < 0.0 ? all : m_bottomLeft,
m_topLeft < 0.0 ? all : m_topLeft
};
}
ShadowedRectangle::ShadowedRectangle( QQuickItem* parentItem )
: QQuickItem( parentItem )
, m_border( new BorderGroup )
, m_shadow( new ShadowGroup )
, m_corners( new CornersGroup )
{
setFlag( QQuickItem::ItemHasContents, true );
connect( m_border.get(), &BorderGroup::changed, this, &ShadowedRectangle::update );
connect( m_shadow.get(), &ShadowGroup::changed, this, &ShadowedRectangle::update );
connect( m_corners.get(), &CornersGroup::changed, this, &ShadowedRectangle::update );
}
ShadowedRectangle::~ShadowedRectangle()
{
}
BorderGroup* ShadowedRectangle::border() const
{
return m_border.get();
}
ShadowGroup* ShadowedRectangle::shadow() const
{
return m_shadow.get();
}
CornersGroup* ShadowedRectangle::corners() const
{
return m_corners.get();
}
qreal ShadowedRectangle::radius() const
{
return m_radius;
}
void ShadowedRectangle::setRadius( qreal newRadius )
{
if( newRadius == m_radius )
{
return;
}
m_radius = newRadius;
if( !isSoftwareRendering() )
{
update();
}
Q_EMIT radiusChanged();
}
QColor ShadowedRectangle::color() const
{
return m_color;
}
void ShadowedRectangle::setColor( const QColor& newColor )
{
if( newColor == m_color )
{
return;
}
m_color = newColor;
if( !isSoftwareRendering() )
{
update();
}
Q_EMIT colorChanged();
}
void ShadowedRectangle::componentComplete()
{
QQuickItem::componentComplete();
checkSoftwareItem();
}
bool ShadowedRectangle::isSoftwareRendering() const
{
return false;
}
PaintedRectangleItem* ShadowedRectangle::softwareItem() const
{
return m_softwareItem;
}
void ShadowedRectangle::itemChange( QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& value )
{
if( change == QQuickItem::ItemSceneChange && value.window )
{
checkSoftwareItem();
//TODO: only conditionally emit?
Q_EMIT softwareRenderingChanged();
}
}
QSGNode* ShadowedRectangle::updatePaintNode( QSGNode* node, QQuickItem::UpdatePaintNodeData* data )
{
Q_UNUSED( data );
auto shadowNode = static_cast<ShadowedRectangleNode*>( node );
if( !shadowNode )
{
shadowNode = new ShadowedRectangleNode{};
// Cache lowPower state so we only execute the full check once.
static bool lowPower = QByteArrayList{"1", "true"}.contains( qgetenv( "KIRIGAMI_LOWPOWER_HARDWARE" ).toLower() );
if( lowPower )
{
shadowNode->setShaderType( ShadowedRectangleMaterial::ShaderType::LowPower );
}
}
shadowNode->setBorderEnabled( m_border->isEnabled() );
shadowNode->setRect( boundingRect() );
shadowNode->setSize( m_shadow->size() );
shadowNode->setRadius( m_corners->toVector4D( m_radius ) );
shadowNode->setOffset( QVector2D{float( m_shadow->xOffset() ), float( m_shadow->yOffset() )} );
shadowNode->setColor( m_color );
shadowNode->setShadowColor( m_shadow->color() );
shadowNode->setBorderWidth( m_border->width() );
shadowNode->setBorderColor( m_border->color() );
shadowNode->updateGeometry();
return shadowNode;
}
void ShadowedRectangle::checkSoftwareItem()
{
if( !m_softwareItem && isSoftwareRendering() )
{
m_softwareItem = new PaintedRectangleItem{this};
// The software item is added as a "normal" child item, this means it
// will be part of the normal item sort order. Since there is no way to
// control the ordering of children, just make sure to have a very low Z
// value for the child, to force it to be the lowest item.
m_softwareItem->setZ( -99.0 );
auto updateItem = [this]()
{
auto borderWidth = m_border->width();
auto rect = boundingRect().adjusted( -borderWidth / 2, -borderWidth / 2, borderWidth / 2, borderWidth / 2 );
m_softwareItem->setX( -borderWidth / 2 );
m_softwareItem->setY( -borderWidth / 2 );
m_softwareItem->setSize( rect.size() );
m_softwareItem->setColor( m_color );
m_softwareItem->setRadius( m_radius );
m_softwareItem->setBorderWidth( borderWidth );
m_softwareItem->setBorderColor( m_border->color() );
};
updateItem();
connect( this, &ShadowedRectangle::widthChanged, m_softwareItem, updateItem );
connect( this, &ShadowedRectangle::heightChanged, m_softwareItem, updateItem );
connect( this, &ShadowedRectangle::colorChanged, m_softwareItem, updateItem );
connect( this, &ShadowedRectangle::radiusChanged, m_softwareItem, updateItem );
connect( m_border.get(), &BorderGroup::changed, m_softwareItem, updateItem );
setFlag( QQuickItem::ItemHasContents, false );
}
}

View File

@ -1,256 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <memory>
#include <QQuickItem>
class PaintedRectangleItem;
/**
* Grouped property for rectangle border.
*/
class BorderGroup : public QObject
{
Q_OBJECT
/**
* The width of the border in pixels.
*
* Default is 0.
*/
Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY changed)
/**
* The color of the border.
*
* Full RGBA colors are supported. The default is fully opaque black.
*/
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed)
public:
explicit BorderGroup(QObject *parent = nullptr);
qreal width() const;
void setWidth(qreal newWidth);
QColor color() const;
void setColor(const QColor &newColor);
Q_SIGNAL void changed();
inline bool isEnabled() const
{
return !qFuzzyIsNull(m_width);
}
private:
qreal m_width = 0.0;
QColor m_color = Qt::black;
};
/**
* Grouped property for rectangle shadow.
*/
class ShadowGroup : public QObject
{
Q_OBJECT
/**
* The size of the shadow.
*
* This is the approximate size of the shadow in pixels. However, due to falloff
* the actual shadow size can differ. The default is 0, which means no shadow will
* be rendered.
*/
Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY changed)
/**
* Offset of the shadow on the X axis.
*
* In pixels. The default is 0.
*/
Q_PROPERTY(qreal xOffset READ xOffset WRITE setXOffset NOTIFY changed)
/**
* Offset of the shadow on the Y axis.
*
* In pixels. The default is 0.
*/
Q_PROPERTY(qreal yOffset READ yOffset WRITE setYOffset NOTIFY changed)
/**
* The color of the shadow.
*
* Full RGBA colors are supported. The default is fully opaque black.
*/
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed)
public:
explicit ShadowGroup(QObject *parent = nullptr);
qreal size() const;
void setSize(qreal newSize);
qreal xOffset() const;
void setXOffset(qreal newXOffset);
qreal yOffset() const;
void setYOffset(qreal newYOffset);
QColor color() const;
void setColor(const QColor &newShadowColor);
Q_SIGNAL void changed();
private:
qreal m_size = 0.0;
qreal m_xOffset = 0.0;
qreal m_yOffset = 0.0;
QColor m_color = Qt::black;
};
/**
* Grouped property for corner radius.
*/
class CornersGroup : public QObject
{
Q_OBJECT
/**
* The radius of the top-left corner.
*
* In pixels. Defaults to -1, which indicates this value should not be used.
*/
Q_PROPERTY(qreal topLeftRadius READ topLeft WRITE setTopLeft NOTIFY changed)
/**
* The radius of the top-right corner.
*
* In pixels. Defaults to -1, which indicates this value should not be used.
*/
Q_PROPERTY(qreal topRightRadius READ topRight WRITE setTopRight NOTIFY changed)
/**
* The radius of the bottom-left corner.
*
* In pixels. Defaults to -1, which indicates this value should not be used.
*/
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeft WRITE setBottomLeft NOTIFY changed)
/**
* The radius of the bottom-right corner.
*
* In pixels. Defaults to -1, which indicates this value should not be used.
*/
Q_PROPERTY(qreal bottomRightRadius READ bottomRight WRITE setBottomRight NOTIFY changed)
public:
explicit CornersGroup(QObject *parent = nullptr);
qreal topLeft() const;
void setTopLeft(qreal newTopLeft);
qreal topRight() const;
void setTopRight(qreal newTopRight);
qreal bottomLeft() const;
void setBottomLeft(qreal newBottomLeft);
qreal bottomRight() const;
void setBottomRight(qreal newBottomRight);
Q_SIGNAL void changed();
QVector4D toVector4D(float all) const;
private:
float m_topLeft = -1.0;
float m_topRight = -1.0;
float m_bottomLeft = -1.0;
float m_bottomRight = -1.0;
};
/**
* A rectangle with a shadow.
*
* This item will render a rectangle, with a shadow below it. The rendering is done
* using distance fields, which provide greatly improved performance. The shadow is
* rendered outside of the item's bounds, so the item's width and height are the
* rectangle's width and height.
*
* @since 5.69 / 2.12
*/
class ShadowedRectangle : public QQuickItem
{
Q_OBJECT
/**
* Corner radius of the rectangle.
*
* This is the amount of rounding to apply to all of the rectangle's
* corners, in pixels. Individual corners can have a different radius, see
* \property corners.
*
* The default is 0.
*/
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
/**
* The color of the rectangle.
*
* Full RGBA colors are supported. The default is fully opaque white.
*/
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
/**
* Border properties.
*
* \sa BorderGroup
*/
Q_PROPERTY(BorderGroup *border READ border CONSTANT)
/**
* Shadow properties.
*
* \sa ShadowGroup
*/
Q_PROPERTY(ShadowGroup *shadow READ shadow CONSTANT)
/**
* Corner radius.
*
* Note that the values from this group override \property radius for the
* corner they affect.
*
* \sa CornerGroup
*/
Q_PROPERTY(CornersGroup *corners READ corners CONSTANT)
Q_PROPERTY(bool softwareRendering READ isSoftwareRendering NOTIFY softwareRenderingChanged)
public:
ShadowedRectangle(QQuickItem *parent = nullptr);
~ShadowedRectangle() override;
BorderGroup *border() const;
ShadowGroup *shadow() const;
CornersGroup *corners() const;
qreal radius() const;
void setRadius(qreal newRadius);
Q_SIGNAL void radiusChanged();
QColor color() const;
void setColor(const QColor &newColor);
Q_SIGNAL void colorChanged();
void componentComplete() override;
bool isSoftwareRendering() const;
Q_SIGNALS:
void softwareRenderingChanged();
protected:
PaintedRectangleItem *softwareItem() const;
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override;
private:
void checkSoftwareItem();
const std::unique_ptr<BorderGroup> m_border;
const std::unique_ptr<ShadowGroup> m_shadow;
const std::unique_ptr<CornersGroup> m_corners;
qreal m_radius = 0.0;
QColor m_color = Qt::white;
PaintedRectangleItem *m_softwareItem = nullptr;
};

View File

@ -0,0 +1,257 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#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_matrixId = -1;
int m_opacityId = -1;
int m_aspectId = -1;
int m_blurExtentId = -1;
int m_radiusId = -1;
int m_colorId = -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 blurExtent = 0.0;
QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0};
QColor color = Qt::black;
};
Shader::Shader()
{
const QString root( ":/iotdashboard/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_matrixId = p->uniformLocation( "matrix" );
m_aspectId = p->uniformLocation( "aspect" );
m_opacityId = p->uniformLocation( "opacity" );
m_blurExtentId = p->uniformLocation( "blurExtent" );
m_radiusId = p->uniformLocation( "radius" );
m_colorId = p->uniformLocation( "color" );
}
void Shader::updateState( const QSGMaterialShader::RenderState& state,
QSGMaterial* newMaterial, QSGMaterial* oldMaterial )
{
auto p = program();
if ( state.isMatrixDirty() )
p->setUniformValue( m_matrixId, state.combinedMatrix() );
if ( state.isOpacityDirty() )
p->setUniformValue( m_opacityId, state.opacity() );
if ( oldMaterial == nullptr || newMaterial->compare( oldMaterial ) != 0
|| state.isCachedMaterialDataDirty( ))
{
auto material = static_cast< const Material* >( newMaterial );
p->setUniformValue( m_aspectId, material->aspect );
p->setUniformValue( m_blurExtentId, material->blurExtent);
p->setUniformValue( m_radiusId, material->radius );
p->setUniformValue( m_colorId, material->color );
}
}
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->aspect == aspect
&& qFuzzyCompare(material->blurExtent, blurExtent)
&& 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 aspect( 1.0, 1.0 );
if ( rect.width() >= rect.height() )
aspect.setX( rect.width() / rect.height() );
else
aspect.setY( rect.height() / rect.width() );
if ( d->material.aspect != aspect )
{
d->material.aspect = aspect;
markDirty( QSGNode::DirtyMaterial );
}
}
void BoxShadowNode::setShape( const QskBoxShapeMetrics& shape )
{
Q_D( BoxShadowNode );
const float t = 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::setBlurRadius( qreal blurRadius )
{
Q_D( BoxShadowNode );
if ( blurRadius <= 0.0 )
blurRadius = 0.0;
const float t = 0.5 * std::min( d->rect.width(), d->rect.height() );
const float uniformExtent = blurRadius / t;
if ( !qFuzzyCompare( d->material.blurExtent, uniformExtent ) )
{
d->material.blurExtent = uniformExtent;
markDirty( QSGNode::DirtyMaterial );
}
}
void BoxShadowNode::setClipRect( const QRectF& rect )
{
Q_UNUSED( rect )
}
void BoxShadowNode::updateGeometry()
{
Q_D( BoxShadowNode );
QSGGeometry::updateTexturedRectGeometry(
&d->geometry, d->rect, QRectF( -0.5, -0.5, 1.0, 1.0 ) );
markDirty( QSGNode::DirtyGeometry );
}

View File

@ -0,0 +1,32 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#pragma once
#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 setBlurRadius( qreal );
void setClipRect( const QRectF& );
void updateGeometry();
private:
Q_DECLARE_PRIVATE( BoxShadowNode )
};

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/iotdashboard">
<file>shaders/boxshadow.vert</file>
<file>shaders/boxshadow.frag</file>
</qresource>
</RCC>

View File

@ -0,0 +1,42 @@
uniform lowp float opacity;
uniform lowp float blurExtent;
uniform lowp vec4 radius;
uniform lowp vec4 color;
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;
}
void main()
{
lowp vec4 col = vec4(0.0);
if ( opacity > 0.0 )
{
const lowp float minRadius = 0.05;
lowp float e2 = 0.5 * blurExtent;
lowp float r = 2.0 * effectiveRadius( radius, coord );
lowp float f = minRadius / max( r, minRadius );
r += e2 * f;
lowp vec2 d = r + blurExtent - aspect * ( 1.0 - abs( 2.0 * coord ) );
lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) );
lowp float shadow = l - r;
lowp float v = smoothstep( -e2, e2, shadow );
col = mix( color, vec4(0.0), v ) * opacity;
}
gl_FragColor = col;
}

View File

@ -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 = in_coord;
gl_Position = matrix * in_vertex;
}