mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
767 lines
25 KiB
767 lines
25 KiB
/* |
|
SPDX-FileCopyrightText: 2016 Eike Hein <[email protected]> |
|
|
|
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; |
|
} |
|
|
|
}
|
|
|