KWin
Loading...
Searching...
No Matches
magiclamp.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2008 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10// based on minimize animation by Rivo Laks <rivolaks@hot.ee>
11
12#include "magiclamp.h"
14// KConfigSkeleton
15#include "magiclampconfig.h"
16
17namespace KWin
18{
19
34
39
40void MagicLampEffect::reconfigure(ReconfigureFlags)
41{
42 MagicLampConfig::self()->read();
43
44 // TODO: Rename animationDuration to duration so we can use
45 // animationTime<MagicLampConfig>(250).
46 const int d = MagicLampConfig::animationDuration() != 0
47 ? MagicLampConfig::animationDuration()
48 : 250;
49 m_duration = std::chrono::milliseconds(static_cast<int>(animationTime(d)));
50}
51
52void MagicLampEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
53{
54 // We need to mark the screen windows as transformed. Otherwise the
55 // whole screen won't be repainted, resulting in artefacts.
57
58 effects->prePaintScreen(data, presentTime);
59}
60
61void MagicLampEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
62{
63 // Schedule window for transformation if the animation is still in
64 // progress
65 auto animationIt = m_animations.find(w);
66 if (animationIt != m_animations.end()) {
67 (*animationIt).timeLine.advance(presentTime);
68 data.setTransformed();
69 }
70
71 effects->prePaintWindow(w, data, presentTime);
72}
73
75{
76 auto animationIt = m_animations.constFind(w);
77 if (animationIt != m_animations.constEnd()) {
78 // 0 = not minimized, 1 = fully minimized
79 const qreal progress = (*animationIt).timeLine.value();
80
81 QRect geo = w->frameGeometry().toRect();
82 QRect icon = w->iconGeometry().toRect();
83 IconPosition position = Top;
84 // If there's no icon geometry, minimize to the center of the screen
85 if (!icon.isValid()) {
86 QRect extG = geo;
87 QPoint pt = cursorPos().toPoint();
88 // focussing inside the window is no good, leads to ugly artefacts, find nearest border
89 if (extG.contains(pt)) {
90 const int d[2][2] = {{pt.x() - extG.x(), extG.right() - pt.x()},
91 {pt.y() - extG.y(), extG.bottom() - pt.y()}};
92 int di = d[1][0];
93 position = Top;
94 if (d[0][0] < di) {
95 di = d[0][0];
96 position = Left;
97 }
98 if (d[1][1] < di) {
99 di = d[1][1];
100 position = Bottom;
101 }
102 if (d[0][1] < di) {
103 position = Right;
104 }
105 switch (position) {
106 case Top:
107 pt.setY(extG.y());
108 break;
109 case Left:
110 pt.setX(extG.x());
111 break;
112 case Bottom:
113 pt.setY(extG.bottom());
114 break;
115 case Right:
116 pt.setX(extG.right());
117 break;
118 }
119 } else {
120 if (pt.y() < geo.y()) {
121 position = Top;
122 } else if (pt.x() < geo.x()) {
123 position = Left;
124 } else if (pt.y() > geo.bottom()) {
125 position = Bottom;
126 } else if (pt.x() > geo.right()) {
127 position = Right;
128 }
129 }
130 icon = QRect(pt, QSize(0, 0));
131 } else {
132 // Assumption: there is a panel containing the icon position
133 EffectWindow *panel = nullptr;
134 const auto stackingOrder = effects->stackingOrder();
135 for (EffectWindow *window : stackingOrder) {
136 if (!window->isDock()) {
137 continue;
138 }
139 // we have to use intersects as there seems to be a Plasma bug
140 // the published icon geometry might be bigger than the panel
141 if (window->frameGeometry().intersects(icon)) {
142 panel = window;
143 break;
144 }
145 }
146 if (panel) {
147 // Assumption: width of horizonal panel is greater than its height and vice versa
148 const QRectF windowScreen = effects->clientArea(ScreenArea, w);
149
150 if (panel->width() >= panel->height()) {
151 // horizontal panel
152 if (icon.center().y() <= windowScreen.center().y()) {
153 position = Top;
154 } else {
155 position = Bottom;
156 }
157 } else {
158 // vertical panel
159 if (icon.center().x() <= windowScreen.center().x()) {
160 position = Left;
161 } else {
162 position = Right;
163 }
164 }
165
166 // If the panel is hidden, move the icon offscreen so the animation looks correct.
167 if (panel->isHidden()) {
168 const QRectF panelScreen = effects->clientArea(ScreenArea, panel);
169 switch (position) {
170 case Bottom:
171 icon.moveTop(panelScreen.y() + panelScreen.height());
172 break;
173 case Top:
174 icon.moveTop(panelScreen.y() - icon.height());
175 break;
176 case Left:
177 icon.moveLeft(panelScreen.x() - icon.width());
178 break;
179 case Right:
180 icon.moveLeft(panelScreen.x() + panelScreen.width());
181 break;
182 }
183 }
184 } else {
185 // we did not find a panel, so it might be autohidden
186 QRectF iconScreen = effects->clientArea(ScreenArea, icon.topLeft(), effects->currentDesktop());
187 // as the icon geometry could be overlap a screen edge we use an intersection
188 QRectF rect = iconScreen.intersected(icon);
189 // here we need a different assumption: icon geometry borders one screen edge
190 // this assumption might be wrong for e.g. task applet being the only applet in panel
191 // in this case the icon borders two screen edges
192 // there might be a wrong animation, but not distorted
193 if (rect.x() == iconScreen.x()) {
194 position = Left;
195 } else if (rect.x() + rect.width() == iconScreen.x() + iconScreen.width()) {
196 position = Right;
197 } else if (rect.y() == iconScreen.y()) {
198 position = Top;
199 } else {
200 position = Bottom;
201 }
202 }
203 }
204
205 quads = quads.makeGrid(40);
206 float quadFactor; // defines how fast a quad is vertically moved: y coordinates near to window top are slowed down
207 // it is used as quadFactor^3/windowHeight^3
208 // quadFactor is the y position of the quad but is changed towards becomming the window height
209 // by that the factor becomes 1 and has no influence any more
210 float offset[2] = {0, 0}; // how far has a quad to be moved? Distance between icon and window multiplied by the progress and by the quadFactor
211 float p_progress[2] = {0, 0}; // the factor which defines how far the x values have to be changed
212 // factor is the current moved y value diveded by the distance between icon and window
213 WindowQuad lastQuad;
214 lastQuad[0].setX(-1);
215 lastQuad[0].setY(-1);
216 lastQuad[1].setX(-1);
217 lastQuad[1].setY(-1);
218 lastQuad[2].setX(-1);
219 lastQuad[2].setY(-1);
220
221 if (position == Bottom) {
222 const float height_cube = float(geo.height()) * float(geo.height()) * float(geo.height());
223 const float maxY = icon.y() - geo.y();
224
225 for (WindowQuad &quad : quads) {
226
227 if (quad[0].y() != lastQuad[0].y() || quad[2].y() != lastQuad[2].y()) {
228 quadFactor = quad[0].y() + (geo.height() - quad[0].y()) * progress;
229 offset[0] = (icon.y() + quad[0].y() - geo.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube);
230 quadFactor = quad[2].y() + (geo.height() - quad[2].y()) * progress;
231 offset[1] = (icon.y() + quad[2].y() - geo.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube);
232 p_progress[1] = std::min(offset[1] / (icon.y() - geo.y() - float(quad[2].y())), 1.0f);
233 p_progress[0] = std::min(offset[0] / (icon.y() - geo.y() - float(quad[0].y())), 1.0f);
234 } else {
235 lastQuad = quad;
236 }
237
238 p_progress[0] = std::abs(p_progress[0]);
239 p_progress[1] = std::abs(p_progress[1]);
240
241 // x values are moved towards the center of the icon
242 quad[0].setX((icon.x() + icon.width() * (quad[0].x() / geo.width()) - (quad[0].x() + geo.x())) * p_progress[0] + quad[0].x());
243 quad[1].setX((icon.x() + icon.width() * (quad[1].x() / geo.width()) - (quad[1].x() + geo.x())) * p_progress[0] + quad[1].x());
244 quad[2].setX((icon.x() + icon.width() * (quad[2].x() / geo.width()) - (quad[2].x() + geo.x())) * p_progress[1] + quad[2].x());
245 quad[3].setX((icon.x() + icon.width() * (quad[3].x() / geo.width()) - (quad[3].x() + geo.x())) * p_progress[1] + quad[3].x());
246
247 quad[0].setY(std::min(maxY, float(quad[0].y()) + offset[0]));
248 quad[1].setY(std::min(maxY, float(quad[1].y()) + offset[0]));
249 quad[2].setY(std::min(maxY, float(quad[2].y()) + offset[1]));
250 quad[3].setY(std::min(maxY, float(quad[3].y()) + offset[1]));
251 }
252 } else if (position == Top) {
253 const float height_cube = float(geo.height()) * float(geo.height()) * float(geo.height());
254 const float minY = icon.y() + icon.height() - geo.y();
255
256 for (WindowQuad &quad : quads) {
257
258 if (quad[0].y() != lastQuad[0].y() || quad[2].y() != lastQuad[2].y()) {
259 quadFactor = geo.height() - quad[0].y() + (quad[0].y()) * progress;
260 offset[0] = (geo.y() - icon.height() + geo.height() + quad[0].y() - icon.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube);
261 quadFactor = geo.height() - quad[2].y() + (quad[2].y()) * progress;
262 offset[1] = (geo.y() - icon.height() + geo.height() + quad[2].y() - icon.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube);
263 p_progress[0] = std::min(offset[0] / (geo.y() - icon.height() + geo.height() - icon.y() - float(geo.height() - quad[0].y())), 1.0f);
264 p_progress[1] = std::min(offset[1] / (geo.y() - icon.height() + geo.height() - icon.y() - float(geo.height() - quad[2].y())), 1.0f);
265 } else {
266 lastQuad = quad;
267 }
268
269 offset[0] = -offset[0];
270 offset[1] = -offset[1];
271
272 p_progress[0] = std::abs(p_progress[0]);
273 p_progress[1] = std::abs(p_progress[1]);
274
275 // x values are moved towards the center of the icon
276 quad[0].setX((icon.x() + icon.width() * (quad[0].x() / geo.width()) - (quad[0].x() + geo.x())) * p_progress[0] + quad[0].x());
277 quad[1].setX((icon.x() + icon.width() * (quad[1].x() / geo.width()) - (quad[1].x() + geo.x())) * p_progress[0] + quad[1].x());
278 quad[2].setX((icon.x() + icon.width() * (quad[2].x() / geo.width()) - (quad[2].x() + geo.x())) * p_progress[1] + quad[2].x());
279 quad[3].setX((icon.x() + icon.width() * (quad[3].x() / geo.width()) - (quad[3].x() + geo.x())) * p_progress[1] + quad[3].x());
280
281 quad[0].setY(std::max(minY, float(quad[0].y()) + offset[0]));
282 quad[1].setY(std::max(minY, float(quad[1].y()) + offset[0]));
283 quad[2].setY(std::max(minY, float(quad[2].y()) + offset[1]));
284 quad[3].setY(std::max(minY, float(quad[3].y()) + offset[1]));
285 }
286 } else if (position == Left) {
287 const float width_cube = float(geo.width()) * float(geo.width()) * float(geo.width());
288 const float minX = icon.x() + icon.width() - geo.x();
289
290 for (WindowQuad &quad : quads) {
291
292 if (quad[0].x() != lastQuad[0].x() || quad[1].x() != lastQuad[1].x()) {
293 quadFactor = geo.width() - quad[0].x() + (quad[0].x()) * progress;
294 offset[0] = (geo.x() - icon.width() + geo.width() + quad[0].x() - icon.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube);
295 quadFactor = geo.width() - quad[1].x() + (quad[1].x()) * progress;
296 offset[1] = (geo.x() - icon.width() + geo.width() + quad[1].x() - icon.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube);
297 p_progress[0] = std::min(offset[0] / (geo.x() - icon.width() + geo.width() - icon.x() - float(geo.width() - quad[0].x())), 1.0f);
298 p_progress[1] = std::min(offset[1] / (geo.x() - icon.width() + geo.width() - icon.x() - float(geo.width() - quad[1].x())), 1.0f);
299 } else {
300 lastQuad = quad;
301 }
302
303 offset[0] = -offset[0];
304 offset[1] = -offset[1];
305
306 p_progress[0] = std::abs(p_progress[0]);
307 p_progress[1] = std::abs(p_progress[1]);
308
309 // y values are moved towards the center of the icon
310 quad[0].setY((icon.y() + icon.height() * (quad[0].y() / geo.height()) - (quad[0].y() + geo.y())) * p_progress[0] + quad[0].y());
311 quad[1].setY((icon.y() + icon.height() * (quad[1].y() / geo.height()) - (quad[1].y() + geo.y())) * p_progress[1] + quad[1].y());
312 quad[2].setY((icon.y() + icon.height() * (quad[2].y() / geo.height()) - (quad[2].y() + geo.y())) * p_progress[1] + quad[2].y());
313 quad[3].setY((icon.y() + icon.height() * (quad[3].y() / geo.height()) - (quad[3].y() + geo.y())) * p_progress[0] + quad[3].y());
314
315 quad[0].setX(std::max(minX, float(quad[0].x()) + offset[0]));
316 quad[1].setX(std::max(minX, float(quad[1].x()) + offset[1]));
317 quad[2].setX(std::max(minX, float(quad[2].x()) + offset[1]));
318 quad[3].setX(std::max(minX, float(quad[3].x()) + offset[0]));
319 }
320 } else if (position == Right) {
321 const float width_cube = float(geo.width()) * float(geo.width()) * float(geo.width());
322 const float maxX = icon.x() - geo.x();
323
324 for (WindowQuad &quad : quads) {
325
326 if (quad[0].x() != lastQuad[0].x() || quad[1].x() != lastQuad[1].x()) {
327 quadFactor = quad[0].x() + (geo.width() - quad[0].x()) * progress;
328 offset[0] = (icon.x() + quad[0].x() - geo.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube);
329 quadFactor = quad[1].x() + (geo.width() - quad[1].x()) * progress;
330 offset[1] = (icon.x() + quad[1].x() - geo.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube);
331 p_progress[0] = std::min(offset[0] / (icon.x() - geo.x() - float(quad[0].x())), 1.0f);
332 p_progress[1] = std::min(offset[1] / (icon.x() - geo.x() - float(quad[1].x())), 1.0f);
333 } else {
334 lastQuad = quad;
335 }
336
337 p_progress[0] = std::abs(p_progress[0]);
338 p_progress[1] = std::abs(p_progress[1]);
339
340 // y values are moved towards the center of the icon
341 quad[0].setY((icon.y() + icon.height() * (quad[0].y() / geo.height()) - (quad[0].y() + geo.y())) * p_progress[0] + quad[0].y());
342 quad[1].setY((icon.y() + icon.height() * (quad[1].y() / geo.height()) - (quad[1].y() + geo.y())) * p_progress[1] + quad[1].y());
343 quad[2].setY((icon.y() + icon.height() * (quad[2].y() / geo.height()) - (quad[2].y() + geo.y())) * p_progress[1] + quad[2].y());
344 quad[3].setY((icon.y() + icon.height() * (quad[3].y() / geo.height()) - (quad[3].y() + geo.y())) * p_progress[0] + quad[3].y());
345
346 quad[0].setX(std::min(maxX, float(quad[0].x()) + offset[0]));
347 quad[1].setX(std::min(maxX, float(quad[1].x()) + offset[1]));
348 quad[2].setX(std::min(maxX, float(quad[2].x()) + offset[1]));
349 quad[3].setX(std::min(maxX, float(quad[3].x()) + offset[0]));
350 }
351 }
352 }
353}
354
356{
357 auto animationIt = m_animations.begin();
358 while (animationIt != m_animations.end()) {
359 if ((*animationIt).timeLine.done()) {
360 unredirect(animationIt.key());
361 animationIt = m_animations.erase(animationIt);
362 } else {
363 ++animationIt;
364 }
365 }
366
368
369 // Call the next effect.
371}
372
374{
375 connect(w, &EffectWindow::minimizedChanged, this, [this, w]() {
376 if (w->isMinimized()) {
377 slotWindowMinimized(w);
378 } else {
379 slotWindowUnminimized(w);
380 }
381 });
382}
383
385{
386 m_animations.remove(w);
387}
388
390{
392 return;
393 }
394
395 MagicLampAnimation &animation = m_animations[w];
396
397 if (animation.timeLine.running()) {
398 animation.timeLine.toggleDirection();
399 } else {
402 animation.timeLine.setDuration(m_duration);
403 animation.timeLine.setEasingCurve(QEasingCurve::Linear);
404 }
405
406 redirect(w);
408}
409
411{
413 return;
414 }
415
416 MagicLampAnimation &animation = m_animations[w];
417
418 if (animation.timeLine.running()) {
419 animation.timeLine.toggleDirection();
420 } else {
423 animation.timeLine.setDuration(m_duration);
424 animation.timeLine.setEasingCurve(QEasingCurve::Linear);
425 }
426
427 redirect(w);
429}
430
432{
433 return !m_animations.isEmpty();
434}
435
436} // namespace
437
438#include "moc_magiclamp.cpp"
Representation of a window used by/for Effect classes.
bool isMinimized() const
void minimizedChanged(KWin::EffectWindow *w)
bool isHidden() const
QRectF frameGeometry() const
void windowDeleted(KWin::EffectWindow *w)
bool animationsSupported() const
QList< EffectWindow * > stackingOrder
KWin::VirtualDesktop * currentDesktop
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
QRectF clientArea(clientAreaOption, const Output *screen, const VirtualDesktop *desktop) const
KSharedConfigPtr config() const
void windowAdded(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaintFull()
Effect * activeFullScreenEffect() const
void slotWindowUnminimized(KWin::EffectWindow *w)
void slotWindowAdded(KWin::EffectWindow *w)
void reconfigure(ReconfigureFlags) override
Definition magiclamp.cpp:40
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
Definition magiclamp.cpp:52
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime) override
Definition magiclamp.cpp:61
void postPaintScreen() override
void slotWindowMinimized(KWin::EffectWindow *w)
bool isActive() const override
static bool supported()
Definition magiclamp.cpp:35
void apply(EffectWindow *window, int mask, WindowPaintData &data, WindowQuadList &quads) override
Definition magiclamp.cpp:74
void slotWindowDeleted(KWin::EffectWindow *w)
void unredirect(EffectWindow *window)
void redirect(EffectWindow *window)
void setVertexSnappingMode(RenderGeometry::VertexSnappingMode mode)
bool running() const
Definition timeline.cpp:165
void setDuration(std::chrono::milliseconds duration)
Definition timeline.cpp:103
void setDirection(Direction direction)
Definition timeline.cpp:122
void setEasingCurve(const QEasingCurve &easingCurve)
Definition timeline.cpp:155
void toggleDirection()
Definition timeline.cpp:145
Class representing one area of a window.
WindowQuadList makeGrid(int maxquadsize) const
static QPointF cursorPos()
Definition effect.cpp:478
static double animationTime(const KConfigGroup &cfg, const QString &key, int defaultTime)
Definition effect.cpp:483
@ PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
Definition effect.h:567
@ ReconfigureAll
Definition effect.h:601
@ ScreenArea
Definition globals.h:57
EffectsHandler * effects
EffectWindowVisibleRef visibleRef
Definition magiclamp.h:21