/* * SPDX-FileCopyrightText: 2020 Arjen Hiemstra * * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "shadowedrectangle.h" #include #include #include #include "scenegraph/paintedrectangleitem.h" #include "scenegraph/shadowedrectanglenode.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(std::make_unique()) , m_shadow(std::make_unique()) , m_corners(std::make_unique()) { 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(); } ShadowedRectangle::RenderType ShadowedRectangle::renderType() const { return m_renderType; } void ShadowedRectangle::setRenderType(RenderType renderType) { if (renderType == m_renderType) { return; } m_renderType = renderType; update(); Q_EMIT renderTypeChanged(); } void ShadowedRectangle::componentComplete() { QQuickItem::componentComplete(); checkSoftwareItem(); } bool ShadowedRectangle::isSoftwareRendering() const { return (window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) || m_renderType == RenderType::Software; } 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(); } QQuickItem::itemChange(change, value); } QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) { Q_UNUSED(data); if (boundingRect().isEmpty()) { delete node; return nullptr; } auto shadowNode = static_cast(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 (m_renderType == RenderType::LowQuality || (m_renderType == RenderType::Auto && 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(); 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); } } #include "moc_shadowedrectangle.cpp"