mod build; mod edit; pub use build::*; pub use edit::*; use iris_core::util::MutDetect; use crate::prelude::*; use std::ops::{Deref, DerefMut}; pub struct Text { pub content: MutDetect, view: TextView, } pub struct TextView { pub attrs: MutDetect, pub buf: MutDetect, // cache tex: Option, width: Option, pub hint: Option, } impl TextView { fn is_empty(&self) -> bool { self.buf.is_empty() } pub fn wrap_width(&self) -> Option { self.width } } impl TextView { pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option) -> Self { Self { attrs: attrs.into(), buf: buf.into(), tex: None, width: None, hint, } } /// region where the text should be draw /// does not include extra height or width from weird unicode pub fn region(&self) -> UiRegion { self.tex() .map(|t| t.size) .unwrap_or(Vec2::ZERO) .align(self.align) } fn render(&mut self, painter: &mut Painter) -> &RenderedText { let width = if self.attrs.wrap { Some(painter.px_size().x) } else { None }; if width != self.width || self.tex.is_none() || self.attrs.changed || self.buf.changed { self.width = width; self.tex = Some(painter.render_text(&mut self.buf, &self.attrs, width)); self.attrs.changed = false; self.buf.changed = false; } self.tex.as_ref().unwrap() } pub fn tex(&self) -> Option<&RenderedText> { self.tex.as_ref() } /// Draws the text, and says where the glyphs went and what they use. pub fn draw(&mut self, painter: &mut Painter) -> (UiRegion, Size) { let align = self.align; if self.is_empty() && self.hint.is_some() { let region = self.render(painter).size.align(align); let size = match &self.hint { Some(hint) => painter.widget(hint).size(), None => Size::ZERO, }; return (region, size); } let tex = self.render(painter); let region = tex.size.align(align); let size = Size::abs(tex.size); let within = region.within(&painter.region()); painter.glyphs(tex, within); (region, size) } /// Wrapping reads the width it is offered, so a wider box reshapes it and /// a taller one does not. Alignment matters too, and separately: glyphs /// anchored to the start of an axis stay put when that extent changes, /// but centred or end-aligned ones move even though the shaping stands. pub fn on_resize(&self, axis: Axis) -> OnResize { let reshapes = axis == Axis::X && self.attrs.wrap; let anchored = match axis { Axis::X => self.align.x, Axis::Y => self.align.y, } == AxisAlign::Neg; match reshapes || !anchored { true => OnResize::Redraw, false => OnResize::Translate, } } pub fn content(&self) -> String { self.buf.text().to_string() } } impl Text { pub fn new(content: impl Into) -> Self { let content: String = content.into(); Self { view: TextView::new(TextBuffer::new(&content), TextAttrs::default(), None), content: content.into(), } } fn update_buf(&mut self) { if self.content.changed { self.content.changed = false; self.view.buf.set_text(self.content.as_str()); } } } impl Widget for Text { fn draw(&mut self, painter: &mut Painter) -> Size { self.update_buf(); self.view.draw(painter).1 } fn on_resize(&self, axis: Axis) -> OnResize { self.view.on_resize(axis) } } impl Deref for Text { type Target = TextAttrs; fn deref(&self) -> &Self::Target { &self.view } } impl DerefMut for Text { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.view } } impl Deref for TextView { type Target = TextAttrs; fn deref(&self) -> &Self::Target { &self.attrs } } impl DerefMut for TextView { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.attrs } }