KWin
Loading...
Searching...
No Matches
windowvieweffect.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "windowvieweffect.h"
9#include "windowview1adaptor.h"
10#include "windowviewconfig.h"
11
12#include <QAction>
13#include <QQuickItem>
14#include <QTimer>
15
16#include <KGlobalAccel>
17#include <KLocalizedString>
18
19namespace KWin
20{
21
22static const QString s_dbusServiceName = QStringLiteral("org.kde.KWin.Effect.WindowView1");
23static const QString s_dbusObjectPath = QStringLiteral("/org/kde/KWin/Effect/WindowView1");
24
26 : m_shutdownTimer(new QTimer(this))
27 , m_exposeAction(new QAction(this))
28 , m_exposeAllAction(new QAction(this))
29 , m_exposeClassAction(new QAction(this))
30 , m_exposeClassCurrentDesktopAction(new QAction(this))
31{
32 qmlRegisterUncreatableType<WindowViewEffect>("org.kde.KWin.Effect.WindowView", 1, 0, "WindowView", QStringLiteral("WindowView cannot be created in QML"));
33 WindowViewConfig::instance(effects->config());
34 new WindowView1Adaptor(this);
35
36 QDBusConnection::sessionBus().registerObject(s_dbusObjectPath, this);
37 QDBusConnection::sessionBus().registerService(s_dbusServiceName);
38
39 m_shutdownTimer->setSingleShot(true);
40 connect(m_shutdownTimer, &QTimer::timeout, this, &WindowViewEffect::realDeactivate);
41 connect(effects, &EffectsHandler::screenAboutToLock, this, &WindowViewEffect::realDeactivate);
42
43 setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/windowview/qml/main.qml"))));
44
45 m_exposeAction->setObjectName(QStringLiteral("Expose"));
46 m_exposeAction->setText(i18n("Toggle Present Windows (Current desktop)"));
47 KGlobalAccel::self()->setDefaultShortcut(m_exposeAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F9));
48 KGlobalAccel::self()->setShortcut(m_exposeAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F9));
49 m_shortcut = KGlobalAccel::self()->shortcut(m_exposeAction);
50 connect(m_exposeAction, &QAction::triggered, this, [this]() {
52 });
53
54 m_exposeAllAction->setObjectName(QStringLiteral("ExposeAll"));
55 m_exposeAllAction->setText(i18n("Toggle Present Windows (All desktops)"));
56 KGlobalAccel::self()->setDefaultShortcut(m_exposeAllAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F10) << Qt::Key_LaunchC);
57 KGlobalAccel::self()->setShortcut(m_exposeAllAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F10) << Qt::Key_LaunchC);
58 m_shortcutAll = KGlobalAccel::self()->shortcut(m_exposeAllAction);
59 connect(m_exposeAllAction, &QAction::triggered, this, [this]() {
61 });
62
63 m_exposeClassAction->setObjectName(QStringLiteral("ExposeClass"));
64 m_exposeClassAction->setText(i18n("Toggle Present Windows (Window class)"));
65 KGlobalAccel::self()->setDefaultShortcut(m_exposeClassAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F7));
66 KGlobalAccel::self()->setShortcut(m_exposeClassAction, QList<QKeySequence>() << (Qt::CTRL | Qt::Key_F7));
67 m_shortcutClass = KGlobalAccel::self()->shortcut(m_exposeClassAction);
68 connect(m_exposeClassAction, &QAction::triggered, this, [this]() {
70 });
71
72 m_exposeClassCurrentDesktopAction->setObjectName(QStringLiteral("ExposeClassCurrentDesktop"));
73 m_exposeClassCurrentDesktopAction->setText(i18n("Toggle Present Windows (Window class on current desktop)"));
74 KGlobalAccel::self()->setDefaultShortcut(m_exposeClassCurrentDesktopAction, QList<QKeySequence>()); // no default shortcut
75 KGlobalAccel::self()->setShortcut(m_exposeClassCurrentDesktopAction, QList<QKeySequence>());
76 m_shortcutClassCurrentDesktop = KGlobalAccel::self()->shortcut(m_exposeClassCurrentDesktopAction);
77 connect(m_exposeClassCurrentDesktopAction, &QAction::triggered, this, [this]() {
79 });
80
81 connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, [this](QAction *action, const QKeySequence &seq) {
82 if (action->objectName() == QStringLiteral("Expose")) {
83 m_shortcut.clear();
84 m_shortcut.append(seq);
85 } else if (action->objectName() == QStringLiteral("ExposeAll")) {
86 m_shortcutAll.clear();
87 m_shortcutAll.append(seq);
88 } else if (action->objectName() == QStringLiteral("ExposeClass")) {
89 m_shortcutClass.clear();
90 m_shortcutClass.append(seq);
91 } else if (action->objectName() == QStringLiteral("ExposeClassCurrentDesktop")) {
92 m_shortcutClassCurrentDesktop.clear();
93 m_shortcutClassCurrentDesktop.append(seq);
94 }
95 });
96
97 m_realtimeToggleAction = new QAction(this);
98 connect(m_realtimeToggleAction, &QAction::triggered, this, [this]() {
99 if (m_status == Status::Deactivating) {
100 if (m_partialActivationFactor < 0.5) {
102 } else {
104 }
105 } else if (m_status == Status::Activating) {
106 if (m_partialActivationFactor > 0.5) {
107 activate();
108 } else {
110 }
111 }
112 });
113
115}
116
118{
119 QDBusConnection::sessionBus().unregisterService(s_dbusServiceName);
120 QDBusConnection::sessionBus().unregisterObject(s_dbusObjectPath);
121}
122
124{
125 return m_animationDuration;
126}
127
129{
130 if (m_animationDuration != duration) {
131 m_animationDuration = duration;
133 }
134}
135
137{
138 return m_layout;
139}
140
142{
143 if (m_layout != layout) {
144 m_layout = layout;
145 Q_EMIT layoutChanged();
146 }
147}
148
150{
151 return WindowViewConfig::ignoreMinimized();
152}
153
155{
156 return 70;
157}
158
159void WindowViewEffect::reconfigure(ReconfigureFlags)
160{
161 WindowViewConfig::self()->read();
163 setLayout(WindowViewConfig::layoutMode());
164
165 for (ElectricBorder border : std::as_const(m_borderActivate)) {
166 effects->unreserveElectricBorder(border, this);
167 }
168 for (ElectricBorder border : std::as_const(m_borderActivateAll)) {
169 effects->unreserveElectricBorder(border, this);
170 }
171
172 m_borderActivate.clear();
173 m_borderActivateAll.clear();
174 m_borderActivateClass.clear();
175
176 const auto borderActivate = WindowViewConfig::borderActivate();
177 for (int i : borderActivate) {
178 m_borderActivate.append(ElectricBorder(i));
180 }
181 const auto activateAll = WindowViewConfig::borderActivateAll();
182 for (int i : activateAll) {
183 m_borderActivateAll.append(ElectricBorder(i));
185 }
186 const auto activateClass = WindowViewConfig::borderActivateClass();
187 for (int i : activateClass) {
188 m_borderActivateClass.append(ElectricBorder(i));
190 }
191 const auto activateClassCurrentDesktop = WindowViewConfig::borderActivateClassCurrentDesktop();
192 for (int i : activateClassCurrentDesktop) {
193 m_borderActivateClassCurrentDesktop.append(ElectricBorder(i));
195 }
196
197 auto touchCallback = [this](ElectricBorder border, const QPointF &deltaProgress, const Output *screen) {
198 if (m_status == Status::Active) {
199 return;
200 }
201 if (m_touchBorderActivate.contains(border)) {
203 } else if (m_touchBorderActivateAll.contains(border)) {
205 } else if (m_touchBorderActivateClass.contains(border)) {
207 } else if (m_touchBorderActivateClassCurrentDesktop.contains(border)) {
209 }
210 const int maxDelta = 500; // Arbitrary logical pixels value seems to behave better than scaledScreenSize
211 if (border == ElectricTop || border == ElectricBottom) {
212 partialActivate(std::min(1.0, std::abs(deltaProgress.y()) / maxDelta));
213 } else {
214 partialActivate(std::min(1.0, std::abs(deltaProgress.x()) / maxDelta));
215 }
216 };
217
218 QList<int> touchActivateBorders = WindowViewConfig::touchBorderActivate();
219 for (const int &border : touchActivateBorders) {
220 m_touchBorderActivate.append(ElectricBorder(border));
221 effects->registerRealtimeTouchBorder(ElectricBorder(border), m_realtimeToggleAction, touchCallback);
222 }
223 touchActivateBorders = WindowViewConfig::touchBorderActivateAll();
224 for (const int &border : touchActivateBorders) {
225 m_touchBorderActivateAll.append(ElectricBorder(border));
226 effects->registerRealtimeTouchBorder(ElectricBorder(border), m_realtimeToggleAction, touchCallback);
227 }
228 touchActivateBorders = WindowViewConfig::touchBorderActivateClass();
229 for (const int &border : touchActivateBorders) {
230 m_touchBorderActivateClass.append(ElectricBorder(border));
231 effects->registerRealtimeTouchBorder(ElectricBorder(border), m_realtimeToggleAction, touchCallback);
232 }
233 touchActivateBorders = WindowViewConfig::touchBorderActivateClassCurrentDesktop();
234 for (const int &border : touchActivateBorders) {
235 m_touchBorderActivateClassCurrentDesktop.append(ElectricBorder(border));
236 effects->registerRealtimeTouchBorder(ElectricBorder(border), m_realtimeToggleAction, touchCallback);
237 }
238}
239
241{
242 if (e->type() == QEvent::KeyPress) {
243 // check for global shortcuts
244 // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155)
245 if (m_mode == ModeCurrentDesktop && m_shortcut.contains(e->key() | e->modifiers())) {
247 return;
248 } else if (m_mode == ModeAllDesktops && m_shortcutAll.contains(e->key() | e->modifiers())) {
250 return;
251 } else if (m_mode == ModeWindowClass && m_shortcutClass.contains(e->key() | e->modifiers())) {
253 return;
254 } else if (m_mode == ModeWindowClassCurrentDesktop && m_shortcutClassCurrentDesktop.contains(e->key() | e->modifiers())) {
256 return;
257 } else if (e->key() == Qt::Key_Escape) {
259 }
260 }
262}
263
265{
266 return m_partialActivationFactor;
267}
268
270{
271 if (m_partialActivationFactor != factor) {
272 m_partialActivationFactor = factor;
274 }
275}
276
278{
279 return m_gestureInProgress;
280}
281
283{
284 if (m_gestureInProgress != gesture) {
285 m_gestureInProgress = gesture;
287 }
288}
289
290void WindowViewEffect::activate(const QStringList &windowIds)
291{
293 QList<QUuid> internalIds;
294 internalIds.reserve(windowIds.count());
295 for (const QString &windowId : windowIds) {
296 if (const auto window = effects->findWindow(QUuid(windowId))) {
297 internalIds.append(window->internalId());
298 continue;
299 }
300
301 // On X11, the task manager can pass a list with X11 ids.
302 bool ok;
303 if (const long legacyId = windowId.toLong(&ok); ok) {
304 if (const auto window = effects->findWindow(legacyId)) {
305 internalIds.append(window->internalId());
306 }
307 }
308 }
309 if (!internalIds.isEmpty()) {
310 setSelectedIds(internalIds);
311 m_searchText = QString();
312 setRunning(true);
313 }
314}
315
317{
318 if (effects->isScreenLocked()) {
319 return;
320 }
321
322 m_status = Status::Active;
323 setSelectedIds(QList<QUuid>());
324
327
328 // This one should be the last.
329 m_searchText = QString();
330 setRunning(true);
331}
332
334{
335 if (effects->isScreenLocked()) {
336 return;
337 }
338
339 m_status = Status::Activating;
340
343
344 // This one should be the last.
345 m_searchText = QString();
346 setRunning(true);
347}
348
353
355{
356 const auto screens = effects->screens();
357 for (const auto screen : screens) {
358 if (QuickSceneView *view = viewForScreen(screen)) {
359 QMetaObject::invokeMethod(view->rootItem(), "stop");
360 }
361 }
362 m_shutdownTimer->start(timeout);
363
366}
367
369{
370 m_status = Status::Deactivating;
371
372 setPartialActivationFactor(1.0 - factor);
374}
375
380
381void WindowViewEffect::realDeactivate()
382{
383 setRunning(false);
384 m_status = Status::Inactive;
385}
386
388{
389 if (mode == m_mode) {
390 return;
391 }
392
393 if (mode != ModeWindowGroup) {
394 setSelectedIds(QList<QUuid>());
395 }
396
397 m_mode = mode;
398 Q_EMIT modeChanged();
399}
400
402{
403 if (!isRunning()) {
404 setMode(mode);
405 activate();
406 } else {
407 if (m_mode != mode) {
408 setMode(mode);
409 } else {
411 }
412 }
413}
414
419
421{
423 return true;
424 }
425
426 if (m_borderActivate.contains(border)) {
428 } else if (m_borderActivateAll.contains(border)) {
430 } else if (m_borderActivateClass.contains(border)) {
432 } else if (m_touchBorderActivateClassCurrentDesktop.contains(border)) {
434 } else {
435 return false;
436 }
437
438 return true;
439}
440
441void WindowViewEffect::setSelectedIds(const QList<QUuid> &ids)
442{
443 if (m_windowIds != ids) {
444 m_windowIds = ids;
445 Q_EMIT selectedIdsChanged();
446 }
447}
448
449} // namespace KWin
450
451#include "moc_windowvieweffect.cpp"
Q_SCRIPTABLE KWin::EffectWindow * findWindow(WId id) const
void reserveElectricBorder(ElectricBorder border, Effect *effect)
QList< Output * > screens() const
void unreserveElectricBorder(ElectricBorder border, Effect *effect)
void registerRealtimeTouchBorder(ElectricBorder border, QAction *action, TouchBorderCallback progressCallback)
KSharedConfigPtr config() const
Effect * activeFullScreenEffect() const
void grabbedKeyboardEvent(QKeyEvent *keyEvent) override
Q_INVOKABLE QuickSceneView * viewForScreen(Output *screen) const
void setSource(const QUrl &url)
void setRunning(bool running)
void setPartialActivationFactor(qreal factor)
bool borderActivated(ElectricBorder border) override
void partialActivate(qreal factor)
void setGestureInProgress(bool gesture)
void grabbedKeyboardEvent(QKeyEvent *e) override
void setMode(PresentWindowsMode mode)
void partialActivationFactorChanged()
void deactivate(int timeout)
void setAnimationDuration(int duration)
PresentWindowsMode mode
void reconfigure(ReconfigureFlags) override
int requestedEffectChainPosition() const override
void partialDeactivate(qreal factor)
void toggleMode(PresentWindowsMode mode)
static double animationTime(const KConfigGroup &cfg, const QString &key, int defaultTime)
Definition effect.cpp:483
@ ReconfigureAll
Definition effect.h:601
ElectricBorder
Definition globals.h:60
@ ElectricBottom
Definition globals.h:65
@ ElectricTop
Definition globals.h:61
EffectsHandler * effects