File indexing completed on 2024-04-28 15:27:44

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "shadowedrectangle.h"
0008 
0009 #include <QQuickWindow>
0010 #include <QSGRectangleNode>
0011 #include <QSGRendererInterface>
0012 
0013 #include "scenegraph/paintedrectangleitem.h"
0014 #if QT_CONFIG(opengl) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0015 #include "scenegraph/shadowedrectanglenode.h"
0016 #endif
0017 
0018 BorderGroup::BorderGroup(QObject *parent)
0019     : QObject(parent)
0020 {
0021 }
0022 
0023 qreal BorderGroup::width() const
0024 {
0025     return m_width;
0026 }
0027 
0028 void BorderGroup::setWidth(qreal newWidth)
0029 {
0030     if (newWidth == m_width) {
0031         return;
0032     }
0033 
0034     m_width = newWidth;
0035     Q_EMIT changed();
0036 }
0037 
0038 QColor BorderGroup::color() const
0039 {
0040     return m_color;
0041 }
0042 
0043 void BorderGroup::setColor(const QColor &newColor)
0044 {
0045     if (newColor == m_color) {
0046         return;
0047     }
0048 
0049     m_color = newColor;
0050     Q_EMIT changed();
0051 }
0052 
0053 ShadowGroup::ShadowGroup(QObject *parent)
0054     : QObject(parent)
0055 {
0056 }
0057 
0058 qreal ShadowGroup::size() const
0059 {
0060     return m_size;
0061 }
0062 
0063 void ShadowGroup::setSize(qreal newSize)
0064 {
0065     if (newSize == m_size) {
0066         return;
0067     }
0068 
0069     m_size = newSize;
0070     Q_EMIT changed();
0071 }
0072 
0073 qreal ShadowGroup::xOffset() const
0074 {
0075     return m_xOffset;
0076 }
0077 
0078 void ShadowGroup::setXOffset(qreal newXOffset)
0079 {
0080     if (newXOffset == m_xOffset) {
0081         return;
0082     }
0083 
0084     m_xOffset = newXOffset;
0085     Q_EMIT changed();
0086 }
0087 
0088 qreal ShadowGroup::yOffset() const
0089 {
0090     return m_yOffset;
0091 }
0092 
0093 void ShadowGroup::setYOffset(qreal newYOffset)
0094 {
0095     if (newYOffset == m_yOffset) {
0096         return;
0097     }
0098 
0099     m_yOffset = newYOffset;
0100     Q_EMIT changed();
0101 }
0102 
0103 QColor ShadowGroup::color() const
0104 {
0105     return m_color;
0106 }
0107 
0108 void ShadowGroup::setColor(const QColor &newColor)
0109 {
0110     if (newColor == m_color) {
0111         return;
0112     }
0113 
0114     m_color = newColor;
0115     Q_EMIT changed();
0116 }
0117 
0118 CornersGroup::CornersGroup(QObject *parent)
0119     : QObject(parent)
0120 {
0121 }
0122 
0123 qreal CornersGroup::topLeft() const
0124 {
0125     return m_topLeft;
0126 }
0127 
0128 void CornersGroup::setTopLeft(qreal newTopLeft)
0129 {
0130     if (newTopLeft == m_topLeft) {
0131         return;
0132     }
0133 
0134     m_topLeft = newTopLeft;
0135     Q_EMIT changed();
0136 }
0137 
0138 qreal CornersGroup::topRight() const
0139 {
0140     return m_topRight;
0141 }
0142 
0143 void CornersGroup::setTopRight(qreal newTopRight)
0144 {
0145     if (newTopRight == m_topRight) {
0146         return;
0147     }
0148 
0149     m_topRight = newTopRight;
0150     Q_EMIT changed();
0151 }
0152 
0153 qreal CornersGroup::bottomLeft() const
0154 {
0155     return m_bottomLeft;
0156 }
0157 
0158 void CornersGroup::setBottomLeft(qreal newBottomLeft)
0159 {
0160     if (newBottomLeft == m_bottomLeft) {
0161         return;
0162     }
0163 
0164     m_bottomLeft = newBottomLeft;
0165     Q_EMIT changed();
0166 }
0167 
0168 qreal CornersGroup::bottomRight() const
0169 {
0170     return m_bottomRight;
0171 }
0172 
0173 void CornersGroup::setBottomRight(qreal newBottomRight)
0174 {
0175     if (newBottomRight == m_bottomRight) {
0176         return;
0177     }
0178 
0179     m_bottomRight = newBottomRight;
0180     Q_EMIT changed();
0181 }
0182 
0183 QVector4D CornersGroup::toVector4D(float all) const
0184 {
0185     return QVector4D{m_bottomRight < 0.0 ? all : m_bottomRight,
0186                      m_topRight < 0.0 ? all : m_topRight,
0187                      m_bottomLeft < 0.0 ? all : m_bottomLeft,
0188                      m_topLeft < 0.0 ? all : m_topLeft};
0189 }
0190 
0191 ShadowedRectangle::ShadowedRectangle(QQuickItem *parentItem)
0192     : QQuickItem(parentItem)
0193     , m_border(std::make_unique<BorderGroup>())
0194     , m_shadow(std::make_unique<ShadowGroup>())
0195     , m_corners(std::make_unique<CornersGroup>())
0196 {
0197     setFlag(QQuickItem::ItemHasContents, true);
0198 
0199     connect(m_border.get(), &BorderGroup::changed, this, &ShadowedRectangle::update);
0200     connect(m_shadow.get(), &ShadowGroup::changed, this, &ShadowedRectangle::update);
0201     connect(m_corners.get(), &CornersGroup::changed, this, &ShadowedRectangle::update);
0202 }
0203 
0204 ShadowedRectangle::~ShadowedRectangle()
0205 {
0206 }
0207 
0208 BorderGroup *ShadowedRectangle::border() const
0209 {
0210     return m_border.get();
0211 }
0212 
0213 ShadowGroup *ShadowedRectangle::shadow() const
0214 {
0215     return m_shadow.get();
0216 }
0217 
0218 CornersGroup *ShadowedRectangle::corners() const
0219 {
0220     return m_corners.get();
0221 }
0222 
0223 qreal ShadowedRectangle::radius() const
0224 {
0225     return m_radius;
0226 }
0227 
0228 void ShadowedRectangle::setRadius(qreal newRadius)
0229 {
0230     if (newRadius == m_radius) {
0231         return;
0232     }
0233 
0234     m_radius = newRadius;
0235     if (!isSoftwareRendering()) {
0236         update();
0237     }
0238     Q_EMIT radiusChanged();
0239 }
0240 
0241 QColor ShadowedRectangle::color() const
0242 {
0243     return m_color;
0244 }
0245 
0246 void ShadowedRectangle::setColor(const QColor &newColor)
0247 {
0248     if (newColor == m_color) {
0249         return;
0250     }
0251 
0252     m_color = newColor;
0253     if (!isSoftwareRendering()) {
0254         update();
0255     }
0256     Q_EMIT colorChanged();
0257 }
0258 
0259 ShadowedRectangle::RenderType ShadowedRectangle::renderType() const
0260 {
0261     return m_renderType;
0262 }
0263 
0264 void ShadowedRectangle::setRenderType(RenderType renderType)
0265 {
0266     if (renderType == m_renderType) {
0267         return;
0268     }
0269     m_renderType = renderType;
0270     update();
0271     Q_EMIT renderTypeChanged();
0272 }
0273 
0274 void ShadowedRectangle::componentComplete()
0275 {
0276     QQuickItem::componentComplete();
0277 
0278     checkSoftwareItem();
0279 }
0280 
0281 bool ShadowedRectangle::isSoftwareRendering() const
0282 {
0283     return (window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) || m_renderType == RenderType::Software;
0284 }
0285 
0286 PaintedRectangleItem *ShadowedRectangle::softwareItem() const
0287 {
0288     return m_softwareItem;
0289 }
0290 
0291 void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
0292 {
0293     if (change == QQuickItem::ItemSceneChange && value.window) {
0294         checkSoftwareItem();
0295         // TODO: only conditionally emit?
0296         Q_EMIT softwareRenderingChanged();
0297     }
0298 
0299     QQuickItem::itemChange(change, value);
0300 }
0301 
0302 QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
0303 {
0304     Q_UNUSED(data);
0305 
0306 #if QT_CONFIG(opengl) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0307     auto shadowNode = static_cast<ShadowedRectangleNode *>(node);
0308 
0309     if (!shadowNode) {
0310         shadowNode = new ShadowedRectangleNode{};
0311 
0312         // Cache lowPower state so we only execute the full check once.
0313         static bool lowPower = QByteArrayList{"1", "true"}.contains(qgetenv("KIRIGAMI_LOWPOWER_HARDWARE").toLower());
0314         if (m_renderType == RenderType::LowQuality || (m_renderType == RenderType::Auto && lowPower)) {
0315             shadowNode->setShaderType(ShadowedRectangleMaterial::ShaderType::LowPower);
0316         }
0317     }
0318 
0319     shadowNode->setBorderEnabled(m_border->isEnabled());
0320     shadowNode->setRect(boundingRect());
0321     shadowNode->setSize(m_shadow->size());
0322     shadowNode->setRadius(m_corners->toVector4D(m_radius));
0323     shadowNode->setOffset(QVector2D{float(m_shadow->xOffset()), float(m_shadow->yOffset())});
0324     shadowNode->setColor(m_color);
0325     shadowNode->setShadowColor(m_shadow->color());
0326     shadowNode->setBorderWidth(m_border->width());
0327     shadowNode->setBorderColor(m_border->color());
0328     shadowNode->updateGeometry();
0329     return shadowNode;
0330 #else
0331     Q_UNUSED(node)
0332     return nullptr;
0333 #endif
0334 }
0335 
0336 void ShadowedRectangle::checkSoftwareItem()
0337 {
0338     if (!m_softwareItem && isSoftwareRendering()) {
0339         m_softwareItem = new PaintedRectangleItem{this};
0340         // The software item is added as a "normal" child item, this means it
0341         // will be part of the normal item sort order. Since there is no way to
0342         // control the ordering of children, just make sure to have a very low Z
0343         // value for the child, to force it to be the lowest item.
0344         m_softwareItem->setZ(-99.0);
0345 
0346         auto updateItem = [this]() {
0347             auto borderWidth = m_border->width();
0348             auto rect = boundingRect();
0349             m_softwareItem->setSize(rect.size());
0350             m_softwareItem->setColor(m_color);
0351             m_softwareItem->setRadius(m_radius);
0352             m_softwareItem->setBorderWidth(borderWidth);
0353             m_softwareItem->setBorderColor(m_border->color());
0354         };
0355 
0356         updateItem();
0357 
0358         connect(this, &ShadowedRectangle::widthChanged, m_softwareItem, updateItem);
0359         connect(this, &ShadowedRectangle::heightChanged, m_softwareItem, updateItem);
0360         connect(this, &ShadowedRectangle::colorChanged, m_softwareItem, updateItem);
0361         connect(this, &ShadowedRectangle::radiusChanged, m_softwareItem, updateItem);
0362         connect(m_border.get(), &BorderGroup::changed, m_softwareItem, updateItem);
0363         setFlag(QQuickItem::ItemHasContents, false);
0364     }
0365 }
0366 
0367 #include "moc_shadowedrectangle.cpp"