~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/effects/magiclamp/magiclamp.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
 Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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
*********************************************************************/
 
20
 
 
21
// based on minimize animation by Rivo Laks <rivolaks@hot.ee>
 
22
 
 
23
#include "magiclamp.h"
 
24
#include <kwinconfig.h>
 
25
#include <kconfiggroup.h>
 
26
#include <QtCore/QTimeLine>
 
27
 
 
28
namespace KWin
 
29
{
 
30
 
 
31
KWIN_EFFECT(magiclamp, MagicLampEffect)
 
32
KWIN_EFFECT_SUPPORTED(magiclamp, MagicLampEffect::supported())
 
33
 
 
34
MagicLampEffect::MagicLampEffect()
 
35
{
 
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*)));
 
41
}
 
42
 
 
43
bool MagicLampEffect::supported()
 
44
{
 
45
    return effects->compositingType() == OpenGLCompositing;
 
46
}
 
47
 
 
48
void MagicLampEffect::reconfigure(ReconfigureFlags)
 
49
{
 
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;
 
63
}
 
64
 
 
65
void MagicLampEffect::prePaintScreen(ScreenPrePaintData& data, int time)
 
66
{
 
67
 
 
68
    QHash< EffectWindow*, QTimeLine* >::iterator entry = mTimeLineWindows.begin();
 
69
    bool erase = false;
 
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);
 
75
        } else {
 
76
            timeline->setCurrentTime(timeline->currentTime() - time);
 
77
            erase = (timeline->currentValue() <= 0.0f);
 
78
        }
 
79
        if (erase) {
 
80
            delete timeline;
 
81
            entry = mTimeLineWindows.erase(entry);
 
82
        } else
 
83
            ++entry;
 
84
    }
 
85
 
 
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;
 
91
 
 
92
    effects->prePaintScreen(data, time);
 
93
}
 
94
 
 
95
void MagicLampEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
 
96
{
 
97
    // Schedule window for transformation if the animation is still in
 
98
    //  progress
 
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);
 
104
    }
 
105
 
 
106
    effects->prePaintWindow(w, data, time);
 
107
}
 
108
 
 
109
void MagicLampEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
 
110
{
 
111
    if (mTimeLineWindows.contains(w)) {
 
112
        // 0 = not minimized, 1 = fully minimized
 
113
        float progress = mTimeLineWindows[w]->currentValue();
 
114
 
 
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()}
 
127
                };
 
128
                int di = d[1][0];
 
129
                position = Top;
 
130
                if (d[0][0] < di) {
 
131
                    di = d[0][0];
 
132
                    position = Left;
 
133
                }
 
134
                if (d[1][1] < di) {
 
135
                    di = d[1][1];
 
136
                    position = Bottom;
 
137
                }
 
138
                if (d[0][1] < di)
 
139
                    position = Right;
 
140
                switch(position) {
 
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;
 
145
                }
 
146
            } else {
 
147
                if (pt.y() < geo.y())
 
148
                    position = Top;
 
149
                else if (pt.x() < geo.x())
 
150
                    position = Left;
 
151
                else if (pt.y() > geo.bottom())
 
152
                    position = Bottom;
 
153
                else if (pt.x() > geo.right())
 
154
                    position = Right;
 
155
            }
 
156
            icon = QRect(pt, QSize(0, 0));
 
157
        } else {
 
158
            // Assumption: there is a panel containing the icon position
 
159
            EffectWindow* panel = NULL;
 
160
            foreach (EffectWindow * window, effects->stackingOrder()) {
 
161
                if (!window->isDock())
 
162
                    continue;
 
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)) {
 
166
                    panel = window;
 
167
                    break;
 
168
                }
 
169
            }
 
170
            if (panel) {
 
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()) {
 
175
                    // horizontal panel
 
176
                    if (panel->y() == panelScreen.y())
 
177
                        position = Top;
 
178
                    else
 
179
                        position = Bottom;
 
180
                } else {
 
181
                    // vertical panel
 
182
                    if (panel->x() == panelScreen.x())
 
183
                        position = Left;
 
184
                    else
 
185
                        position = Right;
 
186
                }
 
187
            } else {
 
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()) {
 
197
                    position = Left;
 
198
                } else if (rect.x() + rect.width() == iconScreen.x() + iconScreen.width()) {
 
199
                    position = Right;
 
200
                } else if (rect.y() == iconScreen.y()) {
 
201
                    position = Top;
 
202
                } else {
 
203
                    position = Bottom;
 
204
                }
 
205
            }
 
206
        }
 
207
 
 
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
 
215
                float quadFactor;
 
216
                // how far has a quad to be moved? Distance between icon and window multiplied by the progress and by the quadFactor
 
217
                float yOffsetTop;
 
218
                float yOffsetBottom;
 
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
 
221
                float topProgress;
 
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);
 
232
                } else {
 
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);
 
243
                }
 
244
                if (position == Top) {
 
245
                    yOffsetTop *= -1;
 
246
                    yOffsetBottom *= -1;
 
247
                }
 
248
                if (topProgress < 0)
 
249
                    topProgress *= -1;
 
250
                if (bottomProgress < 0)
 
251
                    bottomProgress *= -1;
 
252
 
 
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());
 
258
 
 
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);
 
263
            } else {
 
264
                float quadFactor;
 
265
                float xOffsetLeft;
 
266
                float xOffsetRight;
 
267
                float leftProgress;
 
268
                float rightProgress;
 
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);
 
278
                } else {
 
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);
 
289
                }
 
290
                if (position == Left) {
 
291
                    xOffsetLeft *= -1;
 
292
                    xOffsetRight *= -1;
 
293
                }
 
294
                if (leftProgress < 0)
 
295
                    leftProgress *= -1;
 
296
                if (rightProgress < 0)
 
297
                    rightProgress *= -1;
 
298
 
 
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());
 
303
 
 
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);
 
308
            }
 
309
            newQuads.append(quad);
 
310
        }
 
311
        data.quads = newQuads;
 
312
    }
 
313
 
 
314
    // Call the next effect.
 
315
    effects->paintWindow(w, mask, region, data);
 
316
}
 
317
 
 
318
void MagicLampEffect::postPaintScreen()
 
319
{
 
320
    if (mActiveAnimations > 0)
 
321
        // Repaint the workspace so that everything would be repainted next time
 
322
        effects->addRepaintFull();
 
323
    mActiveAnimations = mTimeLineWindows.count();
 
324
 
 
325
    // Call the next effect.
 
326
    effects->postPaintScreen();
 
327
}
 
328
 
 
329
void MagicLampEffect::slotWindowDeleted(EffectWindow* w)
 
330
{
 
331
    delete mTimeLineWindows.take(w);
 
332
}
 
333
 
 
334
void MagicLampEffect::slotWindowMinimized(EffectWindow* w)
 
335
{
 
336
    if (effects->activeFullScreenEffect())
 
337
        return;
 
338
    if (!mTimeLineWindows.contains(w)) {
 
339
        mTimeLineWindows.insert(w, new QTimeLine(mAnimationDuration, this));
 
340
        mTimeLineWindows[w]->setCurveShape(QTimeLine::LinearCurve);
 
341
    }
 
342
    mTimeLineWindows[w]->setCurrentTime(0);
 
343
}
 
344
 
 
345
void MagicLampEffect::slotWindowUnminimized(EffectWindow* w)
 
346
{
 
347
    if (effects->activeFullScreenEffect())
 
348
        return;
 
349
    if (!mTimeLineWindows.contains(w)) {
 
350
        mTimeLineWindows.insert(w, new QTimeLine(mAnimationDuration, this));
 
351
        mTimeLineWindows[w]->setCurveShape(QTimeLine::LinearCurve);
 
352
    }
 
353
    mTimeLineWindows[w]->setCurrentTime(mAnimationDuration);
 
354
}
 
355
 
 
356
} // namespace