KWin
Loading...
Searching...
No Matches
shadowitem.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "scene/shadowitem.h"
8#include "compositor.h"
10#include "shadow.h"
11#include "window.h"
12
13namespace KWin
14{
15
17 : m_shadow(shadow)
18{
19}
20
24
25ShadowItem::ShadowItem(Shadow *shadow, Window *window, Scene *scene, Item *parent)
26 : Item(scene, parent)
27 , m_window(window)
28 , m_shadow(shadow)
29 , m_textureProvider(Compositor::self()->scene()->createShadowTextureProvider(shadow))
30{
31 connect(shadow, &Shadow::offsetChanged, this, &ShadowItem::updateGeometry);
32 connect(shadow, &Shadow::rectChanged, this, &ShadowItem::updateGeometry);
33 connect(shadow, &Shadow::textureChanged, this, &ShadowItem::handleTextureChanged);
34
35 updateGeometry();
36 handleTextureChanged();
37}
38
42
44{
45 return m_shadow;
46}
47
49{
50 return m_textureProvider.get();
51}
52
53void ShadowItem::updateGeometry()
54{
55 const QRectF rect = m_shadow->rect() + m_shadow->offset();
56
57 setPosition(rect.topLeft());
58 setSize(rect.size());
60}
61
62void ShadowItem::handleTextureChanged()
63{
66 m_textureDirty = true;
67}
68
69static inline void distributeHorizontally(QRectF &leftRect, QRectF &rightRect)
70{
71 if (leftRect.right() > rightRect.left()) {
72 const qreal boundedRight = std::min(leftRect.right(), rightRect.right());
73 const qreal boundedLeft = std::max(leftRect.left(), rightRect.left());
74 const qreal halfOverlap = (boundedRight - boundedLeft) / 2.0;
75 leftRect.setRight(boundedRight - halfOverlap);
76 rightRect.setLeft(boundedLeft + halfOverlap);
77 }
78}
79
80static inline void distributeVertically(QRectF &topRect, QRectF &bottomRect)
81{
82 if (topRect.bottom() > bottomRect.top()) {
83 const qreal boundedBottom = std::min(topRect.bottom(), bottomRect.bottom());
84 const qreal boundedTop = std::max(topRect.top(), bottomRect.top());
85 const qreal halfOverlap = (boundedBottom - boundedTop) / 2.0;
86 topRect.setBottom(boundedBottom - halfOverlap);
87 bottomRect.setTop(boundedTop + halfOverlap);
88 }
89}
90
92{
93 // Do not draw shadows if window width or window height is less than 5 px. 5 is an arbitrary choice.
94 if (!m_window->wantsShadowToBeRendered() || m_window->width() < 5 || m_window->height() < 5) {
95 return WindowQuadList();
96 }
97
98 const QSizeF top(m_shadow->elementSize(Shadow::ShadowElementTop));
99 const QSizeF topRight(m_shadow->elementSize(Shadow::ShadowElementTopRight));
100 const QSizeF right(m_shadow->elementSize(Shadow::ShadowElementRight));
101 const QSizeF bottomRight(m_shadow->elementSize(Shadow::ShadowElementBottomRight));
102 const QSizeF bottom(m_shadow->elementSize(Shadow::ShadowElementBottom));
103 const QSizeF bottomLeft(m_shadow->elementSize(Shadow::ShadowElementBottomLeft));
104 const QSizeF left(m_shadow->elementSize(Shadow::ShadowElementLeft));
105 const QSizeF topLeft(m_shadow->elementSize(Shadow::ShadowElementTopLeft));
106
107 const QMarginsF shadowMargins(
108 std::max({topLeft.width(), left.width(), bottomLeft.width()}),
109 std::max({topLeft.height(), top.height(), topRight.height()}),
110 std::max({topRight.width(), right.width(), bottomRight.width()}),
111 std::max({bottomRight.height(), bottom.height(), bottomLeft.height()}));
112
113 const QRectF outerRect = rect();
114
115 const int width = shadowMargins.left() + std::max(top.width(), bottom.width()) + shadowMargins.right();
116 const int height = shadowMargins.top() + std::max(left.height(), right.height()) + shadowMargins.bottom();
117
118 QRectF topLeftRect;
119 if (!topLeft.isEmpty()) {
120 topLeftRect = QRectF(outerRect.topLeft(), topLeft);
121 } else {
122 topLeftRect = QRectF(outerRect.left() + shadowMargins.left(),
123 outerRect.top() + shadowMargins.top(), 0, 0);
124 }
125
126 QRectF topRightRect;
127 if (!topRight.isEmpty()) {
128 topRightRect = QRectF(outerRect.right() - topRight.width(), outerRect.top(),
129 topRight.width(), topRight.height());
130 } else {
131 topRightRect = QRectF(outerRect.right() - shadowMargins.right(),
132 outerRect.top() + shadowMargins.top(), 0, 0);
133 }
134
135 QRectF bottomRightRect;
136 if (!bottomRight.isEmpty()) {
137 bottomRightRect = QRectF(outerRect.right() - bottomRight.width(),
138 outerRect.bottom() - bottomRight.height(),
139 bottomRight.width(), bottomRight.height());
140 } else {
141 bottomRightRect = QRectF(outerRect.right() - shadowMargins.right(),
142 outerRect.bottom() - shadowMargins.bottom(), 0, 0);
143 }
144
145 QRectF bottomLeftRect;
146 if (!bottomLeft.isEmpty()) {
147 bottomLeftRect = QRectF(outerRect.left(), outerRect.bottom() - bottomLeft.height(),
148 bottomLeft.width(), bottomLeft.height());
149 } else {
150 bottomLeftRect = QRectF(outerRect.left() + shadowMargins.left(),
151 outerRect.bottom() - shadowMargins.bottom(), 0, 0);
152 }
153
154 // Re-distribute the corner tiles so no one of them is overlapping with others.
155 // By doing this, we assume that shadow's corner tiles are symmetric
156 // and it is OK to not draw top/right/bottom/left tile between corners.
157 // For example, let's say top-left and top-right tiles are overlapping.
158 // In that case, the right side of the top-left tile will be shifted to left,
159 // the left side of the top-right tile will shifted to right, and the top
160 // tile won't be rendered.
161 distributeHorizontally(topLeftRect, topRightRect);
162 distributeHorizontally(bottomLeftRect, bottomRightRect);
163 distributeVertically(topLeftRect, bottomLeftRect);
164 distributeVertically(topRightRect, bottomRightRect);
165
166 qreal tx1 = 0.0,
167 tx2 = 0.0,
168 ty1 = 0.0,
169 ty2 = 0.0;
170
172 quads.reserve(8);
173
174 if (topLeftRect.isValid()) {
175 tx1 = 0.0;
176 ty1 = 0.0;
177 tx2 = topLeftRect.width();
178 ty2 = topLeftRect.height();
179 WindowQuad topLeftQuad;
180 topLeftQuad[0] = WindowVertex(topLeftRect.left(), topLeftRect.top(), tx1, ty1);
181 topLeftQuad[1] = WindowVertex(topLeftRect.right(), topLeftRect.top(), tx2, ty1);
182 topLeftQuad[2] = WindowVertex(topLeftRect.right(), topLeftRect.bottom(), tx2, ty2);
183 topLeftQuad[3] = WindowVertex(topLeftRect.left(), topLeftRect.bottom(), tx1, ty2);
184 quads.append(topLeftQuad);
185 }
186
187 if (topRightRect.isValid()) {
188 tx1 = width - topRightRect.width();
189 ty1 = 0.0;
190 tx2 = width;
191 ty2 = topRightRect.height();
192 WindowQuad topRightQuad;
193 topRightQuad[0] = WindowVertex(topRightRect.left(), topRightRect.top(), tx1, ty1);
194 topRightQuad[1] = WindowVertex(topRightRect.right(), topRightRect.top(), tx2, ty1);
195 topRightQuad[2] = WindowVertex(topRightRect.right(), topRightRect.bottom(), tx2, ty2);
196 topRightQuad[3] = WindowVertex(topRightRect.left(), topRightRect.bottom(), tx1, ty2);
197 quads.append(topRightQuad);
198 }
199
200 if (bottomRightRect.isValid()) {
201 tx1 = width - bottomRightRect.width();
202 tx2 = width;
203 ty1 = height - bottomRightRect.height();
204 ty2 = height;
205 WindowQuad bottomRightQuad;
206 bottomRightQuad[0] = WindowVertex(bottomRightRect.left(), bottomRightRect.top(), tx1, ty1);
207 bottomRightQuad[1] = WindowVertex(bottomRightRect.right(), bottomRightRect.top(), tx2, ty1);
208 bottomRightQuad[2] = WindowVertex(bottomRightRect.right(), bottomRightRect.bottom(), tx2, ty2);
209 bottomRightQuad[3] = WindowVertex(bottomRightRect.left(), bottomRightRect.bottom(), tx1, ty2);
210 quads.append(bottomRightQuad);
211 }
212
213 if (bottomLeftRect.isValid()) {
214 tx1 = 0.0;
215 tx2 = bottomLeftRect.width();
216 ty1 = height - bottomLeftRect.height();
217 ty2 = height;
218 WindowQuad bottomLeftQuad;
219 bottomLeftQuad[0] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.top(), tx1, ty1);
220 bottomLeftQuad[1] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.top(), tx2, ty1);
221 bottomLeftQuad[2] = WindowVertex(bottomLeftRect.right(), bottomLeftRect.bottom(), tx2, ty2);
222 bottomLeftQuad[3] = WindowVertex(bottomLeftRect.left(), bottomLeftRect.bottom(), tx1, ty2);
223 quads.append(bottomLeftQuad);
224 }
225
226 QRectF topRect(QPointF(topLeftRect.right(), outerRect.top()),
227 QPointF(topRightRect.left(), outerRect.top() + top.height()));
228
229 QRectF rightRect(QPointF(outerRect.right() - right.width(), topRightRect.bottom()),
230 QPointF(outerRect.right(), bottomRightRect.top()));
231
232 QRectF bottomRect(QPointF(bottomLeftRect.right(), outerRect.bottom() - bottom.height()),
233 QPointF(bottomRightRect.left(), outerRect.bottom()));
234
235 QRectF leftRect(QPointF(outerRect.left(), topLeftRect.bottom()),
236 QPointF(outerRect.left() + left.width(), bottomLeftRect.top()));
237
238 // Re-distribute left/right and top/bottom shadow tiles so they don't
239 // overlap when the window is too small. Please notice that we don't
240 // fix overlaps between left/top(left/bottom, right/top, and so on)
241 // corner tiles because corresponding counter parts won't be valid when
242 // the window is too small, which means they won't be rendered.
243 distributeHorizontally(leftRect, rightRect);
244 distributeVertically(topRect, bottomRect);
245
246 if (topRect.isValid()) {
247 tx1 = shadowMargins.left();
248 ty1 = 0.0;
249 tx2 = tx1 + top.width();
250 ty2 = topRect.height();
251 WindowQuad topQuad;
252 topQuad[0] = WindowVertex(topRect.left(), topRect.top(), tx1, ty1);
253 topQuad[1] = WindowVertex(topRect.right(), topRect.top(), tx2, ty1);
254 topQuad[2] = WindowVertex(topRect.right(), topRect.bottom(), tx2, ty2);
255 topQuad[3] = WindowVertex(topRect.left(), topRect.bottom(), tx1, ty2);
256 quads.append(topQuad);
257 }
258
259 if (rightRect.isValid()) {
260 tx1 = width - rightRect.width();
261 ty1 = shadowMargins.top();
262 tx2 = width;
263 ty2 = ty1 + right.height();
264 WindowQuad rightQuad;
265 rightQuad[0] = WindowVertex(rightRect.left(), rightRect.top(), tx1, ty1);
266 rightQuad[1] = WindowVertex(rightRect.right(), rightRect.top(), tx2, ty1);
267 rightQuad[2] = WindowVertex(rightRect.right(), rightRect.bottom(), tx2, ty2);
268 rightQuad[3] = WindowVertex(rightRect.left(), rightRect.bottom(), tx1, ty2);
269 quads.append(rightQuad);
270 }
271
272 if (bottomRect.isValid()) {
273 tx1 = shadowMargins.left();
274 ty1 = height - bottomRect.height();
275 tx2 = tx1 + bottom.width();
276 ty2 = height;
277 WindowQuad bottomQuad;
278 bottomQuad[0] = WindowVertex(bottomRect.left(), bottomRect.top(), tx1, ty1);
279 bottomQuad[1] = WindowVertex(bottomRect.right(), bottomRect.top(), tx2, ty1);
280 bottomQuad[2] = WindowVertex(bottomRect.right(), bottomRect.bottom(), tx2, ty2);
281 bottomQuad[3] = WindowVertex(bottomRect.left(), bottomRect.bottom(), tx1, ty2);
282 quads.append(bottomQuad);
283 }
284
285 if (leftRect.isValid()) {
286 tx1 = 0.0;
287 ty1 = shadowMargins.top();
288 tx2 = leftRect.width();
289 ty2 = ty1 + left.height();
290 WindowQuad leftQuad;
291 leftQuad[0] = WindowVertex(leftRect.left(), leftRect.top(), tx1, ty1);
292 leftQuad[1] = WindowVertex(leftRect.right(), leftRect.top(), tx2, ty1);
293 leftQuad[2] = WindowVertex(leftRect.right(), leftRect.bottom(), tx2, ty2);
294 leftQuad[3] = WindowVertex(leftRect.left(), leftRect.bottom(), tx1, ty2);
295 quads.append(leftQuad);
296 }
297
298 return quads;
299}
300
302{
303 if (m_textureDirty) {
304 m_textureDirty = false;
305 m_textureProvider->update();
306 }
307}
308
309} // namespace KWin
310
311#include "moc_shadowitem.cpp"
WindowQuadList quads() const
Definition item.cpp:355
QRectF rect() const
Definition item.cpp:152
void setSize(const QSizeF &size)
Definition item.cpp:140
void discardQuads()
Definition item.cpp:350
void scheduleRepaint(const QRectF &region)
Definition item.cpp:396
void setPosition(const QPointF &point)
Definition item.cpp:122
Class representing a Window's Shadow to be rendered by the Compositor.
Definition shadow.h:48
QRectF rect() const
Definition shadow.h:103
void textureChanged()
QSize elementSize(ShadowElements element) const
Definition shadow.cpp:331
@ ShadowElementLeft
Definition shadow.h:97
@ ShadowElementTopRight
Definition shadow.h:92
@ ShadowElementBottomLeft
Definition shadow.h:96
@ ShadowElementBottomRight
Definition shadow.h:94
@ ShadowElementTopLeft
Definition shadow.h:98
@ ShadowElementRight
Definition shadow.h:93
@ ShadowElementTop
Definition shadow.h:91
@ ShadowElementBottom
Definition shadow.h:95
void rectChanged()
QMargins offset() const
Definition shadow.h:107
void offsetChanged()
Shadow * shadow() const
ShadowItem(Shadow *shadow, Window *window, Scene *scene, Item *parent=nullptr)
~ShadowItem() override
ShadowTextureProvider * textureProvider() const
void preprocess() override
WindowQuadList buildQuads() const override
ShadowTextureProvider(Shadow *shadow)
qreal width
Definition window.h:99
qreal height
Definition window.h:104
bool wantsShadowToBeRendered() const
Definition window.cpp:297
Class representing one area of a window.
Vertex class.