omp_tui/components/
latex.rs1use 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
11pub 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 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 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 pub fn with_str(self, prop: Prop, value: &str) -> Self {
45 self.with(prop, value)
46 }
47
48 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}