diff --git a/src/shadowedrectangle.cpp b/src/shadowedrectangle.cpp index a5cfbabb..93972fff 100644 --- a/src/shadowedrectangle.cpp +++ b/src/shadowedrectangle.cpp @@ -1,221 +1,233 @@ /* * SPDX-FileCopyrightText: 2020 Arjen Hiemstra * * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "shadowedrectangle.h" #include #include #include #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(); } ShadowedRectangle::ShadowedRectangle(QQuickItem *parentItem) : QQuickItem(parentItem), m_border(new BorderGroup), m_shadow(new ShadowGroup) { setFlag(QQuickItem::ItemHasContents, true); connect(m_border.get(), &BorderGroup::changed, this, &ShadowedRectangle::update); connect(m_shadow.get(), &ShadowGroup::changed, this, &ShadowedRectangle::update); } ShadowedRectangle::~ShadowedRectangle() { } BorderGroup *ShadowedRectangle::border() const { return m_border.get(); } ShadowGroup *ShadowedRectangle::shadow() const { return m_shadow.get(); } qreal ShadowedRectangle::radius() const { return m_radius; } void ShadowedRectangle::setRadius(qreal newRadius) { if (newRadius == m_radius) { return; } m_radius = newRadius; 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; update(); Q_EMIT colorChanged(); } void ShadowedRectangle::componentComplete() { QQuickItem::componentComplete(); - if (window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { + checkSoftwareItem(); +} + +void ShadowedRectangle::checkSoftwareItem() +{ + if (!m_softwareItem && window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { m_softwareItem = new PaintedRectangleItem{this}; 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); } } +void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) +{ + if (change == QQuickItem::ItemSceneChange && value.window) { + checkSoftwareItem(); + } +} + QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) { Q_UNUSED(data); if (!node) { node = new ShadowedRectangleNode; } auto elevatedNode = static_cast(node); elevatedNode->setBorderWidth(m_border->width()); elevatedNode->setRect(boundingRect()); elevatedNode->setSize(m_shadow->size()); elevatedNode->setRadius(m_radius); elevatedNode->setOffset(QVector2D{float(m_shadow->xOffset()), float(m_shadow->yOffset())}); elevatedNode->setColor(m_color); elevatedNode->setShadowColor(m_shadow->color()); elevatedNode->setBorderColor(m_border->color()); elevatedNode->updateGeometry(); return elevatedNode; } diff --git a/src/shadowedrectangle.h b/src/shadowedrectangle.h index 60f2b745..06185f7a 100644 --- a/src/shadowedrectangle.h +++ b/src/shadowedrectangle.h @@ -1,169 +1,171 @@ /* * SPDX-FileCopyrightText: 2020 Arjen Hiemstra * * SPDX-License-Identifier: LGPL-2.0-or-later */ #pragma once #include #include 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(); 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; }; /** * 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. */ class ShadowedRectangle : public QQuickItem { Q_OBJECT /** * Corner radius of the rectangle. * * This is the amount of rounding to apply to the rectangle's corners, in pixels. * 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) public: ShadowedRectangle(QQuickItem *parent = nullptr); ~ShadowedRectangle() override; BorderGroup *border() const; ShadowGroup *shadow() 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; protected: + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value); QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override; private: + void checkSoftwareItem(); const std::unique_ptr m_border; const std::unique_ptr m_shadow; qreal m_radius = 0.0; QColor m_color = Qt::white; PaintedRectangleItem *m_softwareItem = nullptr; };