fission_core/ui/widgets/
text.rs1use crate::lowering::{LoweringContext, NodeBuilder};
2use crate::ui::traits::Lower;
3use fission_ir::{
4 op::{Color as IrColor, LayoutOp, Op, PaintOp},
5 NodeId, Semantics,
6};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
24pub enum TextContent {
25 Literal(String),
27 Key(String),
29}
30
31impl From<&str> for TextContent {
32 fn from(value: &str) -> Self {
33 TextContent::Literal(value.to_string())
34 }
35}
36
37impl From<String> for TextContent {
38 fn from(value: String) -> Self {
39 TextContent::Literal(value)
40 }
41}
42
43impl Default for TextContent {
44 fn default() -> Self {
45 TextContent::Literal(String::new())
46 }
47}
48
49#[derive(Debug, Default, Clone, Serialize, Deserialize)]
64pub struct Text {
65 pub id: Option<NodeId>,
67 pub content: TextContent,
69 pub semantics: Option<Semantics>,
71 pub width: Option<f32>,
73 pub height: Option<f32>,
75 pub min_width: Option<f32>,
77 pub max_width: Option<f32>,
79 pub min_height: Option<f32>,
81 pub max_height: Option<f32>,
83 pub font_size: Option<f32>,
85 pub color: Option<IrColor>,
87 pub underline: bool,
89 pub flex_grow: f32,
91 pub flex_shrink: f32,
93}
94
95impl Text {
96 pub fn new(content: impl Into<TextContent>) -> Self {
97 Self {
98 content: content.into(),
99 ..Default::default()
100 }
101 }
102
103 pub fn width(mut self, w: f32) -> Self {
104 self.width = Some(w);
105 self
106 }
107
108 pub fn height(mut self, h: f32) -> Self {
109 self.height = Some(h);
110 self
111 }
112
113 pub fn min_width(mut self, w: f32) -> Self {
114 self.min_width = Some(w);
115 self
116 }
117
118 pub fn max_width(mut self, w: f32) -> Self {
119 self.max_width = Some(w);
120 self
121 }
122
123 pub fn min_height(mut self, h: f32) -> Self {
124 self.min_height = Some(h);
125 self
126 }
127
128 pub fn max_height(mut self, h: f32) -> Self {
129 self.max_height = Some(h);
130 self
131 }
132
133 pub fn flex_grow(mut self, grow: f32) -> Self {
134 self.flex_grow = grow;
135 self
136 }
137
138 pub fn flex_shrink(mut self, shrink: f32) -> Self {
139 self.flex_shrink = shrink;
140 self
141 }
142
143 pub fn color(mut self, color: IrColor) -> Self {
144 self.color = Some(color);
145 self
146 }
147
148 pub fn underline(mut self, u: bool) -> Self {
149 self.underline = u;
150 self
151 }
152
153 pub fn size(mut self, size: f32) -> Self {
154 self.font_size = Some(size);
155 self
156 }
157
158
159 pub fn weight(self, _w: impl std::fmt::Debug) -> Self {
161 self
162 }
163
164 pub fn into_node(self) -> crate::ui::Node {
165 crate::ui::Node::Text(self)
166 }
167}
168
169impl Lower for Text {
170 fn lower(&self, cx: &mut LoweringContext) -> NodeId {
171 let layout_node_id = self.id.unwrap_or_else(|| cx.next_node_id());
172
173 let resolved_text = match &self.content {
174 TextContent::Literal(s) => s.clone(),
175 TextContent::Key(key) => cx
176 .env
177 .i18n
178 .get(&cx.env.locale, key)
179 .map(|s| s.to_string())
180 .unwrap_or_else(|| format!("MISSING:{}", key)),
181 };
182
183 let paint_node_id = NodeBuilder::new(
184 cx.next_node_id(),
185 Op::Paint(PaintOp::DrawText {
186 text: resolved_text,
187 size: self.font_size.unwrap_or(cx.env.theme.tokens.typography.body_medium_size),
188 color: self.color.unwrap_or(cx.env.theme.tokens.colors.text_primary),
189 underline: self.underline,
190 caret_index: None,
191 }),
192 )
193 .build(cx);
194
195 let mut layout_builder = NodeBuilder::new(
196 layout_node_id,
197 Op::Layout(LayoutOp::Box {
198 width: self.width,
199 height: self.height,
200 min_width: self.min_width,
201 max_width: self.max_width,
202 min_height: self.min_height,
203 max_height: self.max_height,
204 padding: [0.0; 4],
205 flex_grow: self.flex_grow,
206 flex_shrink: self.flex_shrink,
207 aspect_ratio: None,
208 }),
209 );
210 layout_builder.add_child(paint_node_id);
211 let layout_node_id = layout_builder.build(cx);
212
213 if let Some(mut s) = self.semantics.clone() {
214 s.multiline = false;
215 let mut semantics_builder =
216 NodeBuilder::new(cx.next_node_id(), Op::Semantics(s));
217 semantics_builder.add_child(layout_node_id);
218 return semantics_builder.build(cx);
219 }
220
221 layout_node_id
222 }
223}