1 /*
2     SPDX-FileCopyrightText: 2011 Aaron Seigo <aseigo@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "debug.h"
8 #include "panelshadows_p.h"
9 
10 #include <KWindowShadow>
11 
12 class PanelShadows::Private
13 {
14 public:
Private(PanelShadows * shadows)15     Private(PanelShadows *shadows)
16         : q(shadows)
17     {
18     }
19 
~Private()20     ~Private()
21     {
22     }
23 
24     void clearTiles();
25     void setupTiles();
26     void initTile(const QString &element);
27     void updateShadow(QWindow *window, Plasma::FrameSvg::EnabledBorders);
28     void clearShadow(QWindow *window);
29     void updateShadows();
30     bool hasShadows() const;
31 
32     PanelShadows *q;
33 
34     QHash<QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows;
35     QHash<QWindow *, KWindowShadow *> m_shadows;
36     QVector<KWindowShadowTile::Ptr> m_tiles;
37 };
38 
39 class PanelShadowsSingleton
40 {
41 public:
PanelShadowsSingleton()42     PanelShadowsSingleton()
43     {
44     }
45 
46     PanelShadows self;
47 };
48 
Q_GLOBAL_STATIC(PanelShadowsSingleton,privatePanelShadowsSelf)49 Q_GLOBAL_STATIC(PanelShadowsSingleton, privatePanelShadowsSelf)
50 
51 PanelShadows::PanelShadows(QObject *parent, const QString &prefix)
52     : Plasma::Svg(parent)
53     , d(new Private(this))
54 {
55     setImagePath(prefix);
56     connect(this, &Plasma::Svg::repaintNeeded, this, [this]() {
57         d->updateShadows();
58     });
59 }
60 
~PanelShadows()61 PanelShadows::~PanelShadows()
62 {
63     delete d;
64 }
65 
self()66 PanelShadows *PanelShadows::self()
67 {
68     return &privatePanelShadowsSelf->self;
69 }
70 
addWindow(QWindow * window,Plasma::FrameSvg::EnabledBorders enabledBorders)71 void PanelShadows::addWindow(QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
72 {
73     if (!window) {
74         return;
75     }
76 
77     d->m_windows[window] = enabledBorders;
78     d->updateShadow(window, enabledBorders);
79     connect(window, &QObject::destroyed, this, [this, window]() {
80         d->m_windows.remove(window);
81         d->clearShadow(window);
82         if (d->m_windows.isEmpty()) {
83             d->clearTiles();
84         }
85     });
86 }
87 
removeWindow(QWindow * window)88 void PanelShadows::removeWindow(QWindow *window)
89 {
90     if (!d->m_windows.contains(window)) {
91         return;
92     }
93 
94     d->m_windows.remove(window);
95     disconnect(window, nullptr, this, nullptr);
96     d->clearShadow(window);
97 
98     if (d->m_windows.isEmpty()) {
99         d->clearTiles();
100     }
101 }
102 
setEnabledBorders(QWindow * window,Plasma::FrameSvg::EnabledBorders enabledBorders)103 void PanelShadows::setEnabledBorders(QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
104 {
105     if (!window || !d->m_windows.contains(window)) {
106         return;
107     }
108 
109     d->m_windows[window] = enabledBorders;
110     d->updateShadow(window, enabledBorders);
111 }
112 
updateShadows()113 void PanelShadows::Private::updateShadows()
114 {
115     const bool hadShadowsBefore = !m_tiles.isEmpty();
116 
117     // has shadows now?
118     if (hasShadows()) {
119         if (hadShadowsBefore) {
120             clearTiles();
121         }
122         for (auto i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) {
123             updateShadow(i.key(), i.value());
124         }
125     } else {
126         if (hadShadowsBefore) {
127             for (auto i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) {
128                 clearShadow(i.key());
129             }
130             clearTiles();
131         }
132     }
133 }
134 
initTile(const QString & element)135 void PanelShadows::Private::initTile(const QString &element)
136 {
137     const QImage image = q->pixmap(element).toImage();
138 
139     KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create();
140     tile->setImage(image);
141 
142     m_tiles << tile;
143 }
144 
setupTiles()145 void PanelShadows::Private::setupTiles()
146 {
147     clearTiles();
148 
149     initTile(QStringLiteral("shadow-top"));
150     initTile(QStringLiteral("shadow-topright"));
151     initTile(QStringLiteral("shadow-right"));
152     initTile(QStringLiteral("shadow-bottomright"));
153     initTile(QStringLiteral("shadow-bottom"));
154     initTile(QStringLiteral("shadow-bottomleft"));
155     initTile(QStringLiteral("shadow-left"));
156     initTile(QStringLiteral("shadow-topleft"));
157 }
158 
clearTiles()159 void PanelShadows::Private::clearTiles()
160 {
161     m_tiles.clear();
162 }
163 
updateShadow(QWindow * window,Plasma::FrameSvg::EnabledBorders enabledBorders)164 void PanelShadows::Private::updateShadow(QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
165 {
166     if (!hasShadows()) {
167         return;
168     }
169 
170     if (m_tiles.isEmpty()) {
171         setupTiles();
172     }
173 
174     KWindowShadow *&shadow = m_shadows[window];
175 
176     if (!shadow) {
177         shadow = new KWindowShadow(q);
178     }
179 
180     if (shadow->isCreated()) {
181         shadow->destroy();
182     }
183 
184     if (enabledBorders & Plasma::FrameSvg::TopBorder) {
185         shadow->setTopTile(m_tiles.at(0));
186     } else {
187         shadow->setTopTile(nullptr);
188     }
189 
190     if (enabledBorders & Plasma::FrameSvg::TopBorder && enabledBorders & Plasma::FrameSvg::RightBorder) {
191         shadow->setTopRightTile(m_tiles.at(1));
192     } else {
193         shadow->setTopRightTile(nullptr);
194     }
195 
196     if (enabledBorders & Plasma::FrameSvg::RightBorder) {
197         shadow->setRightTile(m_tiles.at(2));
198     } else {
199         shadow->setRightTile(nullptr);
200     }
201 
202     if (enabledBorders & Plasma::FrameSvg::BottomBorder && enabledBorders & Plasma::FrameSvg::RightBorder) {
203         shadow->setBottomRightTile(m_tiles.at(3));
204     } else {
205         shadow->setBottomRightTile(nullptr);
206     }
207 
208     if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
209         shadow->setBottomTile(m_tiles.at(4));
210     } else {
211         shadow->setBottomTile(nullptr);
212     }
213 
214     if (enabledBorders & Plasma::FrameSvg::BottomBorder && enabledBorders & Plasma::FrameSvg::LeftBorder) {
215         shadow->setBottomLeftTile(m_tiles.at(5));
216     } else {
217         shadow->setBottomLeftTile(nullptr);
218     }
219 
220     if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
221         shadow->setLeftTile(m_tiles.at(6));
222     } else {
223         shadow->setLeftTile(nullptr);
224     }
225 
226     if (enabledBorders & Plasma::FrameSvg::TopBorder && enabledBorders & Plasma::FrameSvg::LeftBorder) {
227         shadow->setTopLeftTile(m_tiles.at(7));
228     } else {
229         shadow->setTopLeftTile(nullptr);
230     }
231 
232     QMargins padding;
233 
234     if (enabledBorders & Plasma::FrameSvg::TopBorder) {
235         const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin"));
236         if (marginHint.isValid()) {
237             padding.setTop(marginHint.height());
238         } else {
239             padding.setTop(m_tiles[0]->image().height());
240         }
241     }
242 
243     if (enabledBorders & Plasma::FrameSvg::RightBorder) {
244         const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin"));
245         if (marginHint.isValid()) {
246             padding.setRight(marginHint.width());
247         } else {
248             padding.setRight(m_tiles[2]->image().width());
249         }
250     }
251 
252     if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
253         const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin"));
254         if (marginHint.isValid()) {
255             padding.setBottom(marginHint.height());
256         } else {
257             padding.setBottom(m_tiles[4]->image().height());
258         }
259     }
260 
261     if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
262         const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin"));
263         if (marginHint.isValid()) {
264             padding.setLeft(marginHint.width());
265         } else {
266             padding.setLeft(m_tiles[6]->image().width());
267         }
268     }
269 
270     shadow->setPadding(padding);
271     shadow->setWindow(window);
272 
273     if (!shadow->create()) {
274         qCWarning(PLASMASHELL) << "Couldn't create KWindowShadow for" << window;
275     }
276 }
277 
clearShadow(QWindow * window)278 void PanelShadows::Private::clearShadow(QWindow *window)
279 {
280     delete m_shadows.take(window);
281 }
282 
hasShadows() const283 bool PanelShadows::Private::hasShadows() const
284 {
285     return q->hasElement(QStringLiteral("shadow-left"));
286 }
287 
288 #include "moc_panelshadows_p.cpp"
289