use glyphon::{ cosmic_text::Align, Attrs, AttrsOwned, Buffer, Color, FamilyOwned, FontSystem, Metrics, Shaping, Style, TextBounds, Weight, }; use nalgebra::Vector2; use rhai::ImmutableString; use std::rc::Rc; use winit::window::Window; use super::{super::api::Rect, OwnedTextArea}; use crate::{ui::api, RenderInput}; #[derive(Debug)] pub struct UiTextBox { pub name: ImmutableString, text: String, justify: Align, rect: Rect, buffer: Rc, color: api::Color, attrs: AttrsOwned, } impl UiTextBox { pub fn new( font: &mut FontSystem, name: ImmutableString, font_size: f32, line_height: f32, rect: Rect, color: api::Color, ) -> Self { let mut buffer = Buffer::new(font, Metrics::new(font_size, line_height)); // Do NOT apply UI scale here, that's only done when we make a TextArea buffer.set_size(font, rect.dim.x, rect.dim.y); Self { name, rect, buffer: Rc::new(buffer), color, justify: Align::Left, attrs: AttrsOwned::new(Attrs::new()), text: String::new(), } } fn reflow(&mut self, font: &mut FontSystem) { let buffer = Rc::get_mut(&mut self.buffer).unwrap(); buffer.set_text(font, &self.text, self.attrs.as_attrs(), Shaping::Advanced); for l in &mut buffer.lines { l.set_align(Some(self.justify)); } buffer.shape_until_scroll(font); } pub fn set_text(&mut self, font: &mut FontSystem, text: &str) { self.text.clear(); self.text.push_str(text); self.reflow(font); } pub fn set_align(&mut self, font: &mut FontSystem, align: Align) { self.justify = align; self.reflow(font); } pub fn set_weight(&mut self, font: &mut FontSystem, weight: Weight) { self.attrs.weight = weight; self.reflow(font); } pub fn set_font(&mut self, font: &mut FontSystem, family: FamilyOwned) { self.attrs.family_owned = family; self.reflow(font); } pub fn set_style(&mut self, font: &mut FontSystem, style: Style) { self.attrs.style = style; self.reflow(font); } } impl<'a, 'b: 'a> UiTextBox { pub fn get_textarea(&'b self, input: &RenderInput, window: &Window) -> OwnedTextArea { self.get_textarea_with_offset(input, window, Vector2::new(0.0, 0.0)) } pub fn get_textarea_with_offset( &'b self, input: &RenderInput, window: &Window, offset: Vector2, ) -> OwnedTextArea { let mut rect = self.rect.to_centered(window, input.ct.config.ui_scale); rect.pos += offset; // Glypon works with physical pixels, so we must do some conversion let fac = window.scale_factor() as f32; let corner_ne = Vector2::new( (rect.pos.x - rect.dim.x / 2.0) * fac + window.inner_size().width as f32 / 2.0, window.inner_size().height as f32 / 2.0 - (rect.pos.y * fac + rect.dim.y / 2.0), ); let corner_sw = corner_ne + rect.dim * fac; let c = self.color.as_array_u8(); OwnedTextArea { buffer: self.buffer.clone(), top: corner_ne.y, left: corner_ne.x, scale: input.ct.config.ui_scale, bounds: TextBounds { top: (corner_ne.y) as i32, bottom: (corner_sw.y) as i32, left: (corner_ne.x) as i32, right: (corner_sw.x) as i32, }, default_color: Color::rgba(c[0], c[1], c[2], c[3]), } } }