use std::rc::Rc; use speedy2d::{ color::Color, dimen::Vec2, font::{FormattedTextBlock, TextLayout, TextOptions}, shape::Rectangle, window::{ModifiersState, MouseButton}, }; use crate::gui::{GuiAction, GuiElem, GuiElemCfg, GuiElemTrait}; /* Some basic structs to use everywhere, except they are all text-related. */ #[derive(Clone)] pub struct Label { config: GuiElemCfg, children: Vec, pub content: Content, pub pos: Vec2, } #[derive(Clone)] pub struct Content { text: String, color: Color, background: Option, formatted: Option>, } impl Content { pub fn new(text: String, color: Color) -> Self { Self { text, color, background: None, formatted: None, } } pub fn get_text(&self) -> &String { &self.text } pub fn get_color(&self) -> &Color { &self.color } /// causes text layout reset pub fn text(&mut self) -> &mut String { self.formatted = None; &mut self.text } pub fn color(&mut self) -> &mut Color { &mut self.color } /// returns true if the text needs to be redrawn, probably because it was changed. pub fn will_redraw(&self) -> bool { self.formatted.is_none() } } impl Label { pub fn new( config: GuiElemCfg, text: String, color: Color, background: Option, pos: Vec2, ) -> Self { Self { config, children: vec![], content: Content { text, color, background, formatted: None, }, pos, } } } impl GuiElemTrait for Label { fn config(&self) -> &GuiElemCfg { &self.config } fn config_mut(&mut self) -> &mut GuiElemCfg { &mut self.config } fn children(&mut self) -> Box + '_> { Box::new(self.children.iter_mut()) } fn any(&self) -> &dyn std::any::Any { self } fn any_mut(&mut self) -> &mut dyn std::any::Any { self } fn clone_gui(&self) -> Box { Box::new(self.clone()) } fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) { if self.config.pixel_pos.size() != info.pos.size() { // resize self.content.formatted = None; } let text = if let Some(text) = &self.content.formatted { text } else { let l = info .font .layout_text(&self.content.text, 1.0, TextOptions::new()); let l = info.font.layout_text( &self.content.text, (info.pos.width() / l.width()).min(info.pos.height() / l.height()), TextOptions::new(), ); self.content.formatted = Some(l); self.content.formatted.as_ref().unwrap() }; let top_left = Vec2::new( info.pos.top_left().x + self.pos.x * (info.pos.width() - text.width()), info.pos.top_left().y + self.pos.y * (info.pos.height() - text.height()), ); if let Some(bg) = self.content.background { g.draw_rectangle( Rectangle::new( top_left, Vec2::new(top_left.x + text.width(), top_left.y + text.height()), ), bg, ); } g.draw_text(top_left, self.content.color, text); } } // TODO! this, but requires keyboard events first /// a single-line text field for users to type text into. #[derive(Clone)] pub struct TextField { config: GuiElemCfg, pub children: Vec, } impl TextField { pub fn new(config: GuiElemCfg, hint: String, color_hint: Color, color_input: Color) -> Self { Self { config: config.w_mouse().w_keyboard_focus(), children: vec![ GuiElem::new(Label::new( GuiElemCfg::default(), String::new(), color_input, None, Vec2::new(0.0, 0.5), )), GuiElem::new(Label::new( GuiElemCfg::default(), hint, color_hint, None, Vec2::new(0.0, 0.5), )), ], } } pub fn label_input(&self) -> &Label { self.children[0].inner.any().downcast_ref().unwrap() } pub fn label_input_mut(&mut self) -> &mut Label { self.children[0].inner.any_mut().downcast_mut().unwrap() } pub fn label_hint(&self) -> &Label { self.children[1].inner.any().downcast_ref().unwrap() } pub fn label_hint_mut(&mut self) -> &mut Label { self.children[1].inner.any_mut().downcast_mut().unwrap() } } impl GuiElemTrait for TextField { fn config(&self) -> &GuiElemCfg { &self.config } fn config_mut(&mut self) -> &mut GuiElemCfg { &mut self.config } fn children(&mut self) -> Box + '_> { Box::new(self.children.iter_mut()) } fn any(&self) -> &dyn std::any::Any { self } fn any_mut(&mut self) -> &mut dyn std::any::Any { self } fn clone_gui(&self) -> Box { Box::new(self.clone()) } fn draw(&mut self, info: &mut crate::gui::DrawInfo, g: &mut speedy2d::Graphics2D) { let (t, c) = if info.has_keyboard_focus { (3.0, Color::WHITE) } else { (1.0, Color::GRAY) }; g.draw_line(info.pos.top_left(), info.pos.top_right(), t, c); g.draw_line(info.pos.bottom_left(), info.pos.bottom_right(), t, c); g.draw_line(info.pos.top_left(), info.pos.bottom_left(), t, c); g.draw_line(info.pos.top_right(), info.pos.bottom_right(), t, c); } fn mouse_pressed(&mut self, button: MouseButton) -> Vec { self.config.request_keyboard_focus = true; vec![GuiAction::ResetKeyboardFocus] } fn char_focus(&mut self, modifiers: ModifiersState, key: char) -> Vec { if !(modifiers.ctrl() || modifiers.alt() || modifiers.logo()) && !key.is_control() { let content = &mut self.children[0].try_as_mut::