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}