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: TextAttrs, pub buf: TextBuffer, pub hint: Option, } impl TextView { fn is_empty(&self) -> bool { self.buf.is_empty() } pub fn wrap_width(&self) -> Option { self.buf.wrap_width() } } impl TextView { pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option) -> Self { Self { attrs, buf, 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) } /// The text shaped for the width it is drawn in. The buffer keeps its /// answers under the attrs too, so changing those asks a new question /// rather than invalidating anything. fn render(&mut self, painter: &mut Painter) -> &RenderedText { let width = self.attrs.wrap.then(|| painter.px_len(Axis::X)); // The shaper measures in floats, which is where a glyph advance comes // from; what it answers goes back on the grid. painter.render_text(&mut self.buf, &self.attrs, width.map(Px::to_f32)); if width.is_some() { painter.holds(Axis::X, self.buf.width_holds()); } self.buf.rendered().expect("render_text placed the glyphs") } pub fn tex(&self) -> Option<&RenderedText> { self.buf.rendered() } /// 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); // The step at or above what the shaper measured, so a parent that // hands back the length this reports hands back a box the longest // line fits in. Rounded to the nearest step it is half the time a // hair under that line, and the break made in it is not the break a // cold layout makes there. let size = Size::from_px(PxVec2::ceil_from_f32(tex.size)); painter.glyphs(tex, region); (region, size) } 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 } } 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 } }