1use crate::types::color::Color;
2use cosmic_text::{Align, Attrs, Buffer, FontSystem, Metrics};
3
4#[derive(Debug, Clone, PartialEq)]
7pub struct TextOptions {
8 pub x: f32,
10 pub y: f32,
11
12 pub font_size: f32,
13 pub color: Color,
14 pub font_family: Option<String>,
15 pub font_weight: FontWeight,
16 pub font_style: FontStyle,
17
18 pub highlight_color: Option<Color>,
19
20 pub max_width: Option<f32>,
21 pub max_height: Option<f32>,
22 pub line_height: f32,
23 pub wrap: TextWrap,
24 pub align: Option<HorizontalAlignment>,
26 pub valign: Option<VerticalAlignment>,
28 pub min_width: Option<f32>,
29 pub clip_rect: Option<[f32; 4]>,
30 pub scale_factor: f32,
31 pub ellipsis: bool,
32}
33
34
35
36use std::cell::RefCell;
37thread_local! {
38 static LAST_APPLIED_OPTIONS: RefCell<TextOptions> = RefCell::new(TextOptions::default());
39}
40
41pub fn get_last_applied() -> TextOptions {
42 LAST_APPLIED_OPTIONS.with(|opts| opts.borrow().clone())
43}
44
45impl Default for TextOptions {
46 fn default() -> Self {
47 Self {
48 x: 0.0,
49 y: 0.0,
50 font_size: 16.0,
51 color: Color::WHITE,
52 font_family: None,
53 font_weight: FontWeight::Regular,
54 font_style: FontStyle::Normal,
55 highlight_color: None,
56 max_width: None,
57 max_height: None,
58 line_height: 1.2,
59 wrap: TextWrap::None,
60 align: None,
61 valign: None,
62 min_width: None,
63 clip_rect: None,
64 scale_factor: 1.0,
65 ellipsis: true,
66 }
67 }
68}
69
70
71impl TextOptions {
72 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn min_width(mut self, w: f32) -> Self {
77 self.min_width = Some(w);
78 self
79 }
80
81 pub fn at(mut self, x: f32, y: f32) -> Self {
82 self.x = x;
83 self.y = y;
84 self
85 }
86
87 pub fn font_size(mut self, size: f32) -> Self {
88 self.font_size = size;
89 self
90 }
91
92 pub fn color(mut self, color: Color) -> Self {
93 self.color = color;
94 self
95 }
96
97 pub fn font_family(mut self, family: impl Into<String>) -> Self {
98 self.font_family = Some(family.into());
99 self
100 }
101
102 pub fn font_weight(mut self, weight: FontWeight) -> Self {
103 self.font_weight = weight;
104 self
105 }
106
107 pub fn font_style(mut self, style: FontStyle) -> Self {
108 self.font_style = style;
109 self
110 }
111
112 pub fn highlight(mut self, color: Color) -> Self {
113 self.highlight_color = Some(color);
114 self
115 }
116
117
118 pub fn max_width(mut self, width: f32) -> Self {
119 self.max_width = Some(width);
120 self
121 }
122
123 pub fn max_height(mut self, height: f32) -> Self {
125 self.max_height = Some(height);
126 self
127 }
128
129 pub fn line_height(mut self, height: f32) -> Self {
130 self.line_height = height;
131 self
132 }
133
134 pub fn wrap(mut self, strategy: TextWrap) -> Self {
135 self.wrap = strategy;
136 self
137 }
138
139 pub fn align(mut self, alignment: HorizontalAlignment) -> Self {
140 self.align = Some(alignment);
141 self
142 }
143
144 pub fn valign(mut self, alignment: VerticalAlignment) -> Self {
145 self.valign = Some(alignment);
146 self
147 }
148
149 pub fn clip_rect(mut self, x: f32, y: f32, w: f32, h: f32) -> Self {
150 self.clip_rect = Some([x, y, w, h]);
151 self
152 }
153
154 pub fn scale_factor(mut self, sf: f32) -> Self {
155 self.scale_factor = sf;
156 self
157 }
158
159 pub fn ellipsis(mut self, enabled: bool) -> Self {
160 self.ellipsis = enabled;
161 self
162 }
163
164
165 pub fn apply(&self, font_system: &mut FontSystem, buffer: &mut Buffer) {
166
167 LAST_APPLIED_OPTIONS.with(|opts| {
169 *opts.borrow_mut() = self.clone();
170 });
171
172 buffer.set_metrics(
174 font_system,
175 Metrics::new(self.font_size, self.font_size * self.line_height),
176 );
177
178 let wrap = match self.wrap {
180 TextWrap::Word => cosmic_text::Wrap::Word,
181 TextWrap::Character => cosmic_text::Wrap::Glyph,
182 TextWrap::None => cosmic_text::Wrap::None,
183 };
184
185
186 buffer.set_size(font_system, self.max_width, None);
187 buffer.set_wrap(font_system, wrap);
188
189 buffer.shape_until_scroll(font_system, false);
191
192 if let Some(alignment) = self.align {
193 let align: Align = alignment.into();
194 for line in buffer.lines.iter_mut() {
195 line.set_align(Some(align));
196 }
197 buffer.shape_until_scroll(font_system, false);
199 }
200 }
201
202 pub fn as_attrs(&self) -> Attrs<'_> {
203 use crate::types::color::ColorExt;
204 let mut attrs = Attrs::new()
205 .color(self.color.to_cosmic())
206 .weight(self.font_weight.into())
207 .style(self.font_style.into());
208
209 if let Some(ref family) = self.font_family {
210 let family_enum = match family.to_lowercase().as_str() {
211 "sans-serif" => cosmic_text::Family::SansSerif,
212 "serif" => cosmic_text::Family::Serif,
213 "monospace" => cosmic_text::Family::Monospace,
214 _ => cosmic_text::Family::Name(family),
215 };
216 attrs = attrs.family(family_enum);
217 }
218
219 attrs
220 }
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
225pub enum FontWeight {
226 Thin,
227 ExtraLight,
228 Light,
229 #[default]
230 Regular,
231 Medium,
232 SemiBold,
233 Bold,
234 ExtraBold,
235 Black,
236 Custom(u16),
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
241pub enum FontStyle {
242 #[default]
243 Normal,
244 Italic,
245 Oblique,
246}
247
248impl From<FontStyle> for cosmic_text::Style {
249 fn from(style: FontStyle) -> Self {
250 match style {
251 FontStyle::Normal => cosmic_text::Style::Normal,
252 FontStyle::Italic => cosmic_text::Style::Italic,
253 FontStyle::Oblique => cosmic_text::Style::Oblique,
254 }
255 }
256}
257
258impl From<FontWeight> for cosmic_text::Weight {
259 fn from(weight: FontWeight) -> Self {
260 match weight {
261 FontWeight::Thin => cosmic_text::Weight::THIN,
262 FontWeight::ExtraLight => cosmic_text::Weight::EXTRA_LIGHT,
263 FontWeight::Light => cosmic_text::Weight::LIGHT,
264 FontWeight::Regular => cosmic_text::Weight::NORMAL,
265 FontWeight::Medium => cosmic_text::Weight::MEDIUM,
266 FontWeight::SemiBold => cosmic_text::Weight::SEMIBOLD,
267 FontWeight::Bold => cosmic_text::Weight::BOLD,
268 FontWeight::ExtraBold => cosmic_text::Weight::EXTRA_BOLD,
269 FontWeight::Black => cosmic_text::Weight::BLACK,
270 FontWeight::Custom(w) => cosmic_text::Weight(w),
271 }
272 }
273}
274
275impl From<u16> for FontWeight {
276 fn from(w: u16) -> Self {
277 FontWeight::Custom(w)
278 }
279}
280
281impl From<f32> for FontWeight {
282 fn from(w: f32) -> Self {
283 FontWeight::Custom(w as u16)
284 }
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
289pub enum TextWrap {
290 #[default]
291 Word,
292 Character,
293 None,
294}
295
296impl From<bool> for TextWrap {
297 fn from(b: bool) -> Self {
298 if b { TextWrap::Word } else { TextWrap::None }
299 }
300}
301
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
304pub enum HorizontalAlignment {
305 #[default]
306 Left,
307 Center,
308 Right,
309 Justified,
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
314pub enum VerticalAlignment {
315 #[default]
316 Top,
317 Center,
318 Bottom,
319}
320
321pub use zenthra_core::EdgeInsets as Padding;
323
324impl From<HorizontalAlignment> for Align {
325 fn from(alignment: HorizontalAlignment) -> Self {
326 match alignment {
327 HorizontalAlignment::Left => Align::Left,
328 HorizontalAlignment::Center => Align::Center,
329 HorizontalAlignment::Right => Align::Right,
330 HorizontalAlignment::Justified => Align::Justified,
331 }
332 }
333}