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_blank(&self) -> bool { self.buf.is_empty() } /// The width used by the cached layout. 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, ctx: &mut SizeCtx) -> RenderedText { let width = if self.attrs.wrap { Some(ctx.px_size().x) } else { None }; if width == self.width && let Some(tex) = &self.tex && !self.attrs.changed && !self.buf.changed { return tex.clone(); } self.width = width; let tex = ctx.draw_text(&mut self.buf, &self.attrs, width); self.tex = Some(tex.clone()); self.attrs.changed = false; self.buf.changed = false; tex } pub fn tex(&self) -> Option<&RenderedText> { self.tex.as_ref() } pub fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { if self.is_blank() && let Some(hint) = &self.hint { ctx.width(hint) } else { Len::abs(self.render(ctx).size.x) } } pub fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { if self.is_blank() && let Some(hint) = &self.hint { ctx.height(hint) } else { Len::abs(self.render(ctx).size.y) } } pub fn draw(&mut self, painter: &mut Painter) -> UiRegion { let tex = self.render(&mut painter.size_ctx()); let region = tex.size.align(self.align); if self.is_blank() && let Some(hint) = &self.hint { painter.widget(hint); } else { let within = region.within(&painter.region()); painter.glyphs(&tex, within); } region } 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, _ctx: &mut SizeCtx) { 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) { self.update_buf(&mut painter.size_ctx()); self.view.draw(painter); } fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { self.update_buf(ctx); self.view.desired_width(ctx) } fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { self.update_buf(ctx); self.view.desired_height(ctx) } } 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 } }