From 1e06d76b21046feaa470505146d72311122fdd1d Mon Sep 17 00:00:00 2001 From: Ardox Date: Fri, 14 Nov 2025 10:44:08 +0100 Subject: [PATCH 01/27] FHT: added new cpp modules --- .../sleex/plugins/src/Sleex/services/fhtc.cpp | 143 ++++++++++++++++++ .../sleex/plugins/src/Sleex/services/fhtc.hpp | 57 +++++++ 2 files changed, 200 insertions(+) create mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.cpp create mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.hpp diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp new file mode 100644 index 00000000..d5c3ec98 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp @@ -0,0 +1,143 @@ +#include "fhtc.hpp" +#include +#include +#include + +FhtCompositor::FhtCompositor(QObject *parent) + : QObject(parent) +{ + QString socketPath = QProcessEnvironment::systemEnvironment().value("FHTC_SOCKET_PATH"); + if (socketPath.isEmpty()) { + qWarning() << "FhtCompositor: FHTC_SOCKET_PATH not set."; + return; + } + + m_socket = new QTcpSocket(this); + connect(m_socket, &QTcpSocket::readyRead, this, &FhtCompositor::onReadyRead); + + m_socket->connectToHost(socketPath, 0, QIODevice::ReadWrite); +} + +void FhtCompositor::onReadyRead() +{ + while (m_socket->canReadLine()) { + QByteArray line = m_socket->readLine().trimmed(); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(line, &err); + if (err.error != QJsonParseError::NoError) { + qWarning() << "FhtCompositor: failed to parse event:" << line << err.errorString(); + continue; + } + + if (doc.isObject()) + handleEvent(doc.object()); + } +} + +void FhtCompositor::handleEvent(const QJsonObject &event) +{ + QString type = event.value("event").toString(); + QJsonValue data = event.value("data"); + + enum EventType { + Windows, + FocusedWindowChanged, + WindowClosed, + WindowChanged, + Workspaces, + ActiveWorkspaceChanged, + WorkspaceChanged, + WorkspaceRemoved, + Space, + Unknown + }; + + static const QHash eventMap = { + {"windows", Windows}, + {"focused-window-changed", FocusedWindowChanged}, + {"window-closed", WindowClosed}, + {"window-changed", WindowChanged}, + {"workspaces", Workspaces}, + {"active-workspace-changed", ActiveWorkspaceChanged}, + {"workspace-changed", WorkspaceChanged}, + {"workspace-removed", WorkspaceRemoved}, + {"space", Space} + }; + + switch (eventMap.value(type, Unknown)) { + case Windows: + m_windows = data.toObject().toVariantMap(); + emit windowsChanged(); + break; + + case FocusedWindowChanged: { + int newId = data.toObject().value("id").toInt(-1); + if (newId == -1) { + m_focusedWindowId = -1; + m_focusedWindow = QVariant(); + } else { + m_focusedWindowId = newId; + m_focusedWindow = m_windows.value(QString::number(newId)); + } + emit focusedWindowIdChanged(); + emit focusedWindowChanged(); + break; + } + + case WindowClosed: { + QString id = QString::number(data.toObject().value("id").toInt()); + m_windows.remove(id); + emit windowsChanged(); + break; + } + + case WindowChanged: { + QJsonObject win = data.toObject(); + m_windows[QString::number(win.value("id").toInt())] = win.toVariantMap(); + emit windowsChanged(); + break; + } + + case Workspaces: + m_workspaces = data.toObject().toVariantMap(); + emit workspacesChanged(); + break; + + case ActiveWorkspaceChanged: { + int newId = data.toObject().value("id").toInt(-1); + if (newId == -1) { + m_activeWorkspaceId = -1; + m_activeWorkspace = QVariant(); + } else { + m_activeWorkspaceId = newId; + m_activeWorkspace = m_workspaces.value(QString::number(newId)); + } + emit activeWorkspaceIdChanged(); + emit activeWorkspaceChanged(); + break; + } + + case WorkspaceChanged: { + QJsonObject ws = data.toObject(); + m_workspaces[QString::number(ws.value("id").toInt())] = ws.toVariantMap(); + emit workspacesChanged(); + break; + } + + case WorkspaceRemoved: { + QString id = QString::number(data.toObject().value("id").toInt()); + m_workspaces.remove(id); + emit workspacesChanged(); + break; + } + + case Space: + m_space = data.toVariant(); + emit spaceChanged(); + break; + + case Unknown: + default: + break; + } +} diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp new file mode 100644 index 00000000..b64b9cf1 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class FhtCompositor : public QObject { + Q_OBJECT + QML_SINGLETON + QML_ELEMENT + + Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged) + Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged) + Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged) + Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged) + Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged) + Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged) + Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged) + +public: + explicit FhtCompositor(QObject *parent = nullptr); + + QVariantMap windows() const { return m_windows; } + QVariantMap workspaces() const { return m_workspaces; } + QVariant space() const { return m_space; } + int focusedWindowId() const { return m_focusedWindowId; } + QVariant focusedWindow() const { return m_focusedWindow; } + int activeWorkspaceId() const { return m_activeWorkspaceId; } + QVariant activeWorkspace() const { return m_activeWorkspace; } + +signals: + void windowsChanged(); + void workspacesChanged(); + void spaceChanged(); + void focusedWindowIdChanged(); + void focusedWindowChanged(); + void activeWorkspaceIdChanged(); + void activeWorkspaceChanged(); + +private slots: + void onReadyRead(); + void handleEvent(const QJsonObject &event); + +private: + QTcpSocket *m_socket = nullptr; + QVariantMap m_windows; + QVariantMap m_workspaces; + QVariant m_space; + int m_focusedWindowId = -1; + QVariant m_focusedWindow; + int m_activeWorkspaceId = -1; + QVariant m_activeWorkspace; +}; From a40b08f01ea39faab9c9d8d3c34fa2c73eb30219 Mon Sep 17 00:00:00 2001 From: Ardox Date: Mon, 17 Nov 2025 14:15:13 +0100 Subject: [PATCH 02/27] Added new qml modules Added a fhtc IPC and WORKSPACES entry for qml --- .../plugins/src/Sleex/fhtc/CMakeLists.txt | 16 ++ .../sleex/plugins/src/Sleex/fhtc/ipc.cpp | 207 ++++++++++++++++++ .../sleex/plugins/src/Sleex/fhtc/ipc.hpp | 55 +++++ .../sleex/plugins/src/Sleex/fhtc/plugin.cpp | 14 ++ .../plugins/src/Sleex/fhtc/workspaces.cpp | 124 +++++++++++ .../plugins/src/Sleex/fhtc/workspaces.hpp | 51 +++++ 6 files changed, 467 insertions(+) create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt new file mode 100644 index 00000000..abdfe1c0 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt @@ -0,0 +1,16 @@ + +qml_module(sleex-fhtc + URI Sleex.Fhtc + SOURCES + ipc.hpp ipc.cpp + workspaces.hpp workspaces.cpp + plugin.cpp +) + +target_link_libraries(sleex-fhtc + PRIVATE + Qt6::Core + Qt6::Qml + Qt6::Network + Qt6::Json +) \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp new file mode 100644 index 00000000..392e6c21 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp @@ -0,0 +1,207 @@ +#include "ipc.hpp" +#include +#include +#include +#include +#include + +Ipc::Ipc(QObject *parent) +: QObject(parent), +m_requestSocket(new QLocalSocket(this)), +m_eventSocket(new QLocalSocket(this)) +{ + m_socketPath = qEnvironmentVariable("FHTC_SOCKET_PATH"); + if (m_socketPath.isEmpty()) { + emit error("The environment variable FHTC_SOCKET_PATH is not set."); + qWarning() << "Ipc: FHTC_SOCKET_PATH is not set."; + } + + // Connections for the request socket + connect(m_requestSocket, &QLocalSocket::connected, this, &Ipc::onRequestConnected); + connect(m_requestSocket, &QLocalSocket::readyRead, this, &Ipc::onRequestReadyRead); + connect(m_requestSocket, &QLocalSocket::errorOccurred, this, &Ipc::onRequestError); + connect(m_requestSocket, &QLocalSocket::disconnected, this, &Ipc::onRequestDisconnected); + + // Connections for the event socket + connect(m_eventSocket, &QLocalSocket::connected, this, &Ipc::onEventConnected); + connect(m_eventSocket, &QLocalSocket::readyRead, this, &Ipc::onEventReadyRead); + connect(m_eventSocket, &QLocalSocket::errorOccurred, this, &Ipc::onEventError); + connect(m_eventSocket, &QLocalSocket::disconnected, this, &Ipc::onEventDisconnected); +} + +Ipc::~Ipc() +{ + // Sockets are automatically destroyed because 'this' is their parent +} + +// --- Public slots --- + +void Ipc::subscribe() +{ + if (m_socketPath.isEmpty()) return; + if (m_eventSocket->state() == QLocalSocket::UnconnectedState) { + qDebug() << "Ipc: Connecting to the event socket..."; + m_eventSocket->connectToServer(m_socketPath); + } +} + +void Ipc::sendRequest(const QVariant &request) +{ + if (m_socketPath.isEmpty()) return; + + m_pendingRequests.enqueue(request); + if (m_requestSocket->state() == QLocalSocket::UnconnectedState) { + qDebug() << "Ipc: Connecting to the request socket..."; + m_requestSocket->connectToServer(m_socketPath); + } else if (m_requestSocket->state() == QLocalSocket::ConnectedState) { + // If already connected, send the pending request + writeJson(m_requestSocket, m_pendingRequests.dequeue()); + } + // If 'Connecting', the request will be sent in onRequestConnected +} + +void Ipc::sendAction(const QVariant &action) +{ + QVariantMap request; + request.insert("action", action); + sendRequest(request); +} + +// --- Private slots: Request socket --- + +void Ipc::onRequestConnected() +{ + qDebug() << "Ipc: Request socket connected."; + // Send the first pending request (if any) + if (!m_pendingRequests.isEmpty()) { + writeJson(m_requestSocket, m_pendingRequests.dequeue()); + } +} + +void Ipc::onRequestReadyRead() +{ + m_requestBuffer.append(m_requestSocket->readAll()); + + // The fhtc IPC guarantees one response per request on this socket. + // We do not loop, we just wait for a complete line. + QVariant response = parseLine(m_requestBuffer); + if (!response.isNull()) { + emit requestResponse(response); + + // If there are other requests, send them + if (!m_pendingRequests.isEmpty()) { + writeJson(m_requestSocket, m_pendingRequests.dequeue()); + } + } +} + +void Ipc::onRequestError(QLocalSocket::LocalSocketError socketError) +{ + Q_UNUSED(socketError); + QString msg = "Ipc (Request): " + m_requestSocket->errorString(); + qWarning() << msg; + emit error(msg); + m_pendingRequests.clear(); // Clear pending requests +} + +void Ipc::onRequestDisconnected() +{ + qDebug() << "Ipc: Request socket disconnected."; + if (!m_requestBuffer.isEmpty()) { + qWarning() << "Ipc (Request): DDisconnected with data in buffer:" << m_requestBuffer; + m_requestBuffer.clear(); + } +} + +// --- Private slots: Event socket --- + +void Ipc::onEventConnected() +{ + qDebug() << "Ipc: Event socket connected. Sending 'subscribe' request."; + emit subscribed(); + + // Send the subscribe request + QVariantMap subRequest; + subRequest.insert("subscribe", QVariant()); // "subscribe": null + writeJson(m_eventSocket, subRequest); +} + +void Ipc::onEventReadyRead() +{ + m_eventBuffer.append(m_eventSocket->readAll()); + + // There may be multiple JSON events in the buffer + while (true) { + QVariant event = parseLine(m_eventBuffer); + if (event.isNull()) { + break; // Incomplete line, wait for more data + } + emit newEvent(event); + } +} + +void Ipc::onEventError(QLocalSocket::LocalSocketError socketError) +{ + Q_UNUSED(socketError); + QString msg = "Ipc (Event): " + m_eventSocket->errorString(); + qWarning() << msg; + emit error(msg); +} + +void Ipc::onEventDisconnected() +{ + QString msg = "Ipc: Event socket disconnected. Attempting to reconnect in 5s..."; + qWarning() << msg; + emit error(msg); + + // Attempt automatic reconnection for the event socket + QTimer::singleShot(5000, this, &Ipc::subscribe); +} + +// --- Utility functions --- +void Ipc::writeJson(QLocalSocket *socket, const QVariant &data) +{ + if (socket->state() != QLocalSocket::ConnectedState) { + qWarning() << "Ipc: Attempt to write to a socket that is not connected."; + return; + } + + QJsonDocument doc = QJsonDocument::fromVariant(data); + if (doc.isNull()) { + qWarning() << "Ipc: Failed to convert QVariant to JSON."; + return; + } + + QByteArray json = doc.toJson(QJsonDocument::Compact); + json.append('\n'); // fhtc IPC is delimited by newlines + + socket->write(json); +} + +QVariant Ipc::parseLine(QByteArray &buffer) +{ + int newlineIdx = buffer.indexOf('\n'); + if (newlineIdx == -1) { + return QVariant(); // Incomplete line + } + + // Extract the line and remove it from the buffer + QByteArray line = buffer.left(newlineIdx); + buffer = buffer.mid(newlineIdx + 1); + + if (line.isEmpty()) { + return QVariant(); // Empty line, ignore + } + + QJsonParseError parseError; + QJsonDocument doc = QJsonDocument::fromJson(line, &parseError); + + if (parseError.error != QJsonParseError::NoError) { + QString msg = "Ipc: JSON parse error: " + parseError.errorString() + " | Line: " + QString(line); + qWarning() << msg; + emit error(msg); + return QVariant(); + } + + return doc.toVariant(); +} diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp new file mode 100644 index 00000000..c1cdad4d --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp @@ -0,0 +1,55 @@ +#ifndef IPC_H +#define IPC_H + +#include +#include +#include +#include +#include + +class Ipc : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(Ipc) + QML_SINGLETON + +public: + explicit Ipc(QObject *parent = nullptr); + ~Ipc() override; + +signals: + void error(const QString &message); + void requestResponse(const QVariant &response); + void subscribed(); + void newEvent(const QVariant &event); + +public slots: + void subscribe(); + void sendRequest(const QVariant &request); + void sendAction(const QVariant &action); + +private slots: + void onRequestConnected(); + void onRequestReadyRead(); + void onRequestError(QLocalSocket::LocalSocketError socketError); + void onRequestDisconnected(); + + void onEventConnected(); + void onEventReadyRead(); + void onEventError(QLocalSocket::LocalSocketError socketError); + void onEventDisconnected(); + +private: + void writeJson(QLocalSocket* socket, const QVariant &data); + QVariant parseLine(QByteArray &buffer); + + QString m_socketPath; + QLocalSocket *m_requestSocket; + QLocalSocket *m_eventSocket; + + QByteArray m_eventBuffer; + QByteArray m_requestBuffer; + QQueue m_pendingRequests; +}; + +#endif // IPC_H \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp new file mode 100644 index 00000000..aff6826b --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp @@ -0,0 +1,14 @@ +#include + +class SleexFhtcPlugin : public QQmlExtensionPlugin { + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri) override { + // QML_ELEMENT and QML_SINGLETON macros handle registration automatically + Q_UNUSED(uri) + } +}; + +#include "plugin.moc" \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp new file mode 100644 index 00000000..977ddf48 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp @@ -0,0 +1,124 @@ +#include "workspaces.hpp" +#include +#include + +Workspaces::Workspaces(QObject *parent) + : QObject(parent), m_ipc(nullptr), m_activeWorkspaceId(-1) +{ +} + +void Workspaces::classBegin() +{ + // this "hook" is called by the QML engine after instantiation. + // It's the best moment to get the Ipc singleton instance. + QQmlEngine *engine = qmlEngine(this); + if (!engine) { + qWarning() << "Workspaces: Unable to find the QML engine."; + return; + } + + // Qt 6.2+ + m_ipc = engine->singletonInstance("Sleex.Fhtc", "Ipc"); + + if (!m_ipc) { + qWarning() << "Workspaces: Cannot find the 'Ipc' singleton."; + qWarning() << "Make sure 'Ipc' is imported and used in QML (e.g., Ipc.subscribe())"; + return; + } + + connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleNewEvent); + connect(m_ipc, &Ipc::requestResponse, this, &Workspaces::handleRequestResponse); + connect(m_ipc, &Ipc::subscribed, this, &Workspaces::requestInitialState); + + // If Ipc is already 'subscribed', request the state + requestInitialState(); +} + + +QVariantMap Workspaces::workspaces() const { return m_workspaces; } +QVariantMap Workspaces::space() const { return m_space; } +int Workspaces::activeWorkspaceId() const { return m_activeWorkspaceId; } + +QVariant Workspaces::activeWorkspace() const +{ + return m_workspaces.value(QString::number(m_activeWorkspaceId)); +} + + +void Workspaces::requestInitialState() +{ + if (!m_ipc) return; + + m_ipc->sendRequest(QVariantMap{{"workspaces", QVariant()}}); + m_ipc->sendRequest(QVariantMap{{"space", QVariant()}}); + m_ipc->sendRequest(QVariantMap{{"focused-workspace", QVariant()}}); +} + +void Workspaces::handleNewEvent(const QVariant &event) +{ + QVariantMap eventMap = event.toMap(); + QString type = eventMap.value("event").toString(); + if (type.isEmpty()) return; + + QVariant data = eventMap.value("data"); + bool changed = false; + + if (type == "workspaces") { + m_workspaces = data.toMap(); + emit workspacesChanged(); + changed = true; + } else if (type == "workspace-changed") { + QVariantMap wsMap = data.toMap(); + QString id = wsMap.value("id").toString(); + m_workspaces.insert(id, wsMap); + emit workspacesChanged(); + changed = true; + } else if (type == "workspace-removed") { + QString id = data.toMap().value("id").toString(); + if (m_workspaces.remove(id)) { + emit workspacesChanged(); + changed = true; + } + } else if (type == "active-workspace-changed") { + int id = data.toMap().value("id").toInt(); + if (m_activeWorkspaceId != id) { + m_activeWorkspaceId = id; + emit activeWorkspaceChanged(); + } + } else if (type == "space") { + m_space = data.toMap(); + emit spaceChanged(); + } + + if (changed) { + emit activeWorkspaceChanged(); + } +} + +void Workspaces::handleRequestResponse(const QVariant &response) +{ + QVariantMap resMap = response.toMap(); + + if (resMap.contains("workspaces")) { + m_workspaces = resMap.value("workspaces").toMap(); + emit workspacesChanged(); + emit activeWorkspaceChanged(); + } + + if (resMap.contains("space")) { + m_space = resMap.value("space").toMap(); + emit spaceChanged(); + } + + if (resMap.contains("workspace")) { + QVariant ws = resMap.value("workspace"); + int newId = -1; + if (ws.isValid() && !ws.isNull()) { + newId = ws.toMap().value("id").toInt(); + } + if (m_activeWorkspaceId != newId) { + m_activeWorkspaceId = newId; + emit activeWorkspaceChanged(); + } + } +} \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp new file mode 100644 index 00000000..b3c280b8 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp @@ -0,0 +1,51 @@ +#ifndef WORKSPACES_H +#define WORKSPACES_H + +#include +#include +#include +#include +#include "ipc.hpp" + +class Workspaces : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + QML_NAMED_ELEMENT(Workspaces) + QML_SINGLETON + + Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged) + Q_PROPERTY(QVariantMap space READ space NOTIFY spaceChanged) + Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceChanged) + Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged) + +public: + explicit Workspaces(QObject *parent = nullptr); + + void classBegin() override; + void componentComplete() override; + + QVariantMap workspaces() const; + QVariantMap space() const; + int activeWorkspaceId() const; + QVariant activeWorkspace() const; + +signals: + void workspacesChanged(); + void spaceChanged(); + void activeWorkspaceChanged(); + +private slots: + void handleNewEvent(const QVariant &event); + void handleRequestResponse(const QVariant &response); + void requestInitialState(); + +private: + Ipc *m_ipc; + QVariantMap m_workspaces; + QVariantMap m_space; + int m_activeWorkspaceId; +}; + +#endif // WORKSPACES_H \ No newline at end of file From c35a9e0e799e2b5ef02b3afb049fd5abe9581cce Mon Sep 17 00:00:00 2001 From: Ardox Date: Tue, 9 Dec 2025 14:39:31 +0100 Subject: [PATCH 03/27] FHTC: bar workspace widget handle --- src/share/sleex/modules/bar/Workspaces.qml | 97 ++++++++------ src/share/sleex/services/Fhtc.qml | 148 +++++++++++++++++++++ 2 files changed, 208 insertions(+), 37 deletions(-) create mode 100644 src/share/sleex/services/Fhtc.qml diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index 5583bf93..50392dd8 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -8,7 +8,6 @@ import QtQuick.Controls import QtQuick.Layouts import Quickshell import Quickshell.Wayland -import Quickshell.Hyprland import Quickshell.Io import Quickshell.Widgets import Qt5Compat.GraphicalEffects @@ -16,10 +15,27 @@ import Qt5Compat.GraphicalEffects Item { required property var bar property bool borderless: Config.options.bar.borderless - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) - readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + readonly property var activeWindow: Fhtc.focusedWindow + readonly property string screenName: bar.screen?.name ?? "" + + // Get workspaces for this screen only, sorted by ID + readonly property var screenWorkspaces: { + return Object.values(Fhtc.workspaces) + .filter(ws => ws.output === screenName) + .sort((a, b) => a.id - b.id); + } + + // Active workspace index within this screen (0-based) + readonly property int activeWorkspaceIndex: { + if (!Fhtc.activeWorkspace) return -1; + if (Fhtc.activeWorkspace.output !== screenName) return -1; + // Find the index of the active workspace in our sorted screen workspaces + const idx = screenWorkspaces.findIndex(ws => ws.id === Fhtc.activeWorkspace.id); + // Return -1 if the workspace is beyond the shown limit + if (idx >= Config.options.bar.workspaces.shown) return -1; + return idx; + } - readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown) property list workspaceOccupied: [] property int widgetPadding: 0 property int horizontalPadding: 5 @@ -28,23 +44,31 @@ Item { property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown - // Function to update workspaceOccupied function updateWorkspaceOccupied() { workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => { - return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1); + // Get the workspace at this index for this screen + const ws = screenWorkspaces[i]; + if (!ws) return false; + // Check if the workspace has any windows + return ws.windows && ws.windows.length > 0; }) } // Initialize workspaceOccupied when the component is created Component.onCompleted: updateWorkspaceOccupied() - // Listen for changes in Hyprland.workspaces.values + // Listen for changes in Fhtc.workspaces and windows Connections { - target: Hyprland.workspaces - function onValuesChanged() { + target: Fhtc + function onWorkspacesChanged() { + updateWorkspaceOccupied(); + } + function onWindowsChanged() { + updateWorkspaceOccupied(); + } + function onActiveWorkspaceChanged() { updateWorkspaceOccupied(); } } @@ -57,23 +81,13 @@ Item { WheelHandler { onWheel: (event) => { if (event.angleDelta.y < 0) - Hyprland.dispatch(`workspace +1`); + Fhtc.dispatch("focus-workspace", ["+1"]); else if (event.angleDelta.y > 0) - Hyprland.dispatch(`workspace -1`); + Fhtc.dispatch("focus-workspace", ["-1"]); } acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad } - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.BackButton - onPressed: (event) => { - if (event.button === Qt.BackButton) { - Hyprland.dispatch(`togglespecialworkspace`); - } - } - } - Item { anchors.fill: parent anchors.leftMargin: horizontalPadding @@ -96,8 +110,8 @@ Item { implicitWidth: workspaceButtonWidth implicitHeight: workspaceButtonWidth radius: Appearance.rounding.full - property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index)) - property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2)) + property var leftOccupied: (workspaceOccupied[index-1]) + property var rightOccupied: (workspaceOccupied[index+1]) property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full @@ -107,7 +121,7 @@ Item { bottomRightRadius: radiusRight color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0 + opacity: (workspaceOccupied[index]) ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -129,6 +143,7 @@ Item { // Active workspace Rectangle { z: 2 + visible: activeWorkspaceIndex >= 0 // Make active ws indicator, which has a brighter color, smaller to look like it is of the same size as ws occupied highlight property real activeWorkspaceMargin: 2 implicitHeight: workspaceButtonWidth - activeWorkspaceMargin * 2 @@ -136,8 +151,8 @@ Item { color: Appearance.colors.colPrimary anchors.verticalCenter: parent.verticalCenter - property real idx1: workspaceIndexInGroup - property real idx2: workspaceIndexInGroup + property real idx1: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0 + property real idx2: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0 x: Math.min(idx1, idx2) * workspaceButtonWidth + activeWorkspaceMargin implicitWidth: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - activeWorkspaceMargin * 2 @@ -172,24 +187,32 @@ Item { Button { id: button - property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1 + property var workspace: screenWorkspaces[index] ?? null + property int workspaceId: workspace?.id ?? -1 Layout.fillHeight: true - onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`) + onPressed: { + // Use index + 1 for focus-workspace command (1-based) + Fhtc.dispatch("focus-workspace", [(index + 1).toString()]); + } width: workspaceButtonWidth background: Item { id: workspaceButtonBackground implicitWidth: workspaceButtonWidth implicitHeight: workspaceButtonWidth + + // Get the biggest window from the workspace's window list property var biggestWindow: { - const windowsInThisWorkspace = HyprlandData.windowList.filter(w => w.workspace.id == button.workspaceValue) + if (!button.workspace || !button.workspace.windows || button.workspace.windows.length === 0) return null; + const windowIds = button.workspace.windows; + const windowsInThisWorkspace = windowIds.map(id => Fhtc.windows[id]).filter(w => w != null); return windowsInThisWorkspace.reduce((maxWin, win) => { const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0) const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0) return winArea > maxArea ? win : maxWin }, null) } - property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.["app-id"]), "image-missing") StyledText { // Workspace number text opacity: GlobalStates.workspaceShowNumbers @@ -202,9 +225,9 @@ Item { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) - text: `${button.workspaceValue}` + text: `${index + 1}` elide: Text.ElideRight - color: (monitor.activeWorkspace?.id == button.workspaceValue) ? + color: (activeWorkspaceIndex == index) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) @@ -224,9 +247,9 @@ Item { width: workspaceButtonWidth * 0.18 height: width radius: width / 2 - color: (monitor.activeWorkspace?.id == button.workspaceValue) ? - Appearance.m3colors.m3onPrimary : - (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : + color: (activeWorkspaceIndex == index) ? + Appearance.m3colors.m3onPrimary : + (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) Behavior on opacity { @@ -240,7 +263,7 @@ Item { opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 : (workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ? 1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0 - visible: opacity > 0 + visible: opacity > 0 IconImage { id: mainAppIcon anchors.bottom: parent.bottom diff --git a/src/share/sleex/services/Fhtc.qml b/src/share/sleex/services/Fhtc.qml new file mode 100644 index 00000000..81bfe060 --- /dev/null +++ b/src/share/sleex/services/Fhtc.qml @@ -0,0 +1,148 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + // The socket we connect to. This is the bridge between us and fht-compositor + readonly property string socketPath: Quickshell.env("FHTC_SOCKET_PATH") + + // How things work here is that we store workspace and window data into separate maps, + // and actual data structures (like Monitors, or even Workspaces) store IDs. The stored + // maps are ID->Window/Workspace maps + // + // FIXME: Add typing to these. + property var workspaces: ({}) + property var windows: ({}) + property var space: ({}) + + // Focused window is the one that is currently receiving keyboard input. There can be multiple + // active windows at once, but only one at max focused window can exist at a time. + property int focusedWindowId: -1 + property var focusedWindow: null + // The active workspace is the workspace that currently has the pointer on it. This usually means + // the displayed workspace on the focused monitor. + property int activeWorkspaceId: -1 + property var activeWorkspace: null + + Socket { + id: subscribeSocket + path: root.socketPath + connected: true + + onConnectionStateChanged: { + if (connected) + // When we connect, start turn this socket immediatly into a subcribe + // socket. Allowing us to actually use it. + subscribeSocket.write('"subscribe"\n'); + } + + parser: SplitParser { + onRead: line => { + try { + root.handleEvent(JSON.parse(line)); + } catch (err) { + console.warn("FhtCompositor: failed to parse event: ", line, err); + } + } + } + } + + // The main event handler. The passed in `event` parameter is a fht-compositor-ipc::Event + // https://github.com/nferhat/fht-compositor/blob/ff3d9f3b6549b38e99755d022f5343fda3d6a971/fht-compositor-ipc/src/lib.rs#L131 + function handleEvent(event) { + switch (event.event) { + case "windows": + // Update the window list. The passed in data is a HashMap + root.windows = event.data; + root.windowsChanged(); + break; + case "focused-window-changed": + var newId = event.data.id; + if (newId == null) { + root.focusedWindowId = -1; + root.focusedWindow = null; + } else { + // FIXME: Maybe this event could be sent before a WindowChanged event, in this + // case this could lead to invalid state. + root.focusedWindowId = newId; + root.focusedWindow = root.windows[newId]; + } + + root.focusedWindowChanged(); + root.focusedWindowIdChanged(); + + break; + case "window-closed": + // NOTE: the compositor will sent us a focused-window-changed event, so we don't + // have to update the focusedWindow here. + var id = event.data.id; + delete root.windows[id]; + + root.windowsChanged(); + + break; + case "window-changed": + // This event could either be an existing window changing, or a new window opening + const win = event.data; + root.windows[win.id] = win; + + root.windowsChanged(); + + break; + case "workspaces": + // Update the workspace list. The passed in data is a HashMap + root.workspaces = event.data; + root.workspacesChanged(); + break; + case "active-workspace-changed": + var newId = event.data.id; + if (newId == null) { + root.activeWorkspaceId = -1; + root.activeWorkspace = null; + } else { + // FIXME: Maybe this event could be sent before a WorkspaceChanged event, in this + // case this could lead to invalid state. + root.activeWorkspaceId = newId; + root.activeWorkspace = root.workspaces[newId]; + } + + root.activeWorkspaceChanged(); + root.activeWorkspaceIdChanged(); + + break; + case "workspace-changed": + const ws = event.data; + root.workspaces[ws.id] = ws; + root.workspacesChanged(); + + break; + case "workspace-removed": + var id = event.data.id; + delete root.workspaces[id]; + + root.workspacesChanged(); + + break; + case "space": + root.space = event.data; + root.spaceChanged(); + break; + default: + // console.warn("Unhandled fht-compositor event: ", event.event); + break; + } + } + + function dispatch(command, args) { + var cmd = { + command: command, + args: args + }; + + subscribeSocket.write(JSON.stringify(cmd) + "\n"); + } +} From a60ffd66dc78f09ef341e85838592d408136814e Mon Sep 17 00:00:00 2001 From: Ardox Date: Tue, 9 Dec 2025 15:00:03 +0100 Subject: [PATCH 04/27] FHTC: bar workspace action handle --- src/share/sleex/modules/bar/Workspaces.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index 50392dd8..f7d89c0a 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -81,9 +81,9 @@ Item { WheelHandler { onWheel: (event) => { if (event.angleDelta.y < 0) - Fhtc.dispatch("focus-workspace", ["+1"]); + Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-next-workspace"]); else if (event.angleDelta.y > 0) - Fhtc.dispatch("focus-workspace", ["-1"]); + Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-previous-workspace"]); } acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad } @@ -191,8 +191,9 @@ Item { property int workspaceId: workspace?.id ?? -1 Layout.fillHeight: true onPressed: { - // Use index + 1 for focus-workspace command (1-based) - Fhtc.dispatch("focus-workspace", [(index + 1).toString()]); + if (button.workspaceId >= 0) { + Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-workspace", `${button.workspaceId}`]); + } } width: workspaceButtonWidth From 42bff99683a33556d71490b6ffb4b37fa76fc849 Mon Sep 17 00:00:00 2001 From: Ardox Date: Tue, 9 Dec 2025 15:00:23 +0100 Subject: [PATCH 05/27] FHTC: dock visibility handle --- src/share/sleex/modules/dock/Dock.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/share/sleex/modules/dock/Dock.qml b/src/share/sleex/modules/dock/Dock.qml index d96b82eb..78a4f234 100644 --- a/src/share/sleex/modules/dock/Dock.qml +++ b/src/share/sleex/modules/dock/Dock.qml @@ -10,7 +10,6 @@ import Quickshell.Io import Quickshell import Quickshell.Widgets import Quickshell.Wayland -import Quickshell.Hyprland Scope { // Scope id: root @@ -27,7 +26,7 @@ Scope { // Scope property bool reveal: root.pinned || (Config.options?.dock.hoverToReveal && dockMouseArea.containsMouse) || dockApps.requestDockShow - || (!ToplevelManager.activeToplevel?.activated) + || (ToplevelManager.toplevels?.length === 0) anchors { bottom: true From 07627068ff98eaedf204a91326efa22cebb7d546 Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 12:59:14 +0100 Subject: [PATCH 06/27] Link fhtc c++ classes --- src/share/sleex/plugins/src/Sleex/CMakeLists.txt | 1 + src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt index 2efdd4b1..cb070815 100644 --- a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt +++ b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt @@ -34,3 +34,4 @@ function(qml_module arg_TARGET) endfunction() add_subdirectory(services) +add_subdirectory(fhtc) \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt index abdfe1c0..64aad482 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt +++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt @@ -12,5 +12,4 @@ target_link_libraries(sleex-fhtc Qt6::Core Qt6::Qml Qt6::Network - Qt6::Json ) \ No newline at end of file From e48511e16b9b4d2426c792f969622fa368006feb Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 13:34:28 +0100 Subject: [PATCH 07/27] Impl ActiveWindow with FHTC --- src/share/sleex/modules/bar/ActiveWindow.qml | 6 +++--- src/share/sleex/services/Fhtc.qml | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml index 8262dbfb..f55a1569 100644 --- a/src/share/sleex/modules/bar/ActiveWindow.qml +++ b/src/share/sleex/modules/bar/ActiveWindow.qml @@ -1,5 +1,6 @@ import qs.modules.common import qs.modules.common.widgets +import qs.services import QtQuick import QtQuick.Layouts import Quickshell.Wayland @@ -9,7 +10,6 @@ Item { id: root required property var bar readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) - readonly property Toplevel activeWindow: ToplevelManager.activeToplevel implicitWidth: colLayout.implicitWidth @@ -26,7 +26,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext elide: Text.ElideRight - text: root.activeWindow?.activated ? root.activeWindow?.appId : qsTr("Desktop") + text: Fhtc.activeWindowAppId !== "" ? Fhtc.activeWindowAppId : qsTr("Desktop") } StyledText { @@ -34,7 +34,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnLayer0 elide: Text.ElideRight - text: root.activeWindow?.activated ? root.activeWindow?.title : `${qsTr("Workspace")} ${monitor.activeWorkspace?.id}` + text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspace.id + 1}` } } diff --git a/src/share/sleex/services/Fhtc.qml b/src/share/sleex/services/Fhtc.qml index 81bfe060..191f13d7 100644 --- a/src/share/sleex/services/Fhtc.qml +++ b/src/share/sleex/services/Fhtc.qml @@ -28,6 +28,9 @@ Singleton { property int activeWorkspaceId: -1 property var activeWorkspace: null + property string activeWindowTitle: focusedWindow != null ? focusedWindow.title : "" + property string activeWindowAppId: focusedWindow != null ? focusedWindow["app-id"] : "" + Socket { id: subscribeSocket path: root.socketPath From 8759246d8b29935ca28a261578b2d453cf494fb0 Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 13:49:58 +0100 Subject: [PATCH 08/27] Update active ws id --- src/share/sleex/modules/bar/ActiveWindow.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml index f55a1569..a2c93376 100644 --- a/src/share/sleex/modules/bar/ActiveWindow.qml +++ b/src/share/sleex/modules/bar/ActiveWindow.qml @@ -34,7 +34,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnLayer0 elide: Text.ElideRight - text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspace.id + 1}` + text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}` } } From 52c85fe6dcb48382e72ad80278f075bc485e1af8 Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 13:50:11 +0100 Subject: [PATCH 09/27] Comment out every GlobalShortcut definitions --- src/share/sleex/GlobalStates.qml | 40 ++-- .../sleex/modules/cheatsheet/Cheatsheet.qml | 52 +++--- .../sleex/modules/dashboard/Dashboard.qml | 42 ++--- .../OnScreenDisplayBrightness.qml | 32 ++-- .../onScreenDisplay/OnScreenDisplayVolume.qml | 32 ++-- src/share/sleex/modules/overview/Overview.qml | 176 +++++++++--------- src/share/sleex/modules/session/Session.qml | 34 ++-- .../sleex/modules/sidebarLeft/SidebarLeft.qml | 70 +++---- .../wallpaperSelector/WallpaperSelector.qml | 48 ++--- src/share/sleex/services/Brightness.qml | 22 +-- src/share/sleex/shell.qml | 2 +- 11 files changed, 275 insertions(+), 275 deletions(-) diff --git a/src/share/sleex/GlobalStates.qml b/src/share/sleex/GlobalStates.qml index 7651be22..66a6a56e 100644 --- a/src/share/sleex/GlobalStates.qml +++ b/src/share/sleex/GlobalStates.qml @@ -41,18 +41,18 @@ Singleton { } } - GlobalShortcut { - name: "workspaceNumber" - description: qsTr("Hold to show workspace numbers, release to show icons") - - onPressed: { - workspaceShowNumbersTimer.start() - } - onReleased: { - workspaceShowNumbersTimer.stop() - workspaceShowNumbers = false - } - } + // GlobalShortcut { + // name: "workspaceNumber" + // description: qsTr("Hold to show workspace numbers, release to show icons") + + // onPressed: { + // workspaceShowNumbersTimer.start() + // } + // onReleased: { + // workspaceShowNumbersTimer.stop() + // workspaceShowNumbers = false + // } + // } IpcHandler { target: "zoom" @@ -74,13 +74,13 @@ Singleton { root.screenLocked = true; } } - GlobalShortcut { - name: "lockScreen" - description: qsTr("Lock screen (obviously)") - - onPressed: { - root.screenLocked = true; - } - } + // GlobalShortcut { + // name: "lockScreen" + // description: qsTr("Lock screen (obviously)") + + // onPressed: { + // root.screenLocked = true; + // } + // } } diff --git a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml index 2146fb23..3f7fcfc4 100644 --- a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml +++ b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml @@ -135,31 +135,31 @@ Scope { // Scope } } - GlobalShortcut { - name: "cheatsheetToggle" - description: qsTr("Toggles cheatsheet on press") - - onPressed: { - cheatsheetLoader.active = !cheatsheetLoader.active; - } - } - - GlobalShortcut { - name: "cheatsheetOpen" - description: qsTr("Opens cheatsheet on press") - - onPressed: { - cheatsheetLoader.active = true; - } - } - - GlobalShortcut { - name: "cheatsheetClose" - description: qsTr("Closes cheatsheet on press") - - onPressed: { - cheatsheetLoader.active = false; - } - } + // GlobalShortcut { + // name: "cheatsheetToggle" + // description: qsTr("Toggles cheatsheet on press") + + // onPressed: { + // cheatsheetLoader.active = !cheatsheetLoader.active; + // } + // } + + // GlobalShortcut { + // name: "cheatsheetOpen" + // description: qsTr("Opens cheatsheet on press") + + // onPressed: { + // cheatsheetLoader.active = true; + // } + // } + + // GlobalShortcut { + // name: "cheatsheetClose" + // description: qsTr("Closes cheatsheet on press") + + // onPressed: { + // cheatsheetLoader.active = false; + // } + // } } diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml index 8df2cc8a..519fa347 100644 --- a/src/share/sleex/modules/dashboard/Dashboard.qml +++ b/src/share/sleex/modules/dashboard/Dashboard.qml @@ -208,25 +208,25 @@ Scope { } } - GlobalShortcut { - name: "dashboardToggle" - description: qsTr("Toggles dashboard on press") - onPressed: { - GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen; - if(GlobalStates.dashboardOpen) Notifications.timeoutAll(); - } - } - GlobalShortcut { - name: "dashboardOpen" - description: qsTr("Opens dashboard on press") - onPressed: { - GlobalStates.dashboardOpen = true; - Notifications.timeoutAll(); - } - } - GlobalShortcut { - name: "dashboardClose" - description: qsTr("Closes dashboard on press") - onPressed: { GlobalStates.dashboardOpen = false; } - } + // GlobalShortcut { + // name: "dashboardToggle" + // description: qsTr("Toggles dashboard on press") + // onPressed: { + // GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen; + // if(GlobalStates.dashboardOpen) Notifications.timeoutAll(); + // } + // } + // GlobalShortcut { + // name: "dashboardOpen" + // description: qsTr("Opens dashboard on press") + // onPressed: { + // GlobalStates.dashboardOpen = true; + // Notifications.timeoutAll(); + // } + // } + // GlobalShortcut { + // name: "dashboardClose" + // description: qsTr("Closes dashboard on press") + // onPressed: { GlobalStates.dashboardOpen = false; } + // } } diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index af6115cb..f99b8f24 100644 --- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -132,21 +132,21 @@ Scope { } } - GlobalShortcut { - name: "osdBrightnessTrigger" - description: qsTr("Triggers brightness OSD on press") - - onPressed: { - root.triggerOsd() - } - } - GlobalShortcut { - name: "osdBrightnessHide" - description: qsTr("Hides brightness OSD on press") - - onPressed: { - root.showOsdValues = false - } - } + // GlobalShortcut { + // name: "osdBrightnessTrigger" + // description: qsTr("Triggers brightness OSD on press") + + // onPressed: { + // root.triggerOsd() + // } + // } + // GlobalShortcut { + // name: "osdBrightnessHide" + // description: qsTr("Hides brightness OSD on press") + + // onPressed: { + // root.showOsdValues = false + // } + // } } \ No newline at end of file diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml index 5598f2ef..ac40827f 100644 --- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -184,21 +184,21 @@ Scope { showOsdValues = !showOsdValues } } - GlobalShortcut { - name: "osdVolumeTrigger" - description: qsTr("Triggers volume OSD on press") - - onPressed: { - root.triggerOsd() - } - } - GlobalShortcut { - name: "osdVolumeHide" - description: qsTr("Hides volume OSD on press") - - onPressed: { - root.showOsdValues = false - } - } + // GlobalShortcut { + // name: "osdVolumeTrigger" + // description: qsTr("Triggers volume OSD on press") + + // onPressed: { + // root.triggerOsd() + // } + // } + // GlobalShortcut { + // name: "osdVolumeHide" + // description: qsTr("Hides volume OSD on press") + + // onPressed: { + // root.showOsdValues = false + // } + // } } \ No newline at end of file diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml index 5687357e..54a5ddca 100644 --- a/src/share/sleex/modules/overview/Overview.qml +++ b/src/share/sleex/modules/overview/Overview.qml @@ -98,7 +98,7 @@ Scope { } Keys.onPressed: (event) => { - if (event.key === Qt.Key_Escape) { + if (eçvent.key === Qt.Key_Escape) { GlobalStates.overviewOpen = false; } else if (event.key === Qt.Key_Left) { if (!root.searchingText) Hyprland.dispatch("workspace -1"); @@ -150,92 +150,92 @@ Scope { } } - GlobalShortcut { - name: "overviewToggle" - description: qsTr("Toggles overview on press") - - onPressed: { - GlobalStates.overviewOpen = !GlobalStates.overviewOpen - } - } - GlobalShortcut { - name: "overviewClose" - description: qsTr("Closes overview") - - onPressed: { - GlobalStates.overviewOpen = false - } - } - GlobalShortcut { - name: "overviewToggleRelease" - description: qsTr("Toggles overview on release") - - onPressed: { - GlobalStates.superReleaseMightTrigger = true - } - - onReleased: { - if (!GlobalStates.superReleaseMightTrigger) { - GlobalStates.superReleaseMightTrigger = true - return - } - GlobalStates.overviewOpen = !GlobalStates.overviewOpen - } - } - GlobalShortcut { - name: "overviewToggleReleaseInterrupt" - description: qsTr("Interrupts possibility of overview being toggled on release. ") + - qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + - qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") - - onPressed: { - GlobalStates.superReleaseMightTrigger = false - } - } - GlobalShortcut { - name: "overviewClipboardToggle" - description: qsTr("Toggle clipboard query on overview widget") - - onPressed: { - if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { - GlobalStates.overviewOpen = false; - return; - } - for (let i = 0; i < overviewVariants.instances.length; i++) { - let panelWindow = overviewVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - overviewScope.dontAutoCancelSearch = true; - panelWindow.setSearchingText( - Config.options.search.prefix.clipboard - ); - GlobalStates.overviewOpen = true; - return - } - } - } - } - - GlobalShortcut { - name: "overviewEmojiToggle" - description: qsTr("Toggle emoji query on overview widget") - - onPressed: { - if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { - GlobalStates.overviewOpen = false; - return; - } - for (let i = 0; i < overviewVariants.instances.length; i++) { - let panelWindow = overviewVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - overviewScope.dontAutoCancelSearch = true; - panelWindow.setSearchingText( - Config.options.search.prefix.emojis - ); - GlobalStates.overviewOpen = true; - return - } - } - } - } + // GlobalShortcut { + // name: "overviewToggle" + // description: qsTr("Toggles overview on press") + + // onPressed: { + // GlobalStates.overviewOpen = !GlobalStates.overviewOpen + // } + // } + // GlobalShortcut { + // name: "overviewClose" + // description: qsTr("Closes overview") + + // onPressed: { + // GlobalStates.overviewOpen = false + // } + // } + // GlobalShortcut { + // name: "overviewToggleRelease" + // description: qsTr("Toggles overview on release") + + // onPressed: { + // GlobalStates.superReleaseMightTrigger = true + // } + + // onReleased: { + // if (!GlobalStates.superReleaseMightTrigger) { + // GlobalStates.superReleaseMightTrigger = true + // return + // } + // GlobalStates.overviewOpen = !GlobalStates.overviewOpen + // } + // } + // GlobalShortcut { + // name: "overviewToggleReleaseInterrupt" + // description: qsTr("Interrupts possibility of overview being toggled on release. ") + + // qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + + // qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") + + // onPressed: { + // GlobalStates.superReleaseMightTrigger = false + // } + // } + // GlobalShortcut { + // name: "overviewClipboardToggle" + // description: qsTr("Toggle clipboard query on overview widget") + + // onPressed: { + // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { + // GlobalStates.overviewOpen = false; + // return; + // } + // for (let i = 0; i < overviewVariants.instances.length; i++) { + // let panelWindow = overviewVariants.instances[i]; + // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + // overviewScope.dontAutoCancelSearch = true; + // panelWindow.setSearchingText( + // Config.options.search.prefix.clipboard + // ); + // GlobalStates.overviewOpen = true; + // return + // } + // } + // } + // } + + // GlobalShortcut { + // name: "overviewEmojiToggle" + // description: qsTr("Toggle emoji query on overview widget") + + // onPressed: { + // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { + // GlobalStates.overviewOpen = false; + // return; + // } + // for (let i = 0; i < overviewVariants.instances.length; i++) { + // let panelWindow = overviewVariants.instances[i]; + // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + // overviewScope.dontAutoCancelSearch = true; + // panelWindow.setSearchingText( + // Config.options.search.prefix.emojis + // ); + // GlobalStates.overviewOpen = true; + // return + // } + // } + // } + // } } diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml index 9987efca..4dcccd5c 100644 --- a/src/share/sleex/modules/session/Session.qml +++ b/src/share/sleex/modules/session/Session.qml @@ -207,22 +207,22 @@ Scope { } } - GlobalShortcut { - name: "sessionToggle" - description: qsTr("Toggles session screen on press") - - onPressed: { - sessionLoader.active = !sessionLoader.active; - } - } - - GlobalShortcut { - name: "sessionOpen" - description: qsTr("Opens session screen on press") - - onPressed: { - sessionLoader.active = true; - } - } + // GlobalShortcut { + // name: "sessionToggle" + // description: qsTr("Toggles session screen on press") + + // onPressed: { + // sessionLoader.active = !sessionLoader.active; + // } + // } + + // GlobalShortcut { + // name: "sessionOpen" + // description: qsTr("Opens session screen on press") + + // onPressed: { + // sessionLoader.active = true; + // } + // } } diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml index fbdfe662..abbd7757 100644 --- a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml +++ b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml @@ -164,40 +164,40 @@ Scope { // Scope } } - GlobalShortcut { - name: "sidebarLeftToggle" - description: qsTr("Toggles left sidebar on press") - - onPressed: { - GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; - } - } - - GlobalShortcut { - name: "sidebarLeftOpen" - description: qsTr("Opens left sidebar on press") - - onPressed: { - GlobalStates.sidebarLeftOpen = true; - } - } - - GlobalShortcut { - name: "sidebarLeftClose" - description: qsTr("Closes left sidebar on press") - - onPressed: { - GlobalStates.sidebarLeftOpen = false; - } - } - - GlobalShortcut { - name: "sidebarLeftToggleDetach" - description: qsTr("Detach left sidebar into a window/Attach it back") - - onPressed: { - root.detach = !root.detach; - } - } + // GlobalShortcut { + // name: "sidebarLeftToggle" + // description: qsTr("Toggles left sidebar on press") + + // onPressed: { + // GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + // } + // } + + // GlobalShortcut { + // name: "sidebarLeftOpen" + // description: qsTr("Opens left sidebar on press") + + // onPressed: { + // GlobalStates.sidebarLeftOpen = true; + // } + // } + + // GlobalShortcut { + // name: "sidebarLeftClose" + // description: qsTr("Closes left sidebar on press") + + // onPressed: { + // GlobalStates.sidebarLeftOpen = false; + // } + // } + + // GlobalShortcut { + // name: "sidebarLeftToggleDetach" + // description: qsTr("Detach left sidebar into a window/Attach it back") + + // onPressed: { + // root.detach = !root.detach; + // } + // } } diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml index 71b0d59c..3ce7d89c 100644 --- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml +++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml @@ -173,29 +173,29 @@ Scope { } } - GlobalShortcut { - name: "wppselectorToggle" - description: qsTr("Toggles wallpaper selector on press") - - onPressed: { - GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen; - } - } - GlobalShortcut { - name: "wppselectorOpen" - description: qsTr("Opens wallpaper selector on press") - - onPressed: { - GlobalStates.wppselectorOpen = true; - } - } - GlobalShortcut { - name: "wppselectorClose" - description: qsTr("Closes wallpaper selector on press") - - onPressed: { - GlobalStates.wppselectorOpen = false; - } - } + // GlobalShortcut { + // name: "wppselectorToggle" + // description: qsTr("Toggles wallpaper selector on press") + + // onPressed: { + // GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen; + // } + // } + // GlobalShortcut { + // name: "wppselectorOpen" + // description: qsTr("Opens wallpaper selector on press") + + // onPressed: { + // GlobalStates.wppselectorOpen = true; + // } + // } + // GlobalShortcut { + // name: "wppselectorClose" + // description: qsTr("Closes wallpaper selector on press") + + // onPressed: { + // GlobalStates.wppselectorOpen = false; + // } + // } } \ No newline at end of file diff --git a/src/share/sleex/services/Brightness.qml b/src/share/sleex/services/Brightness.qml index 552b2f88..9b8c4fa4 100644 --- a/src/share/sleex/services/Brightness.qml +++ b/src/share/sleex/services/Brightness.qml @@ -145,15 +145,15 @@ Singleton { } } - GlobalShortcut { - name: "brightnessIncrease" - description: qsTr("Increase brightness") - onPressed: root.increaseBrightness() - } - - GlobalShortcut { - name: "brightnessDecrease" - description: qsTr("Decrease brightness") - onPressed: root.decreaseBrightness() - } + // GlobalShortcut { + // name: "brightnessIncrease" + // description: qsTr("Increase brightness") + // onPressed: root.increaseBrightness() + // } + + // GlobalShortcut { + // name: "brightnessDecrease" + // description: qsTr("Decrease brightness") + // onPressed: root.decreaseBrightness() + // } } diff --git a/src/share/sleex/shell.qml b/src/share/sleex/shell.qml index c1eb2c14..eff13b2e 100644 --- a/src/share/sleex/shell.qml +++ b/src/share/sleex/shell.qml @@ -54,7 +54,7 @@ ShellRoot { FirstRunExperience.load() Cliphist.refresh() Idle.init(); - nightLight.load() + NightLight.load() } LazyLoader { active: enableBar; component: Bar {} } From d49ccdd85b6d26336e465d2d43970f3ff8911fcf Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 14:10:49 +0100 Subject: [PATCH 10/27] Using C++ plugins for workspace and window dispatchers and vars --- src/share/sleex/modules/bar/ActiveWindow.qml | 5 +- src/share/sleex/modules/bar/Workspaces.qml | 1 + .../plugins/src/Sleex/fhtc/workspaces.cpp | 190 ++++++++---------- .../plugins/src/Sleex/fhtc/workspaces.hpp | 57 +++--- src/share/sleex/services/Fhtc.qml | 151 -------------- 5 files changed, 126 insertions(+), 278 deletions(-) delete mode 100644 src/share/sleex/services/Fhtc.qml diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml index a2c93376..db358966 100644 --- a/src/share/sleex/modules/bar/ActiveWindow.qml +++ b/src/share/sleex/modules/bar/ActiveWindow.qml @@ -5,6 +5,7 @@ import QtQuick import QtQuick.Layouts import Quickshell.Wayland import Quickshell.Hyprland +import Sleex.Fhtc Item { id: root @@ -26,7 +27,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext elide: Text.ElideRight - text: Fhtc.activeWindowAppId !== "" ? Fhtc.activeWindowAppId : qsTr("Desktop") + text: Fhtc.focusedWindow['app-id'] !== "" ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop") } StyledText { @@ -34,7 +35,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnLayer0 elide: Text.ElideRight - text: Fhtc.activeWindowTitle !== "" ? Fhtc.activeWindowTitle : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}` + text: Fhtc.focusedWindow.title !== "" ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}` } } diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index f7d89c0a..ee1632cd 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -11,6 +11,7 @@ import Quickshell.Wayland import Quickshell.Io import Quickshell.Widgets import Qt5Compat.GraphicalEffects +import Sleex.Fhtc Item { required property var bar diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp index 977ddf48..325f790b 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp @@ -1,124 +1,112 @@ #include "workspaces.hpp" -#include -#include +#include -Workspaces::Workspaces(QObject *parent) - : QObject(parent), m_ipc(nullptr), m_activeWorkspaceId(-1) +Workspaces::Workspaces(QObject *parent) : QObject(parent) { -} - -void Workspaces::classBegin() -{ - // this "hook" is called by the QML engine after instantiation. - // It's the best moment to get the Ipc singleton instance. - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning() << "Workspaces: Unable to find the QML engine."; - return; - } - - // Qt 6.2+ - m_ipc = engine->singletonInstance("Sleex.Fhtc", "Ipc"); - - if (!m_ipc) { - qWarning() << "Workspaces: Cannot find the 'Ipc' singleton."; - qWarning() << "Make sure 'Ipc' is imported and used in QML (e.g., Ipc.subscribe())"; - return; - } - - connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleNewEvent); - connect(m_ipc, &Ipc::requestResponse, this, &Workspaces::handleRequestResponse); - connect(m_ipc, &Ipc::subscribed, this, &Workspaces::requestInitialState); + m_ipc = new Ipc(this); - // If Ipc is already 'subscribed', request the state - requestInitialState(); -} - - -QVariantMap Workspaces::workspaces() const { return m_workspaces; } -QVariantMap Workspaces::space() const { return m_space; } -int Workspaces::activeWorkspaceId() const { return m_activeWorkspaceId; } - -QVariant Workspaces::activeWorkspace() const -{ - return m_workspaces.value(QString::number(m_activeWorkspaceId)); + connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleEvent); + + m_ipc->subscribe(); } - -void Workspaces::requestInitialState() +void Workspaces::dispatch(const QString &command, const QVariant &args) { - if (!m_ipc) return; + QVariantMap cmd; + cmd["command"] = command; + cmd["args"] = args; - m_ipc->sendRequest(QVariantMap{{"workspaces", QVariant()}}); - m_ipc->sendRequest(QVariantMap{{"space", QVariant()}}); - m_ipc->sendRequest(QVariantMap{{"focused-workspace", QVariant()}}); + // Envoi via le socket de requête géré par Ipc + m_ipc->sendRequest(cmd); } -void Workspaces::handleNewEvent(const QVariant &event) +void Workspaces::handleEvent(const QVariant &eventVar) { - QVariantMap eventMap = event.toMap(); - QString type = eventMap.value("event").toString(); - if (type.isEmpty()) return; - - QVariant data = eventMap.value("data"); - bool changed = false; - - if (type == "workspaces") { - m_workspaces = data.toMap(); - emit workspacesChanged(); - changed = true; - } else if (type == "workspace-changed") { - QVariantMap wsMap = data.toMap(); - QString id = wsMap.value("id").toString(); - m_workspaces.insert(id, wsMap); - emit workspacesChanged(); - changed = true; - } else if (type == "workspace-removed") { - QString id = data.toMap().value("id").toString(); - if (m_workspaces.remove(id)) { - emit workspacesChanged(); - changed = true; + // Conversion de l'événement reçu (QVariant/JSON) + QVariantMap event = eventVar.toMap(); + QString type = event.value("event").toString(); + QVariant data = event.value("data"); + + if (type == "windows") { + m_windows = data.toMap(); + emit windowsChanged(); + } + else if (type == "focused-window-changed") { + QVariantMap dataMap = data.toMap(); + // Vérification si l'ID est null/undefined + QVariant idVar = dataMap.value("id"); + + if (idVar.isNull() || !idVar.isValid()) { + m_focusedWindowId = -1; + m_focusedWindow = QVariant(); + } else { + m_focusedWindowId = idVar.toInt(); + m_focusedWindow = m_windows.value(QString::number(m_focusedWindowId)); } - } else if (type == "active-workspace-changed") { + emit focusedWindowIdChanged(); + emit focusedWindowChanged(); + } + else if (type == "window-closed") { int id = data.toMap().value("id").toInt(); - if (m_activeWorkspaceId != id) { - m_activeWorkspaceId = id; + m_windows.remove(QString::number(id)); + emit windowsChanged(); + } + else if (type == "window-changed") { + QVariantMap win = data.toMap(); + int id = win.value("id").toInt(); + + m_windows.insert(QString::number(id), win); + emit windowsChanged(); + + // Si c'est la fenêtre active qui a changé, on met à jour focusedWindow + if (id == m_focusedWindowId) { + m_focusedWindow = win; + emit focusedWindowChanged(); + } + } + else if (type == "workspaces") { + m_workspaces = data.toMap(); + emit workspacesChanged(); + + // Rafraîchir l'activeWorkspace au cas où ses données auraient changé + if (m_activeWorkspaceId != -1) { + m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId)); emit activeWorkspaceChanged(); } - } else if (type == "space") { - m_space = data.toMap(); - emit spaceChanged(); } - - if (changed) { + else if (type == "active-workspace-changed") { + QVariantMap dataMap = data.toMap(); + QVariant idVar = dataMap.value("id"); + + if (idVar.isNull() || !idVar.isValid()) { + m_activeWorkspaceId = -1; + m_activeWorkspace = QVariant(); + } else { + m_activeWorkspaceId = idVar.toInt(); + m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId)); + } + emit activeWorkspaceIdChanged(); emit activeWorkspaceChanged(); } -} - -void Workspaces::handleRequestResponse(const QVariant &response) -{ - QVariantMap resMap = response.toMap(); - - if (resMap.contains("workspaces")) { - m_workspaces = resMap.value("workspaces").toMap(); + else if (type == "workspace-changed") { + QVariantMap ws = data.toMap(); + int id = ws.value("id").toInt(); + + m_workspaces.insert(QString::number(id), ws); emit workspacesChanged(); - emit activeWorkspaceChanged(); - } - - if (resMap.contains("space")) { - m_space = resMap.value("space").toMap(); - emit spaceChanged(); - } - - if (resMap.contains("workspace")) { - QVariant ws = resMap.value("workspace"); - int newId = -1; - if (ws.isValid() && !ws.isNull()) { - newId = ws.toMap().value("id").toInt(); - } - if (m_activeWorkspaceId != newId) { - m_activeWorkspaceId = newId; + + if (id == m_activeWorkspaceId) { + m_activeWorkspace = ws; emit activeWorkspaceChanged(); } } + else if (type == "workspace-removed") { + int id = data.toMap().value("id").toInt(); + m_workspaces.remove(QString::number(id)); + emit workspacesChanged(); + } + else if (type == "space") { + m_space = data; + emit spaceChanged(); + } } \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp index b3c280b8..0d4460a7 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp @@ -1,51 +1,60 @@ -#ifndef WORKSPACES_H -#define WORKSPACES_H +#pragma once #include -#include -#include +#include #include +#include #include "ipc.hpp" -class Workspaces : public QObject, public QQmlParserStatus +class Workspaces : public QObject { Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - - QML_NAMED_ELEMENT(Workspaces) + QML_NAMED_ELEMENT(Fhtc) QML_SINGLETON + Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged) Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged) - Q_PROPERTY(QVariantMap space READ space NOTIFY spaceChanged) - Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceChanged) + Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged) + + Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged) + Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged) + + Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged) Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged) public: explicit Workspaces(QObject *parent = nullptr); - void classBegin() override; - void componentComplete() override; + QVariantMap windows() const { return m_windows; } + QVariantMap workspaces() const { return m_workspaces; } + QVariant space() const { return m_space; } + int focusedWindowId() const { return m_focusedWindowId; } + QVariant focusedWindow() const { return m_focusedWindow; } + int activeWorkspaceId() const { return m_activeWorkspaceId; } + QVariant activeWorkspace() const { return m_activeWorkspace; } - QVariantMap workspaces() const; - QVariantMap space() const; - int activeWorkspaceId() const; - QVariant activeWorkspace() const; + Q_INVOKABLE void dispatch(const QString &command, const QVariant &args); signals: + void windowsChanged(); void workspacesChanged(); void spaceChanged(); + void focusedWindowIdChanged(); + void focusedWindowChanged(); + void activeWorkspaceIdChanged(); void activeWorkspaceChanged(); private slots: - void handleNewEvent(const QVariant &event); - void handleRequestResponse(const QVariant &response); - void requestInitialState(); + void handleEvent(const QVariant &eventVar); private: Ipc *m_ipc; - QVariantMap m_workspaces; - QVariantMap m_space; - int m_activeWorkspaceId; -}; -#endif // WORKSPACES_H \ No newline at end of file + QVariantMap m_windows; + QVariantMap m_workspaces; + QVariant m_space; + int m_focusedWindowId = -1; + QVariant m_focusedWindow; + int m_activeWorkspaceId = -1; + QVariant m_activeWorkspace; +}; \ No newline at end of file diff --git a/src/share/sleex/services/Fhtc.qml b/src/share/sleex/services/Fhtc.qml deleted file mode 100644 index 191f13d7..00000000 --- a/src/share/sleex/services/Fhtc.qml +++ /dev/null @@ -1,151 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import Quickshell -import Quickshell.Io - -Singleton { - id: root - - // The socket we connect to. This is the bridge between us and fht-compositor - readonly property string socketPath: Quickshell.env("FHTC_SOCKET_PATH") - - // How things work here is that we store workspace and window data into separate maps, - // and actual data structures (like Monitors, or even Workspaces) store IDs. The stored - // maps are ID->Window/Workspace maps - // - // FIXME: Add typing to these. - property var workspaces: ({}) - property var windows: ({}) - property var space: ({}) - - // Focused window is the one that is currently receiving keyboard input. There can be multiple - // active windows at once, but only one at max focused window can exist at a time. - property int focusedWindowId: -1 - property var focusedWindow: null - // The active workspace is the workspace that currently has the pointer on it. This usually means - // the displayed workspace on the focused monitor. - property int activeWorkspaceId: -1 - property var activeWorkspace: null - - property string activeWindowTitle: focusedWindow != null ? focusedWindow.title : "" - property string activeWindowAppId: focusedWindow != null ? focusedWindow["app-id"] : "" - - Socket { - id: subscribeSocket - path: root.socketPath - connected: true - - onConnectionStateChanged: { - if (connected) - // When we connect, start turn this socket immediatly into a subcribe - // socket. Allowing us to actually use it. - subscribeSocket.write('"subscribe"\n'); - } - - parser: SplitParser { - onRead: line => { - try { - root.handleEvent(JSON.parse(line)); - } catch (err) { - console.warn("FhtCompositor: failed to parse event: ", line, err); - } - } - } - } - - // The main event handler. The passed in `event` parameter is a fht-compositor-ipc::Event - // https://github.com/nferhat/fht-compositor/blob/ff3d9f3b6549b38e99755d022f5343fda3d6a971/fht-compositor-ipc/src/lib.rs#L131 - function handleEvent(event) { - switch (event.event) { - case "windows": - // Update the window list. The passed in data is a HashMap - root.windows = event.data; - root.windowsChanged(); - break; - case "focused-window-changed": - var newId = event.data.id; - if (newId == null) { - root.focusedWindowId = -1; - root.focusedWindow = null; - } else { - // FIXME: Maybe this event could be sent before a WindowChanged event, in this - // case this could lead to invalid state. - root.focusedWindowId = newId; - root.focusedWindow = root.windows[newId]; - } - - root.focusedWindowChanged(); - root.focusedWindowIdChanged(); - - break; - case "window-closed": - // NOTE: the compositor will sent us a focused-window-changed event, so we don't - // have to update the focusedWindow here. - var id = event.data.id; - delete root.windows[id]; - - root.windowsChanged(); - - break; - case "window-changed": - // This event could either be an existing window changing, or a new window opening - const win = event.data; - root.windows[win.id] = win; - - root.windowsChanged(); - - break; - case "workspaces": - // Update the workspace list. The passed in data is a HashMap - root.workspaces = event.data; - root.workspacesChanged(); - break; - case "active-workspace-changed": - var newId = event.data.id; - if (newId == null) { - root.activeWorkspaceId = -1; - root.activeWorkspace = null; - } else { - // FIXME: Maybe this event could be sent before a WorkspaceChanged event, in this - // case this could lead to invalid state. - root.activeWorkspaceId = newId; - root.activeWorkspace = root.workspaces[newId]; - } - - root.activeWorkspaceChanged(); - root.activeWorkspaceIdChanged(); - - break; - case "workspace-changed": - const ws = event.data; - root.workspaces[ws.id] = ws; - root.workspacesChanged(); - - break; - case "workspace-removed": - var id = event.data.id; - delete root.workspaces[id]; - - root.workspacesChanged(); - - break; - case "space": - root.space = event.data; - root.spaceChanged(); - break; - default: - // console.warn("Unhandled fht-compositor event: ", event.event); - break; - } - } - - function dispatch(command, args) { - var cmd = { - command: command, - args: args - }; - - subscribeSocket.write(JSON.stringify(cmd) + "\n"); - } -} From 434c971b129a3cf6f5f1f5d1c866f8a7ba07dfcd Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 14:11:29 +0100 Subject: [PATCH 11/27] removed FR comments --- src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp index 325f790b..752eddc3 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp @@ -16,13 +16,11 @@ void Workspaces::dispatch(const QString &command, const QVariant &args) cmd["command"] = command; cmd["args"] = args; - // Envoi via le socket de requête géré par Ipc m_ipc->sendRequest(cmd); } void Workspaces::handleEvent(const QVariant &eventVar) { - // Conversion de l'événement reçu (QVariant/JSON) QVariantMap event = eventVar.toMap(); QString type = event.value("event").toString(); QVariant data = event.value("data"); @@ -33,7 +31,6 @@ void Workspaces::handleEvent(const QVariant &eventVar) } else if (type == "focused-window-changed") { QVariantMap dataMap = data.toMap(); - // Vérification si l'ID est null/undefined QVariant idVar = dataMap.value("id"); if (idVar.isNull() || !idVar.isValid()) { @@ -58,7 +55,6 @@ void Workspaces::handleEvent(const QVariant &eventVar) m_windows.insert(QString::number(id), win); emit windowsChanged(); - // Si c'est la fenêtre active qui a changé, on met à jour focusedWindow if (id == m_focusedWindowId) { m_focusedWindow = win; emit focusedWindowChanged(); @@ -68,7 +64,6 @@ void Workspaces::handleEvent(const QVariant &eventVar) m_workspaces = data.toMap(); emit workspacesChanged(); - // Rafraîchir l'activeWorkspace au cas où ses données auraient changé if (m_activeWorkspaceId != -1) { m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId)); emit activeWorkspaceChanged(); From 8eaf9523432efdd3025aa6a87dad9b3f8ab2dd2b Mon Sep 17 00:00:00 2001 From: Ardox Date: Wed, 4 Feb 2026 14:19:44 +0100 Subject: [PATCH 12/27] Fix app title handling --- src/share/sleex/modules/bar/ActiveWindow.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml index db358966..1cd325db 100644 --- a/src/share/sleex/modules/bar/ActiveWindow.qml +++ b/src/share/sleex/modules/bar/ActiveWindow.qml @@ -27,7 +27,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext elide: Text.ElideRight - text: Fhtc.focusedWindow['app-id'] !== "" ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop") + text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop") } StyledText { @@ -35,7 +35,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnLayer0 elide: Text.ElideRight - text: Fhtc.focusedWindow.title !== "" ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}` + text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}` } } From 8b2419c710356181a01e657b83ef9e23d5776967 Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 09:27:44 +0100 Subject: [PATCH 13/27] Removed old FHTC services --- .../sleex/plugins/src/Sleex/services/fhtc.cpp | 143 ------------------ .../sleex/plugins/src/Sleex/services/fhtc.hpp | 57 ------- 2 files changed, 200 deletions(-) delete mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.cpp delete mode 100644 src/share/sleex/plugins/src/Sleex/services/fhtc.hpp diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp deleted file mode 100644 index d5c3ec98..00000000 --- a/src/share/sleex/plugins/src/Sleex/services/fhtc.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "fhtc.hpp" -#include -#include -#include - -FhtCompositor::FhtCompositor(QObject *parent) - : QObject(parent) -{ - QString socketPath = QProcessEnvironment::systemEnvironment().value("FHTC_SOCKET_PATH"); - if (socketPath.isEmpty()) { - qWarning() << "FhtCompositor: FHTC_SOCKET_PATH not set."; - return; - } - - m_socket = new QTcpSocket(this); - connect(m_socket, &QTcpSocket::readyRead, this, &FhtCompositor::onReadyRead); - - m_socket->connectToHost(socketPath, 0, QIODevice::ReadWrite); -} - -void FhtCompositor::onReadyRead() -{ - while (m_socket->canReadLine()) { - QByteArray line = m_socket->readLine().trimmed(); - QJsonParseError err; - QJsonDocument doc = QJsonDocument::fromJson(line, &err); - if (err.error != QJsonParseError::NoError) { - qWarning() << "FhtCompositor: failed to parse event:" << line << err.errorString(); - continue; - } - - if (doc.isObject()) - handleEvent(doc.object()); - } -} - -void FhtCompositor::handleEvent(const QJsonObject &event) -{ - QString type = event.value("event").toString(); - QJsonValue data = event.value("data"); - - enum EventType { - Windows, - FocusedWindowChanged, - WindowClosed, - WindowChanged, - Workspaces, - ActiveWorkspaceChanged, - WorkspaceChanged, - WorkspaceRemoved, - Space, - Unknown - }; - - static const QHash eventMap = { - {"windows", Windows}, - {"focused-window-changed", FocusedWindowChanged}, - {"window-closed", WindowClosed}, - {"window-changed", WindowChanged}, - {"workspaces", Workspaces}, - {"active-workspace-changed", ActiveWorkspaceChanged}, - {"workspace-changed", WorkspaceChanged}, - {"workspace-removed", WorkspaceRemoved}, - {"space", Space} - }; - - switch (eventMap.value(type, Unknown)) { - case Windows: - m_windows = data.toObject().toVariantMap(); - emit windowsChanged(); - break; - - case FocusedWindowChanged: { - int newId = data.toObject().value("id").toInt(-1); - if (newId == -1) { - m_focusedWindowId = -1; - m_focusedWindow = QVariant(); - } else { - m_focusedWindowId = newId; - m_focusedWindow = m_windows.value(QString::number(newId)); - } - emit focusedWindowIdChanged(); - emit focusedWindowChanged(); - break; - } - - case WindowClosed: { - QString id = QString::number(data.toObject().value("id").toInt()); - m_windows.remove(id); - emit windowsChanged(); - break; - } - - case WindowChanged: { - QJsonObject win = data.toObject(); - m_windows[QString::number(win.value("id").toInt())] = win.toVariantMap(); - emit windowsChanged(); - break; - } - - case Workspaces: - m_workspaces = data.toObject().toVariantMap(); - emit workspacesChanged(); - break; - - case ActiveWorkspaceChanged: { - int newId = data.toObject().value("id").toInt(-1); - if (newId == -1) { - m_activeWorkspaceId = -1; - m_activeWorkspace = QVariant(); - } else { - m_activeWorkspaceId = newId; - m_activeWorkspace = m_workspaces.value(QString::number(newId)); - } - emit activeWorkspaceIdChanged(); - emit activeWorkspaceChanged(); - break; - } - - case WorkspaceChanged: { - QJsonObject ws = data.toObject(); - m_workspaces[QString::number(ws.value("id").toInt())] = ws.toVariantMap(); - emit workspacesChanged(); - break; - } - - case WorkspaceRemoved: { - QString id = QString::number(data.toObject().value("id").toInt()); - m_workspaces.remove(id); - emit workspacesChanged(); - break; - } - - case Space: - m_space = data.toVariant(); - emit spaceChanged(); - break; - - case Unknown: - default: - break; - } -} diff --git a/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp b/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp deleted file mode 100644 index b64b9cf1..00000000 --- a/src/share/sleex/plugins/src/Sleex/services/fhtc.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -class FhtCompositor : public QObject { - Q_OBJECT - QML_SINGLETON - QML_ELEMENT - - Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged) - Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged) - Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged) - Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged) - Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged) - Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged) - Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged) - -public: - explicit FhtCompositor(QObject *parent = nullptr); - - QVariantMap windows() const { return m_windows; } - QVariantMap workspaces() const { return m_workspaces; } - QVariant space() const { return m_space; } - int focusedWindowId() const { return m_focusedWindowId; } - QVariant focusedWindow() const { return m_focusedWindow; } - int activeWorkspaceId() const { return m_activeWorkspaceId; } - QVariant activeWorkspace() const { return m_activeWorkspace; } - -signals: - void windowsChanged(); - void workspacesChanged(); - void spaceChanged(); - void focusedWindowIdChanged(); - void focusedWindowChanged(); - void activeWorkspaceIdChanged(); - void activeWorkspaceChanged(); - -private slots: - void onReadyRead(); - void handleEvent(const QJsonObject &event); - -private: - QTcpSocket *m_socket = nullptr; - QVariantMap m_windows; - QVariantMap m_workspaces; - QVariant m_space; - int m_focusedWindowId = -1; - QVariant m_focusedWindow; - int m_activeWorkspaceId = -1; - QVariant m_activeWorkspace; -}; From 1c561d394a76b0fac70ce2250d27cf50256eb33f Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 10:54:39 +0100 Subject: [PATCH 14/27] Added monitors FHTC binding --- .../plugins/src/Sleex/fhtc/CMakeLists.txt | 1 + .../sleex/plugins/src/Sleex/fhtc/monitors.cpp | 46 +++++++++++++++++++ .../sleex/plugins/src/Sleex/fhtc/monitors.hpp | 40 ++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp create mode 100644 src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt index 64aad482..50cb6f15 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt +++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt @@ -4,6 +4,7 @@ qml_module(sleex-fhtc SOURCES ipc.hpp ipc.cpp workspaces.hpp workspaces.cpp + monitors.hpp monitors.cpp plugin.cpp ) diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp new file mode 100644 index 00000000..05db8968 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp @@ -0,0 +1,46 @@ +#include "monitors.hpp" +#include + +Monitors::Monitors(QObject *parent) : QObject(parent) +{ + m_ipc = new Ipc(this); + + connect(m_ipc, &Ipc::newEvent, this, &Monitors::handleEvent); + + m_ipc->subscribe(); +} + +void Monitors::handleEvent(const QVariant &eventVar) +{ + QVariantMap event = eventVar.toMap(); + QString type = event.value("event").toString(); + QVariant data = event.value("data"); + + if (type == "space") { + QVariant data = event.value("data"); + QVariantMap monitorsMap = data.toMap().value("monitors").toMap(); + + m_monitors = monitorsMap; + + bool activeFound = false; + QMapIterator i(m_monitors); + while (i.hasNext()) { + i.next(); + QVariantMap monitor = i.value().toMap(); + if (monitor.value("active").toBool()) { + m_activeMonitor = monitor; + m_activeMonitorName = monitor.value("output").toString(); + activeFound = true; + break; + } + } + + if (!activeFound) { + m_activeMonitor = QVariant(); + } + + emit monitorsChanged(); + emit activeMonitorChanged(); + emit activeMonitorNameChanged(); + } +} \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp new file mode 100644 index 00000000..fd0a3fc6 --- /dev/null +++ b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include "ipc.hpp" + +class Monitors : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(FhtcMonitors) + QML_SINGLETON + + Q_PROPERTY(QVariantMap monitors READ monitors NOTIFY monitorsChanged) + Q_PROPERTY(QVariant activeMonitor READ activeMonitor NOTIFY activeMonitorChanged) + Q_PROPERTY(QString activeMonitorName READ activeMonitorName NOTIFY activeMonitorNameChanged) + +public: + explicit Monitors(QObject *parent = nullptr); + + QVariantMap monitors() const { return m_monitors; } + QVariant activeMonitor() const { return m_activeMonitor; } + QString activeMonitorName() const { return m_activeMonitorName; } + +signals: + void monitorsChanged(); + void activeMonitorChanged(); + void activeMonitorNameChanged(); + +private slots: + void handleEvent(const QVariant &eventVar); + +private: + Ipc *m_ipc; + + QVariantMap m_monitors; + QVariant m_activeMonitor; + QString m_activeMonitorName; +}; \ No newline at end of file From 5679c726ae59d11963e56293a6ee0d659d91adad Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 10:54:57 +0100 Subject: [PATCH 15/27] Renamed workspaces QML name --- src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp index 0d4460a7..19ab562f 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp @@ -9,7 +9,7 @@ class Workspaces : public QObject { Q_OBJECT - QML_NAMED_ELEMENT(Fhtc) + QML_NAMED_ELEMENT(FhtcWorkspaces) QML_SINGLETON Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged) From d57a147fe34dc84a94abc64ca1c6c810e72de243 Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 10:58:21 +0100 Subject: [PATCH 16/27] Using new fhtc module name --- src/share/sleex/modules/bar/ActiveWindow.qml | 4 ++-- src/share/sleex/modules/bar/Workspaces.qml | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml index 1cd325db..036123e1 100644 --- a/src/share/sleex/modules/bar/ActiveWindow.qml +++ b/src/share/sleex/modules/bar/ActiveWindow.qml @@ -27,7 +27,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colSubtext elide: Text.ElideRight - text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow['app-id'] : qsTr("Desktop") + text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow['app-id'] : qsTr("Desktop") } StyledText { @@ -35,7 +35,7 @@ Item { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnLayer0 elide: Text.ElideRight - text: Fhtc.focusedWindow !== undefined ? Fhtc.focusedWindow.title : `${qsTr("Workspace")} ${Fhtc.activeWorkspaceId + 1}` + text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow.title : `${qsTr("Workspace")} ${FhtcWorkspaces.activeWorkspaceId + 1}` } } diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index ee1632cd..12525684 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -16,22 +16,22 @@ import Sleex.Fhtc Item { required property var bar property bool borderless: Config.options.bar.borderless - readonly property var activeWindow: Fhtc.focusedWindow + readonly property var activeWindow: FhtcWorkspaces.focusedWindow readonly property string screenName: bar.screen?.name ?? "" // Get workspaces for this screen only, sorted by ID readonly property var screenWorkspaces: { - return Object.values(Fhtc.workspaces) + return Object.values(FhtcWorkspaces.workspaces) .filter(ws => ws.output === screenName) .sort((a, b) => a.id - b.id); } // Active workspace index within this screen (0-based) readonly property int activeWorkspaceIndex: { - if (!Fhtc.activeWorkspace) return -1; - if (Fhtc.activeWorkspace.output !== screenName) return -1; + if (!FhtcWorkspaces.activeWorkspace) return -1; + if (FhtcWorkspaces.activeWorkspace.output !== screenName) return -1; // Find the index of the active workspace in our sorted screen workspaces - const idx = screenWorkspaces.findIndex(ws => ws.id === Fhtc.activeWorkspace.id); + const idx = screenWorkspaces.findIndex(ws => ws.id === FhtcWorkspaces.activeWorkspace.id); // Return -1 if the workspace is beyond the shown limit if (idx >= Config.options.bar.workspaces.shown) return -1; return idx; @@ -62,7 +62,7 @@ Item { // Listen for changes in Fhtc.workspaces and windows Connections { - target: Fhtc + target: FhtcWorkspaces function onWorkspacesChanged() { updateWorkspaceOccupied(); } @@ -207,7 +207,7 @@ Item { property var biggestWindow: { if (!button.workspace || !button.workspace.windows || button.workspace.windows.length === 0) return null; const windowIds = button.workspace.windows; - const windowsInThisWorkspace = windowIds.map(id => Fhtc.windows[id]).filter(w => w != null); + const windowsInThisWorkspace = windowIds.map(id => FhtcWorkspaces.windows[id]).filter(w => w != null); return windowsInThisWorkspace.reduce((maxWin, win) => { const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0) const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0) From dad80b85b32f488fb0542b5ba0449d45d5ac942a Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 11:43:02 +0100 Subject: [PATCH 17/27] Cleaned workspaces module --- .../sleex/plugins/src/Sleex/fhtc/workspaces.cpp | 13 ------------- .../sleex/plugins/src/Sleex/fhtc/workspaces.hpp | 6 ------ 2 files changed, 19 deletions(-) diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp index 752eddc3..145f2d5a 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp @@ -10,15 +10,6 @@ Workspaces::Workspaces(QObject *parent) : QObject(parent) m_ipc->subscribe(); } -void Workspaces::dispatch(const QString &command, const QVariant &args) -{ - QVariantMap cmd; - cmd["command"] = command; - cmd["args"] = args; - - m_ipc->sendRequest(cmd); -} - void Workspaces::handleEvent(const QVariant &eventVar) { QVariantMap event = eventVar.toMap(); @@ -100,8 +91,4 @@ void Workspaces::handleEvent(const QVariant &eventVar) m_workspaces.remove(QString::number(id)); emit workspacesChanged(); } - else if (type == "space") { - m_space = data; - emit spaceChanged(); - } } \ No newline at end of file diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp index 19ab562f..72fa77bc 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp @@ -14,7 +14,6 @@ class Workspaces : public QObject Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged) Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged) - Q_PROPERTY(QVariant space READ space NOTIFY spaceChanged) Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged) Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged) @@ -27,18 +26,14 @@ class Workspaces : public QObject QVariantMap windows() const { return m_windows; } QVariantMap workspaces() const { return m_workspaces; } - QVariant space() const { return m_space; } int focusedWindowId() const { return m_focusedWindowId; } QVariant focusedWindow() const { return m_focusedWindow; } int activeWorkspaceId() const { return m_activeWorkspaceId; } QVariant activeWorkspace() const { return m_activeWorkspace; } - Q_INVOKABLE void dispatch(const QString &command, const QVariant &args); - signals: void windowsChanged(); void workspacesChanged(); - void spaceChanged(); void focusedWindowIdChanged(); void focusedWindowChanged(); void activeWorkspaceIdChanged(); @@ -52,7 +47,6 @@ private slots: QVariantMap m_windows; QVariantMap m_workspaces; - QVariant m_space; int m_focusedWindowId = -1; QVariant m_focusedWindow; int m_activeWorkspaceId = -1; From 87ffb7da4d76982be098153df9af9b2b53a7755d Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 11:48:04 +0100 Subject: [PATCH 18/27] [WIP OVERVIEW] fixed monitor selection logic --- src/share/sleex/modules/overview/Overview.qml | 9 +++++---- .../sleex/modules/overview/OverviewWidget.qml | 11 ++++++----- src/share/sleex/test.qml | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 src/share/sleex/test.qml diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml index 54a5ddca..79ae9144 100644 --- a/src/share/sleex/modules/overview/Overview.qml +++ b/src/share/sleex/modules/overview/Overview.qml @@ -9,6 +9,7 @@ import Quickshell import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland +import Sleex.Fhtc Scope { id: overviewScope @@ -20,14 +21,14 @@ Scope { id: root required property var modelData property string searchingText: "" - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) - property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id) + required property ShellScreen screen + property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name) screen: modelData visible: GlobalStates.overviewOpen && monitorIsFocused WlrLayershell.namespace: "quickshell:overview" WlrLayershell.layer: WlrLayer.Overlay - // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None color: "transparent" mask: Region { @@ -98,7 +99,7 @@ Scope { } Keys.onPressed: (event) => { - if (eçvent.key === Qt.Key_Escape) { + if (event.key === Qt.Key_Escape) { GlobalStates.overviewOpen = false; } else if (event.key === Qt.Key_Left) { if (!root.searchingText) Hyprland.dispatch("workspace -1"); diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml index 0b8d73ca..debb4957 100644 --- a/src/share/sleex/modules/overview/OverviewWidget.qml +++ b/src/share/sleex/modules/overview/OverviewWidget.qml @@ -11,15 +11,16 @@ import Quickshell.Io import Quickshell.Widgets import Quickshell.Wayland import Quickshell.Hyprland +import Sleex.Fhtc Item { id: root required property var panelWindow - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) + readonly property var monitor: FhtcMonitors.activeMonitor readonly property var toplevels: ToplevelManager.toplevels readonly property int workspacesShown: Config.options.overview.numOfRows * Config.options.overview.numOfCols - readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown) - property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id) + readonly property int workspaceGroup: Math.floor((monitor["active-workspace-idx"] - 1) / workspacesShown) + property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name) property var windows: HyprlandData.windowList property var windowByAddress: HyprlandData.windowByAddress property var windowAddresses: HyprlandData.addresses @@ -113,7 +114,7 @@ Item { if (root.draggingTargetWorkspace === -1) { // Hyprland.dispatch(`exec qs ipc call overview close`) GlobalStates.overviewOpen = false - Hyprland.dispatch(`workspace ${workspaceValue}`) + Fhtc.dispatch(`workspace ${workspaceValue}`) } } } @@ -213,7 +214,7 @@ Item { window.Drag.active = false root.draggingFromWorkspace = -1 if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { - Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`) + Fhtc.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`) updateWindowPosition.restart() } else { diff --git a/src/share/sleex/test.qml b/src/share/sleex/test.qml new file mode 100644 index 00000000..ac3e9e20 --- /dev/null +++ b/src/share/sleex/test.qml @@ -0,0 +1,15 @@ +import Sleex.Fhtc +import QtQuick + +Item { + Connections { + target: FhtcMonitors + + function onMonitorsChanged() { + // console.log("UPDATE REÇU :", JSON.stringify(FhtcMonitors.activeMonitor)); + console.log(FhtcMonitors.activeMonitorName); + console.log(JSON.stringify(FhtcMonitors.activeMonitor)); + console.log(JSON.stringify(FhtcMonitors.monitors)); + } + } +} \ No newline at end of file From d64c5e36e7d350c86921be96b872eacd6a62567a Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 11:48:22 +0100 Subject: [PATCH 19/27] Removed test file --- src/share/sleex/test.qml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/share/sleex/test.qml diff --git a/src/share/sleex/test.qml b/src/share/sleex/test.qml deleted file mode 100644 index ac3e9e20..00000000 --- a/src/share/sleex/test.qml +++ /dev/null @@ -1,15 +0,0 @@ -import Sleex.Fhtc -import QtQuick - -Item { - Connections { - target: FhtcMonitors - - function onMonitorsChanged() { - // console.log("UPDATE REÇU :", JSON.stringify(FhtcMonitors.activeMonitor)); - console.log(FhtcMonitors.activeMonitorName); - console.log(JSON.stringify(FhtcMonitors.activeMonitor)); - console.log(JSON.stringify(FhtcMonitors.monitors)); - } - } -} \ No newline at end of file From 4b263b46339a222fbbb721c3b6b78ee2ef2fff4d Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 14:36:42 +0100 Subject: [PATCH 20/27] Made the dispatch work --- src/share/sleex/modules/bar/Workspaces.qml | 11 +++++----- .../sleex/plugins/src/Sleex/fhtc/ipc.cpp | 21 +++++++++++++++---- .../sleex/plugins/src/Sleex/fhtc/ipc.hpp | 3 ++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index 12525684..b32115f2 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -81,10 +81,11 @@ Item { // Scroll to switch workspaces WheelHandler { onWheel: (event) => { - if (event.angleDelta.y < 0) - Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-next-workspace"]); - else if (event.angleDelta.y > 0) - Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-previous-workspace"]); + if (event.angleDelta.y < 0) { + FhtcIpc.dispatch("focus-next-workspace", { "output": null }); + } else if (event.angleDelta.y > 0) { + FhtcIpc.dispatch("focus-previous-workspace", { "output": null }); + } } acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad } @@ -193,7 +194,7 @@ Item { Layout.fillHeight: true onPressed: { if (button.workspaceId >= 0) { - Quickshell.execDetached(["fht-compositor", "ipc", "action", "focus-workspace", `${button.workspaceId}`]); + FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": button.workspaceId }); } } width: workspaceButtonWidth diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp index 392e6c21..a2b4454f 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp @@ -34,7 +34,6 @@ Ipc::~Ipc() // Sockets are automatically destroyed because 'this' is their parent } -// --- Public slots --- void Ipc::subscribe() { @@ -45,10 +44,24 @@ void Ipc::subscribe() } } +void Ipc::dispatch(const QString &name, const QVariantMap &args) +{ + if (!args.isEmpty() || name.contains("workspace")) { + QVariantMap actionBody; + actionBody.insert(name, args); + sendAction(actionBody); + } + else { + sendAction(name); + } +} + void Ipc::sendRequest(const QVariant &request) { if (m_socketPath.isEmpty()) return; + // qDebug() << "Ipc: Queuing request:" << request; + m_pendingRequests.enqueue(request); if (m_requestSocket->state() == QLocalSocket::UnconnectedState) { qDebug() << "Ipc: Connecting to the request socket..."; @@ -67,7 +80,6 @@ void Ipc::sendAction(const QVariant &action) sendRequest(request); } -// --- Private slots: Request socket --- void Ipc::onRequestConnected() { @@ -86,6 +98,8 @@ void Ipc::onRequestReadyRead() // We do not loop, we just wait for a complete line. QVariant response = parseLine(m_requestBuffer); if (!response.isNull()) { + // qDebug() << "Ipc: Received response:" << response; + emit requestResponse(response); // If there are other requests, send them @@ -108,12 +122,11 @@ void Ipc::onRequestDisconnected() { qDebug() << "Ipc: Request socket disconnected."; if (!m_requestBuffer.isEmpty()) { - qWarning() << "Ipc (Request): DDisconnected with data in buffer:" << m_requestBuffer; + qWarning() << "Ipc (Request): Disconnected with data in buffer:" << m_requestBuffer; m_requestBuffer.clear(); } } -// --- Private slots: Event socket --- void Ipc::onEventConnected() { diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp index c1cdad4d..ca5abb6f 100644 --- a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp +++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp @@ -10,7 +10,7 @@ class Ipc : public QObject { Q_OBJECT - QML_NAMED_ELEMENT(Ipc) + QML_NAMED_ELEMENT(FhtcIpc) QML_SINGLETON public: @@ -27,6 +27,7 @@ public slots: void subscribe(); void sendRequest(const QVariant &request); void sendAction(const QVariant &action); + void dispatch(const QString &name, const QVariantMap &args = {}); private slots: void onRequestConnected(); From 5afc5c9ba925db2ca0f3f7509eb7987748d315c9 Mon Sep 17 00:00:00 2001 From: Ardox Date: Thu, 5 Feb 2026 14:39:26 +0100 Subject: [PATCH 21/27] ws: remove unnecessary output parameter --- src/share/sleex/modules/bar/Workspaces.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index b32115f2..621902a1 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -82,9 +82,9 @@ Item { WheelHandler { onWheel: (event) => { if (event.angleDelta.y < 0) { - FhtcIpc.dispatch("focus-next-workspace", { "output": null }); + FhtcIpc.dispatch("focus-next-workspace", {}); } else if (event.angleDelta.y > 0) { - FhtcIpc.dispatch("focus-previous-workspace", { "output": null }); + FhtcIpc.dispatch("focus-previous-workspace", {}); } } acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad From 65e69a2bf903e7e537c57cace54c8d2d90d6285d Mon Sep 17 00:00:00 2001 From: Ardox Date: Sun, 15 Mar 2026 10:13:07 +0100 Subject: [PATCH 22/27] Uncomment shortcuts --- src/share/sleex/GlobalStates.qml | 40 ++-- .../sleex/modules/cheatsheet/Cheatsheet.qml | 43 +++-- .../sleex/modules/dashboard/Dashboard.qml | 42 ++--- .../OnScreenDisplayBrightness.qml | 31 ++-- .../onScreenDisplay/OnScreenDisplayVolume.qml | 31 ++-- src/share/sleex/modules/overview/Overview.qml | 173 +++++++++--------- src/share/sleex/modules/session/Session.qml | 29 ++- .../sleex/modules/sidebarLeft/SidebarLeft.qml | 69 ++++--- .../wallpaperSelector/WallpaperSelector.qml | 47 +++-- src/share/sleex/services/Brightness.qml | 22 +-- 10 files changed, 260 insertions(+), 267 deletions(-) diff --git a/src/share/sleex/GlobalStates.qml b/src/share/sleex/GlobalStates.qml index b09b7500..68c1a47a 100644 --- a/src/share/sleex/GlobalStates.qml +++ b/src/share/sleex/GlobalStates.qml @@ -41,18 +41,18 @@ Singleton { } } - // GlobalShortcut { - // name: "workspaceNumber" - // description: qsTr("Hold to show workspace numbers, release to show icons") - - // onPressed: { - // workspaceShowNumbersTimer.start() - // } - // onReleased: { - // workspaceShowNumbersTimer.stop() - // workspaceShowNumbers = false - // } - // } + GlobalShortcut { + name: "workspaceNumber" + description: qsTr("Hold to show workspace numbers, release to show icons") + + onPressed: { + workspaceShowNumbersTimer.start() + } + onReleased: { + workspaceShowNumbersTimer.stop() + workspaceShowNumbers = false + } + } IpcHandler { target: "zoom" @@ -74,14 +74,14 @@ Singleton { root.screenLocked = true; } } - // GlobalShortcut { - // name: "lockScreen" - // description: qsTr("Lock screen (obviously)") - - // onPressed: { - // root.screenLocked = true; - // } - // } + GlobalShortcut { + name: "lockScreen" + description: qsTr("Lock screen (obviously)") + + onPressed: { + root.screenLocked = true; + } + } IpcHandler { target: "background" diff --git a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml index 3f7fcfc4..4ae33395 100644 --- a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml +++ b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml @@ -135,31 +135,30 @@ Scope { // Scope } } - // GlobalShortcut { - // name: "cheatsheetToggle" - // description: qsTr("Toggles cheatsheet on press") + GlobalShortcut { + name: "cheatsheetToggle" + description: qsTr("Toggles cheatsheet on press") - // onPressed: { - // cheatsheetLoader.active = !cheatsheetLoader.active; - // } - // } - - // GlobalShortcut { - // name: "cheatsheetOpen" - // description: qsTr("Opens cheatsheet on press") + onPressed: { + cheatsheetLoader.active = !cheatsheetLoader.active; + } + } - // onPressed: { - // cheatsheetLoader.active = true; - // } - // } + GlobalShortcut { + name: "cheatsheetOpen" + description: qsTr("Opens cheatsheet on press") - // GlobalShortcut { - // name: "cheatsheetClose" - // description: qsTr("Closes cheatsheet on press") + onPressed: { + cheatsheetLoader.active = true; + } + } - // onPressed: { - // cheatsheetLoader.active = false; - // } - // } + GlobalShortcut { + name: "cheatsheetClose" + description: qsTr("Closes cheatsheet on press") + onPressed: { + cheatsheetLoader.active = false; + } + } } diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml index 519fa347..8df2cc8a 100644 --- a/src/share/sleex/modules/dashboard/Dashboard.qml +++ b/src/share/sleex/modules/dashboard/Dashboard.qml @@ -208,25 +208,25 @@ Scope { } } - // GlobalShortcut { - // name: "dashboardToggle" - // description: qsTr("Toggles dashboard on press") - // onPressed: { - // GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen; - // if(GlobalStates.dashboardOpen) Notifications.timeoutAll(); - // } - // } - // GlobalShortcut { - // name: "dashboardOpen" - // description: qsTr("Opens dashboard on press") - // onPressed: { - // GlobalStates.dashboardOpen = true; - // Notifications.timeoutAll(); - // } - // } - // GlobalShortcut { - // name: "dashboardClose" - // description: qsTr("Closes dashboard on press") - // onPressed: { GlobalStates.dashboardOpen = false; } - // } + GlobalShortcut { + name: "dashboardToggle" + description: qsTr("Toggles dashboard on press") + onPressed: { + GlobalStates.dashboardOpen = !GlobalStates.dashboardOpen; + if(GlobalStates.dashboardOpen) Notifications.timeoutAll(); + } + } + GlobalShortcut { + name: "dashboardOpen" + description: qsTr("Opens dashboard on press") + onPressed: { + GlobalStates.dashboardOpen = true; + Notifications.timeoutAll(); + } + } + GlobalShortcut { + name: "dashboardClose" + description: qsTr("Closes dashboard on press") + onPressed: { GlobalStates.dashboardOpen = false; } + } } diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index f99b8f24..94d7018b 100644 --- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -132,21 +132,20 @@ Scope { } } - // GlobalShortcut { - // name: "osdBrightnessTrigger" - // description: qsTr("Triggers brightness OSD on press") - - // onPressed: { - // root.triggerOsd() - // } - // } - // GlobalShortcut { - // name: "osdBrightnessHide" - // description: qsTr("Hides brightness OSD on press") - - // onPressed: { - // root.showOsdValues = false - // } - // } + GlobalShortcut { + name: "osdBrightnessTrigger" + description: qsTr("Triggers brightness OSD on press") + onPressed: { + root.triggerOsd() + } + } + GlobalShortcut { + name: "osdBrightnessHide" + description: qsTr("Hides brightness OSD on press") + + onPressed: { + root.showOsdValues = false + } + } } \ No newline at end of file diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml index ac40827f..8c47e0c5 100644 --- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -184,21 +184,20 @@ Scope { showOsdValues = !showOsdValues } } - // GlobalShortcut { - // name: "osdVolumeTrigger" - // description: qsTr("Triggers volume OSD on press") - - // onPressed: { - // root.triggerOsd() - // } - // } - // GlobalShortcut { - // name: "osdVolumeHide" - // description: qsTr("Hides volume OSD on press") - - // onPressed: { - // root.showOsdValues = false - // } - // } + GlobalShortcut { + name: "osdVolumeTrigger" + description: qsTr("Triggers volume OSD on press") + onPressed: { + root.triggerOsd() + } + } + GlobalShortcut { + name: "osdVolumeHide" + description: qsTr("Hides volume OSD on press") + + onPressed: { + root.showOsdValues = false + } + } } \ No newline at end of file diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml index 79ae9144..c86a3e09 100644 --- a/src/share/sleex/modules/overview/Overview.qml +++ b/src/share/sleex/modules/overview/Overview.qml @@ -151,92 +151,91 @@ Scope { } } - // GlobalShortcut { - // name: "overviewToggle" - // description: qsTr("Toggles overview on press") - - // onPressed: { - // GlobalStates.overviewOpen = !GlobalStates.overviewOpen - // } - // } - // GlobalShortcut { - // name: "overviewClose" - // description: qsTr("Closes overview") - - // onPressed: { - // GlobalStates.overviewOpen = false - // } - // } - // GlobalShortcut { - // name: "overviewToggleRelease" - // description: qsTr("Toggles overview on release") - - // onPressed: { - // GlobalStates.superReleaseMightTrigger = true - // } - - // onReleased: { - // if (!GlobalStates.superReleaseMightTrigger) { - // GlobalStates.superReleaseMightTrigger = true - // return - // } - // GlobalStates.overviewOpen = !GlobalStates.overviewOpen - // } - // } - // GlobalShortcut { - // name: "overviewToggleReleaseInterrupt" - // description: qsTr("Interrupts possibility of overview being toggled on release. ") + - // qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + - // qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") - - // onPressed: { - // GlobalStates.superReleaseMightTrigger = false - // } - // } - // GlobalShortcut { - // name: "overviewClipboardToggle" - // description: qsTr("Toggle clipboard query on overview widget") - - // onPressed: { - // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { - // GlobalStates.overviewOpen = false; - // return; - // } - // for (let i = 0; i < overviewVariants.instances.length; i++) { - // let panelWindow = overviewVariants.instances[i]; - // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - // overviewScope.dontAutoCancelSearch = true; - // panelWindow.setSearchingText( - // Config.options.search.prefix.clipboard - // ); - // GlobalStates.overviewOpen = true; - // return - // } - // } - // } - // } - - // GlobalShortcut { - // name: "overviewEmojiToggle" - // description: qsTr("Toggle emoji query on overview widget") - - // onPressed: { - // if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { - // GlobalStates.overviewOpen = false; - // return; - // } - // for (let i = 0; i < overviewVariants.instances.length; i++) { - // let panelWindow = overviewVariants.instances[i]; - // if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - // overviewScope.dontAutoCancelSearch = true; - // panelWindow.setSearchingText( - // Config.options.search.prefix.emojis - // ); - // GlobalStates.overviewOpen = true; - // return - // } - // } - // } - // } + GlobalShortcut { + name: "overviewToggle" + description: qsTr("Toggles overview on press") + onPressed: { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen + } + } + GlobalShortcut { + name: "overviewClose" + description: qsTr("Closes overview") + + onPressed: { + GlobalStates.overviewOpen = false + } + } + GlobalShortcut { + name: "overviewToggleRelease" + description: qsTr("Toggles overview on release") + + onPressed: { + GlobalStates.superReleaseMightTrigger = true + } + + onReleased: { + if (!GlobalStates.superReleaseMightTrigger) { + GlobalStates.superReleaseMightTrigger = true + return + } + GlobalStates.overviewOpen = !GlobalStates.overviewOpen + } + } + GlobalShortcut { + name: "overviewToggleReleaseInterrupt" + description: qsTr("Interrupts possibility of overview being toggled on release. ") + + qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + + qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") + + onPressed: { + GlobalStates.superReleaseMightTrigger = false + } + } + GlobalShortcut { + name: "overviewClipboardToggle" + description: qsTr("Toggle clipboard query on overview widget") + + onPressed: { + if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { + GlobalStates.overviewOpen = false; + return; + } + for (let i = 0; i < overviewVariants.instances.length; i++) { + let panelWindow = overviewVariants.instances[i]; + if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + overviewScope.dontAutoCancelSearch = true; + panelWindow.setSearchingText( + Config.options.search.prefix.clipboard + ); + GlobalStates.overviewOpen = true; + return + } + } + } + } + + GlobalShortcut { + name: "overviewEmojiToggle" + description: qsTr("Toggle emoji query on overview widget") + + onPressed: { + if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) { + GlobalStates.overviewOpen = false; + return; + } + for (let i = 0; i < overviewVariants.instances.length; i++) { + let panelWindow = overviewVariants.instances[i]; + if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + overviewScope.dontAutoCancelSearch = true; + panelWindow.setSearchingText( + Config.options.search.prefix.emojis + ); + GlobalStates.overviewOpen = true; + return + } + } + } + } } diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml index 4dcccd5c..b52d47ec 100644 --- a/src/share/sleex/modules/session/Session.qml +++ b/src/share/sleex/modules/session/Session.qml @@ -207,22 +207,21 @@ Scope { } } - // GlobalShortcut { - // name: "sessionToggle" - // description: qsTr("Toggles session screen on press") + GlobalShortcut { + name: "sessionToggle" + description: qsTr("Toggles session screen on press") - // onPressed: { - // sessionLoader.active = !sessionLoader.active; - // } - // } - - // GlobalShortcut { - // name: "sessionOpen" - // description: qsTr("Opens session screen on press") + onPressed: { + sessionLoader.active = !sessionLoader.active; + } + } - // onPressed: { - // sessionLoader.active = true; - // } - // } + GlobalShortcut { + name: "sessionOpen" + description: qsTr("Opens session screen on press") + onPressed: { + sessionLoader.active = true; + } + } } diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml index abbd7757..268851bb 100644 --- a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml +++ b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml @@ -164,40 +164,39 @@ Scope { // Scope } } - // GlobalShortcut { - // name: "sidebarLeftToggle" - // description: qsTr("Toggles left sidebar on press") - - // onPressed: { - // GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; - // } - // } - - // GlobalShortcut { - // name: "sidebarLeftOpen" - // description: qsTr("Opens left sidebar on press") - - // onPressed: { - // GlobalStates.sidebarLeftOpen = true; - // } - // } - - // GlobalShortcut { - // name: "sidebarLeftClose" - // description: qsTr("Closes left sidebar on press") - - // onPressed: { - // GlobalStates.sidebarLeftOpen = false; - // } - // } - - // GlobalShortcut { - // name: "sidebarLeftToggleDetach" - // description: qsTr("Detach left sidebar into a window/Attach it back") - - // onPressed: { - // root.detach = !root.detach; - // } - // } + GlobalShortcut { + name: "sidebarLeftToggle" + description: qsTr("Toggles left sidebar on press") + onPressed: { + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + } + } + + GlobalShortcut { + name: "sidebarLeftOpen" + description: qsTr("Opens left sidebar on press") + + onPressed: { + GlobalStates.sidebarLeftOpen = true; + } + } + + GlobalShortcut { + name: "sidebarLeftClose" + description: qsTr("Closes left sidebar on press") + + onPressed: { + GlobalStates.sidebarLeftOpen = false; + } + } + + GlobalShortcut { + name: "sidebarLeftToggleDetach" + description: qsTr("Detach left sidebar into a window/Attach it back") + + onPressed: { + root.detach = !root.detach; + } + } } diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml index f1f3a992..22f501e3 100644 --- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml +++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml @@ -169,29 +169,28 @@ Scope { } } - // GlobalShortcut { - // name: "wppselectorToggle" - // description: qsTr("Toggles wallpaper selector on press") - - // onPressed: { - // GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen; - // } - // } - // GlobalShortcut { - // name: "wppselectorOpen" - // description: qsTr("Opens wallpaper selector on press") - - // onPressed: { - // GlobalStates.wppselectorOpen = true; - // } - // } - // GlobalShortcut { - // name: "wppselectorClose" - // description: qsTr("Closes wallpaper selector on press") - - // onPressed: { - // GlobalStates.wppselectorOpen = false; - // } - // } + GlobalShortcut { + name: "wppselectorToggle" + description: qsTr("Toggles wallpaper selector on press") + onPressed: { + GlobalStates.wppselectorOpen = !GlobalStates.wppselectorOpen; + } + } + GlobalShortcut { + name: "wppselectorOpen" + description: qsTr("Opens wallpaper selector on press") + + onPressed: { + GlobalStates.wppselectorOpen = true; + } + } + GlobalShortcut { + name: "wppselectorClose" + description: qsTr("Closes wallpaper selector on press") + + onPressed: { + GlobalStates.wppselectorOpen = false; + } + } } diff --git a/src/share/sleex/services/Brightness.qml b/src/share/sleex/services/Brightness.qml index 9b8c4fa4..552b2f88 100644 --- a/src/share/sleex/services/Brightness.qml +++ b/src/share/sleex/services/Brightness.qml @@ -145,15 +145,15 @@ Singleton { } } - // GlobalShortcut { - // name: "brightnessIncrease" - // description: qsTr("Increase brightness") - // onPressed: root.increaseBrightness() - // } - - // GlobalShortcut { - // name: "brightnessDecrease" - // description: qsTr("Decrease brightness") - // onPressed: root.decreaseBrightness() - // } + GlobalShortcut { + name: "brightnessIncrease" + description: qsTr("Increase brightness") + onPressed: root.increaseBrightness() + } + + GlobalShortcut { + name: "brightnessDecrease" + description: qsTr("Decrease brightness") + onPressed: root.decreaseBrightness() + } } From bb1d90db14961117c2ae0c4f8173dcb6842538a0 Mon Sep 17 00:00:00 2001 From: Ardox Date: Sun, 15 Mar 2026 10:59:06 +0100 Subject: [PATCH 23/27] Added .vscode to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 50b4af4b..e4e69e73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.tar.zst -pkg/ \ No newline at end of file +pkg/ +.vscode/ \ No newline at end of file From 670df702987abf3baedb453f6f59e30e7e93a7a5 Mon Sep 17 00:00:00 2001 From: Ardox Date: Sun, 15 Mar 2026 10:59:22 +0100 Subject: [PATCH 24/27] Fixed workspace icons --- src/share/sleex/modules/bar/Workspaces.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index 97c30715..7c7eab43 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -220,12 +220,12 @@ Item { return winArea > maxArea ? win : maxWin }, null) } - property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing") + property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.["app-id"]), "image-missing") property string materialIconName: { if (!biggestWindow) return "" - const winClass = biggestWindow.class.toLowerCase() + const winClass = biggestWindow?.["app-id"] const map = { "language": ["firefox", "chromium", "google-chrome", "brave", "edge", "vivaldi", "qutebrowser", "librewolf", "zen-browser"], "terminal": ["foot", "kitty", "alacritty", "wezterm", "gnome-terminal", "konsole", "xfce4-terminal", "xterm"], @@ -336,8 +336,8 @@ Item { (workspaceButtonWidth - workspaceIconSize) / 2 - 2 : workspaceIconMarginShrinked anchors.rightMargin: (!GlobalStates.workspaceShowNumbers) ? (workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked - color: (monitor.activeWorkspace?.id == button.workspaceValue) ? - Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer + color: (activeWorkspaceIndex == index) ? + Appearance.m3colors.m3onPrimary : Appearance.colors.colOnSecondaryContainer Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) From dcec76361f9c0ec2a00f8a29ad2d85fc473194ef Mon Sep 17 00:00:00 2001 From: Ardox Date: Sun, 15 Mar 2026 11:03:33 +0100 Subject: [PATCH 25/27] Remove unused properties from Workspaces.qml --- src/share/sleex/modules/bar/Workspaces.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml index 7c7eab43..bc923bb8 100644 --- a/src/share/sleex/modules/bar/Workspaces.qml +++ b/src/share/sleex/modules/bar/Workspaces.qml @@ -16,8 +16,6 @@ import Sleex.Fhtc Item { id: root required property var bar - property bool borderless: Config.options.bar.borderless - readonly property var activeWindow: FhtcWorkspaces.focusedWindow readonly property string screenName: bar.screen?.name ?? "" // Get workspaces for this screen only, sorted by ID @@ -39,14 +37,12 @@ Item { } property list workspaceOccupied: [] - property int widgetPadding: 0 property int horizontalPadding: 5 property int workspaceButtonWidth: 30 property real workspaceIconSize: workspaceButtonWidth * 0.6 property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown property bool useMaterialIcons: Config.options.bar.workspaces.useMaterialIcons From 321661b2bee5ff1534e88bd903fa1d5ea839c57a Mon Sep 17 00:00:00 2001 From: Ardox Date: Sun, 15 Mar 2026 11:15:38 +0100 Subject: [PATCH 26/27] Update focusedScreen property to use FhtcMonitors.activeMonitorName --- src/share/sleex/modules/session/Session.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml index b52d47ec..9d7a50bf 100644 --- a/src/share/sleex/modules/session/Session.qml +++ b/src/share/sleex/modules/session/Session.qml @@ -10,10 +10,11 @@ import Quickshell.Io import Quickshell.Widgets import Quickshell.Wayland import Quickshell.Hyprland +import Sleex.Fhtc Scope { id: root - property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) + property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName) Loader { id: sessionLoader From 8bf1717b98977951ce30f09fd67a1d9c1bc4b3ca Mon Sep 17 00:00:00 2001 From: Ardox Date: Sun, 15 Mar 2026 13:17:30 +0100 Subject: [PATCH 27/27] Overview: made it work for FHTC --- src/share/sleex/modules/overview/Overview.qml | 4 +- .../sleex/modules/overview/OverviewWidget.qml | 86 ++++++++----------- .../sleex/modules/overview/OverviewWindow.qml | 10 +-- 3 files changed, 42 insertions(+), 58 deletions(-) diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml index c86a3e09..ca55ecc1 100644 --- a/src/share/sleex/modules/overview/Overview.qml +++ b/src/share/sleex/modules/overview/Overview.qml @@ -204,7 +204,7 @@ Scope { } for (let i = 0; i < overviewVariants.instances.length; i++) { let panelWindow = overviewVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) { overviewScope.dontAutoCancelSearch = true; panelWindow.setSearchingText( Config.options.search.prefix.clipboard @@ -227,7 +227,7 @@ Scope { } for (let i = 0; i < overviewVariants.instances.length; i++) { let panelWindow = overviewVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) { overviewScope.dontAutoCancelSearch = true; panelWindow.setSearchingText( Config.options.search.prefix.emojis diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml index debb4957..6894298a 100644 --- a/src/share/sleex/modules/overview/OverviewWidget.qml +++ b/src/share/sleex/modules/overview/OverviewWidget.qml @@ -17,26 +17,20 @@ Item { id: root required property var panelWindow readonly property var monitor: FhtcMonitors.activeMonitor - readonly property var toplevels: ToplevelManager.toplevels + // readonly property var toplevels: ToplevelManager.toplevels readonly property int workspacesShown: Config.options.overview.numOfRows * Config.options.overview.numOfCols - readonly property int workspaceGroup: Math.floor((monitor["active-workspace-idx"] - 1) / workspacesShown) + readonly property int workspaceGroup: Math.floor(((monitor["active-workspace-idx"] ?? 0)) / workspacesShown) property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name) - property var windows: HyprlandData.windowList - property var windowByAddress: HyprlandData.windowByAddress - property var windowAddresses: HyprlandData.addresses - property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id) + readonly property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName) property real scale: Config.options.overview.scale property color activeBorderColor: Appearance.colors.colSecondary - property real workspaceImplicitWidth: (monitorData?.transform % 2 === 1) ? - ((monitor.height - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) : - ((monitor.width - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) - property real workspaceImplicitHeight: (monitorData?.transform % 2 === 1) ? - ((monitor.width - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) : - ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) + property real workspaceImplicitWidth: (focusedScreen?.width ?? 0) * root.scale + property real workspaceImplicitHeight: ((focusedScreen?.height ?? 0) - Appearance.sizes.barHeight) * root.scale property real workspaceNumberMargin: 80 - property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale + property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * (panelWindow.screen.devicePixelRatio ?? 1) + property int workspaceZ: 0 property int windowZ: 1 property int windowDraggingZ: 99999 @@ -83,7 +77,7 @@ Item { Rectangle { // Workspace id: workspace property int colIndex: index - property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * Config.options.overview.numOfCols + colIndex + 1 + property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * Config.options.overview.numOfCols + colIndex property color defaultWorkspaceColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) property color hoveredBorderColor: Appearance.colors.colLayer2Hover @@ -112,9 +106,8 @@ Item { acceptedButtons: Qt.LeftButton onClicked: { if (root.draggingTargetWorkspace === -1) { - // Hyprland.dispatch(`exec qs ipc call overview close`) GlobalStates.overviewOpen = false - Fhtc.dispatch(`workspace ${workspaceValue}`) + FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": workspaceValue }) } } } @@ -147,36 +140,29 @@ Item { Repeater { // Window repeater model: ScriptModel { values: { - // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2)) - return ToplevelManager.toplevels.values.filter((toplevel) => { - const address = `0x${toplevel.HyprlandToplevel.address}` - // console.log(`Checking window with address: ${address}`) - var win = windowByAddress[address] - return (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) + return Object.values(FhtcWorkspaces.windows).filter((win) => { + const wsId = win?.["workspace-id"] ?? -1 + return wsId >= root.workspaceGroup * root.workspacesShown && + wsId < (root.workspaceGroup + 1) * root.workspacesShown }) } } delegate: OverviewWindow { id: window required property var modelData - property var address: `0x${modelData.HyprlandToplevel.address}` - windowData: windowByAddress[address] - toplevel: modelData - monitorData: root.monitorData + windowData: modelData + // toplevel: not yet available with fhtc compositor + monitorData: root.focusedScreen scale: root.scale availableWorkspaceWidth: root.workspaceImplicitWidth availableWorkspaceHeight: root.workspaceImplicitHeight - property int monitorId: windowData?.monitor - property var monitor: HyprlandData.monitors[monitorId] - property bool atInitPosition: (initX == x && initY == y) - restrictToWorkspace: Drag.active || atInitPosition - property int workspaceColIndex: (windowData?.workspace.id - 1) % Config.options.overview.numOfCols - property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / Config.options.overview.numOfCols) - xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - (monitor?.x * root.scale) - yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - (monitor?.y * root.scale) + property int workspaceColIndex: (modelData?.["workspace-id"] ?? 0) % Config.options.overview.numOfCols + property int workspaceRowIndex: Math.floor((modelData?.["workspace-id"] ?? 0) % root.workspacesShown / Config.options.overview.numOfCols) + xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - ((root.focusedScreen?.x ?? 0) * root.scale) + yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - ((root.focusedScreen?.y ?? 0) * root.scale) Timer { id: updateWindowPosition @@ -184,12 +170,13 @@ Item { repeat: false running: false onTriggered: { - window.x = Math.round(Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset) - window.y = Math.round(Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset) - // console.log(`[OverviewWindow] Updated position for window ${windowData?.address} to (${window.x}, ${window.y})`) + window.x = Math.round(Math.max(modelData?.location[0] * root.scale, 0) + xOffset) + window.y = Math.round(Math.max((modelData?.location[1] - Appearance.sizes.barHeight) * root.scale, 0) + yOffset) } } + Component.onCompleted: updateWindowPosition.restart() + z: atInitPosition ? root.windowZ : root.windowDraggingZ Drag.hotSpot.x: targetWindowWidth / 2 Drag.hotSpot.y: targetWindowHeight / 2 @@ -202,35 +189,32 @@ Item { acceptedButtons: Qt.LeftButton | Qt.MiddleButton drag.target: parent onPressed: { - root.draggingFromWorkspace = windowData?.workspace.id + root.draggingFromWorkspace = modelData?.["workspace-id"] window.pressed = true window.Drag.active = true window.Drag.source = window - // console.log(`[OverviewWindow] Dragging window ${windowData?.address} from position (${window.x}, ${window.y})`) } onReleased: { const targetWorkspace = root.draggingTargetWorkspace window.pressed = false window.Drag.active = false root.draggingFromWorkspace = -1 - if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { - Fhtc.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`) + if (targetWorkspace !== -1 && targetWorkspace !== modelData?.["workspace-id"]) { + FhtcIpc.dispatch("send-window-to-workspace", { "window-id": modelData?.id, "workspace-id": targetWorkspace }) updateWindowPosition.restart() - } - else { + } else { window.x = window.initX window.y = window.initY } } onClicked: (event) => { - if (!windowData) return; - + if (!modelData) return; if (event.button === Qt.LeftButton) { GlobalStates.overviewOpen = false - Hyprland.dispatch(`focuswindow address:${windowData.address}`) + FhtcIpc.dispatch("focus-window", { "window-id": modelData.id }) event.accepted = true } else if (event.button === Qt.MiddleButton) { - Hyprland.dispatch(`closewindow address:${windowData.address}`) + FhtcIpc.dispatch("close-window", { "window-id": modelData.id }) event.accepted = true } } @@ -238,7 +222,7 @@ Item { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active - text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n` + text: `${modelData.title}\n[${modelData["app-id"]}]\n` } } } @@ -246,9 +230,9 @@ Item { Rectangle { // Focused workspace indicator id: focusedWorkspaceIndicator - property int activeWorkspaceInGroup: monitor.activeWorkspace?.id - (root.workspaceGroup * root.workspacesShown) - property int activeWorkspaceRowIndex: Math.floor((activeWorkspaceInGroup - 1) / Config.options.overview.numOfCols) - property int activeWorkspaceColIndex: (activeWorkspaceInGroup - 1) % Config.options.overview.numOfCols + property int activeWorkspaceInGroup: (monitor["active-workspace-idx"] ?? 0) - (root.workspaceGroup * root.workspacesShown) + property int activeWorkspaceRowIndex: Math.floor(activeWorkspaceInGroup / Config.options.overview.numOfCols) + property int activeWorkspaceColIndex: activeWorkspaceInGroup % Config.options.overview.numOfCols x: (root.workspaceImplicitWidth + workspaceSpacing) * activeWorkspaceColIndex y: (root.workspaceImplicitHeight + workspaceSpacing) * activeWorkspaceRowIndex z: root.windowZ diff --git a/src/share/sleex/modules/overview/OverviewWindow.qml b/src/share/sleex/modules/overview/OverviewWindow.qml index e75a6487..c6410a68 100644 --- a/src/share/sleex/modules/overview/OverviewWindow.qml +++ b/src/share/sleex/modules/overview/OverviewWindow.qml @@ -22,8 +22,8 @@ Item { // Window property var availableWorkspaceWidth property var availableWorkspaceHeight property bool restrictToWorkspace: true - property real initX: Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset - property real initY: Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset + property real initX: Math.max(windowData?.location[0] * root.scale, 0) + xOffset + property real initY: Math.max((windowData?.location[1] - Appearance.sizes.barHeight) * root.scale, 0) + yOffset property real xOffset: 0 property real yOffset: 0 @@ -35,10 +35,10 @@ Item { // Window property var iconToWindowRatio: 0.35 property var xwaylandIndicatorToIconRatio: 0.35 property var iconToWindowRatioCompact: 0.6 - property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing") + property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.["app-id"]), "image-missing") property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth - property bool indicateXWayland: (Config.options.overview.showXwaylandIndicator && windowData?.xwayland) ?? false + // property bool indicateXWayland: (Config.options.overview.showXwaylandIndicator && windowData?.xwayland) ?? false x: initX y: initY @@ -70,7 +70,7 @@ Item { // Window ScreencopyView { id: windowPreview anchors.fill: parent - captureSource: GlobalStates.overviewOpen ? root.toplevel : null + captureSource: null // GlobalStates.overviewOpen ? root.toplevel : null live: true Rectangle {