File indexing completed on 2024-05-19 16:34:46
0001 /* 0002 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "scene/shadowitem.h" 0008 #include "deleted.h" 0009 #include "shadow.h" 0010 0011 namespace KWin 0012 { 0013 0014 ShadowItem::ShadowItem(Shadow *shadow, Window *window, Scene *scene, Item *parent) 0015 : Item(scene, parent) 0016 , m_window(window) 0017 , m_shadow(shadow) 0018 { 0019 connect(window, &Window::windowClosed, this, &ShadowItem::handleWindowClosed); 0020 0021 connect(shadow, &Shadow::offsetChanged, this, &ShadowItem::updateGeometry); 0022 connect(shadow, &Shadow::rectChanged, this, &ShadowItem::updateGeometry); 0023 connect(shadow, &Shadow::textureChanged, this, &ShadowItem::handleTextureChanged); 0024 0025 updateGeometry(); 0026 handleTextureChanged(); 0027 } 0028 0029 ShadowItem::~ShadowItem() 0030 { 0031 } 0032 0033 Shadow *ShadowItem::shadow() const 0034 { 0035 return m_shadow; 0036 } 0037 0038 void ShadowItem::updateGeometry() 0039 { 0040 const QRectF rect = m_shadow->rect() + m_shadow->offset(); 0041 0042 setPosition(rect.topLeft()); 0043 setSize(rect.size()); 0044 discardQuads(); 0045 } 0046 0047 void ShadowItem::handleTextureChanged() 0048 { 0049 scheduleRepaint(rect()); 0050 discardQuads(); 0051 } 0052 0053 void ShadowItem::handleWindowClosed(Window *original, Deleted *deleted) 0054 { 0055 m_window = deleted; 0056 } 0057 0058 static inline void distributeHorizontally(QRectF &leftRect, QRectF &rightRect) 0059 { 0060 if (leftRect.right() > rightRect.left()) { 0061 const qreal boundedRight = std::min(leftRect.right(), rightRect.right()); 0062 const qreal boundedLeft = std::max(leftRect.left(), rightRect.left()); 0063 const qreal halfOverlap = (boundedRight - boundedLeft) / 2.0; 0064 leftRect.setRight(boundedRight - halfOverlap); 0065 rightRect.setLeft(boundedLeft + halfOverlap); 0066 } 0067 } 0068 0069 static inline void distributeVertically(QRectF &topRect, QRectF &bottomRect) 0070 { 0071 if (topRect.bottom() > bottomRect.top()) { 0072 const qreal boundedBottom = std::min(topRect.bottom(), bottomRect.bottom()); 0073 const qreal boundedTop = std::max(topRect.top(), bottomRect.top()); 0074 const qreal halfOverlap = (boundedBottom - boundedTop) / 2.0; 0075 topRect.setBottom(boundedBottom - halfOverlap); 0076 bottomRect.setTop(boundedTop + halfOverlap); 0077 } 0078 } 0079 0080 WindowQuadList ShadowItem::buildQuads() const 0081 { 0082 // Do not draw shadows if window width or window height is less than 5 px. 5 is an arbitrary choice. 0083 if (!m_window->wantsShadowToBeRendered() || m_window->width() < 5 || m_window->height() < 5) { 0084 return WindowQuadList(); 0085 } 0086 0087 const QSizeF top(m_shadow->elementSize(Shadow::ShadowElementTop)); 0088 const QSizeF topRight(m_shadow->elementSize(Shadow::ShadowElementTopRight)); 0089 const QSizeF right(m_shadow->elementSize(Shadow::ShadowElementRight)); 0090 const QSizeF bottomRight(m_shadow->elementSize(Shadow::ShadowElementBottomRight)); 0091 const QSizeF bottom(m_shadow->elementSize(Shadow::ShadowElementBottom)); 0092 const QSizeF bottomLeft(m_shadow->elementSize(Shadow::ShadowElementBottomLeft)); 0093 const QSizeF left(m_shadow->elementSize(Shadow::ShadowElementLeft)); 0094 const QSizeF topLeft(m_shadow->elementSize(Shadow::ShadowElementTopLeft)); 0095 0096 const QMarginsF shadowMargins( 0097 std::max({topLeft.width(), left.width(), bottomLeft.width()}), 0098 std::max({topLeft.height(), top.height(), topRight.height()}), 0099 std::max({topRight.width(), right.width(), bottomRight.width()}), 0100 std::max({bottomRight.height(), bottom.height(), bottomLeft.height()})); 0101 0102 const QRectF outerRect = rect(); 0103 0104 const int width = shadowMargins.left() + std::max(top.width(), bottom.width()) + shadowMargins.right(); 0105 const int height = shadowMargins.top() + std::max(left.height(), right.height()) + shadowMargins.bottom(); 0106 0107 QRectF topLeftRect; 0108 if (!topLeft.isEmpty()) { 0109 topLeftRect = QRectF(outerRect.topLeft(), topLeft); 0110 } else { 0111 topLeftRect = QRectF(outerRect.left() + shadowMargins.left(), 0112 outerRect.top() + shadowMargins.top(), 0, 0); 0113 } 0114 0115 QRectF topRightRect; 0116 if (!topRight.isEmpty()) { 0117 topRightRect = QRectF(outerRect.right() - topRight.width(), outerRect.top(), 0118 topRight.width(), topRight.height()); 0119 } else { 0120 topRightRect = QRectF(outerRect.right() - shadowMargins.right(), 0121 outerRect.top() + shadowMargins.top(), 0, 0); 0122 } 0123 0124 QRectF bottomRightRect; 0125 if (!bottomRight.isEmpty()) { 0126 bottomRightRect = QRectF(outerRect.right() - bottomRight.width(), 0127 outerRect.bottom() - bottomRight.height(), 0128 bottomRight.width(), bottomRight.height()); 0129 } else { 0130 bottomRightRect = QRectF(outerRect.right() - shadowMargins.right(), 0131 outerRect.bottom() - shadowMargins.bottom(), 0, 0); 0132 } 0133 0134 QRectF bottomLeftRect; 0135 if (!bottomLeft.isEmpty()) { 0136 bottomLeftRect = QRectF(outerRect.left(), outerRect.bottom() - bottomLeft.height(), 0137 bottomLeft.width(), bottomLeft.height()); 0138 } else { 0139 bottomLeftRect = QRectF(outerRect.left() + shadowMargins.left(), 0140 outerRect.bottom() - shadowMargins.bottom(), 0, 0); 0141 } 0142 0143 // Re-distribute the corner tiles so no one of them is overlapping with others. 0144 // By doing this, we assume that shadow's corner tiles are symmetric 0145 // and it is OK to not draw top/right/bottom/left tile between corners. 0146 // For example, let's say top-left and top-right tiles are overlapping. 0147 // In that case, the right side of the top-left tile will be shifted to left, 0148 // the left side of the top-right tile will shifted to right, and the top 0149 // tile won't be rendered. 0150 distributeHorizontally(topLeftRect, topRightRect); 0151 distributeHorizontally(bottomLeftRect, bottomRightRect); 0152 distributeVertically(topLeftRect, bottomLeftRect); 0153 distributeVertically(topRightRect, bottomRightRect); 0154 0155 qreal tx1 = 0.0, 0156 tx2 = 0.0, 0157 ty1 = 0.0, 0158 ty2 = 0.0; 0159 0160 WindowQuadList quads; 0161 quads.reserve(8); 0162 0163 if (topLeftRect.isValid()) { 0164 tx1 = 0.0; 0165 ty1 = 0.0; 0166 tx2 = topLeftRect.width(); 0167 ty2 = topLeftRect.height(); 0168 WindowQuad topLeftQuad; 0169 topLeftQuad[0] = WindowVertex(topLeftRect.left(), topLeftRect.top(), tx1, ty1); 0170 topLeftQuad[1] = WindowVertex(topLeftRect.right(), topLeftRect.top(), tx2, ty1); 0171 topLeftQuad[2] = WindowVertex(topLeftRect.right(), topLeftRect.bottom(), tx2, ty2); 0172 topLeftQuad[3] = WindowVertex(topLeftRect.left(), topLeftRect.bottom(), tx1, ty2); 0173 quads.append(topLeftQuad); 0174 } 0175 0176 if (topRightRect.isValid()) { 0177 tx1 = width - topRightRect.width(); 0178 ty1 = 0.0; 0179 tx2 = width; 0180 ty2 = topRightRect.height(); 0181 WindowQuad topRightQuad; 0182 topRightQuad[0] = WindowVertex(topRightRect.left(), topRightRect.top(), tx1, ty1); 0183 topRightQuad[1] = WindowVertex(topRightRect.right(), topRightRect.top(), tx2, ty1); 0184 topRightQuad[2] = WindowVertex(topRightRect.right(), topRightRect.bottom(), tx2, ty2); 0185 topRightQuad[3] = WindowVertex(topRightRect.left(), topRightRect.bottom(), tx1, ty2); 0186 quads.append(topRightQuad); 0187 } 0188 0189 if (bottomRightRect.isValid()) { 0190 tx1 = width - bottomRightRect.width(); 0191 tx2 = width; 0192 ty1 = height - bottomRightRect.height(); 0193 ty2 = height; 0194 WindowQuad bottomRightQuad; 0195 bottomRightQuad[0] = WindowVertex(bottomRightRect.left(), bottomRightRect.top(), tx1, ty1); 0196 bottomRightQuad[1] = WindowVertex(bottomRightRect.right(), bottomRightRect.top(), tx2, ty1); 0197 bottomRightQuad[2] = WindowVertex(bottomRightRect.right(), bottomRightRect.bottom(), tx2, ty2); 0198 bottomRightQuad[3] = WindowVertex(bottomRightRect.left(), bottomRightRect.bottom(), tx1, ty2); 0199 quads.append(bottomRightQuad); 0200 } 0201 0202 if (bottomLeftRect.isValid()) { 0203 tx1 = 0.0; 0204 tx2 = bottomLeftRect.width(); 0205 ty1 = height - bottomLeftRect.height(); 0206 ty2 = height; 0207 WindowQuad bottomLeftQuad; 0208 bottomLeftQuad[0] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.top(), tx1, ty1); 0209 bottomLeftQuad[1] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.top(), tx2, ty1); 0210 bottomLeftQuad[2] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.bottom(), tx2, ty2); 0211 bottomLeftQuad[3] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.bottom(), tx1, ty2); 0212 quads.append(bottomLeftQuad); 0213 } 0214 0215 QRectF topRect(QPointF(topLeftRect.right(), outerRect.top()), 0216 QPointF(topRightRect.left(), outerRect.top() + top.height())); 0217 0218 QRectF rightRect(QPointF(outerRect.right() - right.width(), topRightRect.bottom()), 0219 QPointF(outerRect.right(), bottomRightRect.top())); 0220 0221 QRectF bottomRect(QPointF(bottomLeftRect.right(), outerRect.bottom() - bottom.height()), 0222 QPointF(bottomRightRect.left(), outerRect.bottom())); 0223 0224 QRectF leftRect(QPointF(outerRect.left(), topLeftRect.bottom()), 0225 QPointF(outerRect.left() + left.width(), bottomLeftRect.top())); 0226 0227 // Re-distribute left/right and top/bottom shadow tiles so they don't 0228 // overlap when the window is too small. Please notice that we don't 0229 // fix overlaps between left/top(left/bottom, right/top, and so on) 0230 // corner tiles because corresponding counter parts won't be valid when 0231 // the window is too small, which means they won't be rendered. 0232 distributeHorizontally(leftRect, rightRect); 0233 distributeVertically(topRect, bottomRect); 0234 0235 if (topRect.isValid()) { 0236 tx1 = shadowMargins.left(); 0237 ty1 = 0.0; 0238 tx2 = tx1 + top.width(); 0239 ty2 = topRect.height(); 0240 WindowQuad topQuad; 0241 topQuad[0] = WindowVertex(topRect.left(), topRect.top(), tx1, ty1); 0242 topQuad[1] = WindowVertex(topRect.right(), topRect.top(), tx2, ty1); 0243 topQuad[2] = WindowVertex(topRect.right(), topRect.bottom(), tx2, ty2); 0244 topQuad[3] = WindowVertex(topRect.left(), topRect.bottom(), tx1, ty2); 0245 quads.append(topQuad); 0246 } 0247 0248 if (rightRect.isValid()) { 0249 tx1 = width - rightRect.width(); 0250 ty1 = shadowMargins.top(); 0251 tx2 = width; 0252 ty2 = ty1 + right.height(); 0253 WindowQuad rightQuad; 0254 rightQuad[0] = WindowVertex(rightRect.left(), rightRect.top(), tx1, ty1); 0255 rightQuad[1] = WindowVertex(rightRect.right(), rightRect.top(), tx2, ty1); 0256 rightQuad[2] = WindowVertex(rightRect.right(), rightRect.bottom(), tx2, ty2); 0257 rightQuad[3] = WindowVertex(rightRect.left(), rightRect.bottom(), tx1, ty2); 0258 quads.append(rightQuad); 0259 } 0260 0261 if (bottomRect.isValid()) { 0262 tx1 = shadowMargins.left(); 0263 ty1 = height - bottomRect.height(); 0264 tx2 = tx1 + bottom.width(); 0265 ty2 = height; 0266 WindowQuad bottomQuad; 0267 bottomQuad[0] = WindowVertex(bottomRect.left(), bottomRect.top(), tx1, ty1); 0268 bottomQuad[1] = WindowVertex(bottomRect.right(), bottomRect.top(), tx2, ty1); 0269 bottomQuad[2] = WindowVertex(bottomRect.right(), bottomRect.bottom(), tx2, ty2); 0270 bottomQuad[3] = WindowVertex(bottomRect.left(), bottomRect.bottom(), tx1, ty2); 0271 quads.append(bottomQuad); 0272 } 0273 0274 if (leftRect.isValid()) { 0275 tx1 = 0.0; 0276 ty1 = shadowMargins.top(); 0277 tx2 = leftRect.width(); 0278 ty2 = ty1 + left.height(); 0279 WindowQuad leftQuad; 0280 leftQuad[0] = WindowVertex(leftRect.left(), leftRect.top(), tx1, ty1); 0281 leftQuad[1] = WindowVertex(leftRect.right(), leftRect.top(), tx2, ty1); 0282 leftQuad[2] = WindowVertex(leftRect.right(), leftRect.bottom(), tx2, ty2); 0283 leftQuad[3] = WindowVertex(leftRect.left(), leftRect.bottom(), tx1, ty2); 0284 quads.append(leftQuad); 0285 } 0286 0287 return quads; 0288 } 0289 0290 } // namespace KWin