hyprland/ipc: expose active and focused properties + activate()

This commit is contained in:
outfoxxed 2025-03-26 02:52:23 -07:00
parent 207e6114a3
commit 62ccab5d30
Signed by untrusted user: outfoxxed
GPG key ID: 4C88A185FB89301E
7 changed files with 92 additions and 33 deletions

View file

@ -314,7 +314,7 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
delete workspace; delete workspace;
for (auto* monitor: this->mMonitors.valueList()) { for (auto* monitor: this->mMonitors.valueList()) {
if (monitor->activeWorkspace() == nullptr) { if (monitor->bindableActiveWorkspace().value() == nullptr) {
// removing a monitor will cause a new workspace to be created and destroyed after removal, // removing a monitor will cause a new workspace to be created and destroyed after removal,
// but it won't go back to a real workspace afterwards and just leaves a null, so we // but it won't go back to a real workspace afterwards and just leaves a null, so we
// re-query monitors if this appears to be the case. // re-query monitors if this appears to be the case.
@ -342,11 +342,11 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
auto id = args.at(0).toInt(); auto id = args.at(0).toInt();
auto name = QString::fromUtf8(args.at(1)); auto name = QString::fromUtf8(args.at(1));
if (this->mFocusedMonitor != nullptr) { if (this->bFocusedMonitor != nullptr) {
auto* workspace = this->findWorkspaceByName(name, true, id); auto* workspace = this->findWorkspaceByName(name, true, id);
this->mFocusedMonitor->setActiveWorkspace(workspace); this->bFocusedMonitor->setActiveWorkspace(workspace);
qCDebug(logHyprlandIpc) << "Workspace" << id << "activated on" qCDebug(logHyprlandIpc) << "Workspace" << id << "activated on"
<< this->mFocusedMonitor->bindableName().value(); << this->bFocusedMonitor->bindableName().value();
} }
} else if (event->name == "moveworkspacev2") { } else if (event->name == "moveworkspacev2") {
auto args = event->parseView(3); auto args = event->parseView(3);
@ -504,8 +504,6 @@ HyprlandIpc::findMonitorByName(const QString& name, bool createIfMissing, qint32
} }
} }
HyprlandMonitor* HyprlandIpc::focusedMonitor() const { return this->mFocusedMonitor; }
HyprlandMonitor* HyprlandIpc::monitorFor(QuickshellScreenInfo* screen) { HyprlandMonitor* HyprlandIpc::monitorFor(QuickshellScreenInfo* screen) {
// Wayland monitors appear after hyprland ones are created and disappear after destruction // Wayland monitors appear after hyprland ones are created and disappear after destruction
// so simply not doing any preemptive creation is enough, however if this call creates // so simply not doing any preemptive creation is enough, however if this call creates
@ -517,22 +515,22 @@ HyprlandMonitor* HyprlandIpc::monitorFor(QuickshellScreenInfo* screen) {
} }
void HyprlandIpc::setFocusedMonitor(HyprlandMonitor* monitor) { void HyprlandIpc::setFocusedMonitor(HyprlandMonitor* monitor) {
if (monitor == this->mFocusedMonitor) return; auto* oldMonitor = this->bFocusedMonitor.value();
if (monitor == oldMonitor) return;
if (this->mFocusedMonitor != nullptr) { if (this->bFocusedMonitor != nullptr) {
QObject::disconnect(this->mFocusedMonitor, nullptr, this, nullptr); QObject::disconnect(this->bFocusedMonitor, nullptr, this, nullptr);
} }
this->mFocusedMonitor = monitor;
if (monitor != nullptr) { if (monitor != nullptr) {
QObject::connect(monitor, &QObject::destroyed, this, &HyprlandIpc::onFocusedMonitorDestroyed); QObject::connect(monitor, &QObject::destroyed, this, &HyprlandIpc::onFocusedMonitorDestroyed);
} }
emit this->focusedMonitorChanged();
this->bFocusedMonitor = monitor;
} }
void HyprlandIpc::onFocusedMonitorDestroyed() { void HyprlandIpc::onFocusedMonitorDestroyed() {
this->mFocusedMonitor = nullptr; this->bFocusedMonitor = nullptr;
emit this->focusedMonitorChanged(); emit this->focusedMonitorChanged();
} }

View file

@ -7,8 +7,10 @@
#include <qhash.h> #include <qhash.h>
#include <qlocalsocket.h> #include <qlocalsocket.h>
#include <qobject.h> #include <qobject.h>
#include <qproperty.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h>
#include "../../../core/model.hpp" #include "../../../core/model.hpp"
#include "../../../core/qmlscreen.hpp" #include "../../../core/qmlscreen.hpp"
@ -74,7 +76,11 @@ public:
void dispatch(const QString& request); void dispatch(const QString& request);
[[nodiscard]] HyprlandMonitor* monitorFor(QuickshellScreenInfo* screen); [[nodiscard]] HyprlandMonitor* monitorFor(QuickshellScreenInfo* screen);
[[nodiscard]] HyprlandMonitor* focusedMonitor() const;
[[nodiscard]] QBindable<HyprlandMonitor*> bindableFocusedMonitor() const {
return &this->bFocusedMonitor;
}
void setFocusedMonitor(HyprlandMonitor* monitor); void setFocusedMonitor(HyprlandMonitor* monitor);
[[nodiscard]] ObjectModel<HyprlandMonitor>* monitors(); [[nodiscard]] ObjectModel<HyprlandMonitor>* monitors();
@ -119,10 +125,15 @@ private:
ObjectModel<HyprlandMonitor> mMonitors {this}; ObjectModel<HyprlandMonitor> mMonitors {this};
ObjectModel<HyprlandWorkspace> mWorkspaces {this}; ObjectModel<HyprlandWorkspace> mWorkspaces {this};
HyprlandMonitor* mFocusedMonitor = nullptr;
//HyprlandWorkspace* activeWorkspace = nullptr;
HyprlandIpcEvent event {this}; HyprlandIpcEvent event {this};
Q_OBJECT_BINDABLE_PROPERTY(
HyprlandIpc,
HyprlandMonitor*,
bFocusedMonitor,
&HyprlandIpc::focusedMonitorChanged
);
}; };
} // namespace qs::hyprland::ipc } // namespace qs::hyprland::ipc

View file

@ -18,6 +18,11 @@ void HyprlandMonitor::updateInitial(qint32 id, const QString& name, const QStrin
this->bId = id; this->bId = id;
this->bName = name; this->bName = name;
this->bDescription = description; this->bDescription = description;
this->bFocused.setBinding([this]() {
return HyprlandIpc::instance()->bindableFocusedMonitor().value() == this;
});
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
} }
@ -38,8 +43,8 @@ void HyprlandMonitor::updateFromObject(QVariantMap object) {
this->bScale = object.value("scale").value<qreal>(); this->bScale = object.value("scale").value<qreal>();
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
if (this->mActiveWorkspace == nullptr if (this->bActiveWorkspace == nullptr
|| this->mActiveWorkspace->bindableName().value() != activeWorkspaceName) || this->bActiveWorkspace->bindableName().value() != activeWorkspaceName)
{ {
auto* workspace = this->ipc->findWorkspaceByName(activeWorkspaceName, true, activeWorkspaceId); auto* workspace = this->ipc->findWorkspaceByName(activeWorkspaceName, true, activeWorkspaceId);
workspace->setMonitor(this); workspace->setMonitor(this);
@ -54,16 +59,15 @@ void HyprlandMonitor::updateFromObject(QVariantMap object) {
} }
} }
HyprlandWorkspace* HyprlandMonitor::activeWorkspace() const { return this->mActiveWorkspace; }
void HyprlandMonitor::setActiveWorkspace(HyprlandWorkspace* workspace) { void HyprlandMonitor::setActiveWorkspace(HyprlandWorkspace* workspace) {
if (workspace == this->mActiveWorkspace) return; auto* oldWorkspace = this->bActiveWorkspace.value();
if (workspace == oldWorkspace) return;
if (this->mActiveWorkspace != nullptr) { if (oldWorkspace != nullptr) {
QObject::disconnect(this->mActiveWorkspace, nullptr, this, nullptr); QObject::disconnect(oldWorkspace, nullptr, this, nullptr);
} }
this->mActiveWorkspace = workspace; Qt::beginPropertyUpdateGroup();
if (workspace != nullptr) { if (workspace != nullptr) {
workspace->setMonitor(this); workspace->setMonitor(this);
@ -76,12 +80,11 @@ void HyprlandMonitor::setActiveWorkspace(HyprlandWorkspace* workspace) {
); );
} }
emit this->activeWorkspaceChanged(); this->bActiveWorkspace = workspace;
Qt::endPropertyUpdateGroup();
} }
void HyprlandMonitor::onActiveWorkspaceDestroyed() { void HyprlandMonitor::onActiveWorkspaceDestroyed() { this->bActiveWorkspace = nullptr; }
this->mActiveWorkspace = nullptr;
emit this->activeWorkspaceChanged();
}
} // namespace qs::hyprland::ipc } // namespace qs::hyprland::ipc

View file

@ -9,6 +9,7 @@
#include <qtypes.h> #include <qtypes.h>
#include "connection.hpp" #include "connection.hpp"
#include "workspace.hpp"
namespace qs::hyprland::ipc { namespace qs::hyprland::ipc {
@ -30,7 +31,9 @@ class HyprlandMonitor: public QObject {
/// > property, run @@Hyprland.refreshMonitors() and wait for this property to update. /// > property, run @@Hyprland.refreshMonitors() and wait for this property to update.
Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged); Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged);
/// The currently active workspace on this monitor. May be null. /// The currently active workspace on this monitor. May be null.
Q_PROPERTY(qs::hyprland::ipc::HyprlandWorkspace* activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged); Q_PROPERTY(qs::hyprland::ipc::HyprlandWorkspace* activeWorkspace READ default NOTIFY activeWorkspaceChanged);
/// If the monitor is currently focused.
Q_PROPERTY(bool focused READ default NOTIFY focusedChanged);
// clang-format on // clang-format on
QML_ELEMENT; QML_ELEMENT;
QML_UNCREATABLE("HyprlandMonitors must be retrieved from the HyprlandIpc object."); QML_UNCREATABLE("HyprlandMonitors must be retrieved from the HyprlandIpc object.");
@ -50,10 +53,15 @@ public:
[[nodiscard]] QBindable<qint32> bindableHeight() { return &this->bHeight; } [[nodiscard]] QBindable<qint32> bindableHeight() { return &this->bHeight; }
[[nodiscard]] QBindable<qreal> bindableScale() { return &this->bScale; } [[nodiscard]] QBindable<qreal> bindableScale() { return &this->bScale; }
[[nodiscard]] QBindable<HyprlandWorkspace*> bindableActiveWorkspace() const {
return &this->bActiveWorkspace;
}
[[nodiscard]] QBindable<bool> bindableFocused() const { return &this->bFocused; }
[[nodiscard]] QVariantMap lastIpcObject() const; [[nodiscard]] QVariantMap lastIpcObject() const;
void setActiveWorkspace(HyprlandWorkspace* workspace); void setActiveWorkspace(HyprlandWorkspace* workspace);
[[nodiscard]] HyprlandWorkspace* activeWorkspace() const;
signals: signals:
void idChanged(); void idChanged();
@ -66,6 +74,7 @@ signals:
void scaleChanged(); void scaleChanged();
void lastIpcObjectChanged(); void lastIpcObjectChanged();
void activeWorkspaceChanged(); void activeWorkspaceChanged();
void focusedChanged();
private slots: private slots:
void onActiveWorkspaceDestroyed(); void onActiveWorkspaceDestroyed();
@ -74,7 +83,6 @@ private:
HyprlandIpc* ipc; HyprlandIpc* ipc;
QVariantMap mLastIpcObject; QVariantMap mLastIpcObject;
HyprlandWorkspace* mActiveWorkspace = nullptr;
// clang-format off // clang-format off
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(HyprlandMonitor, qint32, bId, -1, &HyprlandMonitor::idChanged); Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(HyprlandMonitor, qint32, bId, -1, &HyprlandMonitor::idChanged);
@ -85,6 +93,8 @@ private:
Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, qint32, bWidth, &HyprlandMonitor::widthChanged); Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, qint32, bWidth, &HyprlandMonitor::widthChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, qint32, bHeight, &HyprlandMonitor::heightChanged); Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, qint32, bHeight, &HyprlandMonitor::heightChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, qreal, bScale, &HyprlandMonitor::scaleChanged); Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, qreal, bScale, &HyprlandMonitor::scaleChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, HyprlandWorkspace*, bActiveWorkspace, &HyprlandMonitor::activeWorkspaceChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandMonitor, bool, bFocused, &HyprlandMonitor::focusedChanged);
// clang-format on // clang-format on
}; };

View file

@ -38,7 +38,7 @@ QString HyprlandIpcQml::requestSocketPath() { return HyprlandIpc::instance()->re
QString HyprlandIpcQml::eventSocketPath() { return HyprlandIpc::instance()->eventSocketPath(); } QString HyprlandIpcQml::eventSocketPath() { return HyprlandIpc::instance()->eventSocketPath(); }
HyprlandMonitor* HyprlandIpcQml::focusedMonitor() { HyprlandMonitor* HyprlandIpcQml::focusedMonitor() {
return HyprlandIpc::instance()->focusedMonitor(); return HyprlandIpc::instance()->bindableFocusedMonitor().value();
} }
ObjectModel<HyprlandMonitor>* HyprlandIpcQml::monitors() { ObjectModel<HyprlandMonitor>* HyprlandIpcQml::monitors() {

View file

@ -59,10 +59,24 @@ void HyprlandWorkspace::setMonitor(HyprlandMonitor* monitor) {
this->mMonitor = monitor; this->mMonitor = monitor;
Qt::beginPropertyUpdateGroup();
if (monitor != nullptr) { if (monitor != nullptr) {
QObject::connect(monitor, &QObject::destroyed, this, &HyprlandWorkspace::onMonitorDestroyed); QObject::connect(monitor, &QObject::destroyed, this, &HyprlandWorkspace::onMonitorDestroyed);
this->bActive.setBinding([this]() {
return this->mMonitor->bindableActiveWorkspace().value() == this;
});
this->bFocused.setBinding([this]() {
return this->bActive.value() && this->mMonitor->bindableFocused().value();
});
} else {
this->bActive = false;
this->bFocused = false;
} }
Qt::endPropertyUpdateGroup();
emit this->monitorChanged(); emit this->monitorChanged();
} }
@ -71,4 +85,8 @@ void HyprlandWorkspace::onMonitorDestroyed() {
emit this->monitorChanged(); emit this->monitorChanged();
} }
void HyprlandWorkspace::activate() {
HyprlandIpc::instance()->dispatch(QString("workspace %1").arg(this->bId.value()));
}
} // namespace qs::hyprland::ipc } // namespace qs::hyprland::ipc

View file

@ -16,6 +16,11 @@ class HyprlandWorkspace: public QObject {
Q_OBJECT; Q_OBJECT;
Q_PROPERTY(qint32 id READ default NOTIFY idChanged BINDABLE bindableId); Q_PROPERTY(qint32 id READ default NOTIFY idChanged BINDABLE bindableId);
Q_PROPERTY(QString name READ default NOTIFY nameChanged BINDABLE bindableName); Q_PROPERTY(QString name READ default NOTIFY nameChanged BINDABLE bindableName);
/// If this workspace is currently active on its monitor. See also @@focused.
Q_PROPERTY(bool active READ default NOTIFY activeChanged BINDABLE bindableActive);
/// If this workspace is currently active on a monitor and that monitor is currently
/// focused. See also @@active.
Q_PROPERTY(bool focused READ default NOTIFY focusedChanged BINDABLE bindableFocused);
/// Last json returned for this workspace, as a javascript object. /// Last json returned for this workspace, as a javascript object.
/// ///
/// > [!WARNING] This is *not* updated unless the workspace object is fetched again from /// > [!WARNING] This is *not* updated unless the workspace object is fetched again from
@ -32,8 +37,18 @@ public:
void updateInitial(qint32 id, const QString& name); void updateInitial(qint32 id, const QString& name);
void updateFromObject(QVariantMap object); void updateFromObject(QVariantMap object);
/// Activate the workspace.
///
/// > [!NOTE] This is equivalent to running
/// > ```qml
/// > HyprlandIpc.dispatch(`workspace ${workspace.id}`);
/// > ```
Q_INVOKABLE void activate();
[[nodiscard]] QBindable<qint32> bindableId() { return &this->bId; } [[nodiscard]] QBindable<qint32> bindableId() { return &this->bId; }
[[nodiscard]] QBindable<QString> bindableName() { return &this->bName; } [[nodiscard]] QBindable<QString> bindableName() { return &this->bName; }
[[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; }
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
[[nodiscard]] QVariantMap lastIpcObject() const; [[nodiscard]] QVariantMap lastIpcObject() const;
@ -43,6 +58,8 @@ public:
signals: signals:
void idChanged(); void idChanged();
void nameChanged(); void nameChanged();
void activeChanged();
void focusedChanged();
void lastIpcObjectChanged(); void lastIpcObjectChanged();
void monitorChanged(); void monitorChanged();
@ -58,6 +75,8 @@ private:
// clang-format off // clang-format off
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(HyprlandWorkspace, qint32, bId, -1, &HyprlandWorkspace::idChanged); Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(HyprlandWorkspace, qint32, bId, -1, &HyprlandWorkspace::idChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, QString, bName, &HyprlandWorkspace::nameChanged); Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, QString, bName, &HyprlandWorkspace::nameChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, bool, bActive, &HyprlandWorkspace::activeChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, bool, bFocused, &HyprlandWorkspace::focusedChanged);
// clang-format on // clang-format on
}; };