forked from quickshell/quickshell
		
	hyprland/ipc: expose active and focused properties + activate()
This commit is contained in:
		
							parent
							
								
									207e6114a3
								
							
						
					
					
						commit
						62ccab5d30
					
				
					 7 changed files with 92 additions and 33 deletions
				
			
		| 
						 | 
				
			
			@ -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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue