From 1cd0d292fe6d49577def7f16a3431f7748b8c182 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Thu, 18 Sep 2025 15:53:35 -0600 Subject: [PATCH] calculator works pretty well minus the tearing --- user-apps/calculator/src/main.rs | 179 ++++++++++++++++++++++--------- 1 file changed, 127 insertions(+), 52 deletions(-) diff --git a/user-apps/calculator/src/main.rs b/user-apps/calculator/src/main.rs index 4220235..f66f5e3 100644 --- a/user-apps/calculator/src/main.rs +++ b/user-apps/calculator/src/main.rs @@ -2,28 +2,21 @@ #![no_main] extern crate alloc; -use abi::{KeyCode, display::Display, get_key, print, sleep}; -use alloc::{boxed::Box, format, string::String, vec}; -use core::{panic::PanicInfo, pin::Pin}; +use abi::{KeyCode, display::Display, get_key, print}; +use alloc::{format, string::String, vec, vec::Vec}; +use core::panic::PanicInfo; use embedded_graphics::{ Drawable, geometry::{Dimensions, Point}, - mono_font::{ - MonoTextStyle, - ascii::{self, FONT_6X10}, - iso_8859_1::FONT_10X20, - }, + mono_font::{MonoTextStyle, ascii::FONT_7X14, iso_8859_1::FONT_10X20}, pixelcolor::Rgb565, - prelude::{Primitive, RgbColor, Size}, + prelude::{Primitive, RgbColor}, primitives::{PrimitiveStyle, Rectangle}, - text::{Alignment, Text}, + text::Text, }; use embedded_layout::{ align::{horizontal, vertical}, - layout::linear::{ - LinearLayout, - spacing::{DistributeFill, FixedMargin}, - }, + layout::linear::LinearLayout, object_chain::Chain, prelude::*, }; @@ -48,57 +41,63 @@ pub fn main() { let mut display = Display; let mut input = vec!['e', 'x', 'p', 'r', ':', ' ']; + let input_min = input.len(); let mut dirty = true; - let mut last_area: Option = None; + let mut last_area: Option<(Rectangle, Rectangle)> = None; + + LinearLayout::vertical(Chain::new(Text::new( + "Calculator!", + Point::zero(), + MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE), + ))) + .arrange() + .align_to(&display.bounding_box(), horizontal::Center, vertical::Top) + .draw(&mut display) + .expect("Failed to draw title"); loop { if dirty { + let style = PrimitiveStyle::with_fill(Rgb565::BLACK); if let Some(area) = last_area { - Rectangle::new(area.top_left, area.size) - .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK)) + Rectangle::new(area.0.top_left, area.0.size) + .into_styled(style) + .draw(&mut display) + .unwrap(); + + Rectangle::new(area.1.top_left, area.1.size) + .into_styled(style) .draw(&mut display) .unwrap(); } let text = input.iter().cloned().collect::(); - let expr = Text::new( + let style = MonoTextStyle::new(&FONT_7X14, Rgb565::WHITE); + let expr_layout = LinearLayout::vertical(Chain::new(Text::new( &text, display.bounding_box().center(), - MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE), - ); - - let layout = LinearLayout::vertical( - Chain::new(Text::new( - "Calculator!", - Point::zero(), - MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE), - )) - .append( - LinearLayout::horizontal(Chain::new(expr).append(Text::new( - " = 901", - Point::zero(), - MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE), - ))) - .with_spacing(DistributeFill(expr.size().width)) - .arrange() - .align_to( - &display.bounding_box(), - horizontal::Center, - vertical::Center, - ), - ), - ) - .with_spacing(DistributeFill(50)) + style, + ))) .arrange() - .align_to( - &display.bounding_box(), - horizontal::Center, - vertical::Center, - ); + .align_to(&display.bounding_box(), horizontal::Left, vertical::Center); - last_area = Some(layout.bounds()); - layout.draw(&mut display).unwrap(); + let result = if let Ok(result) = evaluate(&input[input_min..]) { + &format!(" = {}", result) + } else { + " = Error" + }; + + let eq_layout = LinearLayout::vertical(Chain::new(Text::new( + result, + display.bounding_box().center(), + style, + ))) + .arrange() + .align_to(&display.bounding_box(), horizontal::Right, vertical::Center); + + last_area = Some((expr_layout.bounds(), eq_layout.bounds())); + expr_layout.draw(&mut display).unwrap(); + eq_layout.draw(&mut display).unwrap(); dirty = false; } @@ -109,10 +108,12 @@ pub fn main() { input.push(ch); } KeyCode::Del => { - input.clear(); + input.truncate(input_min); } KeyCode::Backspace => { - input.pop(); + if input.len() > input_min { + input.pop(); + } } KeyCode::Esc => return, _ => (), @@ -121,3 +122,77 @@ pub fn main() { } } } + +fn get_int(int: &[char]) -> Result { + let mut output: i32 = 0; + for &c in int { + let digit = c.to_digit(10).ok_or(())? as i32; + output = output + .checked_mul(10) + .and_then(|v| v.checked_add(digit)) + .ok_or(())?; + } + Ok(output) +} + +fn primary(input: &[char], pos: &mut usize) -> Result { + let mut digits = Vec::new(); + while *pos < input.len() && input[*pos].is_ascii_digit() { + digits.push(input[*pos]); + *pos += 1; + } + if digits.is_empty() { + return Err(()); + } + get_int(&digits) +} + +fn mul_div(input: &[char], pos: &mut usize) -> Result { + let mut value = primary(input, pos)?; + while *pos < input.len() { + let op = input[*pos]; + if op != '*' && op != '/' { + break; + } + *pos += 1; + let rhs = primary(input, pos)?; + value = match op { + '*' => value.checked_mul(rhs).ok_or(())?, + '/' => { + if rhs == 0 { + return Err(()); + } + value.checked_div(rhs).ok_or(())? + } + _ => unreachable!(), + }; + } + Ok(value) +} + +fn add_sub(input: &[char], pos: &mut usize) -> Result { + let mut value = mul_div(input, pos)?; + while *pos < input.len() { + let op = input[*pos]; + if op != '+' && op != '-' { + break; + } + *pos += 1; + let rhs = mul_div(input, pos)?; + value = match op { + '+' => value.checked_add(rhs).ok_or(())?, + '-' => value.checked_sub(rhs).ok_or(())?, + _ => unreachable!(), + }; + } + Ok(value) +} + +fn evaluate(input: &[char]) -> Result { + let mut pos = 0; + let result = add_sub(input, &mut pos)?; + if pos != input.len() { + return Err(()); + } + Ok(result) +}