From 9d36f5c314862dc11f437822dbc18fee4f857fb9 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Fri, 20 Mar 2026 00:06:35 +1100 Subject: [PATCH] feat: async calculator --- modules/launcher/items/CalcItem.qml | 9 ++- plugin/src/Caelestia/qalculator.cpp | 102 ++++++++++++++++++++++++++++ plugin/src/Caelestia/qalculator.hpp | 23 +++++++ 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/modules/launcher/items/CalcItem.qml b/modules/launcher/items/CalcItem.qml index 65489d9b..28a05327 100644 --- a/modules/launcher/items/CalcItem.qml +++ b/modules/launcher/items/CalcItem.qml @@ -12,8 +12,13 @@ Item { required property var list readonly property string math: list.search.text.slice(`${Config.launcher.actionPrefix}calc `.length) + onMathChanged: { + if (math.length > 0) + Qalculator.evalAsync(math); + } + function onClicked(): void { - Quickshell.execDetached(["wl-copy", Qalculator.eval(math, false)]); + Quickshell.execDetached(["wl-copy", Qalculator.rawResult]); root.list.visibilities.launcher = false; } @@ -55,7 +60,7 @@ Item { return Colours.palette.m3onSurface; } - text: root.math.length > 0 ? Qalculator.eval(root.math) : qsTr("Type an expression to calculate") + text: root.math.length > 0 ? (Qalculator.result || qsTr("Calculating...")) : qsTr("Type an expression to calculate") elide: Text.ElideLeft Layout.fillWidth: true diff --git a/plugin/src/Caelestia/qalculator.cpp b/plugin/src/Caelestia/qalculator.cpp index 44e8d21e..bfc977e8 100644 --- a/plugin/src/Caelestia/qalculator.cpp +++ b/plugin/src/Caelestia/qalculator.cpp @@ -1,9 +1,13 @@ #include "qalculator.hpp" #include +#include +#include namespace caelestia { +QMutex Qalculator::s_calculatorMutex; + Qalculator::Qalculator(QObject* parent) : QObject(parent) { if (!CALCULATOR) { @@ -19,6 +23,8 @@ QString Qalculator::eval(const QString& expr, bool printExpr) const { return QString(); } + QMutexLocker locker(&s_calculatorMutex); + EvaluationOptions eo; PrintOptions po; @@ -49,4 +55,100 @@ QString Qalculator::eval(const QString& expr, bool printExpr) const { return QString::fromStdString(result); } +void Qalculator::evalAsync(const QString& expr) { + const quint64 gen = ++m_generation; + + if (expr.isEmpty()) { + if (!m_result.isEmpty()) { + m_result.clear(); + emit resultChanged(); + } + if (!m_rawResult.isEmpty()) { + m_rawResult.clear(); + emit rawResultChanged(); + } + if (m_busy) { + m_busy = false; + emit busyChanged(); + } + return; + } + + if (!m_busy) { + m_busy = true; + emit busyChanged(); + } + + const auto future = QtConcurrent::run([expr]() -> QPair { + QMutexLocker locker(&s_calculatorMutex); + + EvaluationOptions eo; + PrintOptions po; + + std::string parsed; + std::string result = CALCULATOR->calculateAndPrint( + CALCULATOR->unlocalizeExpression(expr.toStdString(), eo.parse_options), 100, eo, po, &parsed); + + std::string error; + while (CALCULATOR->message()) { + if (!CALCULATOR->message()->message().empty()) { + if (CALCULATOR->message()->type() == MESSAGE_ERROR) { + error += "error: "; + } else if (CALCULATOR->message()->type() == MESSAGE_WARNING) { + error += "warning: "; + } + error += CALCULATOR->message()->message(); + } + CALCULATOR->nextMessage(); + } + + if (!error.empty()) { + const QString errorStr = QString::fromStdString(error); + return { errorStr, errorStr }; + } + + const QString rawStr = QString::fromStdString(result); + return { QString("%1 = %2").arg(parsed).arg(result), rawStr }; + }); + + auto* watcher = new QFutureWatcher>(this); + + connect(watcher, &QFutureWatcher>::finished, this, [this, watcher, gen]() { + watcher->deleteLater(); + + if (gen != m_generation) { + return; + } + + const auto [formatted, raw] = watcher->result(); + + if (m_result != formatted) { + m_result = formatted; + emit resultChanged(); + } + if (m_rawResult != raw) { + m_rawResult = raw; + emit rawResultChanged(); + } + if (m_busy) { + m_busy = false; + emit busyChanged(); + } + }); + + watcher->setFuture(future); +} + +QString Qalculator::result() const { + return m_result; +} + +QString Qalculator::rawResult() const { + return m_rawResult; +} + +bool Qalculator::busy() const { + return m_busy; +} + } // namespace caelestia diff --git a/plugin/src/Caelestia/qalculator.hpp b/plugin/src/Caelestia/qalculator.hpp index a07a8a2f..b2f5517f 100644 --- a/plugin/src/Caelestia/qalculator.hpp +++ b/plugin/src/Caelestia/qalculator.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -10,10 +11,32 @@ class Qalculator : public QObject { QML_ELEMENT QML_SINGLETON + Q_PROPERTY(QString result READ result NOTIFY resultChanged) + Q_PROPERTY(QString rawResult READ rawResult NOTIFY rawResultChanged) + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) + public: explicit Qalculator(QObject* parent = nullptr); Q_INVOKABLE QString eval(const QString& expr, bool printExpr = true) const; + Q_INVOKABLE void evalAsync(const QString& expr); + + [[nodiscard]] QString result() const; + [[nodiscard]] QString rawResult() const; + [[nodiscard]] bool busy() const; + +signals: + void resultChanged(); + void rawResultChanged(); + void busyChanged(); + +private: + static QMutex s_calculatorMutex; + + QString m_result; + QString m_rawResult; + bool m_busy = false; + quint64 m_generation = 0; }; } // namespace caelestia