summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2026-07-27 14:46:32 +0800
committerlittleblack111 <littleblack11111@gmail.com>2026-07-27 14:57:41 +0800
commit90df3a1a8738d61f747c50f26359ccabf0c7b229 (patch)
tree8df5a8ce0bef4f7cb831ba517ac54969c46fbe19
parent1f9873b40fee2d33c5b7103a25077ec61a387063 (diff)
new: more advanced math via kalk
refactor(components): optimizations and simplify
-rw-r--r--config/Launcher.qml12
-rw-r--r--popups/launcher/components/App.qml13
-rw-r--r--popups/launcher/components/Calc.qml16
-rw-r--r--popups/launcher/components/DateTime.qml (renamed from popups/launcher/components/Date.qml)0
-rw-r--r--popups/launcher/components/ISmartCalc.qml14
-rw-r--r--popups/launcher/components/SelectionState.qml2
-rw-r--r--qml_plugin/Cargo.lock45
-rw-r--r--qml_plugin/Cargo.toml1
-rw-r--r--qml_plugin/build.rs7
-rw-r--r--qml_plugin/src/launcher.rs1
-rw-r--r--qml_plugin/src/launcher/mathcalc.rs111
11 files changed, 196 insertions, 26 deletions
diff --git a/config/Launcher.qml b/config/Launcher.qml
index a1cb193..11a2b6e 100644
--- a/config/Launcher.qml
+++ b/config/Launcher.qml
@@ -31,6 +31,10 @@ Singleton {
readonly property int predictiveCompletionRadius: 6
readonly property list<Component> widgets: [
+ // recommanded order:
+ // 1. components with .startswith
+ // 2. utils
+ // 3. app launcher
Component {
Emoji {}
},
@@ -41,16 +45,16 @@ Singleton {
Spell {}
},
Component {
- App {}
- },
- Component {
Calc {}
},
Component {
- Date {}
+ DateTime {}
},
Component {
Unit {}
},
+ Component {
+ App {}
+ },
]
}
diff --git a/popups/launcher/components/App.qml b/popups/launcher/components/App.qml
index e90bea1..f9c63e3 100644
--- a/popups/launcher/components/App.qml
+++ b/popups/launcher/components/App.qml
@@ -10,7 +10,7 @@ import qs.config
IComponent {
id: root
- property list<DesktopEntry> entries: active ? AppSearch.query(inputCleaned) : []
+ property list<DesktopEntry> entries: AppSearch.query(inputCleaned)
property int selectedIndex: -1
name: "Applications"
@@ -25,14 +25,15 @@ IComponent {
}
}
- property string predictiveCompletion: valid ? entries[selectedIndex]?.name.slice(input.length) || "" : ""
+ property string predictiveCompletion: entries[selectedIndex]?.name.slice(input.length) || ""
process: function () {
- const isValid = entries.length > 0;
- const selected = isValid ? entries[selectedIndex] : "";
+ const valid = entries.length > 0;
+ // TODO: consider removing the valid check
+ const selected = valid ? entries[selectedIndex] : "";
return {
- valid: isValid,
- priority: isValid,
+ valid,
+ priority: valid,
answer: selected?.icon || ""
};
}
diff --git a/popups/launcher/components/Calc.qml b/popups/launcher/components/Calc.qml
index c6cdbc1..4cc45a7 100644
--- a/popups/launcher/components/Calc.qml
+++ b/popups/launcher/components/Calc.qml
@@ -6,6 +6,8 @@ import qs.components
import qs.services
import qs.config
+import core
+
IComponent {
property int cursorPosition: SelectionState.cursorPosition
@@ -17,17 +19,19 @@ IComponent {
}
process: function () {
- const isValid = /^(?=.*\d)(?=.*[+\-*\/\^])[0-9+\-*\/\^().\s]+$/.test(input);
- const answer = isValid ? String(eval(input.replace(/\^/g, '**').replace(/([\d)])\(/g, '$1*(').replace(/\)([\d])/g, ')*$1'))) : null;
+ MathCalc.query(input);
+ const valid = MathCalc.result && MathCalc.result.length > 0;
+ const answer = MathCalc.result;
return {
- valid: isValid,
- priority: isValid,
- answer: answer,
- predictiveCompletion: isValid ? ' = ' + answer : ''
+ valid,
+ priority: valid,
+ answer,
+ predictiveCompletion: valid ? ' = ' + answer : ''
};
}
exec: function () {
Clip.copy(answer);
+ MathCalc.reset_ctx();
}
IInnerComponent {
diff --git a/popups/launcher/components/Date.qml b/popups/launcher/components/DateTime.qml
index 0929b75..0929b75 100644
--- a/popups/launcher/components/Date.qml
+++ b/popups/launcher/components/DateTime.qml
diff --git a/popups/launcher/components/ISmartCalc.qml b/popups/launcher/components/ISmartCalc.qml
index 6e6381c..5474cc5 100644
--- a/popups/launcher/components/ISmartCalc.qml
+++ b/popups/launcher/components/ISmartCalc.qml
@@ -14,7 +14,6 @@ IComponent {
required property string predictiveText
property int cursorPosition: SelectionState.cursorPosition
- property string answer: active && valid ? SmartCalc.result : ""
preview: Component {
Icon {
@@ -24,13 +23,14 @@ IComponent {
process: function () {
SmartCalc.query(input);
- const isValid = active && SmartCalc.result && SmartCalc.result.length > 0 && category;
- const answer = isValid ? SmartCalc.result : "";
+ const answer = SmartCalc.result;
+ // TODO: use answer property rn its circular dependent since it has to be valid which is the line below
+ const valid = SmartCalc.result && SmartCalc.result.length > 0 && category;
return {
- valid: isValid,
- priority: isValid,
- answer: answer,
- predictiveCompletion: isValid ? ` ${predictiveText} ` + answer : ''
+ valid,
+ priority: valid,
+ answer,
+ predictiveCompletion: ` ${predictiveText} ` + answer
};
}
exec: function () {
diff --git a/popups/launcher/components/SelectionState.qml b/popups/launcher/components/SelectionState.qml
index 1ab577a..212bd1d 100644
--- a/popups/launcher/components/SelectionState.qml
+++ b/popups/launcher/components/SelectionState.qml
@@ -2,6 +2,8 @@ pragma Singleton
import Quickshell
import QtQuick
+// FIXME: fix some selection not show/updated when deselected i.e. "pi" from app back to calc wont visually show deselection of last app
+// FIXME: icon/answer not updated when changed to non first priority
Singleton {
id: root
diff --git a/qml_plugin/Cargo.lock b/qml_plugin/Cargo.lock
index b740b04..ee9fb39 100644
--- a/qml_plugin/Cargo.lock
+++ b/qml_plugin/Cargo.lock
@@ -79,6 +79,12 @@ dependencies = [
]
[[package]]
+name = "az"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7"
+
+[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -462,9 +468,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "either"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
+checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
[[package]]
name = "encoding_rs"
@@ -593,6 +599,16 @@ dependencies = [
]
[[package]]
+name = "gmp-mpfr-sys"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7db155b537cb791b133341f99f68371d86ee7fa4c79aacfbc376d72d23c70531"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
name = "h2"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1289,6 +1305,18 @@ dependencies = [
]
[[package]]
+name = "kalk"
+version = "3.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6be3b5fe1fc1b007b90dbb151aa48d480efece8f813f975d53597aaa4df67003"
+dependencies = [
+ "gmp-mpfr-sys",
+ "lazy_static",
+ "rug",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1481,6 +1509,7 @@ dependencies = [
"cxx-qt",
"cxx-qt-build",
"cxx-qt-lib",
+ "kalk",
"smart-calculator",
"tokio",
]
@@ -1681,6 +1710,18 @@ dependencies = [
]
[[package]]
+name = "rug"
+version = "1.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07a8857882aec59d27254b02481c709327c13de6fad1da60bfc4f9783eaaa61e"
+dependencies = [
+ "az",
+ "gmp-mpfr-sys",
+ "libc",
+ "libm",
+]
+
+[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/qml_plugin/Cargo.toml b/qml_plugin/Cargo.toml
index 9f9fa88..5ae6a1c 100644
--- a/qml_plugin/Cargo.toml
+++ b/qml_plugin/Cargo.toml
@@ -10,6 +10,7 @@ crate-type = ["cdylib"]
cxx = "1.0.198"
cxx-qt = "0.9.1"
cxx-qt-lib = "0.9.1"
+kalk = "3.2.3"
smart-calculator = "1.0.3"
tokio = { version = "1.53.1", features = ["rt-multi-thread", "macros"] }
diff --git a/qml_plugin/build.rs b/qml_plugin/build.rs
index 8a02b92..2c73418 100644
--- a/qml_plugin/build.rs
+++ b/qml_plugin/build.rs
@@ -13,6 +13,11 @@ fn main() {
CxxQtBuilder::new_qml_module(QmlModule::new(NAME).plugin_type(PluginType::Dynamic))
.qt_module("Qml")
- .files(["src/launcher/smartcalc.rs"])
+ .files(
+ [
+ "src/launcher/smartcalc.rs",
+ "src/launcher/mathcalc.rs",
+ ],
+ )
.build();
}
diff --git a/qml_plugin/src/launcher.rs b/qml_plugin/src/launcher.rs
index 8cb8676..4274840 100644
--- a/qml_plugin/src/launcher.rs
+++ b/qml_plugin/src/launcher.rs
@@ -1 +1,2 @@
+pub mod mathcalc;
pub mod smartcalc;
diff --git a/qml_plugin/src/launcher/mathcalc.rs b/qml_plugin/src/launcher/mathcalc.rs
new file mode 100644
index 0000000..fd39ed1
--- /dev/null
+++ b/qml_plugin/src/launcher/mathcalc.rs
@@ -0,0 +1,111 @@
+use cxx_qt::{CxxQtType, Threading};
+use cxx_qt_lib::{QString, QVariant};
+use kalk::parser;
+use std::{pin::Pin, sync::Arc};
+use tokio::sync::Mutex;
+
+use crate::RUNTIME;
+
+#[cxx_qt::bridge]
+mod qobject {
+ extern "C++" {
+ include!("cxx-qt-lib/qstring.h");
+ include!("cxx-qt-lib/qvariant.h");
+ type QString = cxx_qt_lib::QString;
+ type QVariant = cxx_qt_lib::QVariant;
+ }
+
+ extern "RustQt" {
+ #[qobject]
+ #[qml_element]
+ #[qml_singleton]
+ #[qproperty(
+ QVariant, result
+ )]
+ type MathCalc = super::MathCalcRs;
+
+ #[qinvokable]
+ fn query(self: Pin<&mut MathCalc>, input: QString);
+
+ #[qinvokable]
+ fn reset(self: Pin<&mut MathCalc>);
+
+ #[qinvokable]
+ fn reset_ctx(self: Pin<&mut MathCalc>);
+ }
+
+ impl cxx_qt::Threading for MathCalc {}
+}
+
+#[derive(Default)]
+pub struct MathCalcRs {
+ result: QVariant,
+
+ ctx: Arc<Mutex<parser::Context>>,
+}
+
+impl qobject::MathCalc {
+ fn query(self: Pin<&mut Self>, input: QString) {
+ let qt_thread = self.qt_thread();
+ let ctx = Arc::clone(
+ &self
+ .rust_mut()
+ .ctx,
+ );
+ // TODO: consider moving to spawn_blocking
+ RUNTIME.spawn(
+ async move {
+ let result = parser::eval(
+ &mut *ctx
+ .lock()
+ .await,
+ &input.to_string(),
+ // TODO: move to const/config
+ 53,
+ );
+ qt_thread.queue(
+ |mut qo| {
+ qo.as_mut()
+ .set_result(
+ result.map_or_default(
+ |r| {
+ r.map_or_default(
+ |r| {
+ let result: QString = r
+ .to_string_big()
+ .into();
+ QVariant::from(&result)
+ },
+ )
+ },
+ ), /* match result {
+ * Ok(value) => {
+ * let result: QString = value
+ * .to_string()
+ * .into();
+ * QVariant::from(&result)
+ * }
+ * // TODO: add some logging
+ * Err(_) => QVariant::default(),
+ * }, */
+ )
+ },
+ )
+ },
+ );
+ }
+
+ fn reset(mut self: Pin<&mut Self>) {
+ self.as_mut()
+ .set_result(QVariant::default());
+ self.as_mut()
+ .rust_mut()
+ .ctx = Arc::new(Mutex::new(parser::Context::default()));
+ }
+
+ fn reset_ctx(mut self: Pin<&mut Self>) {
+ self.as_mut()
+ .rust_mut()
+ .ctx = Arc::new(Mutex::new(parser::Context::default()));
+ }
+}