tessera_ui_basic_components/
text.rs1use derive_builder::Builder;
7use tessera_ui::{Color, ComputedData, DimensionValue, Dp, Px, accesskit::Role, tessera};
8
9use crate::pipelines::{TextCommand, TextConstraint, TextData};
10
11#[derive(Debug, Default, Builder, Clone)]
13#[builder(pattern = "owned")]
14pub struct TextArgs {
15 #[builder(setter(into))]
17 pub text: String,
18
19 #[builder(default = "Color::BLACK")]
21 pub color: Color,
22
23 #[builder(default = "Dp(25.0)")]
25 pub size: Dp,
26
27 #[builder(default, setter(strip_option))]
29 pub line_height: Option<Dp>,
30
31 #[builder(default, setter(strip_option, into))]
33 pub accessibility_label: Option<String>,
34
35 #[builder(default, setter(strip_option, into))]
37 pub accessibility_description: Option<String>,
38}
39
40impl From<String> for TextArgs {
41 fn from(val: String) -> Self {
42 TextArgsBuilder::default().text(val).build().unwrap()
43 }
44}
45
46impl From<&str> for TextArgs {
47 fn from(val: &str) -> Self {
48 TextArgsBuilder::default()
49 .text(val.to_string())
50 .build()
51 .unwrap()
52 }
53}
54
55#[tessera]
87pub fn text(args: impl Into<TextArgs>) {
88 let text_args: TextArgs = args.into();
89 let accessibility_label = text_args.accessibility_label.clone();
90 let accessibility_description = text_args.accessibility_description.clone();
91 let text_for_accessibility = text_args.text.clone();
92
93 input_handler(Box::new(move |input| {
94 let mut builder = input.accessibility().role(Role::Label);
95
96 if let Some(label) = accessibility_label.as_ref() {
97 builder = builder.label(label.clone());
98 } else if !text_for_accessibility.is_empty() {
99 builder = builder.label(text_for_accessibility.clone());
100 }
101
102 if let Some(description) = accessibility_description.as_ref() {
103 builder = builder.description(description.clone());
104 }
105
106 builder.commit();
107 }));
108 measure(Box::new(move |input| {
109 let max_width: Option<Px> = match input.parent_constraint.width {
110 DimensionValue::Fixed(w) => Some(w),
111 DimensionValue::Wrap { max, .. } => max, DimensionValue::Fill { max, .. } => max, };
114
115 let max_height: Option<Px> = match input.parent_constraint.height {
116 DimensionValue::Fixed(h) => Some(h),
117 DimensionValue::Wrap { max, .. } => max, DimensionValue::Fill { max, .. } => max, };
120
121 let line_height = text_args.line_height.unwrap_or(Dp(text_args.size.0 * 1.2));
122
123 let text_data = TextData::new(
124 text_args.text.clone(),
125 text_args.color,
126 text_args.size.to_pixels_f32(),
127 line_height.to_pixels_f32(),
128 TextConstraint {
129 max_width: max_width.map(|px| px.to_f32()),
130 max_height: max_height.map(|px| px.to_f32()),
131 },
132 );
133
134 let size = text_data.size;
135 let drawable = TextCommand { data: text_data };
136
137 input.metadata_mut().push_draw_command(drawable);
139
140 Ok(ComputedData {
141 width: size[0].into(),
142 height: size[1].into(),
143 })
144 }));
145}