tessera_ui_basic_components/
text.rs1use derive_builder::Builder;
7use tessera_ui::{Color, ComputedData, DimensionValue, Dp, Px, accesskit::Role, tessera};
8
9use crate::pipelines::text::{
10 command::{TextCommand, TextConstraint},
11 pipeline::TextData,
12};
13
14pub use crate::pipelines::text::pipeline::{read_font_system, write_font_system};
15
16#[derive(Debug, Builder, Clone)]
18#[builder(pattern = "owned")]
19pub struct TextArgs {
20 #[builder(setter(into))]
22 pub text: String,
23
24 #[builder(default = "crate::material_color::global_material_scheme().on_surface")]
26 pub color: Color,
27
28 #[builder(default = "Dp(25.0)")]
30 pub size: Dp,
31
32 #[builder(default, setter(strip_option))]
34 pub line_height: Option<Dp>,
35
36 #[builder(default, setter(strip_option, into))]
38 pub accessibility_label: Option<String>,
39
40 #[builder(default, setter(strip_option, into))]
42 pub accessibility_description: Option<String>,
43}
44
45impl Default for TextArgs {
46 fn default() -> Self {
47 TextArgsBuilder::default()
48 .text("")
49 .build()
50 .expect("builder construction failed")
51 }
52}
53
54impl From<String> for TextArgs {
55 fn from(val: String) -> Self {
56 TextArgsBuilder::default()
57 .text(val)
58 .build()
59 .expect("builder construction failed")
60 }
61}
62
63impl From<&str> for TextArgs {
64 fn from(val: &str) -> Self {
65 TextArgsBuilder::default()
66 .text(val.to_string())
67 .build()
68 .expect("builder construction failed")
69 }
70}
71
72#[tessera]
104pub fn text(args: impl Into<TextArgs>) {
105 let text_args: TextArgs = args.into();
106 let accessibility_label = text_args.accessibility_label.clone();
107 let accessibility_description = text_args.accessibility_description.clone();
108 let text_for_accessibility = text_args.text.clone();
109
110 input_handler(Box::new(move |input| {
111 let mut builder = input.accessibility().role(Role::Label);
112
113 if let Some(label) = accessibility_label.as_ref() {
114 builder = builder.label(label.clone());
115 } else if !text_for_accessibility.is_empty() {
116 builder = builder.label(text_for_accessibility.clone());
117 }
118
119 if let Some(description) = accessibility_description.as_ref() {
120 builder = builder.description(description.clone());
121 }
122
123 builder.commit();
124 }));
125 measure(Box::new(move |input| {
126 let max_width: Option<Px> = match input.parent_constraint.width {
127 DimensionValue::Fixed(w) => Some(w),
128 DimensionValue::Wrap { max, .. } => max, DimensionValue::Fill { max, .. } => max, };
131
132 let max_height: Option<Px> = match input.parent_constraint.height {
133 DimensionValue::Fixed(h) => Some(h),
134 DimensionValue::Wrap { max, .. } => max, DimensionValue::Fill { max, .. } => max, };
137
138 let line_height = text_args.line_height.unwrap_or(Dp(text_args.size.0 * 1.2));
139
140 let text_data = TextData::new(
141 text_args.text.clone(),
142 text_args.color,
143 text_args.size.to_pixels_f32(),
144 line_height.to_pixels_f32(),
145 TextConstraint {
146 max_width: max_width.map(|px| px.to_f32()),
147 max_height: max_height.map(|px| px.to_f32()),
148 },
149 );
150
151 let size = text_data.size;
152 let drawable = TextCommand { data: text_data };
153
154 input.metadata_mut().push_draw_command(drawable);
156
157 Ok(ComputedData {
158 width: size[0].into(),
159 height: size[1].into(),
160 })
161 }));
162}