calculator/
calculator.rs

1use fltk::{
2    app,
3    button::Button,
4    enums::{Color, Key, Shortcut},
5    group::{Pack, PackType},
6    output::Output,
7    prelude::*,
8    window::Window,
9};
10use std::ops::{Deref, DerefMut};
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13enum Ops {
14    None,
15    Add,
16    Sub,
17    Mul,
18    Div,
19    Eq,
20    CE,
21    C,
22    Back,
23}
24
25#[derive(Debug, Copy, Clone)]
26enum Message {
27    Number(i32),
28    Op(Ops),
29    Dot,
30}
31
32struct MyButton {
33    b: Button,
34}
35
36impl MyButton {
37    pub fn new(title: &'static str) -> MyButton {
38        let mut b = Button::new(0, 0, 90, 0, title);
39        b.set_label_size(20);
40        b.set_compact(true);
41        match title {
42            "0" => {
43                b.resize(0, 0, 90 * 2, 0);
44                b.set_color(Color::Light3);
45                b.set_shortcut(Shortcut::None | '0');
46            }
47            "CE" => {
48                b.set_color(Color::Red);
49                b.set_shortcut(Shortcut::None | Key::Delete);
50            }
51            "x" | "/" | "+" | "-" | "=" | "C" | "@<-" => {
52                b.set_color(Color::Yellow);
53                let shortcut = if title == "x" {
54                    '*'
55                } else {
56                    title.chars().next().unwrap()
57                };
58                b.set_shortcut(Shortcut::None | shortcut);
59                if shortcut == '@' {
60                    b.set_shortcut(Shortcut::None | Key::BackSpace);
61                }
62                if shortcut == '=' {
63                    b.set_shortcut(Shortcut::None | Key::Enter);
64                }
65            }
66            _ => {
67                b.set_color(Color::Light3);
68                b.set_shortcut(Shortcut::None | title.chars().next().unwrap());
69            }
70        }
71        Self { b }
72    }
73}
74
75impl Deref for MyButton {
76    type Target = Button;
77
78    fn deref(&self) -> &Self::Target {
79        &self.b
80    }
81}
82
83impl DerefMut for MyButton {
84    fn deref_mut(&mut self) -> &mut Self::Target {
85        &mut self.b
86    }
87}
88
89fn main() {
90    let app = app::App::default();
91    let win_w = 400;
92    let win_h = 500;
93    let border = 20;
94    let but_row = 180;
95
96    let mut operation = Ops::None;
97    let mut txt = String::from("0");
98    let mut old_val = String::from("0");
99    let mut new_val: String;
100
101    let mut wind = Window::default()
102        .with_label("FLTK Calc")
103        .with_size(win_w, win_h);
104    wind.set_center_screen();
105    wind.set_color(Color::Light3);
106
107    let mut out = Output::new(border, border, win_w - 40, 140, "");
108    out.set_text_size(36);
109    out.set_value("0");
110
111    let vpack = Pack::new(border, but_row, win_w - 40, 300, "");
112
113    let mut hpack = Pack::new(0, 0, win_w - 40, 60, "");
114    let but_ce = MyButton::new("CE");
115    let but_c = MyButton::new("C");
116    let but_back = MyButton::new("@<-");
117    let but_div = MyButton::new("/");
118    hpack.end();
119    hpack.set_type(PackType::Horizontal);
120
121    let mut hpack = Pack::new(0, 0, win_w - 40, 60, "");
122    let mut but7 = MyButton::new("7");
123    let mut but8 = MyButton::new("8");
124    let mut but9 = MyButton::new("9");
125    let but_mul = MyButton::new("x");
126    hpack.end();
127    hpack.set_type(PackType::Horizontal);
128
129    let mut hpack = Pack::new(0, 0, win_w - 40, 60, "");
130    let mut but4 = MyButton::new("4");
131    let mut but5 = MyButton::new("5");
132    let mut but6 = MyButton::new("6");
133    let but_sub = MyButton::new("-");
134    hpack.end();
135    hpack.set_type(PackType::Horizontal);
136
137    let mut hpack = Pack::new(0, 0, win_w - 40, 60, "");
138    let mut but1 = MyButton::new("1");
139    let mut but2 = MyButton::new("2");
140    let mut but3 = MyButton::new("3");
141    let but_add = MyButton::new("+");
142    hpack.end();
143    hpack.set_type(PackType::Horizontal);
144
145    let mut hpack = Pack::new(0, 0, win_w - 40, 60, "");
146    let mut but_dot = MyButton::new(".");
147    let mut but0 = MyButton::new("0");
148    let but_eq = MyButton::new("=");
149    hpack.end();
150    hpack.set_type(PackType::Horizontal);
151
152    vpack.end();
153
154    wind.make_resizable(false);
155    wind.end();
156    wind.show_with_args(&["-scheme", "gtk+", "-nokbd"]);
157
158    app::set_focus(&*but1);
159
160    let but_vec = vec![
161        &mut but1, &mut but2, &mut but3, &mut but4, &mut but5, &mut but6, &mut but7, &mut but8,
162        &mut but9, &mut but0,
163    ];
164
165    let but_op_vec = vec![
166        but_add, but_sub, but_mul, but_div, but_c, but_ce, but_back, but_eq,
167    ];
168
169    let (s, r) = app::channel::<Message>();
170
171    for but in but_vec {
172        let label = but.label().unwrap();
173        but.emit(s, Message::Number(label.parse().unwrap()));
174    }
175
176    for mut but in but_op_vec {
177        let op = match but.label().unwrap().as_str() {
178            "+" => Ops::Add,
179            "-" => Ops::Sub,
180            "x" => Ops::Mul,
181            "/" => Ops::Div,
182            "=" => Ops::Eq,
183            "CE" => Ops::CE,
184            "C" => Ops::C,
185            "@<-" => Ops::Back,
186            _ => Ops::None,
187        };
188        but.emit(s, Message::Op(op));
189    }
190
191    but_dot.emit(s, Message::Dot);
192
193    while app.wait() {
194        if let Some(val) = r.recv() {
195            match val {
196                Message::Number(num) => {
197                    if out.value() == "0" {
198                        txt.clear();
199                    }
200                    txt.push_str(&num.to_string());
201                    out.set_value(txt.as_str());
202                }
203                Message::Dot => {
204                    if operation == Ops::Eq {
205                        txt.clear();
206                        operation = Ops::None;
207                        out.set_value("0.");
208                        txt.push_str("0.");
209                    }
210                    if !txt.contains('.') {
211                        txt.push('.');
212                        out.set_value(txt.as_str());
213                    }
214                }
215                Message::Op(op) => match op {
216                    Ops::Add | Ops::Sub | Ops::Div | Ops::Mul => {
217                        old_val.clear();
218                        old_val.push_str(&out.value());
219                        operation = op;
220                        out.set_value("0");
221                    }
222                    Ops::Back => {
223                        let val = out.value();
224                        txt.pop();
225                        if val.len() > 1 {
226                            out.set_value(txt.as_str());
227                        } else {
228                            out.set_value("0");
229                        }
230                    }
231                    Ops::CE => {
232                        txt.clear();
233                        old_val.clear();
234                        txt.push('0');
235                        out.set_value(txt.as_str());
236                    }
237                    Ops::C => {
238                        txt.clear();
239                        txt.push('0');
240                        out.set_value(txt.as_str());
241                    }
242                    Ops::Eq => {
243                        new_val = out.value();
244                        let old: f64 = old_val.parse().unwrap();
245                        let new: f64 = new_val.parse().unwrap();
246                        let val = match operation {
247                            Ops::Div => old / new,
248                            Ops::Mul => old * new,
249                            Ops::Add => old + new,
250                            Ops::Sub => old - new,
251                            _ => new,
252                        };
253                        operation = Ops::None;
254                        txt = String::from("0");
255                        out.set_value(&val.to_string());
256                    }
257                    _ => (),
258                },
259            }
260        }
261    }
262}