Untitled

 avatar
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