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