use std::ops::Deref; use bevy::{color::palettes::tailwind::*, prelude::*}; use calc::*; use itertools::Itertools; use rust_decimal::prelude::*; fn main() { App::new() .insert_resource(ClearColor(SLATE_950.into())) .add_plugins(DefaultPlugins) .add_systems(Startup, startup) .add_systems(Update, button_system) .observe(calculate) .run(); } fn calculate( trigger: Trigger<PressEvent>, mut display: Local<String>, mut text_query: Query<&mut Text, With<Output>>, ) { match trigger.event().0.deref() { "C" => { *display = "".to_string(); } "=" => { let Ok(result) = Context::<f64>::default() .evaluate(&display) else { *display = "error".to_string(); return; }; if let Some(result) = Decimal::from_f64( (result * 100.).round() / 100., ) { *display = result.normalize().to_string(); } else { *display = result.to_string(); } } x => { display.push_str(x); } } for mut text in &mut text_query { text.sections[0].value = display.chars().tail(11).collect::<String>(); } } #[derive(Component)] struct Output; fn startup(mut commands: Commands) { commands.spawn(Camera2dBundle::default()); let container = commands .spawn(NodeBundle { style: Style { display: Display::Grid, grid_template_columns: RepeatedGridTrack::auto(4), margin: UiRect::all(Val::Auto), ..default() }, ..default() }) .id(); let items = vec![ "C", "", "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+", ]; for item in items { let button = commands .spawn(ButtonBundle { style: Style { grid_column: GridPlacement::span( if item == "" { 3 } else { 1 }, ), border: UiRect::all(Val::Px(1.)), padding: UiRect::all(Val::Px(20.)), margin: UiRect::all(Val::Px(5.)), ..default() }, border_radius: BorderRadius::all( if item == "" { Val::Px(0.) } else { Val::Px(5.) }, ), border_color: BorderColor(if item == "" { SKY_950.into() } else { SLATE_400.into() }), ..default() }) .with_children(|builder| { let mut b = builder.spawn(TextBundle { text: Text::from_section( item, TextStyle::default(), ), ..default() }); if item == "" { b.insert(Output); } }) .id(); commands.entity(container).add_child(button); } } #[derive(Event)] struct PressEvent(String); const NORMAL_BUTTON: Srgba = SLATE_500; const HOVERED_BUTTON: Srgba = SLATE_400; const PRESSED_BUTTON: Srgba = GREEN_400; fn button_system( mut commands: Commands, mut interaction_query: Query< ( &Interaction, &mut BackgroundColor, &Children, ), (Changed<Interaction>, With<Button>), >, text_query: Query<&Text>, ) { for (interaction, mut color, children) in &mut interaction_query { let text = text_query.get(children[0]).unwrap(); match *interaction { Interaction::Pressed => { *color = PRESSED_BUTTON.into(); commands.trigger(PressEvent( text.sections[0].value.to_string(), )); } Interaction::Hovered => { *color = HOVERED_BUTTON.into(); } Interaction::None => { *color = NORMAL_BUTTON.into(); } } } }