use cacao::text::{Font, Label, TextAlign}; use cacao::layout::{LayoutConstraint, Layout}; use cacao::button::{Button, BezelStyle}; use cacao::color::Color; use cacao::appkit::FocusRingType; use cacao::view::{View, ViewDelegate}; use crate::button_row::ButtonRow; use crate::calculator::{dispatch, Msg}; pub const BUTTON_WIDTH: f64 = 57.; pub const BUTTON_HEIGHT: f64 = 47.; pub fn button(text: &str, msg: Msg) -> Button { let mut button = Button::new(text); button.set_bordered(false); button.set_bezel_style(BezelStyle::SmallSquare); button.set_focus_ring_type(FocusRingType::None); button.set_action(move || dispatch(msg.clone())); button.set_key_equivalent(&*text.to_lowercase()); let font = Font::system(22.); button.set_font(&font); button.set_text_color(Color::SystemWhite); button } pub struct CalculatorView { pub results_wrapper: View, pub label: Label, pub row0: ButtonRow, pub row1: ButtonRow, pub row2: ButtonRow, pub row3: ButtonRow, pub dot: Button, pub zero: Button, pub equals: Button } impl CalculatorView { pub fn new() -> Self { let results_wrapper = View::new(); let label = Label::new(); let font = Font::system(40.); label.set_font(&font); label.set_text("0"); label.set_text_color(Color::rgb(255, 255, 255)); label.set_text_alignment(TextAlign::Right); Self { results_wrapper, label, row0: ButtonRow::new([ Msg::Clear, Msg::Invert, Msg::Mod, Msg::Divide ], Color::rgb(69, 69, 69), Color::rgb(255, 148, 10)), row1: ButtonRow::new([ Msg::Push(7), Msg::Push(8), Msg::Push(9), Msg::Multiply ], Color::rgb(100, 100, 100), Color::rgb(255, 148, 10)), row2: ButtonRow::new([ Msg::Push(4), Msg::Push(5), Msg::Push(6), Msg::Subtract ], Color::rgb(100, 100, 100), Color::rgb(255, 148, 10)), row3: ButtonRow::new([ Msg::Push(1), Msg::Push(2), Msg::Push(3), Msg::Add ], Color::rgb(100, 100, 100), Color::rgb(255, 148, 10)), zero: button("0", Msg::Push(0)), dot: button(".", Msg::Decimal), equals: button("=", Msg::Equals) } } pub fn render_update(&self, message: String) { self.label.set_text(&message); } } impl ViewDelegate for CalculatorView { const NAME: &'static str = "CalculatorView"; fn did_load(&mut self, view: View) { view.set_background_color(Color::rgb(49, 49, 49)); self.zero.set_background_color(Color::rgb(100, 100, 100)); self.dot.set_background_color(Color::rgb(100, 100, 100)); self.equals.set_background_color(Color::rgb(255, 148, 10)); self.zero.set_key_equivalent("0"); view.add_subview(&self.row0.view); view.add_subview(&self.row1.view); view.add_subview(&self.row2.view); view.add_subview(&self.row3.view); for button in &[&self.zero, &self.dot, &self.equals] { view.add_subview(button); } self.results_wrapper.add_subview(&self.label); view.add_subview(&self.results_wrapper); LayoutConstraint::activate(&[ self.results_wrapper.top.constraint_equal_to(&view.top), self.results_wrapper.leading.constraint_equal_to(&view.leading), self.results_wrapper.trailing.constraint_equal_to(&view.trailing), self.results_wrapper.height.constraint_equal_to_constant(80.), self.label.leading.constraint_equal_to(&self.results_wrapper.leading).offset(22.), self.label.trailing.constraint_equal_to(&self.results_wrapper.trailing).offset(-16.), self.label.bottom.constraint_equal_to(&self.results_wrapper.bottom).offset(-4.), // Buttons laid out from top-left self.row0.view.top.constraint_equal_to(&self.results_wrapper.bottom).offset(1.), self.row0.view.leading.constraint_equal_to(&view.leading), self.row0.view.trailing.constraint_equal_to(&view.trailing), self.row1.view.top.constraint_equal_to(&self.row0.view.bottom).offset(1.), self.row1.view.leading.constraint_equal_to(&view.leading), self.row1.view.trailing.constraint_equal_to(&view.trailing), self.row2.view.top.constraint_equal_to(&self.row1.view.bottom).offset(1.), self.row2.view.leading.constraint_equal_to(&view.leading), self.row2.view.trailing.constraint_equal_to(&view.trailing), self.row3.view.top.constraint_equal_to(&self.row2.view.bottom).offset(1.), self.row3.view.leading.constraint_equal_to(&view.leading), self.row3.view.trailing.constraint_equal_to(&view.trailing), self.zero.top.constraint_equal_to(&self.row3.view.bottom).offset(1.), self.zero.leading.constraint_equal_to(&view.leading), self.zero.bottom.constraint_equal_to(&view.bottom), self.dot.top.constraint_equal_to(&self.row3.view.bottom).offset(1.), self.dot.leading.constraint_equal_to(&self.zero.trailing).offset(1.), self.dot.bottom.constraint_equal_to(&view.bottom), self.dot.width.constraint_equal_to_constant(BUTTON_WIDTH), self.dot.height.constraint_equal_to_constant(BUTTON_HEIGHT), self.equals.top.constraint_equal_to(&self.row3.view.bottom).offset(1.), self.equals.leading.constraint_equal_to(&self.dot.trailing).offset(1.), self.equals.trailing.constraint_equal_to(&view.trailing), self.equals.bottom.constraint_equal_to(&view.bottom), self.equals.width.constraint_equal_to_constant(BUTTON_WIDTH), self.equals.height.constraint_equal_to_constant(BUTTON_HEIGHT) ]) } }