scripting: Introduce ClientModel V3
ClientModel V3 is much easier to extend and maintain than the one in V2.
This commit is contained in:
parent
cecf2ee7a1
commit
5435865329
4 changed files with 440 additions and 0 deletions
|
@ -100,6 +100,7 @@ set(kwin_SRCS
|
|||
screenlockerwatcher.cpp
|
||||
screens.cpp
|
||||
scripting/v2/clientmodel.cpp
|
||||
scripting/v3/clientmodel.cpp
|
||||
scripting/dbuscall.cpp
|
||||
scripting/screenedgeitem.cpp
|
||||
scripting/scriptedeffect.cpp
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "thumbnailitem.h"
|
||||
|
||||
#include "v2/clientmodel.h"
|
||||
#include "v3/clientmodel.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "options.h"
|
||||
|
@ -642,6 +643,12 @@ void KWin::Scripting::init()
|
|||
qmlRegisterType<ScriptingModels::V2::ClientModelByScreenAndActivity>("org.kde.kwin", 2, 1, "ClientModelByScreenAndActivity");
|
||||
qmlRegisterType<ScriptingModels::V2::ClientFilterModel>("org.kde.kwin", 2, 0, "ClientFilterModel");
|
||||
|
||||
qmlRegisterType<WindowThumbnailItem>("org.kde.kwin", 3, 0, "WindowThumbnailItem");
|
||||
qmlRegisterType<DBusCall>("org.kde.kwin", 3, 0, "DBusCall");
|
||||
qmlRegisterType<ScreenEdgeItem>("org.kde.kwin", 3, 0, "ScreenEdgeItem");
|
||||
qmlRegisterType<ScriptingModels::V3::ClientModel>("org.kde.kwin", 3, 0, "ClientModel");
|
||||
qmlRegisterType<ScriptingModels::V3::ClientFilterModel>("org.kde.kwin", 3, 0, "ClientFilterModel");
|
||||
|
||||
qmlRegisterType<KWin::AbstractClient>();
|
||||
qmlRegisterType<KWin::X11Client>();
|
||||
qmlRegisterType<QAbstractItemModel>();
|
||||
|
|
314
src/scripting/v3/clientmodel.cpp
Normal file
314
src/scripting/v3/clientmodel.cpp
Normal file
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "clientmodel.h"
|
||||
#include "abstract_client.h"
|
||||
#include "screens.h"
|
||||
#include "workspace.h"
|
||||
|
||||
namespace KWin::ScriptingModels::V3
|
||||
{
|
||||
|
||||
ClientModel::ClientModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
connect(workspace(), &Workspace::clientAdded, this, &ClientModel::handleClientAdded);
|
||||
connect(workspace(), &Workspace::clientRemoved, this, &ClientModel::handleClientRemoved);
|
||||
|
||||
m_clients = workspace()->allClientList();
|
||||
for (AbstractClient *client : qAsConst(m_clients)) {
|
||||
setupClientConnections(client);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientModel::markRoleChanged(AbstractClient *client, int role)
|
||||
{
|
||||
const QModelIndex row = index(m_clients.indexOf(client), 0);
|
||||
Q_EMIT dataChanged(row, row, {role});
|
||||
}
|
||||
|
||||
void ClientModel::setupClientConnections(AbstractClient *client)
|
||||
{
|
||||
connect(client, &AbstractClient::desktopChanged, this, [this, client]() {
|
||||
markRoleChanged(client, DesktopRole);
|
||||
});
|
||||
connect(client, &AbstractClient::screenChanged, this, [this, client]() {
|
||||
markRoleChanged(client, ScreenRole);
|
||||
});
|
||||
connect(client, &AbstractClient::activitiesChanged, this, [this, client]() {
|
||||
markRoleChanged(client, ActivityRole);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientModel::handleClientAdded(AbstractClient *client)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), m_clients.count(), m_clients.count());
|
||||
m_clients.append(client);
|
||||
endInsertRows();
|
||||
|
||||
setupClientConnections(client);
|
||||
}
|
||||
|
||||
void ClientModel::handleClientRemoved(AbstractClient *client)
|
||||
{
|
||||
const int index = m_clients.indexOf(client);
|
||||
Q_ASSERT(index != -1);
|
||||
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_clients.removeAt(index);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ClientModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{ Qt::DisplayRole, QByteArrayLiteral("display") },
|
||||
{ ClientRole, QByteArrayLiteral("client") },
|
||||
{ ScreenRole, QByteArrayLiteral("screen") },
|
||||
{ DesktopRole, QByteArrayLiteral("desktop") },
|
||||
{ ActivityRole, QByteArrayLiteral("activity") },
|
||||
};
|
||||
}
|
||||
|
||||
QVariant ClientModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= m_clients.count()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
AbstractClient *client = m_clients[index.row()];
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case ClientRole:
|
||||
return QVariant::fromValue(client);
|
||||
case ScreenRole:
|
||||
return client->screen();
|
||||
case DesktopRole:
|
||||
return client->desktop();
|
||||
case ActivityRole:
|
||||
return client->activities();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
int ClientModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : m_clients.count();
|
||||
}
|
||||
|
||||
ClientFilterModel::ClientFilterModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ClientModel *ClientFilterModel::clientModel() const
|
||||
{
|
||||
return m_clientModel;
|
||||
}
|
||||
|
||||
void ClientFilterModel::setClientModel(ClientModel *clientModel)
|
||||
{
|
||||
if (clientModel == m_clientModel) {
|
||||
return;
|
||||
}
|
||||
m_clientModel = clientModel;
|
||||
setSourceModel(m_clientModel);
|
||||
Q_EMIT clientModelChanged();
|
||||
}
|
||||
|
||||
QString ClientFilterModel::activity() const
|
||||
{
|
||||
return m_activity.value_or(QString());
|
||||
}
|
||||
|
||||
void ClientFilterModel::setActivity(const QString &activity)
|
||||
{
|
||||
if (m_activity != activity) {
|
||||
m_activity = activity;
|
||||
Q_EMIT activityChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientFilterModel::resetActivity()
|
||||
{
|
||||
if (m_activity.has_value()) {
|
||||
m_activity.reset();
|
||||
Q_EMIT activityChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
int ClientFilterModel::desktop() const
|
||||
{
|
||||
return m_desktop.value_or(0);
|
||||
}
|
||||
|
||||
void ClientFilterModel::setDesktop(int desktop)
|
||||
{
|
||||
if (m_desktop != desktop) {
|
||||
m_desktop = desktop;
|
||||
Q_EMIT desktopChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientFilterModel::resetDesktop()
|
||||
{
|
||||
if (m_desktop.has_value()) {
|
||||
m_desktop.reset();
|
||||
Q_EMIT desktopChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
QString ClientFilterModel::filter() const
|
||||
{
|
||||
return m_filter;
|
||||
}
|
||||
|
||||
void ClientFilterModel::setFilter(const QString &filter)
|
||||
{
|
||||
if (filter == m_filter) {
|
||||
return;
|
||||
}
|
||||
m_filter = filter;
|
||||
Q_EMIT filterChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
QString ClientFilterModel::screenName() const
|
||||
{
|
||||
return m_screenName.value_or(QString());
|
||||
}
|
||||
|
||||
void ClientFilterModel::setScreenName(const QString &screen)
|
||||
{
|
||||
if (m_screenName != screen) {
|
||||
m_screenName = screen;
|
||||
Q_EMIT screenNameChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientFilterModel::resetScreenName()
|
||||
{
|
||||
if (m_screenName.has_value()) {
|
||||
m_screenName.reset();
|
||||
Q_EMIT screenNameChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
ClientFilterModel::WindowTypes ClientFilterModel::windowType() const
|
||||
{
|
||||
return m_windowType.value_or(WindowTypes());
|
||||
}
|
||||
|
||||
void ClientFilterModel::setWindowType(WindowTypes windowType)
|
||||
{
|
||||
if (m_windowType != windowType) {
|
||||
m_windowType = windowType;
|
||||
Q_EMIT windowTypeChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientFilterModel::resetWindowType()
|
||||
{
|
||||
if (m_windowType.has_value()) {
|
||||
m_windowType.reset();
|
||||
Q_EMIT windowTypeChanged();
|
||||
invalidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
if (!m_clientModel) {
|
||||
return false;
|
||||
}
|
||||
const QModelIndex index = m_clientModel->index(sourceRow, 0, sourceParent);
|
||||
if (!index.isValid()) {
|
||||
return false;
|
||||
}
|
||||
const QVariant data = index.data();
|
||||
if (!data.isValid()) {
|
||||
// an invalid QVariant is valid data
|
||||
return true;
|
||||
}
|
||||
|
||||
AbstractClient *client = qvariant_cast<AbstractClient *>(data);
|
||||
if (!client) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_activity.has_value()) {
|
||||
if (!client->isOnActivity(*m_activity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_desktop.has_value()) {
|
||||
if (!client->isOnDesktop(*m_desktop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_screenName.has_value()) {
|
||||
if (screens()->name(client->screen()) != m_screenName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_windowType.has_value()) {
|
||||
if (!(windowTypeMask(client) & *m_windowType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_filter.isEmpty()) {
|
||||
if (client->caption().contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
const QString windowRole(QString::fromUtf8(client->windowRole()));
|
||||
if (windowRole.contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
const QString resourceName(QString::fromUtf8(client->resourceName()));
|
||||
if (resourceName.contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
const QString resourceClass(QString::fromUtf8(client->resourceClass()));
|
||||
if (resourceClass.contains(m_filter, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ClientFilterModel::WindowTypes ClientFilterModel::windowTypeMask(AbstractClient *client) const
|
||||
{
|
||||
WindowTypes mask;
|
||||
if (client->isNormalWindow()) {
|
||||
mask |= WindowType::Normal;
|
||||
} else if (client->isDialog()) {
|
||||
mask |= WindowType::Dialog;
|
||||
} else if (client->isDock()) {
|
||||
mask |= WindowType::Dock;
|
||||
} else if (client->isDesktop()) {
|
||||
mask |= WindowType::Desktop;
|
||||
} else if (client->isNotification()) {
|
||||
mask |= WindowType::Notification;
|
||||
} else if (client->isCriticalNotification()) {
|
||||
mask |= WindowType::CriticalNotification;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
} // namespace KWin::ScriptingModels::V3
|
118
src/scripting/v3/clientmodel.h
Normal file
118
src/scripting/v3/clientmodel.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractClient;
|
||||
|
||||
namespace ScriptingModels::V3
|
||||
{
|
||||
|
||||
class ClientModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
ClientRole = Qt::UserRole + 1,
|
||||
ScreenRole,
|
||||
DesktopRole,
|
||||
ActivityRole
|
||||
};
|
||||
|
||||
explicit ClientModel(QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
private:
|
||||
void markRoleChanged(AbstractClient *client, int role);
|
||||
|
||||
void handleClientAdded(AbstractClient *client);
|
||||
void handleClientRemoved(AbstractClient *client);
|
||||
void setupClientConnections(AbstractClient *client);
|
||||
|
||||
QList<AbstractClient *> m_clients;
|
||||
};
|
||||
|
||||
class ClientFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(ClientModel *clientModel READ clientModel WRITE setClientModel NOTIFY clientModelChanged)
|
||||
Q_PROPERTY(QString activity READ activity WRITE setActivity RESET resetActivity NOTIFY activityChanged)
|
||||
Q_PROPERTY(int desktop READ desktop WRITE setDesktop RESET resetDesktop NOTIFY desktopChanged)
|
||||
Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||
Q_PROPERTY(QString screenName READ screenName WRITE setScreenName RESET resetScreenName NOTIFY screenNameChanged)
|
||||
Q_PROPERTY(WindowTypes windowType READ windowType WRITE setWindowType RESET resetWindowType NOTIFY windowTypeChanged)
|
||||
|
||||
public:
|
||||
enum WindowType {
|
||||
Normal = 0x1,
|
||||
Dialog = 0x2,
|
||||
Dock = 0x4,
|
||||
Desktop = 0x8,
|
||||
Notification = 0x10,
|
||||
CriticalNotification = 0x20,
|
||||
};
|
||||
Q_DECLARE_FLAGS(WindowTypes, WindowType)
|
||||
Q_FLAG(WindowTypes)
|
||||
|
||||
explicit ClientFilterModel(QObject *parent = nullptr);
|
||||
|
||||
ClientModel *clientModel() const;
|
||||
void setClientModel(ClientModel *clientModel);
|
||||
|
||||
QString activity() const;
|
||||
void setActivity(const QString &activity);
|
||||
void resetActivity();
|
||||
|
||||
int desktop() const;
|
||||
void setDesktop(int desktop);
|
||||
void resetDesktop();
|
||||
|
||||
QString filter() const;
|
||||
void setFilter(const QString &filter);
|
||||
|
||||
QString screenName() const;
|
||||
void setScreenName(const QString &screenName);
|
||||
void resetScreenName();
|
||||
|
||||
WindowTypes windowType() const;
|
||||
void setWindowType(WindowTypes windowType);
|
||||
void resetWindowType();
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void activityChanged();
|
||||
void desktopChanged();
|
||||
void screenNameChanged();
|
||||
void clientModelChanged();
|
||||
void filterChanged();
|
||||
void windowTypeChanged();
|
||||
|
||||
private:
|
||||
WindowTypes windowTypeMask(AbstractClient *client) const;
|
||||
|
||||
ClientModel *m_clientModel = nullptr;
|
||||
std::optional<QString> m_activity;
|
||||
std::optional<int> m_desktop;
|
||||
QString m_filter;
|
||||
std::optional<QString> m_screenName;
|
||||
std::optional<WindowTypes> m_windowType;
|
||||
};
|
||||
|
||||
} // namespace ScriptingModels::V3
|
||||
} // namespace KWin
|
Loading…
Reference in a new issue