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"