summaryrefslogtreecommitdiff
path: root/qml_plugin/src/qml_interface/mathcalc.rs
blob: 601dc3b64a38febbb3c12dffa9e124f1f362c4da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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 unwrap_print::PrintableResult;

use crate::ExclusiveExecutor;

const DEFAULT_ANGLE_UNIT: &str = "deg";

#[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>);

        #[qinvokable]
        fn reset_result(self: Pin<&mut MathCalc>);
    }

    impl cxx_qt::Threading for MathCalc {}
}

pub struct MathCalcRs {
    result: QVariant,
    ctx: Arc<Mutex<parser::Context>>,

    executor: ExclusiveExecutor,
}

impl Default for MathCalcRs {
    fn default() -> Self {
        Self {
            result: QVariant::default(),
            ctx: Arc::new(Mutex::new(qobject::MathCalc::new_ctx())),
            executor: ExclusiveExecutor::default(),
        }
    }
}

impl qobject::MathCalc {
    fn query(mut self: Pin<&mut Self>, input: QString) {
        let qt_thread = self.qt_thread();
        let ctx = Arc::clone(&self.as_mut().rust_mut().ctx);
        // TODO: consider moving to spawn_blocking
        self.rust_mut().executor.spawn(async move {
            let input = input.to_string();
            let result = parser::eval(
                &mut *ctx.lock().await,
                &input,
                // TODO: move to const/config
                53,
            )
            .map_or_default(|r| {
                r.map_or_default(|r| {
                    let r: QString = r
                        // to display scientific notation properly
                        // other ones show both version without e or shows *10^_
                        // and have a = prefix
                        // to_string_big will result in 1/2=5.0000000000000000e − 1
                        .to_string()
                        .into();
                    if r != input.into() { QVariant::from(&r) } else { QVariant::default() }
                })
            });
            _ = qt_thread.queue(|mut qo| qo.as_mut().set_result(result)).unwrap_print();
        });
    }

    fn reset(mut self: Pin<&mut Self>) {
        self.as_mut().reset_result();
        self.reset_ctx();
    }

    fn new_ctx() -> parser::Context {
        parser::Context::default().set_angle_unit(DEFAULT_ANGLE_UNIT)
    }

    fn reset_ctx(mut self: Pin<&mut Self>) {
        self.as_mut().rust_mut().ctx = Arc::new(Mutex::new(Self::new_ctx()))
    }

    fn reset_result(mut self: Pin<&mut Self>) {
        self.as_mut().set_result(QVariant::default())
    }
}