feat: add support for getting and setting workdir

This commit is contained in:
outfoxxed 2024-03-03 17:05:19 -08:00
parent b5f50cd68f
commit bbe64f42f3
Signed by: outfoxxed
GPG Key ID: 4C88A185FB89301E
8 changed files with 109 additions and 2 deletions

View File

@ -2,7 +2,9 @@
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qdir.h>
#include <qguiapplication.h> #include <qguiapplication.h>
#include <qjsengine.h>
#include <qlogging.h> #include <qlogging.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlcontext.h> #include <qqmlcontext.h>
@ -83,3 +85,29 @@ QVariant QuickshellGlobal::env(const QString& variable) { // NOLINT
return qEnvironmentVariable(vstr.data()); return qEnvironmentVariable(vstr.data());
} }
QString QuickshellGlobal::workingDirectory() const { // NOLINT
return QDir::current().absolutePath();
}
void QuickshellGlobal::setWorkingDirectory(const QString& workingDirectory) { // NOLINT
QDir::setCurrent(workingDirectory);
emit this->workingDirectoryChanged();
}
static QuickshellGlobal* g_instance = nullptr; // NOLINT
QuickshellGlobal* QuickshellGlobal::create(QQmlEngine* /*unused*/, QJSEngine* /*unused*/) {
return QuickshellGlobal::instance();
}
QuickshellGlobal* QuickshellGlobal::instance() {
if (g_instance == nullptr) g_instance = new QuickshellGlobal();
QJSEngine::setObjectOwnership(g_instance, QJSEngine::CppOwnership);
return g_instance;
}
void QuickshellGlobal::deleteInstance() {
delete g_instance;
g_instance = nullptr;
}

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qjsengine.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlengine.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qqmllist.h> #include <qqmllist.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
@ -12,6 +14,7 @@
class QuickshellGlobal: public QObject { class QuickshellGlobal: public QObject {
Q_OBJECT; Q_OBJECT;
// clang-format off
/// All currently connected screens. /// All currently connected screens.
/// ///
/// This property updates as connected screens change. /// This property updates as connected screens change.
@ -33,6 +36,9 @@ class QuickshellGlobal: public QObject {
/// This creates an instance of your window once on every screen. /// This creates an instance of your window once on every screen.
/// As screens are added or removed your window will be created or destroyed on those screens. /// As screens are added or removed your window will be created or destroyed on those screens.
Q_PROPERTY(QQmlListProperty<QuickshellScreenInfo> screens READ screens NOTIFY screensChanged); Q_PROPERTY(QQmlListProperty<QuickshellScreenInfo> screens READ screens NOTIFY screensChanged);
/// Quickshell's working directory. Defaults to whereever quickshell was launched from.
Q_PROPERTY(QString workingDirectory READ workingDirectory WRITE setWorkingDirectory NOTIFY workingDirectoryChanged);
// clang-format on
QML_SINGLETON; QML_SINGLETON;
QML_NAMED_ELEMENT(Quickshell); QML_NAMED_ELEMENT(Quickshell);
@ -54,8 +60,16 @@ public:
/// Returns the string value of an environment variable or null if it is not set. /// Returns the string value of an environment variable or null if it is not set.
Q_INVOKABLE QVariant env(const QString& variable); Q_INVOKABLE QVariant env(const QString& variable);
[[nodiscard]] QString workingDirectory() const;
void setWorkingDirectory(const QString& workingDirectory);
static QuickshellGlobal* create(QQmlEngine* /*unused*/, QJSEngine* /*unused*/);
static QuickshellGlobal* instance();
static void deleteInstance();
signals: signals:
void screensChanged(); void screensChanged();
void workingDirectoryChanged();
public slots: public slots:
void updateScreens(); void updateScreens();

View File

@ -2,6 +2,7 @@
#include <cstdlib> #include <cstdlib>
#include <utility> #include <utility>
#include <qdir.h>
#include <qfileinfo.h> #include <qfileinfo.h>
#include <qlogging.h> #include <qlogging.h>
#include <qobject.h> #include <qobject.h>
@ -10,6 +11,7 @@
#include <qtimer.h> #include <qtimer.h>
#include <qurl.h> #include <qurl.h>
#include "qmlglobal.hpp"
#include "reload.hpp" #include "reload.hpp"
#include "shell.hpp" #include "shell.hpp"
#include "watcher.hpp" #include "watcher.hpp"
@ -17,7 +19,8 @@
RootWrapper::RootWrapper(QString rootPath) RootWrapper::RootWrapper(QString rootPath)
: QObject(nullptr) : QObject(nullptr)
, rootPath(std::move(rootPath)) , rootPath(std::move(rootPath))
, engine(this) { , engine(this)
, originalWorkingDirectory(QDir::current().absolutePath()) {
this->reloadGraph(true); this->reloadGraph(true);
if (this->root == nullptr) { if (this->root == nullptr) {
@ -28,14 +31,19 @@ RootWrapper::RootWrapper(QString rootPath)
RootWrapper::~RootWrapper() { RootWrapper::~RootWrapper() {
// event loop may no longer be running so deleteLater is not an option // event loop may no longer be running so deleteLater is not an option
QuickshellGlobal::deleteInstance();
delete this->root; delete this->root;
} }
void RootWrapper::reloadGraph(bool hard) { void RootWrapper::reloadGraph(bool hard) {
QuickshellGlobal::deleteInstance();
if (this->root != nullptr) { if (this->root != nullptr) {
this->engine.clearComponentCache(); this->engine.clearComponentCache();
} }
QDir::setCurrent(this->originalWorkingDirectory);
auto component = QQmlComponent(&this->engine, QUrl::fromLocalFile(this->rootPath)); auto component = QQmlComponent(&this->engine, QUrl::fromLocalFile(this->rootPath));
auto* obj = component.beginCreate(this->engine.rootContext()); auto* obj = component.beginCreate(this->engine.rootContext());

View File

@ -28,4 +28,5 @@ private:
QQmlEngine engine; QQmlEngine engine;
ShellRoot* root = nullptr; ShellRoot* root = nullptr;
FiletreeWatcher* configWatcher = nullptr; FiletreeWatcher* configWatcher = nullptr;
QString originalWorkingDirectory;
}; };

View File

@ -1,5 +1,6 @@
#include "shell.hpp" #include "shell.hpp"
#include <qdir.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
void ShellRoot::setConfig(ShellConfig config) { void ShellRoot::setConfig(ShellConfig config) {
@ -9,3 +10,7 @@ void ShellRoot::setConfig(ShellConfig config) {
} }
ShellConfig ShellRoot::config() const { return this->mConfig; } ShellConfig ShellRoot::config() const { return this->mConfig; }
void ShellConfig::setWorkingDirectory(const QString& workingDirectory) { // NOLINT
QDir::setCurrent(workingDirectory);
}

View File

@ -10,9 +10,12 @@
class ShellConfig { class ShellConfig {
Q_GADGET; Q_GADGET;
Q_PROPERTY(bool watchFiles MEMBER mWatchFiles); Q_PROPERTY(bool watchFiles MEMBER mWatchFiles);
Q_PROPERTY(QString workingDirectory WRITE setWorkingDirectory);
public: public:
bool mWatchFiles = true; bool mWatchFiles = true;
void setWorkingDirectory(const QString& workingDirectory);
}; };
///! Root config element ///! Root config element
@ -20,6 +23,8 @@ class ShellRoot: public ReloadPropagator {
Q_OBJECT; Q_OBJECT;
/// If `config.watchFiles` is true the configuration will be reloaded whenever it changes. /// If `config.watchFiles` is true the configuration will be reloaded whenever it changes.
/// Defaults to true. /// Defaults to true.
///
/// `config.workingDirectory` corrosponds to [Quickshell.workingDirectory](../quickshell#prop.workingDirectory).
Q_PROPERTY(ShellConfig config READ config WRITE setConfig); Q_PROPERTY(ShellConfig config READ config WRITE setConfig);
QML_ELEMENT; QML_ELEMENT;

View File

@ -2,6 +2,7 @@
#include <csignal> // NOLINT #include <csignal> // NOLINT
#include <utility> #include <utility>
#include <qdir.h>
#include <qlist.h> #include <qlist.h>
#include <qlogging.h> #include <qlogging.h>
#include <qobject.h> #include <qobject.h>
@ -9,8 +10,18 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include "../core/qmlglobal.hpp"
#include "datastream.hpp" #include "datastream.hpp"
Process::Process(QObject* parent): QObject(parent) {
QObject::connect(
QuickshellGlobal::instance(),
&QuickshellGlobal::workingDirectoryChanged,
this,
&Process::onGlobalWorkingDirectoryChanged
);
}
bool Process::isRunning() const { return this->process != nullptr; } bool Process::isRunning() const { return this->process != nullptr; }
void Process::setRunning(bool running) { void Process::setRunning(bool running) {
@ -24,6 +35,25 @@ QVariant Process::pid() const {
return QVariant::fromValue(this->process->processId()); return QVariant::fromValue(this->process->processId());
} }
QString Process::workingDirectory() const {
if (this->mWorkingDirectory.isEmpty()) return QDir::current().absolutePath();
else return this->mWorkingDirectory;
}
void Process::setWorkingDirectory(const QString& workingDirectory) {
auto absolute =
workingDirectory.isEmpty() ? workingDirectory : QDir(workingDirectory).absolutePath();
if (absolute == this->mWorkingDirectory) return;
this->mWorkingDirectory = absolute;
emit this->workingDirectoryChanged();
}
void Process::onGlobalWorkingDirectoryChanged() {
if (this->mWorkingDirectory.isEmpty()) {
emit this->workingDirectoryChanged();
}
}
QList<QString> Process::command() const { return this->mCommand; } QList<QString> Process::command() const { return this->mCommand; }
void Process::setCommand(QList<QString> command) { void Process::setCommand(QList<QString> command) {
@ -137,6 +167,8 @@ void Process::startProcessIfReady() {
if (this->mStderrParser == nullptr) this->process->closeReadChannel(QProcess::StandardError); if (this->mStderrParser == nullptr) this->process->closeReadChannel(QProcess::StandardError);
if (!this->mStdinEnabled) this->process->closeWriteChannel(); if (!this->mStdinEnabled) this->process->closeWriteChannel();
if (!this->mWorkingDirectory.isEmpty())
this->process->setWorkingDirectory(this->mWorkingDirectory);
this->process->start(cmd, args); this->process->start(cmd, args);
} }

View File

@ -42,6 +42,14 @@ class Process: public QObject {
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged); Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged);
/// The process ID of the running process or `null` if `running` is false. /// The process ID of the running process or `null` if `running` is false.
Q_PROPERTY(QVariant pid READ pid NOTIFY pidChanged); Q_PROPERTY(QVariant pid READ pid NOTIFY pidChanged);
/// The working directory of the process. Defaults to [quickshell's working directory].
///
/// If the process is already running changing this property will affect the next
/// started process. If the property has been changed after starting a process it will
/// return the new value, not the one for the currently running process.
///
/// [quickshell's working directory]: ../../quickshell/quickshell#prop.workingDirectory
Q_PROPERTY(QString workingDirectory READ workingDirectory WRITE setWorkingDirectory NOTIFY workingDirectoryChanged);
/// The command to execute. /// The command to execute.
/// ///
/// If the process is already running changing this property will affect the next /// If the process is already running changing this property will affect the next
@ -64,7 +72,7 @@ class Process: public QObject {
QML_ELEMENT; QML_ELEMENT;
public: public:
explicit Process(QObject* parent = nullptr): QObject(parent) {} explicit Process(QObject* parent = nullptr);
/// Sends a signal to the process if `running` is true, otherwise does nothing. /// Sends a signal to the process if `running` is true, otherwise does nothing.
Q_INVOKABLE void signal(qint32 signal); Q_INVOKABLE void signal(qint32 signal);
@ -77,6 +85,9 @@ public:
[[nodiscard]] QVariant pid() const; [[nodiscard]] QVariant pid() const;
[[nodiscard]] QString workingDirectory() const;
void setWorkingDirectory(const QString& workingDirectory);
[[nodiscard]] QList<QString> command() const; [[nodiscard]] QList<QString> command() const;
void setCommand(QList<QString> command); void setCommand(QList<QString> command);
@ -95,6 +106,7 @@ signals:
void runningChanged(); void runningChanged();
void pidChanged(); void pidChanged();
void workingDirectoryChanged();
void commandChanged(); void commandChanged();
void stdoutParserChanged(); void stdoutParserChanged();
void stderrParserChanged(); void stderrParserChanged();
@ -108,11 +120,13 @@ private slots:
void onStderrReadyRead(); void onStderrReadyRead();
void onStdoutParserDestroyed(); void onStdoutParserDestroyed();
void onStderrParserDestroyed(); void onStderrParserDestroyed();
void onGlobalWorkingDirectoryChanged();
private: private:
void startProcessIfReady(); void startProcessIfReady();
QProcess* process = nullptr; QProcess* process = nullptr;
QString mWorkingDirectory;
QList<QString> mCommand; QList<QString> mCommand;
DataStreamParser* mStdoutParser = nullptr; DataStreamParser* mStdoutParser = nullptr;
DataStreamParser* mStderrParser = nullptr; DataStreamParser* mStderrParser = nullptr;