1use crate::alignment;
24use crate::layout;
25use crate::mouse;
26use crate::renderer;
27use crate::text;
28use crate::text::paragraph::{self, Paragraph};
29use crate::widget::tree::{self, Tree};
30use crate::{Color, Element, Layout, Length, Pixels, Rectangle, Size, Theme, Widget};
31
32pub use text::{Alignment, Ellipsis, LineHeight, Shaping, Wrapping};
33
34#[must_use]
57pub struct Text<'a, Theme, Renderer>
58where
59 Theme: Catalog,
60 Renderer: text::Renderer,
61{
62 fragment: text::Fragment<'a>,
63 format: Format<Renderer::Font>,
64 class: Theme::Class<'a>,
65}
66
67impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
68where
69 Theme: Catalog,
70 Renderer: text::Renderer,
71{
72 pub fn new(fragment: impl text::IntoFragment<'a>) -> Self {
74 Text {
75 fragment: fragment.into_fragment(),
76 format: Format::default(),
77 class: Theme::default(),
78 }
79 }
80
81 pub fn size(mut self, size: impl Into<Pixels>) -> Self {
83 self.format.size = Some(size.into());
84 self
85 }
86
87 pub fn line_height(mut self, line_height: impl Into<LineHeight>) -> Self {
89 self.format.line_height = line_height.into();
90 self
91 }
92
93 pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
97 self.format.font = Some(font.into());
98 self
99 }
100
101 pub fn font_maybe(mut self, font: Option<impl Into<Renderer::Font>>) -> Self {
105 self.format.font = font.map(Into::into);
106 self
107 }
108
109 pub fn width(mut self, width: impl Into<Length>) -> Self {
111 self.format.width = width.into();
112 self
113 }
114
115 pub fn height(mut self, height: impl Into<Length>) -> Self {
117 self.format.height = height.into();
118 self
119 }
120
121 pub fn center(self) -> Self {
123 self.align_x(alignment::Horizontal::Center)
124 .align_y(alignment::Vertical::Center)
125 }
126
127 pub fn align_x(mut self, alignment: impl Into<text::Alignment>) -> Self {
129 self.format.align_x = alignment.into();
130 self
131 }
132
133 pub fn align_y(mut self, alignment: impl Into<alignment::Vertical>) -> Self {
135 self.format.align_y = alignment.into();
136 self
137 }
138
139 pub fn shaping(mut self, shaping: Shaping) -> Self {
141 self.format.shaping = shaping;
142 self
143 }
144
145 pub fn wrapping(mut self, wrapping: Wrapping) -> Self {
147 self.format.wrapping = wrapping;
148 self
149 }
150
151 pub fn ellipsis(mut self, ellipsis: Ellipsis) -> Self {
153 self.format.ellipsis = ellipsis;
154 self
155 }
156
157 pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
159 where
160 Theme::Class<'a>: From<StyleFn<'a, Theme>>,
161 {
162 self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
163 self
164 }
165
166 pub fn color(self, color: impl Into<Color>) -> Self
168 where
169 Theme::Class<'a>: From<StyleFn<'a, Theme>>,
170 {
171 self.color_maybe(Some(color))
172 }
173
174 pub fn color_maybe(self, color: Option<impl Into<Color>>) -> Self
176 where
177 Theme::Class<'a>: From<StyleFn<'a, Theme>>,
178 {
179 let color = color.map(Into::into);
180
181 self.style(move |_theme| Style { color })
182 }
183
184 #[cfg(feature = "advanced")]
186 pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
187 self.class = class.into();
188 self
189 }
190}
191
192pub type State<P> = paragraph::Plain<P>;
194
195impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Text<'_, Theme, Renderer>
196where
197 Theme: Catalog,
198 Renderer: text::Renderer,
199{
200 fn tag(&self) -> tree::Tag {
201 tree::Tag::of::<State<Renderer::Paragraph>>()
202 }
203
204 fn state(&self) -> tree::State {
205 tree::State::new(paragraph::Plain::<Renderer::Paragraph>::default())
206 }
207
208 fn size(&self) -> Size<Length> {
209 Size {
210 width: self.format.width,
211 height: self.format.height,
212 }
213 }
214
215 fn layout(
216 &mut self,
217 tree: &mut Tree,
218 renderer: &Renderer,
219 limits: &layout::Limits,
220 ) -> layout::Node {
221 layout(
222 tree.state.downcast_mut::<State<Renderer::Paragraph>>(),
223 renderer,
224 limits,
225 &self.fragment,
226 self.format,
227 )
228 }
229
230 fn draw(
231 &self,
232 tree: &Tree,
233 renderer: &mut Renderer,
234 theme: &Theme,
235 defaults: &renderer::Style,
236 layout: Layout<'_>,
237 _cursor_position: mouse::Cursor,
238 viewport: &Rectangle,
239 ) {
240 let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
241 let style = theme.style(&self.class);
242
243 draw(
244 renderer,
245 defaults,
246 layout.bounds(),
247 state.raw(),
248 style,
249 viewport,
250 );
251 }
252
253 fn operate(
254 &mut self,
255 _tree: &mut Tree,
256 layout: Layout<'_>,
257 _renderer: &Renderer,
258 operation: &mut dyn super::Operation,
259 ) {
260 operation.text(None, layout.bounds(), &self.fragment);
261 }
262}
263
264#[derive(Debug, Clone, Copy)]
269#[allow(missing_docs)]
270pub struct Format<Font> {
271 pub width: Length,
272 pub height: Length,
273 pub size: Option<Pixels>,
274 pub font: Option<Font>,
275 pub line_height: LineHeight,
276 pub align_x: text::Alignment,
277 pub align_y: alignment::Vertical,
278 pub shaping: Shaping,
279 pub wrapping: Wrapping,
280 pub ellipsis: Ellipsis,
281}
282
283impl<Font> Default for Format<Font> {
284 fn default() -> Self {
285 Self {
286 size: None,
287 line_height: LineHeight::default(),
288 font: None,
289 width: Length::Shrink,
290 height: Length::Shrink,
291 align_x: text::Alignment::Default,
292 align_y: alignment::Vertical::Top,
293 shaping: Shaping::default(),
294 wrapping: Wrapping::default(),
295 ellipsis: Ellipsis::default(),
296 }
297 }
298}
299
300pub fn layout<Renderer>(
302 paragraph: &mut paragraph::Plain<Renderer::Paragraph>,
303 renderer: &Renderer,
304 limits: &layout::Limits,
305 content: &str,
306 format: Format<Renderer::Font>,
307) -> layout::Node
308where
309 Renderer: text::Renderer,
310{
311 layout::sized(limits, format.width, format.height, |limits| {
312 let bounds = limits.max();
313
314 let size = format.size.unwrap_or_else(|| renderer.default_size());
315 let font = format.font.unwrap_or_else(|| renderer.default_font());
316
317 let _ = paragraph.update(text::Text {
318 content,
319 bounds,
320 size,
321 line_height: format.line_height,
322 font,
323 align_x: format.align_x,
324 align_y: format.align_y,
325 shaping: format.shaping,
326 wrapping: format.wrapping,
327 ellipsis: format.ellipsis,
328 hint_factor: renderer.scale_factor(),
329 });
330
331 paragraph.min_bounds()
332 })
333}
334
335pub fn draw<Renderer>(
337 renderer: &mut Renderer,
338 style: &renderer::Style,
339 bounds: Rectangle,
340 paragraph: &Renderer::Paragraph,
341 appearance: Style,
342 viewport: &Rectangle,
343) where
344 Renderer: text::Renderer,
345{
346 let anchor = bounds.anchor(
347 paragraph.min_bounds(),
348 paragraph.align_x(),
349 paragraph.align_y(),
350 );
351
352 renderer.fill_paragraph(
353 paragraph,
354 anchor,
355 appearance.color.unwrap_or(style.text_color),
356 *viewport,
357 );
358}
359
360impl<'a, Message, Theme, Renderer> From<Text<'a, Theme, Renderer>>
361 for Element<'a, Message, Theme, Renderer>
362where
363 Theme: Catalog + 'a,
364 Renderer: text::Renderer + 'a,
365{
366 fn from(text: Text<'a, Theme, Renderer>) -> Element<'a, Message, Theme, Renderer> {
367 Element::new(text)
368 }
369}
370
371impl<'a, Theme, Renderer> From<&'a str> for Text<'a, Theme, Renderer>
372where
373 Theme: Catalog + 'a,
374 Renderer: text::Renderer,
375{
376 fn from(content: &'a str) -> Self {
377 Self::new(content)
378 }
379}
380
381impl<'a, Message, Theme, Renderer> From<&'a str> for Element<'a, Message, Theme, Renderer>
382where
383 Theme: Catalog + 'a,
384 Renderer: text::Renderer + 'a,
385{
386 fn from(content: &'a str) -> Self {
387 Text::from(content).into()
388 }
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Default)]
393pub struct Style {
394 pub color: Option<Color>,
398}
399
400pub trait Catalog: Sized {
402 type Class<'a>;
404
405 fn default<'a>() -> Self::Class<'a>;
407
408 fn style(&self, item: &Self::Class<'_>) -> Style;
410}
411
412pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>;
416
417impl Catalog for Theme {
418 type Class<'a> = StyleFn<'a, Self>;
419
420 fn default<'a>() -> Self::Class<'a> {
421 Box::new(|_theme| Style::default())
422 }
423
424 fn style(&self, class: &Self::Class<'_>) -> Style {
425 class(self)
426 }
427}
428
429pub fn default(_theme: &Theme) -> Style {
431 Style { color: None }
432}
433
434pub fn base(theme: &Theme) -> Style {
436 Style {
437 color: Some(theme.palette().text),
438 }
439}
440
441pub fn primary(theme: &Theme) -> Style {
443 Style {
444 color: Some(theme.palette().primary),
445 }
446}
447
448pub fn secondary(theme: &Theme) -> Style {
450 Style {
451 color: Some(theme.extended_palette().secondary.base.color),
452 }
453}
454
455pub fn success(theme: &Theme) -> Style {
457 Style {
458 color: Some(theme.palette().success),
459 }
460}
461
462pub fn warning(theme: &Theme) -> Style {
464 Style {
465 color: Some(theme.palette().warning),
466 }
467}
468
469pub fn danger(theme: &Theme) -> Style {
471 Style {
472 color: Some(theme.palette().danger),
473 }
474}