1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
6     SPDX-FileCopyrightText: 2007 Christian Nitschkowski <christian.nitschkowski@kdemail.net>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 
11 #include "thumbnailaside.h"
12 // KConfigSkeleton
13 #include "thumbnailasideconfig.h"
14 
15 #include <KGlobalAccel>
16 #include <KLocalizedString>
17 
18 #include <QAction>
19 #include <QMatrix4x4>
20 
21 namespace KWin
22 {
23 
ThumbnailAsideEffect()24 ThumbnailAsideEffect::ThumbnailAsideEffect()
25 {
26     initConfig<ThumbnailAsideConfig>();
27     QAction* a = new QAction(this);
28     a->setObjectName(QStringLiteral("ToggleCurrentThumbnail"));
29     a->setText(i18n("Toggle Thumbnail for Current Window"));
30     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << Qt::META + Qt::CTRL + Qt::Key_T);
31     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << Qt::META + Qt::CTRL + Qt::Key_T);
32     effects->registerGlobalShortcut(Qt::META + Qt::CTRL + Qt::Key_T, a);
33     connect(a, &QAction::triggered, this, &ThumbnailAsideEffect::toggleCurrentThumbnail);
34 
35     connect(effects, &EffectsHandler::windowClosed, this, &ThumbnailAsideEffect::slotWindowClosed);
36     connect(effects, &EffectsHandler::windowFrameGeometryChanged, this, &ThumbnailAsideEffect::slotWindowFrameGeometryChanged);
37     connect(effects, &EffectsHandler::windowDamaged, this, &ThumbnailAsideEffect::slotWindowDamaged);
38     connect(effects, &EffectsHandler::screenLockingChanged, this, &ThumbnailAsideEffect::repaintAll);
39     reconfigure(ReconfigureAll);
40 }
41 
reconfigure(ReconfigureFlags)42 void ThumbnailAsideEffect::reconfigure(ReconfigureFlags)
43 {
44     ThumbnailAsideConfig::self()->read();
45     maxwidth = ThumbnailAsideConfig::maxWidth();
46     spacing = ThumbnailAsideConfig::spacing();
47     opacity = ThumbnailAsideConfig::opacity()/100.0;
48     screen = ThumbnailAsideConfig::screen(); // Xinerama screen TODO add gui option
49     arrange();
50 }
51 
paintScreen(int mask,const QRegion & region,ScreenPaintData & data)52 void ThumbnailAsideEffect::paintScreen(int mask, const QRegion &region, ScreenPaintData& data)
53 {
54     painted = QRegion();
55     effects->paintScreen(mask, region, data);
56 
57     const QMatrix4x4 projectionMatrix = data.projectionMatrix();
58     Q_FOREACH (const Data & d, windows) {
59         if (painted.intersects(d.rect)) {
60             WindowPaintData data(d.window, projectionMatrix);
61             data.multiplyOpacity(opacity);
62             QRect region;
63             setPositionTransformations(data, region, d.window, d.rect, Qt::KeepAspectRatio);
64             effects->drawWindow(d.window, PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS,
65                                 region, data);
66         }
67     }
68 }
69 
paintWindow(EffectWindow * w,int mask,QRegion region,WindowPaintData & data)70 void ThumbnailAsideEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
71 {
72     effects->paintWindow(w, mask, region, data);
73     painted |= region;
74 }
75 
slotWindowDamaged(EffectWindow * w,const QRegion &)76 void ThumbnailAsideEffect::slotWindowDamaged(EffectWindow* w, const QRegion&)
77 {
78     Q_FOREACH (const Data & d, windows) {
79         if (d.window == w)
80             effects->addRepaint(d.rect);
81     }
82 }
83 
slotWindowFrameGeometryChanged(EffectWindow * w,const QRect & old)84 void ThumbnailAsideEffect::slotWindowFrameGeometryChanged(EffectWindow* w, const QRect& old)
85 {
86     Q_FOREACH (const Data & d, windows) {
87         if (d.window == w) {
88             if (w->size() == old.size())
89                 effects->addRepaint(d.rect);
90             else
91                 arrange();
92             return;
93         }
94     }
95 }
96 
slotWindowClosed(EffectWindow * w)97 void ThumbnailAsideEffect::slotWindowClosed(EffectWindow* w)
98 {
99     removeThumbnail(w);
100 }
101 
toggleCurrentThumbnail()102 void ThumbnailAsideEffect::toggleCurrentThumbnail()
103 {
104     EffectWindow* active = effects->activeWindow();
105     if (active == nullptr)
106         return;
107     if (windows.contains(active))
108         removeThumbnail(active);
109     else
110         addThumbnail(active);
111 }
112 
addThumbnail(EffectWindow * w)113 void ThumbnailAsideEffect::addThumbnail(EffectWindow* w)
114 {
115     repaintAll(); // repaint old areas
116     Data d;
117     d.window = w;
118     d.index = windows.count();
119     windows[ w ] = d;
120     arrange();
121 }
122 
removeThumbnail(EffectWindow * w)123 void ThumbnailAsideEffect::removeThumbnail(EffectWindow* w)
124 {
125     if (!windows.contains(w))
126         return;
127     repaintAll(); // repaint old areas
128     int index = windows[ w ].index;
129     windows.remove(w);
130     for (QHash< EffectWindow*, Data >::Iterator it = windows.begin();
131             it != windows.end();
132             ++it) {
133         Data& d = *it;
134         if (d.index > index)
135             --d.index;
136     }
137     arrange();
138 }
139 
arrange()140 void ThumbnailAsideEffect::arrange()
141 {
142     if (windows.size() == 0)
143         return;
144     int height = 0;
145     QVector< int > pos(windows.size());
146     int mwidth = 0;
147     Q_FOREACH (const Data & d, windows) {
148         height += d.window->height();
149         mwidth = qMax(mwidth, d.window->width());
150         pos[ d.index ] = d.window->height();
151     }
152     int effectiveScreen = screen;
153     if (effectiveScreen == -1) {
154         effectiveScreen = effects->activeScreen();
155     }
156     QRect area = effects->clientArea(MaximizeArea, effectiveScreen, effects->currentDesktop());
157     double scale = area.height() / double(height);
158     scale = qMin(scale, maxwidth / double(mwidth));    // don't be wider than maxwidth pixels
159     int add = 0;
160     for (int i = 0;
161             i < windows.size();
162             ++i) {
163         pos[ i ] = int(pos[ i ] * scale);
164         pos[ i ] += spacing + add; // compute offset of each item
165         add = pos[ i ];
166     }
167     for (QHash< EffectWindow*, Data >::Iterator it = windows.begin();
168             it != windows.end();
169             ++it) {
170         Data& d = *it;
171         int width = int(d.window->width() * scale);
172         d.rect = QRect(area.right() - width, area.bottom() - pos[ d.index ], width, int(d.window->height() * scale));
173     }
174     repaintAll();
175 }
176 
repaintAll()177 void ThumbnailAsideEffect::repaintAll()
178 {
179     Q_FOREACH (const Data & d, windows)
180     effects->addRepaint(d.rect);
181 }
182 
isActive() const183 bool ThumbnailAsideEffect::isActive() const
184 {
185     return !windows.isEmpty() && !effects->isScreenLocked();
186 }
187 
188 } // namespace
189 
190