text update for more code reuse + much better caching

This commit is contained in:
2025-09-29 13:45:48 -04:00
parent c98a43f94d
commit 628840d5cd
9 changed files with 319 additions and 273 deletions

130
src/core/text/mod.rs Normal file
View File

@@ -0,0 +1,130 @@
mod build;
mod edit;
pub use build::*;
pub use edit::*;
use crate::{prelude::*, util::MutDetect};
use cosmic_text::{Attrs, Metrics, Shaping};
use std::ops::{Deref, DerefMut};
pub struct Text {
pub content: MutDetect<String>,
view: TextView,
}
pub struct TextView {
pub attrs: MutDetect<TextAttrs>,
pub buf: MutDetect<TextBuffer>,
// cache
tex: Option<TextTexture>,
width: Option<f32>,
}
impl TextView {
pub fn new(buf: TextBuffer, attrs: TextAttrs) -> Self {
Self {
attrs: attrs.into(),
buf: buf.into(),
tex: None,
width: None,
}
}
pub fn draw(&mut self, ctx: &mut SizeCtx) -> TextTexture {
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 font_system = &mut ctx.text.font_system;
self.attrs.apply(font_system, &mut self.buf, width);
self.buf.shape_until_scroll(font_system, false);
let tex = ctx.draw_text(&mut self.buf, &self.attrs);
self.tex = Some(tex.clone());
self.attrs.changed = false;
self.buf.changed = false;
tex
}
pub fn tex(&self) -> Option<&TextTexture> {
self.tex.as_ref()
}
}
impl Text {
pub fn new(content: impl Into<String>) -> Self {
let attrs = TextAttrs::default();
let buf = TextBuffer::new_empty(Metrics::new(attrs.font_size, attrs.line_height));
Self {
content: content.into().into(),
view: TextView::new(buf, attrs),
}
}
fn update_buf(&mut self, ctx: &mut SizeCtx) -> TextTexture {
if self.content.changed {
self.content.changed = false;
self.view.buf.set_text(
&mut ctx.text.font_system,
&self.content,
&Attrs::new().family(self.view.attrs.family),
Shaping::Advanced,
);
}
self.view.draw(ctx)
}
}
impl Widget for Text {
fn draw(&mut self, painter: &mut Painter) {
let tex = self.update_buf(&mut painter.size_ctx());
let region = text_region(&tex, self.align);
painter.texture_within(&tex.handle, region);
}
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
UiVec2::abs(self.update_buf(ctx).size())
}
}
pub fn text_region(tex: &TextTexture, align: Align) -> UiRegion {
let tex_dims = tex.handle.size();
let mut region = UiRegion::from_size_align(tex.size(), align);
region.top_left.abs += tex.top_left;
region.bot_right.abs = region.top_left.abs + tex_dims;
region
}
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
}
}