From 628840d5cd44228e23c816c5122c62b7a1d60604 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Mon, 29 Sep 2025 13:45:48 -0400 Subject: [PATCH] text update for more code reuse + much better caching --- src/core/mod.rs | 2 - src/core/text.rs | 140 ------------------------ src/core/text/build.rs | 117 ++++++++++++++++++++ src/core/{text_edit.rs => text/edit.rs} | 129 +++++----------------- src/core/text/mod.rs | 130 ++++++++++++++++++++++ src/layout/painter.rs | 39 +++---- src/layout/text.rs | 28 +++-- src/layout/ui.rs | 1 + src/testing/mod.rs | 6 +- 9 files changed, 319 insertions(+), 273 deletions(-) delete mode 100644 src/core/text.rs create mode 100644 src/core/text/build.rs rename src/core/{text_edit.rs => text/edit.rs} (64%) create mode 100644 src/core/text/mod.rs diff --git a/src/core/mod.rs b/src/core/mod.rs index d65980a..608d2ab 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -8,7 +8,6 @@ mod sized; mod span; mod stack; mod text; -mod text_edit; mod trait_fns; pub use align::*; @@ -21,5 +20,4 @@ pub use sized::*; pub use span::*; pub use stack::*; pub use text::*; -pub use text_edit::*; pub use trait_fns::*; diff --git a/src/core/text.rs b/src/core/text.rs deleted file mode 100644 index 58728b6..0000000 --- a/src/core/text.rs +++ /dev/null @@ -1,140 +0,0 @@ -use cosmic_text::{Attrs, Family, FontSystem, Metrics, Shaping}; - -use crate::{prelude::*, util::MutDetect}; - -pub struct Text { - pub content: MutDetect, - pub attrs: TextAttrs, - /// inner alignment of text region (within where its drawn) - pub align: Align, - pub(super) buf: TextBuffer, - size: Vec2, -} - -impl Text { - pub fn size(mut self, size: impl UiNum) -> Self { - self.attrs.font_size = size.to_f32(); - self.attrs.line_height = self.attrs.font_size * 1.1; - self - } - pub fn color(mut self, color: UiColor) -> Self { - self.attrs.color = color; - self - } - pub fn family(mut self, family: Family<'static>) -> Self { - self.attrs.family = family; - self - } - pub fn line_height(mut self, height: f32) -> Self { - self.attrs.line_height = height; - self - } - pub fn new(content: impl Into) -> Self { - let attrs = TextAttrs::default(); - Self { - content: content.into().into(), - buf: TextBuffer::new_empty(Metrics::new(attrs.font_size, attrs.line_height)), - attrs, - align: Align::Center, - size: Vec2::ZERO, - } - } - - pub fn region(&self) -> UiRegion { - UiRegion::from_size_align(self.size, self.align) - } - - fn update_buf(&mut self, font_system: &mut FontSystem, width: Option) { - self.attrs.apply(font_system, &mut self.buf, width); - if self.content.changed { - self.content.changed = false; - self.buf.set_text( - font_system, - &self.content, - &Attrs::new().family(self.attrs.family), - Shaping::Advanced, - ); - } - } -} - -impl Widget for Text { - fn draw(&mut self, painter: &mut Painter) { - let width = if self.attrs.wrap { - Some(painter.px_size().x) - } else { - None - }; - let font_system = &mut painter.text_data().font_system; - self.update_buf(font_system, width); - let (handle, offset) = painter.render_text(&mut self.buf, &self.attrs); - let dims = handle.size(); - self.size = offset.size(&handle); - let mut region = self.region(); - region.top_left.abs += offset.top_left; - region.bot_right.abs = region.top_left.abs + dims; - painter.texture_within(&handle, region); - } - - fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 { - self.update_buf(&mut ctx.text.font_system, None); - let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs); - UiVec2::abs(offset.size(&handle)) - } -} - -pub fn text(content: impl Into) -> TextBuilder { - TextBuilder { - content: content.into(), - align: Align::Center, - attrs: TextAttrs::default(), - } -} - -pub struct TextBuilder { - pub content: String, - pub attrs: TextAttrs, - /// inner alignment of text region (within where its drawn) - pub align: Align, -} - -impl TextBuilder { - pub fn size(mut self, size: impl UiNum) -> Self { - self.attrs.font_size = size.to_f32(); - self.attrs.line_height = self.attrs.font_size * 1.1; - self - } - pub fn color(mut self, color: UiColor) -> Self { - self.attrs.color = color; - self - } - pub fn family(mut self, family: Family<'static>) -> Self { - self.attrs.family = family; - self - } - pub fn line_height(mut self, height: f32) -> Self { - self.attrs.line_height = height; - self - } -} - -impl FnOnce<(&mut Ui,)> for TextBuilder { - type Output = Text; - - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { - let mut buf = - TextBuffer::new_empty(Metrics::new(self.attrs.font_size, self.attrs.line_height)); - let font_system = &mut args.0.data.text.font_system; - buf.set_text(font_system, &self.content, &Attrs::new(), Shaping::Advanced); - let mut text = Text { - content: self.content.into(), - buf, - attrs: self.attrs, - align: self.align, - size: Vec2::ZERO, - }; - text.content.changed = false; - self.attrs.apply(font_system, &mut text.buf, None); - text - } -} diff --git a/src/core/text/build.rs b/src/core/text/build.rs new file mode 100644 index 0000000..ce2cc16 --- /dev/null +++ b/src/core/text/build.rs @@ -0,0 +1,117 @@ +use std::marker::{PhantomData, Sized}; + +use crate::prelude::*; +use cosmic_text::{Attrs, Family, Metrics, Shaping}; + +pub trait TextBuilderOutput: Sized { + type Output; + fn run(ui: &mut Ui, builder: TextBuilder) -> Self::Output; +} + +pub struct TextBuilder { + pub content: String, + pub attrs: TextAttrs, + _pd: PhantomData, +} + +impl TextBuilder { + pub fn size(mut self, size: impl UiNum) -> Self { + self.attrs.font_size = size.to_f32(); + self.attrs.line_height = self.attrs.font_size * 1.1; + self + } + pub fn color(mut self, color: UiColor) -> Self { + self.attrs.color = color; + self + } + pub fn family(mut self, family: Family<'static>) -> Self { + self.attrs.family = family; + self + } + pub fn line_height(mut self, height: f32) -> Self { + self.attrs.line_height = height; + self + } + pub fn text_align(mut self, align: Align) -> Self { + self.attrs.align = align; + self + } + pub fn wrap(mut self, wrap: bool) -> Self { + self.attrs.wrap = wrap; + self + } + pub fn editable(self) -> TextBuilder { + TextBuilder { + content: self.content, + attrs: self.attrs, + _pd: PhantomData, + } + } +} + +pub struct TextOutput; +impl TextBuilderOutput for TextOutput { + type Output = Text; + + fn run(ui: &mut Ui, builder: TextBuilder) -> Self::Output { + let mut buf = TextBuffer::new_empty(Metrics::new( + builder.attrs.font_size, + builder.attrs.line_height, + )); + let font_system = &mut ui.data.text.font_system; + buf.set_text( + font_system, + &builder.content, + &Attrs::new(), + Shaping::Advanced, + ); + let mut text = Text { + content: builder.content.into(), + view: TextView::new(buf, builder.attrs), + }; + text.content.changed = false; + builder.attrs.apply(font_system, &mut text.view.buf, None); + text + } +} + +pub struct TextEditOutput; +impl TextBuilderOutput for TextEditOutput { + type Output = TextEdit; + + fn run(ui: &mut Ui, builder: TextBuilder) -> Self::Output { + let buf = TextBuffer::new_empty(Metrics::new( + builder.attrs.font_size, + builder.attrs.line_height, + )); + let mut text = TextEdit { + view: TextView::new(buf, builder.attrs), + cursor: None, + }; + let font_system = &mut ui.data.text.font_system; + text.buf.set_text( + font_system, + &builder.content, + &Attrs::new(), + Shaping::Advanced, + ); + builder.attrs.apply(font_system, &mut text.buf, None); + text + } +} + +impl FnOnce<(&mut Ui,)> for TextBuilder { + type Output = O::Output; + + extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { + O::run(args.0, self) + } +} + +pub fn text(content: impl Into) -> TextBuilder { + TextBuilder { + content: content.into(), + attrs: TextAttrs::default(), + _pd: PhantomData, + } +} diff --git a/src/core/text_edit.rs b/src/core/text/edit.rs similarity index 64% rename from src/core/text_edit.rs rename to src/core/text/edit.rs index 5b65bb0..c477117 100644 --- a/src/core/text_edit.rs +++ b/src/core/text/edit.rs @@ -1,5 +1,7 @@ +use std::ops::{Deref, DerefMut}; + use crate::prelude::*; -use cosmic_text::{Affinity, Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping}; +use cosmic_text::{Affinity, Attrs, Cursor, FontSystem, Motion, Shaping}; use unicode_segmentation::UnicodeSegmentation; use winit::{ event::KeyEvent, @@ -7,38 +9,24 @@ use winit::{ }; pub struct TextEdit { - pub attrs: TextAttrs, - /// inner alignment of text region (within where its drawn) - pub align: Align, - buf: TextBuffer, - cursor: Option, - size: Vec2, + pub(super) view: TextView, + pub(super) cursor: Option, } impl TextEdit { pub fn region(&self) -> UiRegion { - UiRegion::from_size_align(self.size, self.align) + UiRegion::from_size_align( + self.tex().map(|t| t.size()).unwrap_or(Vec2::ZERO), + self.align, + ) } } impl Widget for TextEdit { fn draw(&mut self, painter: &mut Painter) { - let width = if self.attrs.wrap { - Some(painter.px_size().x) - } else { - None - }; - let font_system = &mut painter.text_data().font_system; - self.attrs.apply(font_system, &mut self.buf, width); - self.buf.shape_until_scroll(font_system, false); - let (handle, tex_offset) = painter.render_text(&mut self.buf, &self.attrs); - let dims = handle.size(); - self.size = tex_offset.size(&handle); - let region = self.region(); - let mut tex_region = region; - tex_region.top_left.abs += tex_offset.top_left; - tex_region.bot_right.abs = tex_region.top_left.abs + dims; - painter.texture_within(&handle, tex_region); + let tex = self.view.draw(&mut painter.size_ctx()); + let region = text_region(&tex, self.align); + painter.texture_within(&tex.handle, region); if let Some(cursor) = &self.cursor && let Some(offset) = cursor_pos(cursor, &self.buf) @@ -50,24 +38,11 @@ impl Widget for TextEdit { .offset(offset) .within(®ion), ); - } else { - // keep number of primitives constant so shifting isn't needed - painter.primitive(RectPrimitive::color(Color::NONE)); } } fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 { - let width = if self.attrs.wrap { - Some(ctx.px_size().x) - } else { - None - }; - self.attrs - .apply(&mut ctx.text.font_system, &mut self.buf, width); - self.buf - .shape_until_scroll(&mut ctx.text.font_system, false); - let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs); - UiVec2::abs(offset.size(&handle)) + UiVec2::abs(self.view.draw(ctx).size()) } } @@ -104,41 +79,6 @@ fn cursor_pos(cursor: &Cursor, buf: &TextBuffer) -> Option<(f32, f32)> { prev } -pub struct TextEditBuilder { - pub content: String, - pub attrs: TextAttrs, - /// inner alignment of text region (within where its drawn) - pub align: Align, -} - -impl TextEditBuilder { - pub fn size(mut self, size: impl UiNum) -> Self { - self.attrs.font_size = size.to_f32(); - self.attrs.line_height = self.attrs.font_size * 1.1; - self - } - pub fn color(mut self, color: UiColor) -> Self { - self.attrs.color = color; - self - } - pub fn family(mut self, family: Family<'static>) -> Self { - self.attrs.family = family; - self - } - pub fn line_height(mut self, height: f32) -> Self { - self.attrs.line_height = height; - self - } - pub fn text_align(mut self, align: Align) -> Self { - self.align = align; - self - } - pub fn wrap(mut self, wrap: bool) -> Self { - self.attrs.wrap = wrap; - self - } -} - pub struct TextEditCtx<'a> { pub text: &'a mut TextEdit, pub font_system: &'a mut FontSystem, @@ -190,7 +130,7 @@ impl<'a> TextEditCtx<'a> { fn insert_inner(&mut self, text: &str) { if let Some(cursor) = &mut self.text.cursor { - let line = &mut self.text.buf.lines[cursor.line]; + let line = &mut self.text.view.buf.lines[cursor.line]; let mut line_text = line.text().to_string(); line_text.insert_str(cursor.index, text); line.set_text(line_text, line.ending(), line.attrs_list().clone()); @@ -202,10 +142,11 @@ impl<'a> TextEditCtx<'a> { pub fn newline(&mut self) { if let Some(cursor) = &mut self.text.cursor { - let line = &mut self.text.buf.lines[cursor.line]; + let lines = &mut self.text.view.buf.lines; + let line = &mut lines[cursor.line]; let new = line.split_off(cursor.index); cursor.line += 1; - self.text.buf.lines.insert(cursor.line, new); + lines.insert(cursor.line, new); cursor.index = 0; } } @@ -221,13 +162,14 @@ impl<'a> TextEditCtx<'a> { pub fn delete(&mut self) { if let Some(cursor) = &mut self.text.cursor { - let line = &mut self.text.buf.lines[cursor.line]; + let lines = &mut self.text.view.buf.lines; + let line = &mut lines[cursor.line]; if cursor.index == line.text().len() { - if cursor.line == self.text.buf.lines.len() - 1 { + if cursor.line == lines.len() - 1 { return; } - let add = self.text.buf.lines.remove(cursor.line + 1).into_text(); - let line = &mut self.text.buf.lines[cursor.line]; + let add = lines.remove(cursor.line + 1).into_text(); + let line = &mut lines[cursor.line]; let mut cur = line.text().to_string(); cur.push_str(&add); line.set_text(cur, line.ending(), line.attrs_list().clone()); @@ -311,29 +253,16 @@ impl TextInputResult { } } -impl FnOnce<(&mut Ui,)> for TextEditBuilder { - type Output = TextEdit; +impl Deref for TextEdit { + type Target = TextView; - extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { - let mut text = TextEdit { - buf: TextBuffer::new_empty(Metrics::new(self.attrs.font_size, self.attrs.line_height)), - attrs: self.attrs, - align: self.align, - cursor: None, - size: Vec2::ZERO, - }; - let font_system = &mut args.0.data.text.font_system; - text.buf - .set_text(font_system, &self.content, &Attrs::new(), Shaping::Advanced); - self.attrs.apply(font_system, &mut text.buf, None); - text + fn deref(&self) -> &Self::Target { + &self.view } } -pub fn text_edit(content: impl Into) -> TextEditBuilder { - TextEditBuilder { - content: content.into(), - attrs: TextAttrs::default(), - align: Align::Center, +impl DerefMut for TextEdit { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.view } } diff --git a/src/core/text/mod.rs b/src/core/text/mod.rs new file mode 100644 index 0000000..e2d6b5e --- /dev/null +++ b/src/core/text/mod.rs @@ -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, + view: TextView, +} + +pub struct TextView { + pub attrs: MutDetect, + pub buf: MutDetect, + // cache + tex: Option, + width: Option, +} + +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) -> 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 + } +} diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 78759c9..192de2b 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -1,6 +1,6 @@ use crate::{ layout::{ - Layers, Modules, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures, + Layers, Modules, TextAttrs, TextBuffer, TextData, TextTexture, TextureHandle, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets, }, render::{Primitive, PrimitiveHandle}, @@ -29,7 +29,7 @@ pub struct PainterCtx<'a> { pub screen_size: Vec2, pub modules: &'a mut Modules, pub px_dependent: &'a mut HashSet, - drawing: HashSet, + draw_started: HashSet, } pub struct WidgetInstance { @@ -80,12 +80,14 @@ impl<'a> PainterCtx<'a> { screen_size: data.output_size, modules: &mut data.modules, px_dependent: &mut data.px_dependent, - drawing: HashSet::default(), + draw_started: HashSet::default(), } } pub fn redraw(&mut self, id: Id) { - self.drawing.clear(); + if self.draw_started.contains(&id) { + return; + } let Some(active) = self.active.get(&id) else { return; }; @@ -99,11 +101,13 @@ impl<'a> PainterCtx<'a> { widgets: self.widgets, region: UiRegion::full(), screen_size: self.screen_size, + px_dependent: self.px_dependent, + id, }; let desired = ctx.size_inner(id, active.region); if size != desired { self.redraw(rid); - if self.drawing.contains(&id) { + if self.draw_started.contains(&id) { return; } } @@ -124,7 +128,7 @@ impl<'a> PainterCtx<'a> { } pub fn draw(&mut self, id: Id) { - self.drawing.clear(); + self.draw_started.clear(); self.layers.clear(); self.draw_inner(0, id, UiRegion::full(), None, None); } @@ -143,7 +147,7 @@ impl<'a> PainterCtx<'a> { // but this has a very weird issue where you can't move widgets unless u remove first // so swapping is impossible rn I think? // there's definitely better solutions like a counter (>1 = panic) but don't care rn - if self.drawing.contains(&id) { + if self.draw_started.contains(&id) { panic!("Cannot draw the same widget twice (1)"); } let mut old_children = old_children.unwrap_or_default(); @@ -165,7 +169,7 @@ impl<'a> PainterCtx<'a> { resize = active.resize; } - self.drawing.insert(id); + self.draw_started.insert(id); let mut painter = Painter { region, @@ -308,11 +312,7 @@ impl<'a, 'c> Painter<'a, 'c> { } /// returns (handle, offset from top left) - pub fn render_text( - &mut self, - buffer: &mut TextBuffer, - attrs: &TextAttrs, - ) -> (TextureHandle, TextOffset) { + pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture { self.ctx.text.draw(buffer, attrs, self.ctx.textures) } @@ -331,6 +331,8 @@ impl<'a, 'c> Painter<'a, 'c> { widgets: self.ctx.widgets, checked: &mut self.sized_children, screen_size: self.ctx.screen_size, + px_dependent: self.ctx.px_dependent, + id: self.id, region: self.region, } } @@ -357,9 +359,11 @@ pub struct SizeCtx<'a> { pub text: &'a mut TextData, pub textures: &'a mut Textures, widgets: &'a Widgets, + px_dependent: &'a mut HashSet, checked: &'a mut HashMap, region: UiRegion, screen_size: Vec2, + id: Id, } impl SizeCtx<'_> { @@ -381,14 +385,11 @@ impl SizeCtx<'_> { pub fn size_within(&mut self, id: &WidgetId, region: UiRegion) -> UiVec2 { self.size_inner(id.id, region.within(&self.region)) } - pub fn px_size(&self) -> Vec2 { + pub fn px_size(&mut self) -> Vec2 { + self.px_dependent.insert(self.id); self.region.in_size(self.screen_size) } - pub fn draw_text( - &mut self, - buffer: &mut TextBuffer, - attrs: &TextAttrs, - ) -> (TextureHandle, TextOffset) { + pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture { self.text.draw(buffer, attrs, self.textures) } } diff --git a/src/layout/text.rs b/src/layout/text.rs index f1c96c3..2c99ea4 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -2,7 +2,7 @@ use cosmic_text::{Attrs, AttrsList, Buffer, Family, FontSystem, Metrics, SwashCa use image::{Rgba, RgbaImage}; use crate::{ - layout::{TextureHandle, Textures, UiColor, Vec2}, + layout::{Align, TextureHandle, Textures, UiColor, Vec2}, util::HashMap, }; @@ -27,6 +27,8 @@ pub struct TextAttrs { pub line_height: f32, pub family: Family<'static>, pub wrap: bool, + /// inner alignment of text region (within where its drawn) + pub align: Align, } impl TextAttrs { @@ -56,6 +58,7 @@ impl Default for TextAttrs { line_height: size * 1.2, family: Family::SansSerif, wrap: false, + align: Align::Center, } } } @@ -66,7 +69,11 @@ impl TextData { buffer: &mut TextBuffer, attrs: &TextAttrs, textures: &mut Textures, - ) -> (TextureHandle, TextOffset) { + ) -> TextTexture { + // TODO: either this or the layout stuff (or both) is super slow, + // should probably do texture packing and things if possible. + // very visible if you add just a couple of wrapping texts and resize window + // should also be timed to figure out exactly what points need to be sped up let mut pixels = HashMap::default(); let mut min_x = 0; let mut min_y = 0; @@ -113,22 +120,23 @@ impl TextData { let y = (y - min_y) as u32; image.put_pixel(x, y, color); } - let offset = TextOffset { + TextTexture { + handle: textures.add(image), top_left: Vec2::new(min_x as f32, min_y as f32), bot_right: Vec2::new(max_width - max_x as f32, height - max_y as f32), - }; - (textures.add(image), offset) + } } } -#[derive(Clone, Copy)] -pub struct TextOffset { +#[derive(Clone)] +pub struct TextTexture { + pub handle: TextureHandle, pub top_left: Vec2, pub bot_right: Vec2, } -impl TextOffset { - pub fn size(&self, handle: &TextureHandle) -> Vec2 { - handle.size() - self.top_left + self.bot_right +impl TextTexture { + pub fn size(&self) -> Vec2 { + self.handle.size() - self.top_left + self.bot_right } } diff --git a/src/layout/ui.rs b/src/layout/ui.rs index e4345fb..25cdeb1 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -14,6 +14,7 @@ use std::{ }; pub struct Ui { + // TODO: make this at least pub(super) pub(crate) data: PainterData, root: Option, updates: Vec, diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 0db449e..8bbbe17 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -130,7 +130,8 @@ impl Client { let texts = Span::empty(Dir::DOWN).add_static(&mut ui); let msg_area = (Rect::new(Color::SKY), texts.scroll()).stack(); - let add_text = text_edit("add") + let add_text = text("add") + .editable() .text_align(Align::Left) .size(30) .id_on(CursorSense::click(), |id, client: &mut Client, ctx| { @@ -139,7 +140,8 @@ impl Client { }) .id_on(Submit, move |id, client: &mut Client, _| { let content = client.ui.text(id).take(); - let text = text_edit(content) + let text = text(content) + .editable() .size(30) .text_align(Align::Left) .wrap(true)