Plasma-workspace

waylandtasksmodel.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Eike Hein <hein@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "waylandtasksmodel.h"
8#include "libtaskmanager_debug.h"
9#include "tasktools.h"
10#include "virtualdesktopinfo.h"
11
12#include <KDirWatch>
13#include <KSharedConfig>
14#include <KWindowSystem>
15
16#include <qwayland-plasma-window-management.h>
17
18#include <QFuture>
19#include <QFutureWatcher>
20#include <QGuiApplication>
21#include <QMimeData>
22#include <QQuickItem>
23#include <QQuickWindow>
24#include <QSet>
25#include <QUrl>
26#include <QUuid>
27#include <QWaylandClientExtension>
28#include <QWindow>
29#include <QtConcurrentRun>
30#include <qpa/qplatformwindow_p.h>
31
32#include <fcntl.h>
33#include <sys/poll.h>
34#include <unistd.h>
35
36namespace TaskManager
37{
38
39class PlasmaWindow : public QObject, public QtWayland::org_kde_plasma_window
40{
42public:
43 PlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id)
44 : org_kde_plasma_window(id)
45 , uuid(uuid)
46 {
47 }
48 ~PlasmaWindow()
49 {
50 destroy();
51 }
52 using state = QtWayland::org_kde_plasma_window_management::state;
53 const QString uuid;
54 QString title;
55 QString appId;
56 QIcon icon;
57 QFlags<state> windowState;
58 QList<QString> virtualDesktops;
59 QRect geometry;
60 QString applicationMenuService;
61 QString applicationMenuObjectPath;
62 QList<QString> activities;
63 quint32 pid;
64 QString resourceName;
65 QPointer<PlasmaWindow> parentWindow;
66 bool wasUnmapped = false;
67
69 void unmapped();
70 void titleChanged();
71 void appIdChanged();
72 void iconChanged();
73 void activeChanged();
74 void minimizedChanged();
75 void maximizedChanged();
76 void fullscreenChanged();
77 void keepAboveChanged();
78 void keepBelowChanged();
79 void onAllDesktopsChanged();
80 void demandsAttentionChanged();
81 void closeableChanged();
82 void minimizeableChanged();
83 void maximizeableChanged();
84 void fullscreenableChanged();
85 void skiptaskbarChanged();
86 void shadeableChanged();
87 void shadedChanged();
88 void movableChanged();
89 void resizableChanged();
90 void virtualDesktopChangeableChanged();
91 void skipSwitcherChanged();
92 void virtualDesktopEntered();
93 void virtualDesktopLeft();
94 void geometryChanged();
95 void skipTaskbarChanged();
96 void applicationMenuChanged();
97 void activitiesChanged();
98 void parentWindowChanged();
99 void initialStateDone();
100
101protected:
102 void org_kde_plasma_window_unmapped() override
103 {
104 wasUnmapped = true;
105 Q_EMIT unmapped();
106 }
107 void org_kde_plasma_window_title_changed(const QString &title) override
108 {
109 this->title = title;
110 Q_EMIT titleChanged();
111 }
112 void org_kde_plasma_window_app_id_changed(const QString &app_id) override
113 {
114 appId = app_id;
115 Q_EMIT appIdChanged();
116 }
117 void org_kde_plasma_window_icon_changed() override
118 {
119 int pipeFds[2];
120 if (pipe2(pipeFds, O_CLOEXEC) != 0) {
121 qCWarning(TASKMANAGER_DEBUG) << "failed creating pipe";
122 return;
123 }
124 get_icon(pipeFds[1]);
125 ::close(pipeFds[1]);
126 auto readIcon = [uuid = uuid](int fd) {
127 auto closeGuard = qScopeGuard([fd]() {
128 ::close(fd);
129 });
130 pollfd pollFd;
131 pollFd.fd = fd;
132 pollFd.events = POLLIN;
133 QByteArray data;
134 while (true) {
135 int ready = poll(&pollFd, 1, 1000);
136 if (ready < 0 && errno != EINTR) {
137 qCWarning(TASKMANAGER_DEBUG) << "polling for icon of window" << uuid << "failed";
138 return QIcon();
139 } else if (ready == 0) {
140 qCWarning(TASKMANAGER_DEBUG) << "time out polling for icon of window" << uuid;
141 return QIcon();
142 } else {
143 char buffer[4096];
144 int n = read(fd, buffer, sizeof(buffer));
145 if (n < 0) {
146 qCWarning(TASKMANAGER_DEBUG) << "error reading icon of window" << uuid;
147 return QIcon();
148 } else if (n > 0) {
149 data.append(buffer, n);
150 } else {
151 QIcon icon;
152 QDataStream ds(data);
153 ds >> icon;
154 return icon;
155 }
156 }
157 }
158 };
159 QFuture<QIcon> future = QtConcurrent::run(readIcon, pipeFds[0]);
160 auto watcher = new QFutureWatcher<QIcon>();
161 connect(watcher, &QFutureWatcher<QIcon>::finished, this, [this, watcher] {
162 icon = watcher->future().result();
163 Q_EMIT iconChanged();
164 });
166 watcher->setFuture(future);
167 }
168 void org_kde_plasma_window_themed_icon_name_changed(const QString &name) override
169 {
170 icon = QIcon::fromTheme(name);
171 Q_EMIT iconChanged();
172 }
173 void org_kde_plasma_window_state_changed(uint32_t flags) override
174 {
175 auto diff = windowState ^ flags;
176 if (diff & state::state_active) {
177 windowState.setFlag(state::state_active, flags & state::state_active);
178 Q_EMIT activeChanged();
179 }
180 if (diff & state::state_minimized) {
181 windowState.setFlag(state::state_minimized, flags & state::state_minimized);
182 Q_EMIT minimizedChanged();
183 }
184 if (diff & state::state_maximized) {
185 windowState.setFlag(state::state_maximized, flags & state::state_maximized);
186 Q_EMIT maximizedChanged();
187 }
188 if (diff & state::state_fullscreen) {
189 windowState.setFlag(state::state_fullscreen, flags & state::state_fullscreen);
190 Q_EMIT fullscreenChanged();
191 }
192 if (diff & state::state_keep_above) {
193 windowState.setFlag(state::state_keep_above, flags & state::state_keep_above);
194 Q_EMIT keepAboveChanged();
195 }
196 if (diff & state::state_keep_below) {
197 windowState.setFlag(state::state_keep_below, flags & state::state_keep_below);
198 Q_EMIT keepBelowChanged();
199 }
200 if (diff & state::state_on_all_desktops) {
201 windowState.setFlag(state::state_on_all_desktops, flags & state::state_on_all_desktops);
202 Q_EMIT onAllDesktopsChanged();
203 }
204 if (diff & state::state_demands_attention) {
205 windowState.setFlag(state::state_demands_attention, flags & state::state_demands_attention);
206 Q_EMIT demandsAttentionChanged();
207 }
208 if (diff & state::state_closeable) {
209 windowState.setFlag(state::state_closeable, flags & state::state_closeable);
210 Q_EMIT closeableChanged();
211 }
212 if (diff & state::state_minimizable) {
213 windowState.setFlag(state::state_minimizable, flags & state::state_minimizable);
214 Q_EMIT minimizeableChanged();
215 }
216 if (diff & state::state_maximizable) {
217 windowState.setFlag(state::state_maximizable, flags & state::state_maximizable);
218 Q_EMIT maximizeableChanged();
219 }
220 if (diff & state::state_fullscreenable) {
221 windowState.setFlag(state::state_fullscreenable, flags & state::state_fullscreenable);
222 Q_EMIT fullscreenableChanged();
223 }
224 if (diff & state::state_skiptaskbar) {
225 windowState.setFlag(state::state_skiptaskbar, flags & state::state_skiptaskbar);
226 Q_EMIT skipTaskbarChanged();
227 }
228 if (diff & state::state_shadeable) {
229 windowState.setFlag(state::state_shadeable, flags & state::state_shadeable);
230 Q_EMIT shadeableChanged();
231 }
232 if (diff & state::state_shaded) {
233 windowState.setFlag(state::state_shaded, flags & state::state_shaded);
234 Q_EMIT shadedChanged();
235 }
236 if (diff & state::state_movable) {
237 windowState.setFlag(state::state_movable, flags & state::state_movable);
238 Q_EMIT movableChanged();
239 }
240 if (diff & state::state_resizable) {
241 windowState.setFlag(state::state_resizable, flags & state::state_resizable);
242 Q_EMIT resizableChanged();
243 }
244 if (diff & state::state_virtual_desktop_changeable) {
245 windowState.setFlag(state::state_virtual_desktop_changeable, flags & state::state_virtual_desktop_changeable);
246 Q_EMIT virtualDesktopChangeableChanged();
247 }
248 if (diff & state::state_skipswitcher) {
249 windowState.setFlag(state::state_skipswitcher, flags & state::state_skipswitcher);
250 Q_EMIT skipSwitcherChanged();
251 }
252 }
253 void org_kde_plasma_window_virtual_desktop_entered(const QString &id) override
254 {
255 virtualDesktops.push_back(id);
256 Q_EMIT virtualDesktopEntered();
257 }
258
259 void org_kde_plasma_window_virtual_desktop_left(const QString &id) override
260 {
261 virtualDesktops.removeAll(id);
262 Q_EMIT virtualDesktopLeft();
263 }
264 void org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) override
265 {
266 geometry = QRect(x, y, width, height);
267 Q_EMIT geometryChanged();
268 }
269 void org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) override
270
271 {
272 applicationMenuService = service_name;
273 applicationMenuObjectPath = object_path;
274 Q_EMIT applicationMenuChanged();
275 }
276 void org_kde_plasma_window_activity_entered(const QString &id) override
277 {
278 activities.push_back(id);
279 Q_EMIT activitiesChanged();
280 }
281 void org_kde_plasma_window_activity_left(const QString &id) override
282 {
283 activities.removeAll(id);
284 Q_EMIT activitiesChanged();
285 }
286 void org_kde_plasma_window_pid_changed(uint32_t pid) override
287 {
288 this->pid = pid;
289 }
290 void org_kde_plasma_window_resource_name_changed(const QString &resource_name) override
291 {
292 resourceName = resource_name;
293 }
294 void org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) override
295 {
296 PlasmaWindow *parentWindow = nullptr;
297 if (parent) {
298 parentWindow = dynamic_cast<PlasmaWindow *>(PlasmaWindow::fromObject(parent));
299 }
300 setParentWindow(parentWindow);
301 }
302 void org_kde_plasma_window_initial_state() override
303 {
304 Q_EMIT initialStateDone();
305 }
306
307private:
308 void setParentWindow(PlasmaWindow *parent)
309 {
310 const auto old = parentWindow;
311 QObject::disconnect(parentWindowUnmappedConnection);
312 QObject::disconnect(parentWindowDestroyedConnection);
313
314 if (parent && !parent->wasUnmapped) {
315 parentWindow = QPointer<PlasmaWindow>(parent);
316 parentWindowUnmappedConnection = QObject::connect(parent, &PlasmaWindow::unmapped, this, [this] {
317 setParentWindow(nullptr);
318 });
319 // QPointer nulling itself wouldn't cause the change signal to be emitted.
320 parentWindowDestroyedConnection = QObject::connect(parent, &QObject::destroyed, this, &PlasmaWindow::parentWindowChanged);
321 } else {
322 parentWindow = QPointer<PlasmaWindow>();
323 parentWindowUnmappedConnection = QMetaObject::Connection();
324 parentWindowDestroyedConnection = QMetaObject::Connection();
325 }
326
327 if (parentWindow.data() != old.data()) {
328 Q_EMIT parentWindowChanged();
329 }
330 }
331
332 QMetaObject::Connection parentWindowUnmappedConnection;
333 QMetaObject::Connection parentWindowDestroyedConnection;
334};
335
336class PlasmaStackingOrder;
337
338class PlasmaWindowManagement : public QWaylandClientExtensionTemplate<PlasmaWindowManagement>, public QtWayland::org_kde_plasma_window_management
339{
340 Q_OBJECT
341public:
342 static constexpr int s_version = 17;
343 PlasmaWindowManagement()
344 : QWaylandClientExtensionTemplate(s_version)
345 {
346 connect(this, &QWaylandClientExtension::activeChanged, this, [this] {
347 if (!isActive()) {
348 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object()));
349 } else if (QtWayland::org_kde_plasma_window_management::version() >= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_GET_STACKING_ORDER_SINCE_VERSION) {
350 // fetch the stacking order
351 org_kde_plasma_window_management_stacking_order_changed_2();
352 }
353 });
354 initialize();
355 }
356 ~PlasmaWindowManagement()
357 {
358 if (isActive()) {
359 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object()));
360 }
361 }
362 void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override
363 {
364 Q_UNUSED(id)
365 Q_EMIT windowCreated(new PlasmaWindow(uuid, get_window_by_uuid(uuid)));
366 }
367 void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override
368 {
369 Q_EMIT stackingOrderChanged(uuids.split(QLatin1Char(';')));
370 }
371 void org_kde_plasma_window_management_stacking_order_changed_2() override;
372Q_SIGNALS:
373 void windowCreated(PlasmaWindow *window);
374 void stackingOrderChanged(const QList<QString> &uuids);
375};
376
377class PlasmaStackingOrder : public QtWayland::org_kde_plasma_stacking_order
378{
379public:
380 explicit PlasmaStackingOrder(PlasmaWindowManagement *windowManagement, ::org_kde_plasma_stacking_order *id)
381 : QtWayland::org_kde_plasma_stacking_order(id)
382 , m_windowManagement(windowManagement)
383 {
384 }
385 ~PlasmaStackingOrder()
386 {
387 org_kde_plasma_stacking_order_destroy(object());
388 }
389
390 void org_kde_plasma_stacking_order_window(const QString &uuid) override
391 {
392 m_uuids.push_back(uuid);
393 }
394
395 void org_kde_plasma_stacking_order_done() override
396 {
397 Q_EMIT m_windowManagement->stackingOrderChanged(m_uuids);
398 delete this;
399 }
400
401 PlasmaWindowManagement *const m_windowManagement;
402 QList<QString> m_uuids;
403};
404
405void PlasmaWindowManagement::org_kde_plasma_window_management_stacking_order_changed_2()
406{
407 new PlasmaStackingOrder(this, org_kde_plasma_window_management_get_stacking_order(object()));
408}
409
410class Q_DECL_HIDDEN WaylandTasksModel::Private
411{
412public:
413 Private(WaylandTasksModel *q);
415 QHash<PlasmaWindow *, QTime> lastActivated;
416 PlasmaWindow *activeWindow = nullptr;
417 std::vector<std::unique_ptr<PlasmaWindow>> windows;
418 // key=transient child, value=leader
420 // key=leader, values=transient children
421 QMultiHash<PlasmaWindow *, PlasmaWindow *> transientsDemandingAttention;
422 std::unique_ptr<PlasmaWindowManagement> windowManagement;
423 KSharedConfig::Ptr rulesConfig;
424 KDirWatch *configWatcher = nullptr;
425 VirtualDesktopInfo *virtualDesktopInfo = nullptr;
426 static QUuid uuid;
427 QList<QString> stackingOrder;
428
429 void init();
430 void initWayland();
431 auto findWindow(PlasmaWindow *window) const;
432 void addWindow(PlasmaWindow *window);
433
434 const AppData &appData(PlasmaWindow *window);
435
436 QIcon icon(PlasmaWindow *window);
437
438 static QString mimeType();
439 static QString groupMimeType();
440
441 void dataChanged(PlasmaWindow *window, int role);
442 void dataChanged(PlasmaWindow *window, const QList<int> &roles);
443
444private:
445 WaylandTasksModel *q;
446};
447
448QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid();
449
450WaylandTasksModel::Private::Private(WaylandTasksModel *q)
451 : q(q)
452{
453}
454
455void WaylandTasksModel::Private::init()
456{
457 auto clearCacheAndRefresh = [this] {
458 if (windows.empty()) {
459 return;
460 }
461
462 appDataCache.clear();
463
464 // Emit changes of all roles satisfied from app data cache.
465 Q_EMIT q->dataChanged(q->index(0, 0),
466 q->index(windows.size() - 1, 0),
467 QList<int>{Qt::DecorationRole,
468 AbstractTasksModel::AppId,
469 AbstractTasksModel::AppName,
470 AbstractTasksModel::GenericName,
471 AbstractTasksModel::LauncherUrl,
472 AbstractTasksModel::LauncherUrlWithoutIcon,
473 AbstractTasksModel::CanLaunchNewInstance,
474 AbstractTasksModel::SkipTaskbar});
475 };
476
477 rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc"));
478 configWatcher = new KDirWatch(q);
479
481 configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc"));
482 }
483
484 auto rulesConfigChange = [this, clearCacheAndRefresh] {
485 rulesConfig->reparseConfiguration();
486 clearCacheAndRefresh();
487 };
488
489 QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange);
490 QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange);
491 QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange);
492
493 virtualDesktopInfo = new VirtualDesktopInfo(q);
494
495 initWayland();
496}
497
498void WaylandTasksModel::Private::initWayland()
499{
501 return;
502 }
503
504 windowManagement = std::make_unique<PlasmaWindowManagement>();
505
506 QObject::connect(windowManagement.get(), &PlasmaWindowManagement::activeChanged, q, [this] {
507 q->beginResetModel();
508 windows.clear();
509 q->endResetModel();
510 });
511
512 QObject::connect(windowManagement.get(), &PlasmaWindowManagement::windowCreated, q, [this](PlasmaWindow *window) {
513 connect(window, &PlasmaWindow::initialStateDone, q, [this, window] {
514 addWindow(window);
515 });
516 });
517
518 QObject::connect(windowManagement.get(), &PlasmaWindowManagement::stackingOrderChanged, q, [this](const QList<QString> &order) {
519 stackingOrder = order;
520 for (const auto &window : std::as_const(windows)) {
521 this->dataChanged(window.get(), StackingOrder);
522 }
523 });
524}
525
526auto WaylandTasksModel::Private::findWindow(PlasmaWindow *window) const
527{
528 return std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr<PlasmaWindow> &candidate) {
529 return candidate.get() == window;
530 });
531}
532
533void WaylandTasksModel::Private::addWindow(PlasmaWindow *window)
534{
535 if (findWindow(window) != windows.end()) {
536 return;
537 }
538
539 auto removeWindow = [window, this] {
540 // findWindow() is const, we need a non-const iterator for the "take" below.
541 auto it = std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr<PlasmaWindow> &candidate) {
542 return candidate.get() == window;
543 });
544
545 if (it != windows.end()) {
546 const int row = it - windows.begin();
547 q->beginRemoveRows(QModelIndex(), row, row);
548
549 // "take"... don't use just erase() here as it will destroy the unique_ptr and thus
550 // the window, which will trigger QObject::destroyed handlers which might
551 // add/remove items from the model whilst we're removing rows ourselves.
552 const std::unique_ptr<PlasmaWindow> removedWindow = std::move(*it);
553 windows.erase(it);
554
555 transientsDemandingAttention.remove(window);
556 appDataCache.remove(window);
557 lastActivated.remove(window);
558 q->endRemoveRows();
559 // Removing a transient might change the demands attention state of the leader.
560 if (transients.remove(window)) {
561 if (PlasmaWindow *leader = transientsDemandingAttention.key(window)) {
562 transientsDemandingAttention.remove(leader, window);
563 dataChanged(leader, QVector<int>{IsDemandingAttention});
564 }
565 }
566 }
567
568 if (activeWindow == window) {
569 activeWindow = nullptr;
570 }
571 };
572
573 QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow);
574
575 QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] {
576 this->dataChanged(window, Qt::DisplayRole);
577 });
578
579 QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] {
580 // The icon in the AppData struct might come from PlasmaWindow if it wasn't
581 // filled in by windowUrlFromMetadata+appDataFromUrl.
582 // TODO: Don't evict the cache unnecessarily if this isn't the case. As icons
583 // are currently very static on Wayland, this eviction is unlikely to happen
584 // frequently as of now.
585 appDataCache.remove(window);
586 this->dataChanged(window, Qt::DecorationRole);
587 });
588
589 QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] {
590 // The AppData struct in the cache is derived from this and needs
591 // to be evicted in favor of a fresh struct based on the changed
592 // window metadata.
593 appDataCache.remove(window);
594
595 // Refresh roles satisfied from the app data cache.
596 this->dataChanged(window,
597 QList<int>{Qt::DecorationRole, AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance});
598 });
599
600 if (window->windowState & PlasmaWindow::state::state_active) {
601 PlasmaWindow *effectiveActive = window;
602 while (effectiveActive->parentWindow) {
603 effectiveActive = effectiveActive->parentWindow;
604 }
605
606 lastActivated[effectiveActive] = QTime::currentTime();
607 activeWindow = effectiveActive;
608 }
609
610 QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] {
611 const bool active = window->windowState & PlasmaWindow::state::state_active;
612
613 PlasmaWindow *effectiveWindow = window;
614
615 while (effectiveWindow->parentWindow) {
616 effectiveWindow = effectiveWindow->parentWindow;
617 }
618
619 if (active) {
620 lastActivated[effectiveWindow] = QTime::currentTime();
621
622 if (activeWindow != effectiveWindow) {
623 activeWindow = effectiveWindow;
624 this->dataChanged(effectiveWindow, IsActive);
625 }
626 } else {
627 if (activeWindow == effectiveWindow) {
628 activeWindow = nullptr;
629 this->dataChanged(effectiveWindow, IsActive);
630 }
631 }
632 });
633
634 QObject::connect(window, &PlasmaWindow::parentWindowChanged, q, [window, this] {
635 PlasmaWindow *leader = window->parentWindow.data();
636
637 // Migrate demanding attention to new leader.
638 if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) {
639 if (auto *oldLeader = transientsDemandingAttention.key(window)) {
640 if (window->parentWindow != oldLeader) {
641 transientsDemandingAttention.remove(oldLeader, window);
642 transientsDemandingAttention.insert(leader, window);
643 dataChanged(oldLeader, QVector<int>{IsDemandingAttention});
644 dataChanged(leader, QVector<int>{IsDemandingAttention});
645 }
646 }
647 }
648
649 if (transients.remove(window)) {
650 if (leader) { // leader change.
651 transients.insert(window, leader);
652 } else { // lost a leader, add to regular windows list.
653 dataChanged(window, SkipTaskbar);
654 }
655 } else if (leader) { // gained a leader, remove from regular windows list.
656 transients.insert(window, leader);
657 dataChanged(window, SkipTaskbar);
658 }
659 });
660
661 QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] {
662 this->dataChanged(window, IsClosable);
663 });
664
665 QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] {
666 this->dataChanged(window, IsMovable);
667 });
668
669 QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] {
670 this->dataChanged(window, IsResizable);
671 });
672
673 QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] {
674 this->dataChanged(window, IsFullScreenable);
675 });
676
677 QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] {
678 this->dataChanged(window, IsFullScreen);
679 });
680
681 QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] {
682 this->dataChanged(window, IsMaximizable);
683 });
684
685 QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] {
686 this->dataChanged(window, IsMaximized);
687 });
688
689 QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] {
690 this->dataChanged(window, IsMinimizable);
691 });
692
693 QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] {
694 this->dataChanged(window, IsMinimized);
695 });
696
697 QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] {
698 this->dataChanged(window, IsKeepAbove);
699 });
700
701 QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] {
702 this->dataChanged(window, IsKeepBelow);
703 });
704
705 QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] {
706 this->dataChanged(window, IsShadeable);
707 });
708
709 QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] {
710 this->dataChanged(window, IsVirtualDesktopsChangeable);
711 });
712
713 QObject::connect(window, &PlasmaWindow::virtualDesktopEntered, q, [window, this] {
714 this->dataChanged(window, VirtualDesktops);
715
716 // If the count has changed from 0, the window may no longer be on all virtual
717 // desktops.
718 if (window->virtualDesktops.count() > 0) {
719 this->dataChanged(window, IsOnAllVirtualDesktops);
720 }
721 });
722
723 QObject::connect(window, &PlasmaWindow::virtualDesktopLeft, q, [window, this] {
724 this->dataChanged(window, VirtualDesktops);
725
726 // If the count has changed to 0, the window is now on all virtual desktops.
727 if (window->virtualDesktops.count() == 0) {
728 this->dataChanged(window, IsOnAllVirtualDesktops);
729 }
730 });
731
732 QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] {
733 this->dataChanged(window, QList<int>{Geometry, ScreenGeometry});
734 });
735
736 QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] {
737 // Changes to a transient's state might change demands attention state for leader.
738 if (auto *leader = transients.value(window)) {
739 if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) {
740 if (!transientsDemandingAttention.values(leader).contains(window)) {
741 transientsDemandingAttention.insert(leader, window);
742 this->dataChanged(leader, QVector<int>{IsDemandingAttention});
743 }
744 } else if (transientsDemandingAttention.remove(window)) {
745 this->dataChanged(leader, QVector<int>{IsDemandingAttention});
746 }
747 } else {
748 this->dataChanged(window, QVector<int>{IsDemandingAttention});
749 }
750 });
751
752 QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] {
753 this->dataChanged(window, SkipTaskbar);
754 });
755
756 QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] {
757 this->dataChanged(window, QList<int>{ApplicationMenuServiceName, ApplicationMenuObjectPath});
758 });
759
760 QObject::connect(window, &PlasmaWindow::activitiesChanged, q, [window, this] {
761 this->dataChanged(window, Activities);
762 });
763
764 // Handle transient.
765 if (PlasmaWindow *leader = window->parentWindow.data()) {
766 transients.insert(window, leader);
767
768 // Update demands attention state for leader.
769 if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) {
770 transientsDemandingAttention.insert(leader, window);
771 dataChanged(leader, QVector<int>{IsDemandingAttention});
772 }
773 }
774
775 const int count = windows.size();
776
777 q->beginInsertRows(QModelIndex(), count, count);
778
779 windows.emplace_back(window);
780
781 q->endInsertRows();
782}
783
784const AppData &WaylandTasksModel::Private::appData(PlasmaWindow *window)
785{
786 static_assert(!std::is_trivially_copy_assignable_v<AppData>);
787 if (auto it = appDataCache.constFind(window); it != appDataCache.constEnd()) {
788 return *it;
789 }
790
791 return *appDataCache.emplace(window, appDataFromUrl(windowUrlFromMetadata(window->appId, window->pid, rulesConfig, window->resourceName)));
792}
793
794QIcon WaylandTasksModel::Private::icon(PlasmaWindow *window)
795{
796 const AppData &app = appData(window);
797
798 if (!app.icon.isNull()) {
799 return app.icon;
800 }
801
802 appDataCache[window].icon = window->icon;
803
804 return window->icon;
805}
806
807QString WaylandTasksModel::Private::mimeType()
808{
809 // Use a unique format id to make this intentionally useless for
810 // cross-process DND.
811 return u"windowsystem/winid+" + uuid.toString();
812}
813
814QString WaylandTasksModel::Private::groupMimeType()
815{
816 // Use a unique format id to make this intentionally useless for
817 // cross-process DND.
818 return u"windowsystem/multiple-winids+" + uuid.toString();
819}
820
821void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, int role)
822{
823 auto it = findWindow(window);
824 if (it == windows.end()) {
825 return;
826 }
827 QModelIndex idx = q->index(it - windows.begin());
828 Q_EMIT q->dataChanged(idx, idx, QList<int>{role});
829}
830
831void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, const QList<int> &roles)
832{
833 auto it = findWindow(window);
834 if (it == windows.end()) {
835 return;
836 }
837 QModelIndex idx = q->index(it - windows.begin());
838 Q_EMIT q->dataChanged(idx, idx, roles);
839}
840
841WaylandTasksModel::WaylandTasksModel(QObject *parent)
842 : AbstractWindowTasksModel(parent)
843 , d(new Private(this))
844{
845 d->init();
846}
847
848WaylandTasksModel::~WaylandTasksModel()
849{
850 for (auto &window : d->windows) {
851 QObject::disconnect(window.get(), &PlasmaWindow::parentWindowChanged, this, nullptr);
852 }
853}
854
855QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const
856{
857 // Note: when index is valid, its row >= 0, so casting to unsigned is safe
858 if (!index.isValid() || static_cast<size_t>(index.row()) >= d->windows.size()) {
859 return QVariant();
860 }
861
862 PlasmaWindow *window = d->windows.at(index.row()).get();
863
864 if (role == Qt::DisplayRole) {
865 return window->title;
866 } else if (role == Qt::DecorationRole) {
867 return d->icon(window);
868 } else if (role == AppId) {
869 const QString &id = d->appData(window).id;
870
871 if (id.isEmpty()) {
872 return window->appId;
873 } else {
874 return id;
875 }
876 } else if (role == AppName) {
877 return d->appData(window).name;
878 } else if (role == GenericName) {
879 return d->appData(window).genericName;
880 } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) {
881 return d->appData(window).url;
882 } else if (role == WinIdList) {
883 return QVariantList{window->uuid};
884 } else if (role == MimeType) {
885 return d->mimeType();
886 } else if (role == MimeData) {
887 return window->uuid;
888 } else if (role == IsWindow) {
889 return true;
890 } else if (role == IsActive) {
891 return (window == d->activeWindow);
892 } else if (role == IsClosable) {
893 return window->windowState.testFlag(PlasmaWindow::state::state_closeable);
894 } else if (role == IsMovable) {
895 return window->windowState.testFlag(PlasmaWindow::state::state_movable);
896 } else if (role == IsResizable) {
897 return window->windowState.testFlag(PlasmaWindow::state::state_resizable);
898 } else if (role == IsMaximizable) {
899 return window->windowState.testFlag(PlasmaWindow::state::state_maximizable);
900 } else if (role == IsMaximized) {
901 return window->windowState.testFlag(PlasmaWindow::state::state_maximized);
902 } else if (role == IsMinimizable) {
903 return window->windowState.testFlag(PlasmaWindow::state::state_minimizable);
904 } else if (role == IsMinimized || role == IsHidden) {
905 return window->windowState.testFlag(PlasmaWindow::state::state_minimized);
906 } else if (role == IsKeepAbove) {
907 return window->windowState.testFlag(PlasmaWindow::state::state_keep_above);
908 } else if (role == IsKeepBelow) {
909 return window->windowState.testFlag(PlasmaWindow::state::state_keep_below);
910 } else if (role == IsFullScreenable) {
911 return window->windowState.testFlag(PlasmaWindow::state::state_fullscreenable);
912 } else if (role == IsFullScreen) {
913 return window->windowState.testFlag(PlasmaWindow::state::state_fullscreen);
914 } else if (role == IsShadeable) {
915 return window->windowState.testFlag(PlasmaWindow::state::state_shadeable);
916 } else if (role == IsShaded) {
917 return window->windowState.testFlag(PlasmaWindow::state::state_shaded);
918 } else if (role == IsVirtualDesktopsChangeable) {
919 return window->windowState.testFlag(PlasmaWindow::state::state_virtual_desktop_changeable);
920 } else if (role == VirtualDesktops) {
921 return window->virtualDesktops;
922 } else if (role == IsOnAllVirtualDesktops) {
923 return window->virtualDesktops.isEmpty();
924 } else if (role == Geometry) {
925 return window->geometry;
926 } else if (role == ScreenGeometry) {
927 return screenGeometry(window->geometry.center());
928 } else if (role == Activities) {
929 return window->activities;
930 } else if (role == IsDemandingAttention) {
931 return window->windowState.testFlag(PlasmaWindow::state::state_demands_attention) || d->transientsDemandingAttention.contains(window);
932 } else if (role == SkipTaskbar) {
933 return window->windowState.testFlag(PlasmaWindow::state::state_skiptaskbar) || d->appData(window).skipTaskbar || d->transients.contains(window);
934 } else if (role == SkipPager) {
935 // FIXME Implement.
936 } else if (role == AppPid) {
937 return window->pid;
938 } else if (role == StackingOrder) {
939 return d->stackingOrder.indexOf(window->uuid);
940 } else if (role == LastActivated) {
941 if (d->lastActivated.contains(window)) {
942 return d->lastActivated.value(window);
943 }
944 } else if (role == ApplicationMenuObjectPath) {
945 return window->applicationMenuObjectPath;
946 } else if (role == ApplicationMenuServiceName) {
947 return window->applicationMenuService;
948 } else if (role == CanLaunchNewInstance) {
949 return canLauchNewInstance(d->appData(window));
950 }
951
952 return AbstractTasksModel::data(index, role);
953}
954
955int WaylandTasksModel::rowCount(const QModelIndex &parent) const
956{
957 return parent.isValid() ? 0 : d->windows.size();
958}
959
960QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const
961{
962 return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row).get()) : QModelIndex();
963}
964
965void WaylandTasksModel::requestActivate(const QModelIndex &index)
966{
967 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
968 return;
969 }
970
971 PlasmaWindow *window = d->windows.at(index.row()).get();
972
973 // Pull forward any transient demanding attention.
974 if (auto *transientDemandingAttention = d->transientsDemandingAttention.value(window)) {
975 window = transientDemandingAttention;
976 } else {
977 // TODO Shouldn't KWin take care of that?
978 // Bringing a transient to the front usually brings its parent with it
979 // but focus is not handled properly.
980 // TODO take into account d->lastActivation instead
981 // of just taking the first one.
982 while (d->transients.key(window)) {
983 window = d->transients.key(window);
984 }
985 }
986
987 window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
988}
989
990void WaylandTasksModel::requestNewInstance(const QModelIndex &index)
991{
992 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
993 return;
994 }
995
996 runApp(d->appData(d->windows.at(index.row()).get()));
997}
998
999void WaylandTasksModel::requestOpenUrls(const QModelIndex &index, const QList<QUrl> &urls)
1000{
1001 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent) || urls.isEmpty()) {
1002 return;
1003 }
1004
1005 runApp(d->appData(d->windows.at(index.row()).get()), urls);
1006}
1007
1008void WaylandTasksModel::requestClose(const QModelIndex &index)
1009{
1010 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1011 return;
1012 }
1013
1014 d->windows.at(index.row())->close();
1015}
1016
1017void WaylandTasksModel::requestMove(const QModelIndex &index)
1018{
1019 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1020 return;
1021 }
1022
1023 auto &window = d->windows.at(index.row());
1024
1025 window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1026 window->request_move();
1027}
1028
1029void WaylandTasksModel::requestResize(const QModelIndex &index)
1030{
1031 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1032 return;
1033 }
1034
1035 auto &window = d->windows.at(index.row());
1036
1037 window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1038 window->request_resize();
1039}
1040
1041void WaylandTasksModel::requestToggleMinimized(const QModelIndex &index)
1042{
1043 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1044 return;
1045 }
1046
1047 auto &window = d->windows.at(index.row());
1048
1049 if (window->windowState & PlasmaWindow::state::state_minimized) {
1050 window->set_state(PlasmaWindow::state::state_minimized, 0);
1051 } else {
1052 window->set_state(PlasmaWindow::state::state_minimized, PlasmaWindow::state::state_minimized);
1053 }
1054}
1055
1056void WaylandTasksModel::requestToggleMaximized(const QModelIndex &index)
1057{
1058 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1059 return;
1060 }
1061
1062 auto &window = d->windows.at(index.row());
1063
1064 if (window->windowState & PlasmaWindow::state::state_maximized) {
1065 window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, PlasmaWindow::state::state_active);
1066 } else {
1067 window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active,
1068 PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active);
1069 }
1070}
1071
1072void WaylandTasksModel::requestToggleKeepAbove(const QModelIndex &index)
1073{
1074 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1075 return;
1076 }
1077
1078 auto &window = d->windows.at(index.row());
1079
1080 if (window->windowState & PlasmaWindow::state::state_keep_above) {
1081 window->set_state(PlasmaWindow::state::state_keep_above, 0);
1082 } else {
1083 window->set_state(PlasmaWindow::state::state_keep_above, PlasmaWindow::state::state_keep_above);
1084 }
1085}
1086
1087void WaylandTasksModel::requestToggleKeepBelow(const QModelIndex &index)
1088{
1089 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1090 return;
1091 }
1092 auto &window = d->windows.at(index.row());
1093
1094 if (window->windowState & PlasmaWindow::state::state_keep_below) {
1095 window->set_state(PlasmaWindow::state::state_keep_below, 0);
1096 } else {
1097 window->set_state(PlasmaWindow::state::state_keep_below, PlasmaWindow::state::state_keep_below);
1098 }
1099}
1100
1101void WaylandTasksModel::requestToggleFullScreen(const QModelIndex &index)
1102{
1103 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1104 return;
1105 }
1106
1107 auto &window = d->windows.at(index.row());
1108
1109 if (window->windowState & PlasmaWindow::state::state_fullscreen) {
1110 window->set_state(PlasmaWindow::state::state_fullscreen, 0);
1111 } else {
1112 window->set_state(PlasmaWindow::state::state_fullscreen, PlasmaWindow::state::state_fullscreen);
1113 }
1114}
1115
1116void WaylandTasksModel::requestToggleShaded(const QModelIndex &index)
1117{
1118 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1119 return;
1120 }
1121
1122 auto &window = d->windows.at(index.row());
1123
1124 if (window->windowState & PlasmaWindow::state::state_shaded) {
1125 window->set_state(PlasmaWindow::state::state_shaded, 0);
1126 } else {
1127 window->set_state(PlasmaWindow::state::state_shaded, PlasmaWindow::state::state_shaded);
1128 };
1129}
1130
1131void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops)
1132{
1133 // FIXME TODO: Lacks the "if we've requested the current desktop, force-activate
1134 // the window" logic from X11 version. This behavior should be in KWin rather than
1135 // libtm however.
1136
1137 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1138 return;
1139 }
1140
1141 auto &window = d->windows.at(index.row());
1142
1143 if (desktops.isEmpty()) {
1144 const QStringList virtualDesktops = window->virtualDesktops;
1145 for (const QString &desktop : virtualDesktops) {
1146 window->request_leave_virtual_desktop(desktop);
1147 }
1148 } else {
1149 const QStringList &now = window->virtualDesktops;
1150 QStringList next;
1151
1152 for (const QVariant &desktop : desktops) {
1153 const QString &desktopId = desktop.toString();
1154
1155 if (!desktopId.isEmpty()) {
1156 next << desktopId;
1157
1158 if (!now.contains(desktopId)) {
1159 window->request_enter_virtual_desktop(desktopId);
1160 }
1161 }
1162 }
1163
1164 for (const QString &desktop : now) {
1165 if (!next.contains(desktop)) {
1166 window->request_leave_virtual_desktop(desktop);
1167 }
1168 }
1169 }
1170}
1171
1172void WaylandTasksModel::requestNewVirtualDesktop(const QModelIndex &index)
1173{
1174 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1175 return;
1176 }
1177
1178 d->windows.at(index.row())->request_enter_new_virtual_desktop();
1179}
1180
1181void WaylandTasksModel::requestActivities(const QModelIndex &index, const QStringList &activities)
1182{
1183 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1184 return;
1185 }
1186
1187 auto &window = d->windows.at(index.row());
1188 const auto newActivities = QSet(activities.begin(), activities.end());
1189 const auto plasmaActivities = window->activities;
1190 const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end());
1191
1192 const auto activitiesToAdd = newActivities - oldActivities;
1193 for (const auto &activity : activitiesToAdd) {
1194 window->request_enter_activity(activity);
1195 }
1196
1197 const auto activitiesToRemove = oldActivities - newActivities;
1198 for (const auto &activity : activitiesToRemove) {
1199 window->request_leave_activity(activity);
1200 }
1201}
1202
1203void WaylandTasksModel::requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate)
1204{
1205 /*
1206 FIXME: This introduces the dependency on Qt::Quick. I might prefer
1207 reversing this and publishing the window pointer through the model,
1208 then calling PlasmaWindow::setMinimizeGeometry in the applet backend,
1209 rather than hand delegate items into the lib, keeping the lib more UI-
1210 agnostic.
1211 */
1212
1213 Q_UNUSED(geometry)
1214
1215 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) {
1216 return;
1217 }
1218
1219 const QQuickItem *item = qobject_cast<const QQuickItem *>(delegate);
1220
1221 if (!item || !item->parentItem()) {
1222 return;
1223 }
1224
1225 QWindow *itemWindow = item->window();
1226
1227 if (!itemWindow) {
1228 return;
1229 }
1230
1231 auto waylandWindow = itemWindow->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
1232
1233 if (!waylandWindow || !waylandWindow->surface()) {
1234 return;
1235 }
1236
1237 QRect rect(item->x(), item->y(), item->width(), item->height());
1238 rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint());
1239
1240 auto &window = d->windows.at(index.row());
1241
1242 window->set_minimized_geometry(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height());
1243}
1244
1245QUuid WaylandTasksModel::winIdFromMimeData(const QMimeData *mimeData, bool *ok)
1246{
1247 Q_ASSERT(mimeData);
1248
1249 if (ok) {
1250 *ok = false;
1251 }
1252
1253 if (!mimeData->hasFormat(Private::mimeType())) {
1254 return {};
1255 }
1256
1257 QUuid id(mimeData->data(Private::mimeType()));
1258 *ok = !id.isNull();
1259
1260 return id;
1261}
1262
1263QList<QUuid> WaylandTasksModel::winIdsFromMimeData(const QMimeData *mimeData, bool *ok)
1264{
1265 Q_ASSERT(mimeData);
1266 QList<QUuid> ids;
1267
1268 if (ok) {
1269 *ok = false;
1270 }
1271
1272 if (!mimeData->hasFormat(Private::groupMimeType())) {
1273 // Try to extract single window id.
1274 bool singularOk;
1275 QUuid id = winIdFromMimeData(mimeData, &singularOk);
1276
1277 if (ok) {
1278 *ok = singularOk;
1279 }
1280
1281 if (singularOk) {
1282 ids << id;
1283 }
1284
1285 return ids;
1286 }
1287
1288 // FIXME: Extracting multiple winids is still unimplemented;
1289 // TaskGroupingProxy::data(..., ::MimeData) can't produce
1290 // a payload with them anyways.
1291
1292 return ids;
1293}
1294
1295}
1296
1297#include "waylandtasksmodel.moc"
void deleted(const QString &path)
void dirty(const QString &path)
void created(const QString &path)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static bool isPlatformWayland()
KCALUTILS_EXPORT QString mimeType()
KCRASH_EXPORT void initialize()
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
QVariant read(const QByteArray &data, int versionOverride=0)
QWidget * window(QObject *job)
QByteArray & append(QByteArrayView data)
QFlags< T > & setFlag(Enum flag, bool on)
QIcon fromTheme(const QString &name)
iterator begin()
iterator end()
bool isEmpty() const const
void push_back(parameter_type value)
qsizetype removeAll(const AT &t)
QByteArray data(const QString &mimeType) const const
virtual bool hasFormat(const QString &mimeType) const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
Q_SIGNALSQ_SIGNALS
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void destroyed(QObject *obj)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
QPoint toPoint() const const
QPointF mapToScene(const QPointF &point) const const
QQuickItem * parentItem() const const
QQuickWindow * window() const const
int height() const const
void moveTopLeft(const QPoint &position)
QPoint topLeft() const const
int width() const const
int x() const const
int y() const const
QStringList standardLocations(StandardLocation type)
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
DisplayRole
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QTime currentTime()
QUuid createUuid()
Qt::WindowStates windowState() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:06:10 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.