1use std::rc::Rc;
6
7use ravenrs::core::Key;
8use ravenrs::prelude::*;
9
10pub mod engine;
11
12use engine::{Engine, Op, Press};
13
14const KEYS: [(&str, &str, Press, ButtonStyle); 20] = [
16 ("C", "Clear", Press::Clear, ButtonStyle::Ghost),
17 ("⌫", "Erase", Press::Backspace, ButtonStyle::Ghost),
18 ("%", "Percent", Press::Percent, ButtonStyle::Ghost),
19 ("÷", "Divide", Press::Op(Op::Divide), ButtonStyle::Secondary),
20 ("7", "7", Press::Digit(7), ButtonStyle::Secondary),
21 ("8", "8", Press::Digit(8), ButtonStyle::Secondary),
22 ("9", "9", Press::Digit(9), ButtonStyle::Secondary),
23 ("×", "Multiply", Press::Op(Op::Multiply), ButtonStyle::Secondary),
24 ("4", "4", Press::Digit(4), ButtonStyle::Secondary),
25 ("5", "5", Press::Digit(5), ButtonStyle::Secondary),
26 ("6", "6", Press::Digit(6), ButtonStyle::Secondary),
27 ("−", "Subtract", Press::Op(Op::Subtract), ButtonStyle::Secondary),
28 ("1", "1", Press::Digit(1), ButtonStyle::Secondary),
29 ("2", "2", Press::Digit(2), ButtonStyle::Secondary),
30 ("3", "3", Press::Digit(3), ButtonStyle::Secondary),
31 ("+", "Add", Press::Op(Op::Add), ButtonStyle::Secondary),
32 ("±", "Change sign", Press::Negate, ButtonStyle::Secondary),
33 ("0", "0", Press::Digit(0), ButtonStyle::Secondary),
34 (".", "Point", Press::Point, ButtonStyle::Secondary),
35 ("=", "Equals", Press::Equals, ButtonStyle::Primary),
36];
37
38fn keyboard() -> Vec<(Key, Press)> {
40 let character = |text: &str| Key::Character(text.into());
41 let mut keys: Vec<(Key, Press)> =
42 (0..=9).map(|digit| (character(&digit.to_string()), Press::Digit(digit))).collect();
43 keys.extend([
44 (character("."), Press::Point),
45 (character(","), Press::Point),
46 (character("+"), Press::Op(Op::Add)),
47 (character("-"), Press::Op(Op::Subtract)),
48 (character("*"), Press::Op(Op::Multiply)),
49 (character("/"), Press::Op(Op::Divide)),
50 (character("%"), Press::Percent),
51 (character("="), Press::Equals),
52 (Key::Enter, Press::Equals),
53 (Key::Backspace, Press::Backspace),
54 (Key::Escape, Press::Clear),
55 ]);
56 keys
57}
58
59#[derive(Debug, Default)]
61pub struct Calculator {
62 engine: State<Engine>,
63}
64
65impl View for Calculator {
66 fn build(&self, cx: &mut BuildCx<'_>) -> NodeId {
67 for (key, press) in keyboard() {
68 let engine = self.engine.clone();
69 cx.shortcut(key, false, Rc::new(move || engine.update(|engine| engine.press(press))));
70 }
71 let keys: Vec<AnyView> = KEYS
72 .iter()
73 .map(|&(label, name, press, style)| -> AnyView {
74 let engine = self.engine.clone();
75 Box::new(
76 Button::new(label)
77 .accessible_name(name)
78 .style(style)
79 .size(ButtonSize::Large)
80 .full_width()
81 .on_click(move || engine.update(|engine| engine.press(press))),
82 )
83 })
84 .collect();
85 cx.child(
86 &VStack::new((
87 VStack::new((Text::bound(&self.engine, |engine| engine.display().to_owned())
88 .style(TextStyle::Metric)
89 .truncate(),))
90 .align(Align::End),
91 Grid::new(4, keys).spacing(8.0),
92 ))
93 .align(Align::Stretch)
94 .spacing(16.0)
95 .padding(16.0)
96 .grow(1.0),
97 )
98 }
99}
100
101pub fn app() -> Calculator {
103 Calculator::default()
104}
105
106#[cfg(target_arch = "wasm32")]
107mod web_entry {
108 use std::cell::RefCell;
109
110 use wasm_bindgen::prelude::*;
111
112 thread_local! {
113 static APP: RefCell<Option<ravenrs::web::WebApp>> = const { RefCell::new(None) };
114 }
115
116 #[wasm_bindgen(start)]
118 pub fn start() {
119 console_error_panic_hook::set_once();
120 wasm_bindgen_futures::spawn_local(async {
121 match ravenrs::web::start(super::app(), ravenrs::web::WebOptions::new("raven-canvas")).await {
122 Ok(app) => APP.with(|a| *a.borrow_mut() = Some(app)),
123 Err(e) => web_sys::console::error_1(&e.to_string().into()),
124 }
125 });
126 }
127}