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: 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;
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,
// 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.
@ -342,11 +342,11 @@ void HyprlandIpc::onEvent(HyprlandIpcEvent* event) {
auto id = args.at(0).toInt();
auto name = QString::fromUtf8(args.at(1));
if (this->mFocusedMonitor != nullptr) {
if (this->bFocusedMonitor != nullptr) {
auto* workspace = this->findWorkspaceByName(name, true, id);
this->mFocusedMonitor->setActiveWorkspace(workspace);
this->bFocusedMonitor->setActiveWorkspace(workspace);
qCDebug(logHyprlandIpc) << "Workspace" << id << "activated on"
<< this->mFocusedMonitor->bindableName().value();
<< this->bFocusedMonitor->bindableName().value();
}
} else if (event->name == "moveworkspacev2") {
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) {
// 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
@ -517,22 +515,22 @@ HyprlandMonitor* HyprlandIpc::monitorFor(QuickshellScreenInfo* screen) {
}
void HyprlandIpc::setFocusedMonitor(HyprlandMonitor* monitor) {
if (monitor == this->mFocusedMonitor) return;
auto* oldMonitor = this->bFocusedMonitor.value();
if (monitor == oldMonitor) return;
if (this->mFocusedMonitor != nullptr) {
QObject::disconnect(this->mFocusedMonitor, nullptr, this, nullptr);
if (this->bFocusedMonitor != nullptr) {
QObject::disconnect(this->bFocusedMonitor, nullptr, this, nullptr);
}
this->mFocusedMonitor = monitor;
if (monitor != nullptr) {
QObject::connect(monitor, &QObject::destroyed, this, &HyprlandIpc::onFocusedMonitorDestroyed);
}
emit this->focusedMonitorChanged();
this->bFocusedMonitor = monitor;
}
void HyprlandIpc::onFocusedMonitorDestroyed() {
this->mFocusedMonitor = nullptr;
this->bFocusedMonitor = nullptr;
emit this->focusedMonitorChanged();
}

View file

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

View file

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

View file

@ -9,6 +9,7 @@
#include <qtypes.h>
#include "connection.hpp"
#include "workspace.hpp"
namespace qs::hyprland::ipc {
@ -30,7 +31,9 @@ class HyprlandMonitor: public QObject {
/// > property, run @@Hyprland.refreshMonitors() and wait for this property to update.
Q_PROPERTY(QVariantMap lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged);
/// 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
QML_ELEMENT;
QML_UNCREATABLE("HyprlandMonitors must be retrieved from the HyprlandIpc object.");
@ -50,10 +53,15 @@ public:
[[nodiscard]] QBindable<qint32> bindableHeight() { return &this->bHeight; }
[[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;
void setActiveWorkspace(HyprlandWorkspace* workspace);
[[nodiscard]] HyprlandWorkspace* activeWorkspace() const;
signals:
void idChanged();
@ -66,6 +74,7 @@ signals:
void scaleChanged();
void lastIpcObjectChanged();
void activeWorkspaceChanged();
void focusedChanged();
private slots:
void onActiveWorkspaceDestroyed();
@ -74,7 +83,6 @@ private:
HyprlandIpc* ipc;
QVariantMap mLastIpcObject;
HyprlandWorkspace* mActiveWorkspace = nullptr;
// clang-format off
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, bHeight, &HyprlandMonitor::heightChanged);
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
};

View file

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

View file

@ -59,10 +59,24 @@ void HyprlandWorkspace::setMonitor(HyprlandMonitor* monitor) {
this->mMonitor = monitor;
Qt::beginPropertyUpdateGroup();
if (monitor != nullptr) {
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();
}
@ -71,4 +85,8 @@ void HyprlandWorkspace::onMonitorDestroyed() {
emit this->monitorChanged();
}
void HyprlandWorkspace::activate() {
HyprlandIpc::instance()->dispatch(QString("workspace %1").arg(this->bId.value()));
}
} // namespace qs::hyprland::ipc

View file

@ -16,6 +16,11 @@ class HyprlandWorkspace: public QObject {
Q_OBJECT;
Q_PROPERTY(qint32 id READ default NOTIFY idChanged BINDABLE bindableId);
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.
///
/// > [!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 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<QString> bindableName() { return &this->bName; }
[[nodiscard]] QBindable<bool> bindableActive() { return &this->bActive; }
[[nodiscard]] QBindable<bool> bindableFocused() { return &this->bFocused; }
[[nodiscard]] QVariantMap lastIpcObject() const;
@ -43,6 +58,8 @@ public:
signals:
void idChanged();
void nameChanged();
void activeChanged();
void focusedChanged();
void lastIpcObjectChanged();
void monitorChanged();
@ -58,6 +75,8 @@ private:
// clang-format off
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, bool, bActive, &HyprlandWorkspace::activeChanged);
Q_OBJECT_BINDABLE_PROPERTY(HyprlandWorkspace, bool, bFocused, &HyprlandWorkspace::focusedChanged);
// clang-format on
};