Skip to main content

omp_tui/components/
latex.rs

1use omp_core::Str;
2
3use super::text::{append, paint_rich, truncate_rich};
4use crate::{
5	component::{Component, MemoKey, PaintCtx, Slot, next_slot},
6	frame::Rect,
7	props::{Prop, PropValue, Props},
8	rich::{Measure, RichText},
9};
10
11/// Typeset mathematical text backing the `<latex>` markup tag.
12pub struct Latex {
13	props:        Props,
14	slot:         Slot,
15	text:         Str,
16	rich:         RichText,
17	version:      u64,
18	cached_width: u16,
19	cached:       Option<MemoKey>,
20}
21
22impl Latex {
23	/// Creates an empty LaTeX block.
24	pub fn new() -> Self {
25		Self {
26			props:        Props::new(),
27			slot:         next_slot(),
28			text:         Str::default(),
29			rich:         RichText::default(),
30			version:      1,
31			cached_width: 0,
32			cached:       None,
33		}
34	}
35
36	/// Sets one LaTeX property.
37	pub fn with(mut self, prop: Prop, value: impl Into<PropValue>) -> Self {
38		self.props.set(prop, value);
39		self.version = self.version.wrapping_add(1);
40		self
41	}
42
43	/// Sets one LaTeX property from a string.
44	pub fn with_str(self, prop: Prop, value: &str) -> Self {
45		self.with(prop, value)
46	}
47
48	/// Appends LaTeX source text.
49	pub fn text(mut self, text: impl Into<Str>) -> Self {
50		append(&mut self.text, text.into());
51		self.version = self.version.wrapping_add(1);
52		self
53	}
54
55	fn render(&mut self, ctx: &crate::UiContext, width: u16) {
56		let width = width.max(1);
57		let key = MemoKey::new(self.version, ctx);
58		if self.cached_width == width && self.cached == Some(key) {
59			return;
60		}
61		let style = self.props.style(&ctx.theme);
62		self.rich.clear();
63		if !crate::latex::latex_block(&self.text, style, &mut self.rich) {
64			crate::latex::latex_inline(&self.text, style, &mut self.rich);
65		}
66		truncate_rich(&mut self.rich, width, style, self.props.truncate());
67		self.cached_width = width;
68		self.cached = Some(key);
69	}
70}
71
72impl Default for Latex {
73	fn default() -> Self {
74		Self::new()
75	}
76}
77
78impl Component for Latex {
79	fn props(&self) -> &Props {
80		&self.props
81	}
82
83	fn props_mut(&mut self) -> &mut Props {
84		&mut self.props
85	}
86
87	fn slot(&self) -> Slot {
88		self.slot
89	}
90
91	fn measure(&mut self, ctx: &crate::UiContext) -> (u16, u16) {
92		let style = self.props.style(&ctx.theme);
93		let mut measure = Measure::default();
94		if !crate::latex::latex_block(&self.text, style, &mut measure) {
95			crate::latex::latex_inline(&self.text, style, &mut measure);
96		}
97		let width = measure.widest.max(1);
98		(width, width)
99	}
100
101	fn height(&mut self, ctx: &crate::UiContext, width: u16) -> u16 {
102		self.render(ctx, width);
103		RichText::rows(&self.rich)
104	}
105
106	fn paint(&mut self, pc: &mut PaintCtx<'_>, rect: Rect) {
107		self.render(pc.ctx, rect.width);
108		paint_rich(pc, rect, &self.rich, self.props.align());
109	}
110
111	fn set_text(&mut self, _ctx: &crate::UiContext, text: Str) -> bool {
112		if self.text == text {
113			return false;
114		}
115		self.text = text;
116		self.version = self.version.wrapping_add(1);
117		true
118	}
119}