use std::fmt::Display; use ori::prelude::*; #[derive(Clone, Copy, Debug, Default)] struct Number { value: f64, position: Option, } impl Number { fn new(value: f64) -> Self { Self { value, position: None, } } fn add_digit(&mut self, digit: u8) { let Some(position) = self.position else { self.position = Some(1); self.value = digit as f64; return; }; let sign = self.value.signum(); if position < 0 { let pow = 10.0f64.powi(position as i32); self.value += digit as f64 * pow * sign; self.position = Some(position - 1); } else { self.value *= 10.0; self.value += digit as f64 * sign; self.position = Some(position + 1); } } fn remove_digit(&mut self) { let Some(position) = self.position else { self.position = Some(0); self.value = 0.0; return; }; if position < -1 { self.value *= 10.0f64.powi(-position as i32 - 2); self.value = self.value.trunc(); self.value /= 10.0f64.powi(-position as i32 - 2); if position == -2 { self.position = Some(0); } else { self.position = Some(position + 1); } } else if position >= 0 { self.value /= 10.0; self.value = self.value.trunc(); self.position = Some((position - 1).max(0)); } else { self.position = Some(0); } // ensure that -0.0 is not displayed if self.value == -0.0 { self.value = 0.0; } } } impl Display for Number { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Some(position) = self.position else { return write!(f, "{}", self.value); }; if position == -1 { write!(f, "{}.", self.value) } else if position < 0 { write!(f, "{:.1$}", self.value, -position as usize - 1) } else { write!(f, "{}", self.value) } } } #[derive(Clone, Copy, Debug)] enum Operator { None, Add, Subtract, Multiply, Divide, } fn result_bar<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { let text = cx.memo(|| { let result = result.get(); let operator = operator.get(); let rhs = rhs.get(); match *operator { Operator::None => format!("{}", result), Operator::Add => format!("{} + {}", result, rhs), Operator::Subtract => format!("{} - {}", result, rhs), Operator::Multiply => format!("{} × {}", result, rhs), Operator::Divide => format!("{} ÷ {}", result, rhs), } }); view! {
} } fn bar0<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { let clear_all = |_: &PointerEvent| { operator.set(Operator::None); result.set(Number::new(0.0)); rhs.set(Number::new(0.0)); }; let clear = |_: &PointerEvent| { if matches!(*operator.get(), Operator::None) { result.set(Number::new(0.0)); } else { rhs.set(Number::new(0.0)); } }; let remove_digit = |_: &PointerEvent| { if matches!(*operator.get(), Operator::None) { result.modify().remove_digit(); } else { rhs.modify().remove_digit(); } }; let divide = |_: &PointerEvent| { operator.set(Operator::Divide); }; view! {
} } fn add_digit<'a>( operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, digit: u8, ) -> impl Fn(&PointerEvent) + 'a { move |_| { if matches!(*operator.get(), Operator::None) { result.modify().add_digit(digit); } else { rhs.modify().add_digit(digit); } } } fn bar1<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { let multiply = |_: &PointerEvent| { operator.set(Operator::Multiply); }; view! {
} } fn bar2<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { let subtract = |_: &PointerEvent| { operator.set(Operator::Subtract); }; view! {
} } fn bar3<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { let add = |_: &PointerEvent| { operator.set(Operator::Add); }; view! {
} } fn bar4<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { let negate = |_: &PointerEvent| { if result.get().value == 0.0 { return; } if matches!(*operator.get(), Operator::None) { result.modify().value *= -1.0; } else { rhs.modify().value *= -1.0; } }; let add_point = |_: &PointerEvent| { if let Some(position) = result.get().position { if position < 0 { return; } } if matches!(*operator.get(), Operator::None) { result.modify().position = Some(-1); } else { rhs.modify().position = Some(-1); } }; let equals = |_: &PointerEvent| { let mut result = result.modify(); let mut rhs = rhs.modify(); let mut operator = operator.modify(); match *operator { Operator::None => {} Operator::Add => { *result = Number::new(result.value + rhs.value); } Operator::Subtract => { *result = Number::new(result.value - rhs.value); } Operator::Multiply => { *result = Number::new(result.value * rhs.value); } Operator::Divide => { *result = Number::new(result.value / rhs.value); } } *operator = Operator::None; *rhs = Number::new(0.0); }; view! {
} } fn buttons<'a>( cx: Scope<'a>, operator: &'a Signal, result: &'a Signal, rhs: &'a Signal, ) -> impl View { view! {
{ bar0(cx, operator, result, rhs) } { bar1(cx, operator, result, rhs) } { bar2(cx, operator, result, rhs) } { bar3(cx, operator, result, rhs) } { bar4(cx, operator, result, rhs) }
} } fn ui(cx: Scope) -> impl View { let operator = cx.signal(Operator::None); let result = cx.signal(Number::new(0.0)); let rhs = cx.signal(Number::new(0.0)); view! { { result_bar(cx, operator, result, rhs) } { buttons(cx, operator, result, rhs) } } } fn main() { App::new(ui) .title("Calculator (examples/calculator.rs)") .style(include_stylesheet!("style/calculator.css")) .reziseable(false) .transparent() .size(300.0, 400.0) .run(); }