calculator2/
calculator2.rs

1use fltk::{
2    app,
3    button::Button,
4    enums::{Align, Color, Event, FrameType, Key, Shortcut},
5    frame::Frame,
6    group::{Pack, PackType},
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, 100, 0, title);
39        b.set_label_size(24);
40        b.set_frame(FrameType::FlatBox);
41        match title {
42            "CE" => {
43                b.set_color(Color::from_hex(0xd50000));
44                b.set_shortcut(Shortcut::None | Key::Delete);
45            }
46            "x" | "/" | "+" | "-" | "=" | "C" | "@<-" => {
47                b.set_color(Color::from_hex(0xffee58));
48                b.set_label_color(Color::Black);
49                let shortcut = if title == "x" {
50                    '*'
51                } else {
52                    title.chars().next().unwrap()
53                };
54                b.set_shortcut(Shortcut::None | shortcut);
55                if shortcut == '@' {
56                    b.set_shortcut(Shortcut::None | Key::BackSpace);
57                }
58                if shortcut == '=' {
59                    b.set_shortcut(Shortcut::None | Key::Enter);
60                }
61            }
62            _ => {
63                if title == "0" {
64                    b.resize(0, 0, 100 * 2, 0);
65                }
66                b.set_label_color(Color::White);
67                b.set_selection_color(Color::from_hex(0x1b1b1b));
68                b.set_shortcut(Shortcut::None | title.chars().next().unwrap());
69                b.handle(move |b, ev| match ev {
70                    Event::Enter => {
71                        b.set_color(Color::from_hex(0x2b2b2b));
72                        b.redraw();
73                        true
74                    }
75                    Event::Leave => {
76                        b.set_color(Color::from_hex(0x424242));
77                        b.redraw();
78                        true
79                    }
80                    _ => false,
81                });
82            }
83        }
84        Self { b }
85    }
86}
87
88impl Deref for MyButton {
89    type Target = Button;
90
91    fn deref(&self) -> &Self::Target {
92        &self.b
93    }
94}
95
96impl DerefMut for MyButton {
97    fn deref_mut(&mut self) -> &mut Self::Target {
98        &mut self.b
99    }
100}
101
102fn main() {
103    let app = app::App::default();
104    app::set_visible_focus(false);
105    app::background(0x42, 0x42, 0x42);
106
107    let win_w = 400;
108    let win_h = 500;
109    let but_row = 160;
110
111    let mut operation = Ops::None;
112    let mut txt = String::from("0");
113    let mut old_val = String::from("0");
114    let mut new_val: String;
115
116    let mut wind = Window::default()
117        .with_label("FLTK Calc")
118        .with_size(win_w, win_h);
119    wind.set_center_screen();
120
121    let mut out = Frame::new(0, 0, win_w, 160, "").with_align(Align::Right | Align::Inside);
122    out.set_color(Color::from_hex(0x1b1b1b));
123    out.set_frame(FrameType::FlatBox);
124    out.set_label_color(Color::White);
125    out.set_label_size(36);
126    out.set_label("0");
127
128    let vpack = Pack::new(0, but_row, win_w, win_h - 170, "");
129
130    let mut hpack = Pack::new(0, 0, win_w, 68, "");
131    let but_ce = MyButton::new("CE");
132    let but_c = MyButton::new("C");
133    let but_back = MyButton::new("@<-");
134    let but_div = MyButton::new("/");
135    hpack.end();
136    hpack.set_type(PackType::Horizontal);
137
138    let mut hpack = Pack::new(0, 0, win_w, 68, "");
139    let mut but7 = MyButton::new("7");
140    let mut but8 = MyButton::new("8");
141    let mut but9 = MyButton::new("9");
142    let but_mul = MyButton::new("x");
143    hpack.end();
144    hpack.set_type(PackType::Horizontal);
145
146    let mut hpack = Pack::new(0, 0, win_w, 68, "");
147    let mut but4 = MyButton::new("4");
148    let mut but5 = MyButton::new("5");
149    let mut but6 = MyButton::new("6");
150    let but_sub = MyButton::new("-");
151    hpack.end();
152    hpack.set_type(PackType::Horizontal);
153
154    let mut hpack = Pack::new(0, 0, win_w, 68, "");
155    let mut but1 = MyButton::new("1");
156    let mut but2 = MyButton::new("2");
157    let mut but3 = MyButton::new("3");
158    let but_add = MyButton::new("+");
159    hpack.end();
160    hpack.set_type(PackType::Horizontal);
161
162    let mut hpack = Pack::new(0, 0, win_w, 68, "");
163    let mut but_dot = MyButton::new(".");
164    let mut but0 = MyButton::new("0");
165    let but_eq = MyButton::new("=");
166    hpack.end();
167    hpack.set_type(PackType::Horizontal);
168
169    vpack.end();
170
171    wind.make_resizable(false);
172    wind.end();
173    wind.show();
174
175    app::set_focus(&*but1);
176    app::get_system_colors();
177
178    let but_vec = vec![
179        &mut but1, &mut but2, &mut but3, &mut but4, &mut but5, &mut but6, &mut but7, &mut but8,
180        &mut but9, &mut but0,
181    ];
182
183    let but_op_vec = vec![
184        but_add, but_sub, but_mul, but_div, but_c, but_ce, but_back, but_eq,
185    ];
186
187    let (s, r) = app::channel::<Message>();
188
189    for but in but_vec {
190        let label = but.label().unwrap();
191        but.emit(s, Message::Number(label.parse().unwrap()));
192    }
193
194    for mut but in but_op_vec {
195        let op = match but.label().unwrap().as_str() {
196            "+" => Ops::Add,
197            "-" => Ops::Sub,
198            "x" => Ops::Mul,
199            "/" => Ops::Div,
200            "=" => Ops::Eq,
201            "CE" => Ops::CE,
202            "C" => Ops::C,
203            "@<-" => Ops::Back,
204            _ => Ops::None,
205        };
206        but.emit(s, Message::Op(op));
207    }
208
209    but_dot.emit(s, Message::Dot);
210
211    while app.wait() {
212        if let Some(val) = r.recv() {
213            match val {
214                Message::Number(num) => {
215                    if out.label().unwrap() == "0" {
216                        txt.clear();
217                    }
218                    txt.push_str(&num.to_string());
219                    out.set_label(txt.as_str());
220                }
221                Message::Dot => {
222                    if operation == Ops::Eq {
223                        txt.clear();
224                        operation = Ops::None;
225                        out.set_label("0.");
226                        txt.push_str("0.");
227                    }
228                    if !txt.contains('.') {
229                        txt.push('.');
230                        out.set_label(txt.as_str());
231                    }
232                }
233                Message::Op(op) => match op {
234                    Ops::Add | Ops::Sub | Ops::Div | Ops::Mul => {
235                        old_val.clear();
236                        old_val.push_str(&out.label().unwrap());
237                        operation = op;
238                        out.set_label("0");
239                    }
240                    Ops::Back => {
241                        let val = out.label().unwrap();
242                        txt.pop();
243                        if val.len() > 1 {
244                            out.set_label(txt.as_str());
245                        } else {
246                            out.set_label("0");
247                        }
248                    }
249                    Ops::CE => {
250                        txt.clear();
251                        old_val.clear();
252                        txt.push('0');
253                        out.set_label(txt.as_str());
254                    }
255                    Ops::C => {
256                        txt.clear();
257                        txt.push('0');
258                        out.set_label(txt.as_str());
259                    }
260                    Ops::Eq => {
261                        new_val = out.label().unwrap();
262                        let old: f64 = old_val.parse().unwrap();
263                        let new: f64 = new_val.parse().unwrap();
264                        let val = match operation {
265                            Ops::Div => old / new,
266                            Ops::Mul => old * new,
267                            Ops::Add => old + new,
268                            Ops::Sub => old - new,
269                            _ => new,
270                        };
271                        operation = Ops::None;
272                        txt = String::from("0");
273                        out.set_label(&val.to_string());
274                    }
275                    _ => (),
276                },
277            }
278        }
279    }
280}