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

KDE's Doxygen guidelines are available online.