feat: async calculator

This commit is contained in:
2 * r + 2 * t 2026-03-20 00:06:35 +11:00
parent 3c819cac1b
commit 9d36f5c314
3 changed files with 132 additions and 2 deletions

View file

@ -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

View file

@ -1,9 +1,13 @@
#include "qalculator.hpp"
#include <libqalculate/qalculate.h>
#include <qfuturewatcher.h>
#include <qtconcurrentrun.h>
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<QString, QString> {
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<QPair<QString, QString>>(this);
connect(watcher, &QFutureWatcher<QPair<QString, QString>>::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

View file

@ -1,5 +1,6 @@
#pragma once
#include <qmutex.h>
#include <qobject.h>
#include <qqmlintegration.h>
@ -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