Untitled
unknown
plain_text
10 months ago
5.4 kB
17
Indexable
use iced::{button, column, container, row, text, Align, Button, Column, Command, Container, Element, Length, Row, Text, Settings, Theme};
#[derive(Default)]
struct Calc {
display: String,
first: Option<f64>,
op: Option<Op>,
last_op: Option<Op>,
last_rhs: Option<f64>,
entering_new: bool,
}
#[derive(Debug, Clone, Copy)]
enum Op {
Add,
Sub,
Mul,
Div,
}
#[derive(Debug, Clone)]
enum Message {
Digit(u8),
SetOp(Op),
Equals,
Clear,
Exit,
}
impl iced::Program<Message> for Calc {
fn update(&mut self, msg: Message) -> Command<Message> {
// Any new input (digit or operator) should clear the "repeat" state
if let Message::Digit(_) | Message::SetOp(_) = &msg {
self.last_op = None;
self.last_rhs = None;
}
match msg {
Message::Digit(n) => {
if self.entering_new || self.display == "0" {
self.display = n.to_string();
self.entering_new = false;
} else {
self.display.push(char::from(b'0' + n));
}
}
Message::SetOp(new_op) => {
let current = parse_f64(&self.display);
match (self.first, self.op) {
(None, _) => {
self.first = Some(current);
}
(Some(lhs), Some(prev_op)) if !self.entering_new => {
let result = apply(prev_op, lhs, current);
self.display = format_display(result);
self.first = Some(result);
}
_ => {}
}
self.op = Some(new_op);
self.entering_new = true;
}
Message::Equals => {
let current_display_val = parse_f64(&self.display);
// Case 1: First equals press after an operation.
if let (Some(lhs), Some(op)) = (self.first, self.op) {
let result = apply(op, lhs, current_display_val);
self.display = format_display(result);
// Store for repeating the operation
self.last_op = Some(op);
self.last_rhs = Some(current_display_val);
// Clear the current operation state
self.first = None;
self.op = None;
}
// Case 2: Subsequent equals presses
else if let (Some(op), Some(rhs)) = (self.last_op, self.last_rhs) {
let lhs = current_display_val;
let result = apply(op, lhs, rhs);
self.display = format_display(result);
}
self.entering_new = true;
}
Message::Clear => {
*self = Self::default();
self.display = "0".into();
}
Message::Exit => {
std::process::exit(0);
}
}
Command::none()
}
fn view(&self) -> Element<Message> {
let btn = |label: &str, msg: Message| {
Button::new(Text::new(label))
.on_press(msg)
.padding(10)
.width(Length::Units(64))
.height(Length::Units(64))
};
let digit = |n: u8| btn(&n.to_string(), Message::Digit(n));
let op = |sym: &str, o: Op| btn(sym, Message::SetOp(o));
let display = Container::new(Text::new(&self.display).size(36))
.width(Length::Units(256))
.padding(10)
.align_x(Align::Right);
let r1 = Row::new()
.spacing(8)
.push(digit(7))
.push(digit(8))
.push(digit(9))
.push(op("/", Op::Div));
let r2 = Row::new()
.spacing(8)
.push(digit(4))
.push(digit(5))
.push(digit(6))
.push(op("*", Op::Mul));
let r3 = Row::new()
.spacing(8)
.push(digit(1))
.push(digit(2))
.push(digit(3))
.push(op("-", Op::Sub));
let r4 = Row::new()
.spacing(8)
.push(digit(0))
.push(btn("C", Message::Clear))
.push(btn("=", Message::Equals))
.push(op("+", Op::Add));
let exit = Button::new(Text::new("Exit (X)"))
.on_press(Message::Exit)
.padding(10)
.width(Length::Units(256))
.height(Length::Units(48));
Column::new()
.spacing(8)
.push(display)
.push(r1)
.push(r2)
.push(r3)
.push(r4)
.push(exit)
.padding(12)
.into()
}
}
fn parse_f64(s: &str) -> f64 {
s.parse::<f64>().unwrap_or(0.0)
}
fn apply(op: Op, a: f64, b: f64) -> f64 {
match op {
Op::Add => a + b,
Op::Sub => a - b,
Op::Mul => a * b,
Op::Div => if b == 0.0 { f64::INFINITY } else { a / b },
}
}
fn format_display(v: f64) -> String {
if v.fract() == 0.0 {
(v as i64).to_string()
} else {
v.to_string()
}
}
fn main() -> iced::Result {
Calc::run(Settings::default())
}
Editor is loading...
Leave a Comment