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 ®ion, 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