mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-07 06:44:18 +00:00
768 lines
25 KiB
C++
768 lines
25 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2016 Eike Hein <hein@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
*/
|
|
|
|
#include "waylandtasksmodel.h"
|
|
#include "tasktools.h"
|
|
#include "virtualdesktopinfo.h"
|
|
|
|
#include <KDirWatch>
|
|
#include <KSharedConfig>
|
|
#include <KWayland/Client/connection_thread.h>
|
|
#include <KWayland/Client/plasmawindowmanagement.h>
|
|
#include <KWayland/Client/registry.h>
|
|
#include <KWayland/Client/surface.h>
|
|
#include <KWindowSystem>
|
|
|
|
#include <QGuiApplication>
|
|
#include <QMimeData>
|
|
#include <QQuickItem>
|
|
#include <QQuickWindow>
|
|
#include <QSet>
|
|
#include <QUrl>
|
|
#include <QUuid>
|
|
#include <QWindow>
|
|
|
|
namespace TaskManager
|
|
{
|
|
class Q_DECL_HIDDEN WaylandTasksModel::Private
|
|
{
|
|
public:
|
|
Private(WaylandTasksModel *q);
|
|
QList<KWayland::Client::PlasmaWindow *> windows;
|
|
QHash<KWayland::Client::PlasmaWindow *, AppData> appDataCache;
|
|
QHash<KWayland::Client::PlasmaWindow *, QTime> lastActivated;
|
|
KWayland::Client::PlasmaWindowManagement *windowManagement = nullptr;
|
|
KSharedConfig::Ptr rulesConfig;
|
|
KDirWatch *configWatcher = nullptr;
|
|
VirtualDesktopInfo *virtualDesktopInfo = nullptr;
|
|
static QUuid uuid;
|
|
|
|
void init();
|
|
void initWayland();
|
|
void addWindow(KWayland::Client::PlasmaWindow *window);
|
|
|
|
AppData appData(KWayland::Client::PlasmaWindow *window);
|
|
|
|
QIcon icon(KWayland::Client::PlasmaWindow *window);
|
|
|
|
static QString mimeType();
|
|
static QString groupMimeType();
|
|
|
|
void dataChanged(KWayland::Client::PlasmaWindow *window, int role);
|
|
void dataChanged(KWayland::Client::PlasmaWindow *window, const QVector<int> &roles);
|
|
|
|
private:
|
|
WaylandTasksModel *q;
|
|
};
|
|
|
|
QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid();
|
|
|
|
WaylandTasksModel::Private::Private(WaylandTasksModel *q)
|
|
: q(q)
|
|
{
|
|
}
|
|
|
|
void WaylandTasksModel::Private::init()
|
|
{
|
|
auto clearCacheAndRefresh = [this] {
|
|
if (!windows.count()) {
|
|
return;
|
|
}
|
|
|
|
appDataCache.clear();
|
|
|
|
// Emit changes of all roles satisfied from app data cache.
|
|
Q_EMIT q->dataChanged(q->index(0, 0),
|
|
q->index(windows.count() - 1, 0),
|
|
QVector<int>{Qt::DecorationRole,
|
|
AbstractTasksModel::AppId,
|
|
AbstractTasksModel::AppName,
|
|
AbstractTasksModel::GenericName,
|
|
AbstractTasksModel::LauncherUrl,
|
|
AbstractTasksModel::LauncherUrlWithoutIcon,
|
|
AbstractTasksModel::CanLaunchNewInstance,
|
|
AbstractTasksModel::SkipTaskbar});
|
|
};
|
|
|
|
rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc"));
|
|
configWatcher = new KDirWatch(q);
|
|
|
|
foreach (const QString &location, QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)) {
|
|
configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc"));
|
|
}
|
|
|
|
auto rulesConfigChange = [this, clearCacheAndRefresh] {
|
|
rulesConfig->reparseConfiguration();
|
|
clearCacheAndRefresh();
|
|
};
|
|
|
|
QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange);
|
|
QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange);
|
|
QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange);
|
|
|
|
virtualDesktopInfo = new VirtualDesktopInfo(q);
|
|
|
|
initWayland();
|
|
}
|
|
|
|
void WaylandTasksModel::Private::initWayland()
|
|
{
|
|
if (!KWindowSystem::isPlatformWayland()) {
|
|
return;
|
|
}
|
|
|
|
KWayland::Client::ConnectionThread *connection = KWayland::Client::ConnectionThread::fromApplication(q);
|
|
|
|
if (!connection) {
|
|
return;
|
|
}
|
|
|
|
KWayland::Client::Registry *registry = new KWayland::Client::Registry(q);
|
|
registry->create(connection);
|
|
|
|
QObject::connect(registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, q, [this, registry](quint32 name, quint32 version) {
|
|
windowManagement = registry->createPlasmaWindowManagement(name, version, q);
|
|
|
|
QObject::connect(windowManagement, &KWayland::Client::PlasmaWindowManagement::interfaceAboutToBeReleased, q, [this] {
|
|
q->beginResetModel();
|
|
windows.clear();
|
|
q->endResetModel();
|
|
});
|
|
|
|
QObject::connect(windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, q, [this](KWayland::Client::PlasmaWindow *window) {
|
|
addWindow(window);
|
|
});
|
|
|
|
QObject::connect(windowManagement, &KWayland::Client::PlasmaWindowManagement::stackingOrderUuidsChanged, q, [this]() {
|
|
for (const auto window : qAsConst(windows)) {
|
|
this->dataChanged(window, StackingOrder);
|
|
}
|
|
});
|
|
|
|
const auto windows = windowManagement->windows();
|
|
for (auto it = windows.constBegin(); it != windows.constEnd(); ++it) {
|
|
addWindow(*it);
|
|
}
|
|
});
|
|
|
|
registry->setup();
|
|
}
|
|
|
|
void WaylandTasksModel::Private::addWindow(KWayland::Client::PlasmaWindow *window)
|
|
{
|
|
if (windows.indexOf(window) != -1) {
|
|
return;
|
|
}
|
|
|
|
const int count = windows.count();
|
|
|
|
q->beginInsertRows(QModelIndex(), count, count);
|
|
|
|
windows.append(window);
|
|
|
|
q->endInsertRows();
|
|
|
|
auto removeWindow = [window, this] {
|
|
const int row = windows.indexOf(window);
|
|
if (row != -1) {
|
|
q->beginRemoveRows(QModelIndex(), row, row);
|
|
windows.removeAt(row);
|
|
appDataCache.remove(window);
|
|
lastActivated.remove(window);
|
|
q->endRemoveRows();
|
|
}
|
|
};
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::unmapped, q, removeWindow);
|
|
QObject::connect(window, &QObject::destroyed, q, removeWindow);
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::titleChanged, q, [window, this] {
|
|
this->dataChanged(window, Qt::DisplayRole);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::iconChanged, q, [window, this] {
|
|
// The icon in the AppData struct might come from PlasmaWindow if it wasn't
|
|
// filled in by windowUrlFromMetadata+appDataFromUrl.
|
|
// TODO: Don't evict the cache unnecessarily if this isn't the case. As icons
|
|
// are currently very static on Wayland, this eviction is unlikely to happen
|
|
// frequently as of now.
|
|
appDataCache.remove(window);
|
|
|
|
this->dataChanged(window, Qt::DecorationRole);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::appIdChanged, q, [window, this] {
|
|
// The AppData struct in the cache is derived from this and needs
|
|
// to be evicted in favor of a fresh struct based on the changed
|
|
// window metadata.
|
|
appDataCache.remove(window);
|
|
|
|
// Refresh roles satisfied from the app data cache.
|
|
this->dataChanged(window, QVector<int>{AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance});
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::activeChanged, q, [window, this] {
|
|
if (window->isActive()) {
|
|
lastActivated[window] = QTime::currentTime();
|
|
}
|
|
this->dataChanged(window, IsActive);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::closeableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsClosable);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::movableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsMovable);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::resizableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsResizable);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::fullscreenableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsFullScreenable);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::fullscreenChanged, q, [window, this] {
|
|
this->dataChanged(window, IsFullScreen);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::maximizeableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsMaximizable);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::maximizedChanged, q, [window, this] {
|
|
this->dataChanged(window, IsMaximized);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::minimizeableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsMinimizable);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::minimizedChanged, q, [window, this] {
|
|
this->dataChanged(window, IsMinimized);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::keepAboveChanged, q, [window, this] {
|
|
this->dataChanged(window, IsKeepAbove);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::keepBelowChanged, q, [window, this] {
|
|
this->dataChanged(window, IsKeepBelow);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::shadeableChanged, q, [window, this] {
|
|
this->dataChanged(window, IsShadeable);
|
|
});
|
|
|
|
// FIXME
|
|
// QObject::connect(window, &KWayland::Client::PlasmaWindow::virtualDesktopChangeableChanged, q,
|
|
// // TODO: This is marked deprecated in KWayland, but (IMHO) shouldn't be.
|
|
// [window, this] { this->dataChanged(window, IsVirtualDesktopsChangeable); }
|
|
// );
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopEntered, q, [window, this] {
|
|
this->dataChanged(window, VirtualDesktops);
|
|
|
|
// If the count has changed from 0, the window may no longer be on all virtual
|
|
// desktops.
|
|
if (window->plasmaVirtualDesktops().count() > 0) {
|
|
this->dataChanged(window, IsOnAllVirtualDesktops);
|
|
}
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopLeft, q, [window, this] {
|
|
this->dataChanged(window, VirtualDesktops);
|
|
|
|
// If the count has changed to 0, the window is now on all virtual desktops.
|
|
if (window->plasmaVirtualDesktops().count() == 0) {
|
|
this->dataChanged(window, IsOnAllVirtualDesktops);
|
|
}
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::geometryChanged, q, [window, this] {
|
|
this->dataChanged(window, QVector<int>{Geometry, ScreenGeometry});
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::demandsAttentionChanged, q, [window, this] {
|
|
this->dataChanged(window, IsDemandingAttention);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::skipTaskbarChanged, q, [window, this] {
|
|
this->dataChanged(window, SkipTaskbar);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::applicationMenuChanged, q, [window, this] {
|
|
this->dataChanged(window, QVector<int>{ApplicationMenuServiceName, ApplicationMenuObjectPath});
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::plasmaActivityEntered, q, [window, this] {
|
|
this->dataChanged(window, Activities);
|
|
});
|
|
|
|
QObject::connect(window, &KWayland::Client::PlasmaWindow::plasmaActivityLeft, q, [window, this] {
|
|
this->dataChanged(window, Activities);
|
|
});
|
|
}
|
|
|
|
AppData WaylandTasksModel::Private::appData(KWayland::Client::PlasmaWindow *window)
|
|
{
|
|
const auto &it = appDataCache.constFind(window);
|
|
|
|
if (it != appDataCache.constEnd()) {
|
|
return *it;
|
|
}
|
|
|
|
const AppData &data = appDataFromUrl(windowUrlFromMetadata(window->appId(), window->pid(), rulesConfig));
|
|
|
|
appDataCache.insert(window, data);
|
|
|
|
return data;
|
|
}
|
|
|
|
QIcon WaylandTasksModel::Private::icon(KWayland::Client::PlasmaWindow *window)
|
|
{
|
|
const AppData &app = appData(window);
|
|
|
|
if (!app.icon.isNull()) {
|
|
return app.icon;
|
|
}
|
|
|
|
appDataCache[window].icon = window->icon();
|
|
|
|
return window->icon();
|
|
}
|
|
|
|
QString WaylandTasksModel::Private::mimeType()
|
|
{
|
|
// Use a unique format id to make this intentionally useless for
|
|
// cross-process DND.
|
|
return QStringLiteral("windowsystem/winid+") + uuid.toString();
|
|
}
|
|
|
|
QString WaylandTasksModel::Private::groupMimeType()
|
|
{
|
|
// Use a unique format id to make this intentionally useless for
|
|
// cross-process DND.
|
|
return QStringLiteral("windowsystem/multiple-winids+") + uuid.toString();
|
|
}
|
|
|
|
void WaylandTasksModel::Private::dataChanged(KWayland::Client::PlasmaWindow *window, int role)
|
|
{
|
|
QModelIndex idx = q->index(windows.indexOf(window));
|
|
Q_EMIT q->dataChanged(idx, idx, QVector<int>{role});
|
|
}
|
|
|
|
void WaylandTasksModel::Private::dataChanged(KWayland::Client::PlasmaWindow *window, const QVector<int> &roles)
|
|
{
|
|
QModelIndex idx = q->index(windows.indexOf(window));
|
|
Q_EMIT q->dataChanged(idx, idx, roles);
|
|
}
|
|
|
|
WaylandTasksModel::WaylandTasksModel(QObject *parent)
|
|
: AbstractWindowTasksModel(parent)
|
|
, d(new Private(this))
|
|
{
|
|
d->init();
|
|
}
|
|
|
|
WaylandTasksModel::~WaylandTasksModel() = default;
|
|
|
|
QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid() || index.row() >= d->windows.count()) {
|
|
return QVariant();
|
|
}
|
|
|
|
KWayland::Client::PlasmaWindow *window = d->windows.at(index.row());
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
return window->title();
|
|
} else if (role == Qt::DecorationRole) {
|
|
return d->icon(window);
|
|
} else if (role == AppId) {
|
|
const QString &id = d->appData(window).id;
|
|
|
|
if (id.isEmpty()) {
|
|
return window->appId();
|
|
} else {
|
|
return id;
|
|
}
|
|
} else if (role == AppName) {
|
|
return d->appData(window).name;
|
|
} else if (role == GenericName) {
|
|
return d->appData(window).genericName;
|
|
} else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) {
|
|
return d->appData(window).url;
|
|
} else if (role == WinIdList) {
|
|
return QVariantList() << window->uuid();
|
|
} else if (role == MimeType) {
|
|
return d->mimeType();
|
|
} else if (role == MimeData) {
|
|
return window->uuid();
|
|
} else if (role == IsWindow) {
|
|
return true;
|
|
} else if (role == IsActive) {
|
|
return window->isActive();
|
|
} else if (role == IsClosable) {
|
|
return window->isCloseable();
|
|
} else if (role == IsMovable) {
|
|
return window->isMovable();
|
|
} else if (role == IsResizable) {
|
|
return window->isResizable();
|
|
} else if (role == IsMaximizable) {
|
|
return window->isMaximizeable();
|
|
} else if (role == IsMaximized) {
|
|
return window->isMaximized();
|
|
} else if (role == IsMinimizable) {
|
|
return window->isMinimizeable();
|
|
} else if (role == IsMinimized || role == IsHidden) {
|
|
return window->isMinimized();
|
|
} else if (role == IsKeepAbove) {
|
|
return window->isKeepAbove();
|
|
} else if (role == IsKeepBelow) {
|
|
return window->isKeepBelow();
|
|
} else if (role == IsFullScreenable) {
|
|
return window->isFullscreenable();
|
|
} else if (role == IsFullScreen) {
|
|
return window->isFullscreen();
|
|
} else if (role == IsShadeable) {
|
|
return window->isShadeable();
|
|
} else if (role == IsShaded) {
|
|
return window->isShaded();
|
|
} else if (role == IsVirtualDesktopsChangeable) {
|
|
// FIXME Currently not implemented in KWayland.
|
|
return true;
|
|
} else if (role == VirtualDesktops) {
|
|
return window->plasmaVirtualDesktops();
|
|
} else if (role == IsOnAllVirtualDesktops) {
|
|
return window->plasmaVirtualDesktops().isEmpty();
|
|
} else if (role == Geometry) {
|
|
return window->geometry();
|
|
} else if (role == ScreenGeometry) {
|
|
return screenGeometry(window->geometry().center());
|
|
} else if (role == Activities) {
|
|
return window->plasmaActivities();
|
|
} else if (role == IsDemandingAttention) {
|
|
return window->isDemandingAttention();
|
|
} else if (role == SkipTaskbar) {
|
|
return window->skipTaskbar() || d->appData(window).skipTaskbar;
|
|
} else if (role == SkipPager) {
|
|
// FIXME Implement.
|
|
} else if (role == AppPid) {
|
|
return window->pid();
|
|
} else if (role == StackingOrder) {
|
|
return d->windowManagement->stackingOrderUuids().indexOf(window->uuid());
|
|
} else if (role == LastActivated) {
|
|
if (d->lastActivated.contains(window)) {
|
|
return d->lastActivated.value(window);
|
|
}
|
|
} else if (role == ApplicationMenuObjectPath) {
|
|
return window->applicationMenuObjectPath();
|
|
} else if (role == ApplicationMenuServiceName) {
|
|
return window->applicationMenuServiceName();
|
|
} else if (role == CanLaunchNewInstance) {
|
|
return canLauchNewInstance(d->appData(window));
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
int WaylandTasksModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
return parent.isValid() ? 0 : d->windows.count();
|
|
}
|
|
|
|
QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const
|
|
{
|
|
return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row)) : QModelIndex();
|
|
}
|
|
|
|
void WaylandTasksModel::requestActivate(const QModelIndex &index)
|
|
{
|
|
// FIXME Lacks transient handling of the XWindows version.
|
|
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestActivate();
|
|
}
|
|
|
|
void WaylandTasksModel::requestNewInstance(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
runApp(d->appData(d->windows.at(index.row())));
|
|
}
|
|
|
|
void WaylandTasksModel::requestOpenUrls(const QModelIndex &index, const QList<QUrl> &urls)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count() || urls.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
runApp(d->appData(d->windows.at(index.row())), urls);
|
|
}
|
|
|
|
void WaylandTasksModel::requestClose(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestClose();
|
|
}
|
|
|
|
void WaylandTasksModel::requestMove(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
KWayland::Client::PlasmaWindow *window = d->windows.at(index.row());
|
|
|
|
window->requestActivate();
|
|
window->requestMove();
|
|
}
|
|
|
|
void WaylandTasksModel::requestResize(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
KWayland::Client::PlasmaWindow *window = d->windows.at(index.row());
|
|
|
|
window->requestActivate();
|
|
window->requestResize();
|
|
}
|
|
|
|
void WaylandTasksModel::requestToggleMinimized(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestToggleMinimized();
|
|
}
|
|
|
|
void WaylandTasksModel::requestToggleMaximized(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
KWayland::Client::PlasmaWindow *window = d->windows.at(index.row());
|
|
|
|
window->requestActivate();
|
|
window->requestToggleMaximized();
|
|
}
|
|
|
|
void WaylandTasksModel::requestToggleKeepAbove(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestToggleKeepAbove();
|
|
}
|
|
|
|
void WaylandTasksModel::requestToggleKeepBelow(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestToggleKeepBelow();
|
|
}
|
|
|
|
void WaylandTasksModel::requestToggleFullScreen(const QModelIndex &index)
|
|
{
|
|
Q_UNUSED(index)
|
|
|
|
// FIXME Implement.
|
|
}
|
|
|
|
void WaylandTasksModel::requestToggleShaded(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestToggleShaded();
|
|
}
|
|
|
|
void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops)
|
|
{
|
|
// FIXME TODO: Lacks the "if we've requested the current desktop, force-activate
|
|
// the window" logic from X11 version. This behavior should be in KWin rather than
|
|
// libtm however.
|
|
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
KWayland::Client::PlasmaWindow *window = d->windows.at(index.row());
|
|
|
|
if (desktops.isEmpty()) {
|
|
const QStringList virtualDesktops = window->plasmaVirtualDesktops();
|
|
for (const QString &desktop : virtualDesktops) {
|
|
window->requestLeaveVirtualDesktop(desktop);
|
|
}
|
|
} else {
|
|
const QStringList &now = window->plasmaVirtualDesktops();
|
|
QStringList next;
|
|
|
|
for (const QVariant &desktop : desktops) {
|
|
const QString &desktopId = desktop.toString();
|
|
|
|
if (!desktopId.isEmpty()) {
|
|
next << desktopId;
|
|
|
|
if (!now.contains(desktopId)) {
|
|
window->requestEnterVirtualDesktop(desktopId);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const QString &desktop : now) {
|
|
if (!next.contains(desktop)) {
|
|
window->requestLeaveVirtualDesktop(desktop);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WaylandTasksModel::requestNewVirtualDesktop(const QModelIndex &index)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
d->windows.at(index.row())->requestEnterNewVirtualDesktop();
|
|
}
|
|
|
|
void WaylandTasksModel::requestActivities(const QModelIndex &index, const QStringList &activities)
|
|
{
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
auto *const window = d->windows.at(index.row());
|
|
const auto newActivities = QSet(activities.begin(), activities.end());
|
|
const auto plasmaActivities = window->plasmaActivities();
|
|
const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end());
|
|
|
|
const auto activitiesToAdd = newActivities - oldActivities;
|
|
for (const auto &activity : activitiesToAdd) {
|
|
window->requestEnterActivity(activity);
|
|
}
|
|
|
|
const auto activitiesToRemove = oldActivities - newActivities;
|
|
for (const auto &activity : activitiesToRemove) {
|
|
window->requestLeaveActivity(activity);
|
|
}
|
|
}
|
|
|
|
void WaylandTasksModel::requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate)
|
|
{
|
|
/*
|
|
FIXME: This introduces the dependency on Qt::Quick. I might prefer
|
|
reversing this and publishing the window pointer through the model,
|
|
then calling PlasmaWindow::setMinimizeGeometry in the applet backend,
|
|
rather than hand delegate items into the lib, keeping the lib more UI-
|
|
agnostic.
|
|
*/
|
|
|
|
Q_UNUSED(geometry)
|
|
|
|
if (!index.isValid() || index.model() != this || index.row() < 0 || index.row() >= d->windows.count()) {
|
|
return;
|
|
}
|
|
|
|
const QQuickItem *item = qobject_cast<const QQuickItem *>(delegate);
|
|
|
|
if (!item || !item->parentItem() || !item->window()) {
|
|
return;
|
|
}
|
|
|
|
QWindow *itemWindow = item->window();
|
|
|
|
if (!itemWindow) {
|
|
return;
|
|
}
|
|
|
|
using namespace KWayland::Client;
|
|
Surface *surface = Surface::fromWindow(itemWindow);
|
|
|
|
if (!surface) {
|
|
return;
|
|
}
|
|
|
|
QRect rect(item->x(), item->y(), item->width(), item->height());
|
|
rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint());
|
|
|
|
KWayland::Client::PlasmaWindow *window = d->windows.at(index.row());
|
|
|
|
window->setMinimizedGeometry(surface, rect);
|
|
}
|
|
|
|
QUuid WaylandTasksModel::winIdFromMimeData(const QMimeData *mimeData, bool *ok)
|
|
{
|
|
Q_ASSERT(mimeData);
|
|
|
|
if (ok) {
|
|
*ok = false;
|
|
}
|
|
|
|
if (!mimeData->hasFormat(Private::mimeType())) {
|
|
return 0;
|
|
}
|
|
|
|
QUuid id(mimeData->data(Private::mimeType()));
|
|
*ok = !id.isNull();
|
|
|
|
return id;
|
|
}
|
|
|
|
QList<QUuid> WaylandTasksModel::winIdsFromMimeData(const QMimeData *mimeData, bool *ok)
|
|
{
|
|
Q_ASSERT(mimeData);
|
|
QList<QUuid> ids;
|
|
|
|
if (ok) {
|
|
*ok = false;
|
|
}
|
|
|
|
if (!mimeData->hasFormat(Private::groupMimeType())) {
|
|
// Try to extract single window id.
|
|
bool singularOk;
|
|
QUuid id = winIdFromMimeData(mimeData, &singularOk);
|
|
|
|
if (ok) {
|
|
*ok = singularOk;
|
|
}
|
|
|
|
if (singularOk) {
|
|
ids << id;
|
|
}
|
|
|
|
return ids;
|
|
}
|
|
|
|
// FIXME: Extracting multiple winids is still unimplemented;
|
|
// TaskGroupingProxy::data(..., ::MimeData) can't produce
|
|
// a payload with them anyways.
|
|
|
|
return ids;
|
|
}
|
|
|
|
}
|