1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
*********************************************************************/
21
// based on minimize animation by Rivo Laks <rivolaks@hot.ee>
23
#include "magiclamp.h"
24
#include <kwinconfig.h>
25
#include <kconfiggroup.h>
26
#include <QtCore/QTimeLine>
31
KWIN_EFFECT(magiclamp, MagicLampEffect)
32
KWIN_EFFECT_SUPPORTED(magiclamp, MagicLampEffect::supported())
34
MagicLampEffect::MagicLampEffect()
36
mActiveAnimations = 0;
37
reconfigure(ReconfigureAll);
38
connect(effects, SIGNAL(windowDeleted(EffectWindow*)), this, SLOT(slotWindowDeleted(EffectWindow*)));
39
connect(effects, SIGNAL(windowMinimized(EffectWindow*)), this, SLOT(slotWindowMinimized(EffectWindow*)));
40
connect(effects, SIGNAL(windowUnminimized(EffectWindow*)), this, SLOT(slotWindowUnminimized(EffectWindow*)));
43
bool MagicLampEffect::supported()
45
return effects->compositingType() == OpenGLCompositing;
48
void MagicLampEffect::reconfigure(ReconfigureFlags)
50
KConfigGroup conf = effects->effectConfig("MagicLamp");
51
mAnimationDuration = animationTime(conf, "AnimationDuration", 250);
52
conf = effects->effectConfig("Shadow");
53
int v = conf.readEntry("Size", 5);
54
v += conf.readEntry("Fuzzyness", 10);
55
mShadowOffset[0] = mShadowOffset[1] = -v;
56
mShadowOffset[2] = mShadowOffset[3] = v;
57
v = conf.readEntry("XOffset", 0);
58
mShadowOffset[0] -= v;
59
mShadowOffset[2] += v;
60
v = conf.readEntry("YOffset", 3);
61
mShadowOffset[1] -= v;
62
mShadowOffset[3] += v;
65
void MagicLampEffect::prePaintScreen(ScreenPrePaintData& data, int time)
68
QHash< EffectWindow*, QTimeLine* >::iterator entry = mTimeLineWindows.begin();
70
while (entry != mTimeLineWindows.end()) {
71
QTimeLine *timeline = entry.value();
72
if (entry.key()->isMinimized()) {
73
timeline->setCurrentTime(timeline->currentTime() + time);
74
erase = (timeline->currentValue() >= 1.0f);
76
timeline->setCurrentTime(timeline->currentTime() - time);
77
erase = (timeline->currentValue() <= 0.0f);
81
entry = mTimeLineWindows.erase(entry);
86
mActiveAnimations = mTimeLineWindows.count();
87
if (mActiveAnimations > 0)
88
// We need to mark the screen windows as transformed. Otherwise the
89
// whole screen won't be repainted, resulting in artefacts
90
data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
92
effects->prePaintScreen(data, time);
95
void MagicLampEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
97
// Schedule window for transformation if the animation is still in
99
if (mTimeLineWindows.contains(w)) {
100
// We'll transform this window
101
data.setTransformed();
102
data.quads = data.quads.makeGrid(40);
103
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE);
106
effects->prePaintWindow(w, data, time);
109
void MagicLampEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
111
if (mTimeLineWindows.contains(w)) {
112
// 0 = not minimized, 1 = fully minimized
113
float progress = mTimeLineWindows[w]->currentValue();
115
QRect geo = w->geometry();
116
QRect icon = w->iconGeometry();
117
QRect area = effects->clientArea(ScreenArea, w);
118
IconPosition position = Top;
119
// If there's no icon geometry, minimize to the center of the screen
120
if (!icon.isValid()) {
121
QRect extG = geo.adjusted(mShadowOffset[0], mShadowOffset[1], mShadowOffset[2], mShadowOffset[3]);
122
QPoint pt = QCursor::pos();
123
// focussing inside the window is no good, leads to ugly artefacts, find nearest border
124
if (extG.contains(pt)) {
125
const int d[2][2] = { {pt.x() - extG.x(), extG.right() - pt.x()},
126
{pt.y() - extG.y(), extG.bottom() - pt.y()}
141
case Top: pt.setY(extG.y()); break;
142
case Left: pt.setX(extG.x()); break;
143
case Bottom: pt.setY(extG.bottom()); break;
144
case Right: pt.setX(extG.right()); break;
147
if (pt.y() < geo.y())
149
else if (pt.x() < geo.x())
151
else if (pt.y() > geo.bottom())
153
else if (pt.x() > geo.right())
156
icon = QRect(pt, QSize(0, 0));
158
// Assumption: there is a panel containing the icon position
159
EffectWindow* panel = NULL;
160
foreach (EffectWindow * window, effects->stackingOrder()) {
161
if (!window->isDock())
163
// we have to use intersects as there seems to be a Plasma bug
164
// the published icon geometry might be bigger than the panel
165
if (window->geometry().intersects(icon)) {
171
// Assumption: width of horizonal panel is greater than its height and vice versa
172
// The panel has to border one screen edge, so get it's screen area
173
QRect panelScreen = effects->clientArea(ScreenArea, panel);
174
if (panel->width() >= panel->height()) {
176
if (panel->y() == panelScreen.y())
182
if (panel->x() == panelScreen.x())
188
// we did not find a panel, so it might be autohidden
189
QRect iconScreen = effects->clientArea(ScreenArea, icon.topLeft(), effects->currentDesktop());
190
// as the icon geometry could be overlap a screen edge we use an intersection
191
QRect rect = iconScreen.intersected(icon);
192
// here we need a different assumption: icon geometry borders one screen edge
193
// this assumption might be wrong for e.g. task applet being the only applet in panel
194
// in this case the icon borders two screen edges
195
// there might be a wrong animation, but not distorted
196
if (rect.x() == iconScreen.x()) {
198
} else if (rect.x() + rect.width() == iconScreen.x() + iconScreen.width()) {
200
} else if (rect.y() == iconScreen.y()) {
208
WindowQuadList newQuads;
209
foreach (WindowQuad quad, data.quads) { // krazy:exclude=foreach
210
if (position == Top || position == Bottom) {
211
// quadFactor defines how fast a quad is vertically moved: y coordinates near to window top are slowed down
212
// it is used as quadFactor^3/windowHeight^3
213
// quadFactor is the y position of the quad but is changed towards becomming the window height
214
// by that the factor becomes 1 and has no influence any more
216
// how far has a quad to be moved? Distance between icon and window multiplied by the progress and by the quadFactor
219
// top and bottom progress is the factor which defines how far the x values have to be changed
220
// factor is the current moved y value diveded by the distance between icon and window
222
float bottomProgress;
223
if (position == Bottom) {
224
quadFactor = quad[0].y() + (geo.height() - quad[0].y()) * progress;
225
yOffsetTop = (icon.y() + quad[0].y() - geo.y()) * progress *
226
((quadFactor * quadFactor * quadFactor) / (geo.height() * geo.height() * geo.height()));
227
quadFactor = quad[2].y() + (geo.height() - quad[2].y()) * progress;
228
yOffsetBottom = (icon.y() + quad[2].y() - geo.y()) * progress *
229
((quadFactor * quadFactor * quadFactor) / (geo.height() * geo.height() * geo.height()));
230
topProgress = qMin(yOffsetTop / (icon.y() + icon.height() - geo.y() - (float)(quad[0].y() / geo.height() * geo.height())), 1.0f);
231
bottomProgress = qMin(yOffsetBottom / (icon.y() + icon.height() - geo.y() - (float)(quad[2].y() / geo.height() * geo.height())), 1.0f);
233
quadFactor = geo.height() - quad[0].y() + (quad[0].y()) * progress;
234
yOffsetTop = (geo.y() - icon.height() + geo.height() + quad[0].y() - icon.y()) * progress *
235
((quadFactor * quadFactor * quadFactor) / (geo.height() * geo.height() * geo.height()));
236
quadFactor = geo.height() - quad[2].y() + (quad[2].y()) * progress;
237
yOffsetBottom = (geo.y() - icon.height() + geo.height() + quad[2].y() - icon.y()) * progress *
238
((quadFactor * quadFactor * quadFactor) / (geo.height() * geo.height() * geo.height()));
239
topProgress = qMin(yOffsetTop / (geo.y() - icon.height() + geo.height() - icon.y() -
240
(float)((geo.height() - quad[0].y()) / geo.height() * geo.height())), 1.0f);
241
bottomProgress = qMin(yOffsetBottom / (geo.y() - icon.height() + geo.height() - icon.y() -
242
(float)((geo.height() - quad[2].y()) / geo.height() * geo.height())), 1.0f);
244
if (position == Top) {
250
if (bottomProgress < 0)
251
bottomProgress *= -1;
253
// x values are moved towards the center of the icon
254
quad[0].setX((icon.x() + icon.width()*(quad[0].x() / geo.width()) - (quad[0].x() + geo.x()))*topProgress + quad[0].x());
255
quad[1].setX((icon.x() + icon.width()*(quad[1].x() / geo.width()) - (quad[1].x() + geo.x()))*topProgress + quad[1].x());
256
quad[2].setX((icon.x() + icon.width()*(quad[2].x() / geo.width()) - (quad[2].x() + geo.x()))*bottomProgress + quad[2].x());
257
quad[3].setX((icon.x() + icon.width()*(quad[3].x() / geo.width()) - (quad[3].x() + geo.x()))*bottomProgress + quad[3].x());
259
quad[0].setY(quad[0].y() + yOffsetTop);
260
quad[1].setY(quad[1].y() + yOffsetTop);
261
quad[2].setY(quad[2].y() + yOffsetBottom);
262
quad[3].setY(quad[3].y() + yOffsetBottom);
269
if (position == Right) {
270
quadFactor = quad[0].x() + (geo.width() - quad[0].x()) * progress;
271
xOffsetLeft = (icon.x() + quad[0].x() - geo.x()) * progress *
272
((quadFactor * quadFactor * quadFactor) / (geo.width() * geo.width() * geo.width()));
273
quadFactor = quad[1].x() + (geo.width() - quad[1].x()) * progress;
274
xOffsetRight = (icon.x() + quad[1].x() - geo.x()) * progress *
275
((quadFactor * quadFactor * quadFactor) / (geo.width() * geo.width() * geo.width()));
276
leftProgress = qMin(xOffsetLeft / (icon.x() + icon.width() - geo.x() - (float)(quad[0].x() / geo.width() * geo.width())), 1.0f);
277
rightProgress = qMin(xOffsetRight / (icon.x() + icon.width() - geo.x() - (float)(quad[1].x() / geo.width() * geo.width())), 1.0f);
279
quadFactor = geo.width() - quad[0].x() + (quad[0].x()) * progress;
280
xOffsetLeft = (geo.x() - icon.width() + geo.width() + quad[0].x() - icon.x()) * progress *
281
((quadFactor * quadFactor * quadFactor) / (geo.width() * geo.width() * geo.width()));
282
quadFactor = geo.width() - quad[1].x() + (quad[1].x()) * progress;
283
xOffsetRight = (geo.x() - icon.width() + geo.width() + quad[1].x() - icon.x()) * progress *
284
((quadFactor * quadFactor * quadFactor) / (geo.width() * geo.width() * geo.width()));
285
leftProgress = qMin(xOffsetLeft / (geo.x() - icon.width() + geo.width() - icon.x() -
286
(float)((geo.width() - quad[0].x()) / geo.width() * geo.width())), 1.0f);
287
rightProgress = qMin(xOffsetRight / (geo.x() - icon.width() + geo.width() - icon.x() -
288
(float)((geo.width() - quad[1].x()) / geo.width() * geo.width())), 1.0f);
290
if (position == Left) {
294
if (leftProgress < 0)
296
if (rightProgress < 0)
299
quad[0].setY((icon.y() + icon.height()*(quad[0].y() / geo.height()) - (quad[0].y() + geo.y()))*leftProgress + quad[0].y());
300
quad[1].setY((icon.y() + icon.height()*(quad[1].y() / geo.height()) - (quad[1].y() + geo.y()))*rightProgress + quad[1].y());
301
quad[2].setY((icon.y() + icon.height()*(quad[2].y() / geo.height()) - (quad[2].y() + geo.y()))*rightProgress + quad[2].y());
302
quad[3].setY((icon.y() + icon.height()*(quad[3].y() / geo.height()) - (quad[3].y() + geo.y()))*leftProgress + quad[3].y());
304
quad[0].setX(quad[0].x() + xOffsetLeft);
305
quad[1].setX(quad[1].x() + xOffsetRight);
306
quad[2].setX(quad[2].x() + xOffsetRight);
307
quad[3].setX(quad[3].x() + xOffsetLeft);
309
newQuads.append(quad);
311
data.quads = newQuads;
314
// Call the next effect.
315
effects->paintWindow(w, mask, region, data);
318
void MagicLampEffect::postPaintScreen()
320
if (mActiveAnimations > 0)
321
// Repaint the workspace so that everything would be repainted next time
322
effects->addRepaintFull();
323
mActiveAnimations = mTimeLineWindows.count();
325
// Call the next effect.
326
effects->postPaintScreen();
329
void MagicLampEffect::slotWindowDeleted(EffectWindow* w)
331
delete mTimeLineWindows.take(w);
334
void MagicLampEffect::slotWindowMinimized(EffectWindow* w)
336
if (effects->activeFullScreenEffect())
338
if (!mTimeLineWindows.contains(w)) {
339
mTimeLineWindows.insert(w, new QTimeLine(mAnimationDuration, this));
340
mTimeLineWindows[w]->setCurveShape(QTimeLine::LinearCurve);
342
mTimeLineWindows[w]->setCurrentTime(0);
345
void MagicLampEffect::slotWindowUnminimized(EffectWindow* w)
347
if (effects->activeFullScreenEffect())
349
if (!mTimeLineWindows.contains(w)) {
350
mTimeLineWindows.insert(w, new QTimeLine(mAnimationDuration, this));
351
mTimeLineWindows[w]->setCurveShape(QTimeLine::LinearCurve);
353
mTimeLineWindows[w]->setCurrentTime(mAnimationDuration);