cacao/examples/calculator/calculator.rs
Ryan McGrath 5cd59b5636
Begin reworking many internals to push for v0.1.
- Beginning to transition View types to use Rc/RefCell internally,
  which should provide better guarantees about ownership on the Rust side.
  This is also important for certain Objective-C side scenarios where we
  may need to set an ivar after creation, which requires some level of
  mutability. This may also possibly help bring down the unsafe usage,
  which would be cool.

- Rewrote the Color module; this now handles system colors better, and
  provides support for dynamic color creation. Supporting combinations
  of dark/light/contrast is now possible with handler passed in via
  `Color::dynamic`. This still has work to do in terms of some accessor
  methods and such, but it works well for now. The `to_platform...`
  method should be removed before v0.1.

- Added a new feature for enabling fallback color usage on older macOS
  versions that don't support system colors. This may honestly never be
  used, but it cost nothing to implement.

- Fixed a bug in the Autolayout wrapper where dereferencing could cause
  constraints to crash at runtime.

- Support setting text color on labels.

- Support setting text color on buttons, albeit very hacky right now.
  This needs to be extracted and/or cleaned up, but getting it sketched
  out was important for this commit.

- Support setting a key equivalent on buttons.

- Creating a local event monitor is now possible.

- Examples updated; Calculator clone example added.

The only API breaking change in this commit from earlier commits should
be `color::rgb` needing to be `color::Color` followed by a
`Color::rgb(...)` call.
2021-02-12 17:57:06 -08:00

106 lines
3 KiB
Rust

use std::sync::{Arc, RwLock};
use cacao::lazy_static::lazy_static;
use cacao::macos::App;
use cacao::notification_center::Dispatcher;
use crate::CalculatorApp;
#[derive(Clone, Debug)]
pub enum Msg {
Push(i32),
Add,
Subtract,
Multiply,
Divide,
Decimal,
Clear,
Mod,
Invert,
Equals
}
/// Asynchronously calls back through to the top of the application
/// on the main thread.
pub fn dispatch(msg: Msg) {
println!("Dispatching UI message: {:?}", msg);
//App::<CalculatorApp, Msg>::dispatch_main(msg)
CALCULATOR.run(msg)
}
lazy_static! {
pub static ref CALCULATOR: Calculator = Calculator::new();
}
#[derive(Debug)]
pub struct Calculator(Arc<RwLock<Vec<String>>>);
impl Calculator {
pub fn new() -> Self {
Calculator(Arc::new(RwLock::new(Vec::new())))
}
pub fn run(&self, message: Msg) {
let mut expression = self.0.write().unwrap();
match message {
Msg::Push(i) => {
// Realistically you might want to check decimal length here or something.
// We're not bothering for this example.
(*expression).push(i.to_string());
let display = (*expression).join("").split(" ").last().unwrap_or("0").to_string();
App::<CalculatorApp, String>::dispatch_main(display);
},
Msg::Decimal => {
let display = (*expression).join("").split(" ").last().unwrap_or("0").to_string();
if !display.contains(".") {
(*expression).push(".".to_string());
App::<CalculatorApp, String>::dispatch_main(display + ".");
}
},
Msg::Add => {
if let Some(last_entry) = (*expression).last() {
if !last_entry.ends_with(" ") {
(*expression).push(" + ".to_string());
}
}
},
Msg::Subtract => {
(*expression).push(" - ".to_string());
},
Msg::Multiply => {
(*expression).push(" * ".to_string());
},
Msg::Divide => {
(*expression).push(" / ".to_string());
},
Msg::Clear => {
(*expression) = Vec::new();
App::<CalculatorApp, String>::dispatch_main("0".to_string())
},
Msg::Equals => {
let mut expr = (*expression).join("");
if expr.ends_with(" ") {
expr.truncate(expr.len() - 3);
}
println!("Expr: {}", expr);
match eval::eval(&expr) {
Ok(val) => { App::<CalculatorApp, String>::dispatch_main(val.to_string()); },
Err(e) => { eprintln!("Error parsing expression: {:?}", e); }
}
}
_ => {}
}
}
}