Skip to main content

egui/
widget_text.rs

1use core::fmt::Formatter;
2use epaint::text::{IntoTag, TextFormat, VariationCoords};
3use std::{borrow::Cow, sync::Arc};
4
5use crate::{
6    Align, Color32, FontFamily, FontSelection, Galley, Style, TextStyle, TextWrapMode, Ui, Visuals,
7    text::{LayoutJob, TextWrapping},
8};
9
10/// Text and optional style choices for it.
11///
12/// The style choices (font, color) are applied to the entire text.
13/// For more detailed control, use [`crate::text::LayoutJob`] instead.
14///
15/// A [`RichText`] can be used in most widgets and helper functions, e.g. [`Ui::label`] and [`Ui::button`].
16///
17/// ### Example
18/// ```
19/// use egui::{RichText, Color32};
20///
21/// RichText::new("Plain");
22/// RichText::new("colored").color(Color32::RED);
23/// RichText::new("Large and underlined").size(20.0).underline();
24/// ```
25#[derive(Clone, Debug, PartialEq)]
26pub struct RichText {
27    text: String,
28    size: Option<f32>,
29    extra_letter_spacing: f32,
30    line_height: Option<f32>,
31    family: Option<FontFamily>,
32    text_style: Option<TextStyle>,
33    background_color: Color32,
34    expand_bg: f32,
35    text_color: Option<Color32>,
36    coords: VariationCoords,
37    code: bool,
38    strong: bool,
39    weak: bool,
40    strikethrough: bool,
41    underline: bool,
42    italics: bool,
43    raised: bool,
44}
45
46impl Default for RichText {
47    fn default() -> Self {
48        Self {
49            text: Default::default(),
50            size: Default::default(),
51            extra_letter_spacing: Default::default(),
52            line_height: Default::default(),
53            family: Default::default(),
54            text_style: Default::default(),
55            background_color: Default::default(),
56            expand_bg: 1.0,
57            text_color: Default::default(),
58            coords: Default::default(),
59            code: Default::default(),
60            strong: Default::default(),
61            weak: Default::default(),
62            strikethrough: Default::default(),
63            underline: Default::default(),
64            italics: Default::default(),
65            raised: Default::default(),
66        }
67    }
68}
69
70impl From<&str> for RichText {
71    #[inline]
72    fn from(text: &str) -> Self {
73        Self::new(text)
74    }
75}
76
77impl From<&String> for RichText {
78    #[inline]
79    fn from(text: &String) -> Self {
80        Self::new(text)
81    }
82}
83
84impl From<&mut String> for RichText {
85    #[inline]
86    fn from(text: &mut String) -> Self {
87        Self::new(text.clone())
88    }
89}
90
91impl From<String> for RichText {
92    #[inline]
93    fn from(text: String) -> Self {
94        Self::new(text)
95    }
96}
97
98impl From<&Box<str>> for RichText {
99    #[inline]
100    fn from(text: &Box<str>) -> Self {
101        Self::new(text.clone())
102    }
103}
104
105impl From<&mut Box<str>> for RichText {
106    #[inline]
107    fn from(text: &mut Box<str>) -> Self {
108        Self::new(text.clone())
109    }
110}
111
112impl From<Box<str>> for RichText {
113    #[inline]
114    fn from(text: Box<str>) -> Self {
115        Self::new(text)
116    }
117}
118
119impl From<Cow<'_, str>> for RichText {
120    #[inline]
121    fn from(text: Cow<'_, str>) -> Self {
122        Self::new(text)
123    }
124}
125
126impl RichText {
127    #[inline]
128    pub fn new(text: impl Into<String>) -> Self {
129        Self {
130            text: text.into(),
131            ..Default::default()
132        }
133    }
134
135    #[inline]
136    pub fn is_empty(&self) -> bool {
137        self.text.is_empty()
138    }
139
140    #[inline]
141    pub fn text(&self) -> &str {
142        &self.text
143    }
144
145    /// Select the font size (in points).
146    /// This overrides the value from [`Self::text_style`].
147    #[inline]
148    pub fn size(mut self, size: f32) -> Self {
149        self.size = Some(size);
150        self
151    }
152
153    /// Extra spacing between letters, in points.
154    ///
155    /// Default: 0.0.
156    ///
157    /// For even text it is recommended you round this to an even number of _pixels_,
158    /// e.g. using [`emath::GuiRounding`].
159    #[inline]
160    pub fn extra_letter_spacing(mut self, extra_letter_spacing: f32) -> Self {
161        self.extra_letter_spacing = extra_letter_spacing;
162        self
163    }
164
165    /// Explicit line height of the text in points.
166    ///
167    /// This is the distance between the bottom row of two subsequent lines of text.
168    ///
169    /// If `None` (the default), the line height is determined by the font.
170    ///
171    /// For even text it is recommended you round this to an even number of _pixels_,
172    /// e.g. using [`emath::GuiRounding`].
173    #[inline]
174    pub fn line_height(mut self, line_height: Option<f32>) -> Self {
175        self.line_height = line_height;
176        self
177    }
178
179    /// Select the font family.
180    ///
181    /// This overrides the value from [`Self::text_style`].
182    ///
183    /// Only the families available in [`crate::FontDefinitions::families`] may be used.
184    #[inline]
185    pub fn family(mut self, family: FontFamily) -> Self {
186        self.family = Some(family);
187        self
188    }
189
190    /// Select the font and size.
191    /// This overrides the value from [`Self::text_style`].
192    #[inline]
193    pub fn font(mut self, font_id: crate::FontId) -> Self {
194        let crate::FontId { size, family } = font_id;
195        self.size = Some(size);
196        self.family = Some(family);
197        self
198    }
199
200    /// Add a variation coordinate.
201    #[inline]
202    pub fn variation(mut self, tag: impl IntoTag, coord: f32) -> Self {
203        self.coords.push(tag, coord);
204        self
205    }
206
207    /// Override the variation coordinates completely.
208    #[inline]
209    pub fn variations<T: IntoTag>(
210        mut self,
211        variations: impl IntoIterator<Item = (T, f32)>,
212    ) -> Self {
213        self.coords = VariationCoords::new(variations);
214        self
215    }
216
217    /// Override the [`TextStyle`].
218    #[inline]
219    pub fn text_style(mut self, text_style: TextStyle) -> Self {
220        self.text_style = Some(text_style);
221        self
222    }
223
224    /// Set the [`TextStyle`] unless it has already been set
225    #[inline]
226    pub fn fallback_text_style(mut self, text_style: TextStyle) -> Self {
227        self.text_style.get_or_insert(text_style);
228        self
229    }
230
231    /// Use [`TextStyle::Heading`].
232    #[inline]
233    pub fn heading(self) -> Self {
234        self.text_style(TextStyle::Heading)
235    }
236
237    /// Use [`TextStyle::Monospace`].
238    #[inline]
239    pub fn monospace(self) -> Self {
240        self.text_style(TextStyle::Monospace)
241    }
242
243    /// Monospace label with different background color.
244    #[inline]
245    pub fn code(mut self) -> Self {
246        self.code = true;
247        self.text_style(TextStyle::Monospace)
248    }
249
250    /// Extra strong text (stronger color).
251    #[inline]
252    pub fn strong(mut self) -> Self {
253        self.strong = true;
254        self
255    }
256
257    /// Extra weak text (fainter color).
258    #[inline]
259    pub fn weak(mut self) -> Self {
260        self.weak = true;
261        self
262    }
263
264    /// Draw a line under the text.
265    ///
266    /// If you want to control the line color, use [`LayoutJob`] instead.
267    #[inline]
268    pub fn underline(mut self) -> Self {
269        self.underline = true;
270        self
271    }
272
273    /// Draw a line through the text, crossing it out.
274    ///
275    /// If you want to control the strikethrough line color, use [`LayoutJob`] instead.
276    #[inline]
277    pub fn strikethrough(mut self) -> Self {
278        self.strikethrough = true;
279        self
280    }
281
282    /// Tilt the characters to the right.
283    #[inline]
284    pub fn italics(mut self) -> Self {
285        self.italics = true;
286        self
287    }
288
289    /// Smaller text.
290    #[inline]
291    pub fn small(self) -> Self {
292        self.text_style(TextStyle::Small)
293    }
294
295    /// For e.g. exponents.
296    #[inline]
297    pub fn small_raised(self) -> Self {
298        self.text_style(TextStyle::Small).raised()
299    }
300
301    /// Align text to top. Only applicable together with [`Self::small()`].
302    #[inline]
303    pub fn raised(mut self) -> Self {
304        self.raised = true;
305        self
306    }
307
308    /// Fill-color behind the text.
309    #[inline]
310    pub fn background_color(mut self, background_color: impl Into<Color32>) -> Self {
311        self.background_color = background_color.into();
312        self
313    }
314
315    /// Override text color.
316    ///
317    /// If not set, [`Color32::PLACEHOLDER`] will be used,
318    /// which will be replaced with a color chosen by the widget that paints the text.
319    #[inline]
320    pub fn color(mut self, color: impl Into<Color32>) -> Self {
321        self.text_color = Some(color.into());
322        self
323    }
324
325    /// Read the font height of the selected text style.
326    ///
327    /// Returns a value rounded to [`emath::GUI_ROUNDING`].
328    pub fn font_height(&self, fonts: &mut epaint::FontsView<'_>, style: &Style) -> f32 {
329        let mut font_id = self.text_style.as_ref().map_or_else(
330            || FontSelection::Default.resolve(style),
331            |text_style| text_style.resolve(style),
332        );
333
334        if let Some(size) = self.size {
335            font_id.size = size;
336        }
337        if let Some(family) = &self.family {
338            font_id.family = family.clone();
339        }
340        fonts.row_height(&font_id)
341    }
342
343    /// Append to an existing [`LayoutJob`]
344    ///
345    /// Note that the color of the [`RichText`] must be set, or may default to an undesirable color.
346    ///
347    /// ### Example
348    /// ```
349    /// use egui::{Style, RichText, text::LayoutJob, Color32, FontSelection, Align};
350    ///
351    /// let style = Style::default();
352    /// let mut layout_job = LayoutJob::default();
353    /// RichText::new("Normal")
354    ///     .color(style.visuals.text_color())
355    ///     .append_to(
356    ///         &mut layout_job,
357    ///         &style,
358    ///         FontSelection::Default,
359    ///         Align::Center,
360    ///     );
361    /// RichText::new("Large and underlined")
362    ///     .color(style.visuals.text_color())
363    ///     .size(20.0)
364    ///     .underline()
365    ///     .append_to(
366    ///         &mut layout_job,
367    ///         &style,
368    ///         FontSelection::Default,
369    ///         Align::Center,
370    ///     );
371    /// ```
372    pub fn append_to(
373        self,
374        layout_job: &mut LayoutJob,
375        style: &Style,
376        fallback_font: FontSelection,
377        default_valign: Align,
378    ) {
379        let (text, format) = self.into_text_and_format(style, fallback_font, default_valign);
380
381        layout_job.append(&text, 0.0, format);
382    }
383
384    fn into_layout_job(
385        self,
386        style: &Style,
387        fallback_font: FontSelection,
388        default_valign: Align,
389    ) -> LayoutJob {
390        let (text, text_format) = self.into_text_and_format(style, fallback_font, default_valign);
391        LayoutJob::single_section(text, text_format)
392    }
393
394    fn into_text_and_format(
395        self,
396        style: &Style,
397        fallback_font: FontSelection,
398        default_valign: Align,
399    ) -> (String, crate::text::TextFormat) {
400        let text_color = self.get_text_color(&style.visuals);
401
402        let Self {
403            text,
404            size,
405            extra_letter_spacing,
406            line_height,
407            family,
408            text_style,
409            background_color,
410            expand_bg,
411            text_color: _, // already used by `get_text_color`
412            coords,
413            code,
414            strong: _, // already used by `get_text_color`
415            weak: _,   // already used by `get_text_color`
416            strikethrough,
417            underline,
418            italics,
419            raised,
420        } = self;
421
422        let line_color = text_color.unwrap_or_else(|| style.visuals.text_color());
423        let text_color = text_color.unwrap_or(crate::Color32::PLACEHOLDER);
424
425        let font_id = {
426            let mut font_id = style.override_font_id.clone().unwrap_or_else(|| {
427                (text_style.as_ref().or(style.override_text_style.as_ref()))
428                    .map(|text_style| text_style.resolve(style))
429                    .unwrap_or_else(|| fallback_font.resolve(style))
430            });
431            if let Some(size) = size {
432                font_id.size = size;
433            }
434            if let Some(family) = family {
435                font_id.family = family;
436            }
437            font_id
438        };
439
440        let background_color = if code {
441            style.visuals.code_bg_color
442        } else {
443            background_color
444        };
445
446        let underline = if underline {
447            crate::Stroke::new(1.0, line_color)
448        } else {
449            crate::Stroke::NONE
450        };
451        let strikethrough = if strikethrough {
452            crate::Stroke::new(1.0, line_color)
453        } else {
454            crate::Stroke::NONE
455        };
456
457        let valign = if raised {
458            crate::Align::TOP
459        } else {
460            default_valign
461        };
462
463        (
464            text,
465            crate::text::TextFormat {
466                font_id,
467                extra_letter_spacing,
468                line_height,
469                color: text_color,
470                background: background_color,
471                coords,
472                italics,
473                underline,
474                strikethrough,
475                valign,
476                expand_bg,
477            },
478        )
479    }
480
481    fn get_text_color(&self, visuals: &Visuals) -> Option<Color32> {
482        if let Some(text_color) = self.text_color {
483            Some(text_color)
484        } else if self.strong {
485            Some(visuals.strong_text_color())
486        } else if self.weak {
487            Some(visuals.weak_text_color())
488        } else {
489            visuals.override_text_color
490        }
491    }
492}
493
494// ----------------------------------------------------------------------------
495
496/// This is how you specify text for a widget.
497///
498/// A lot of widgets use `impl Into<WidgetText>` as an argument,
499/// allowing you to pass in [`String`], [`RichText`], [`LayoutJob`], and more.
500///
501/// Often a [`WidgetText`] is just a simple [`String`],
502/// but it can be a [`RichText`] (text with color, style, etc),
503/// a [`LayoutJob`] (for when you want full control of how the text looks)
504/// or text that has already been laid out in a [`Galley`].
505///
506/// You can color the text however you want, or use [`Color32::PLACEHOLDER`]
507/// which will be replaced with a color chosen by the widget that paints the text.
508#[derive(Clone)]
509pub enum WidgetText {
510    /// Plain unstyled text.
511    ///
512    /// We have this as a special case, as it is the common-case,
513    /// and it uses less memory than [`Self::RichText`].
514    Text(String),
515
516    /// Text and optional style choices for it.
517    ///
518    /// Prefer [`Self::Text`] if there is no styling, as it will be faster.
519    RichText(Arc<RichText>),
520
521    /// Use this [`LayoutJob`] when laying out the text.
522    ///
523    /// Only [`LayoutJob::text`] and [`LayoutJob::sections`] are guaranteed to be respected.
524    ///
525    /// [`TextWrapping::max_width`](epaint::text::TextWrapping::max_width), [`LayoutJob::halign`], [`LayoutJob::justify`]
526    /// and [`LayoutJob::first_row_min_height`] will likely be determined by the [`crate::Layout`]
527    /// of the [`Ui`] the widget is placed in.
528    /// If you want all parts of the [`LayoutJob`] respected, then convert it to a
529    /// [`Galley`] and use [`Self::Galley`] instead.
530    ///
531    /// You can color the text however you want, or use [`Color32::PLACEHOLDER`]
532    /// which will be replaced with a color chosen by the widget that paints the text.
533    LayoutJob(Arc<LayoutJob>),
534
535    /// Use exactly this galley when painting the text.
536    ///
537    /// You can color the text however you want, or use [`Color32::PLACEHOLDER`]
538    /// which will be replaced with a color chosen by the widget that paints the text.
539    Galley(Arc<Galley>),
540}
541
542impl core::fmt::Debug for WidgetText {
543    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
544        let text = self.text();
545        match self {
546            Self::Text(_) => write!(f, "Text({text:?})"),
547            Self::RichText(_) => write!(f, "RichText({text:?})"),
548            Self::LayoutJob(_) => write!(f, "LayoutJob({text:?})"),
549            Self::Galley(_) => write!(f, "Galley({text:?})"),
550        }
551    }
552}
553
554impl Default for WidgetText {
555    fn default() -> Self {
556        Self::Text(String::new())
557    }
558}
559
560impl WidgetText {
561    /// Override the font size.
562    ///
563    /// For [`Self::Galley`], this does nothing because it has already been laid out.
564    #[must_use]
565    pub fn size(self, size: f32) -> Self {
566        match self {
567            Self::Text(text) => RichText::new(text).size(size).into(),
568            Self::RichText(text) => Self::RichText(Arc::new(Arc::unwrap_or_clone(text).size(size))),
569            Self::LayoutJob(job) => {
570                let mut job = Arc::unwrap_or_clone(job);
571                for section in &mut job.sections {
572                    section.format.font_id.size = size;
573                }
574                Self::LayoutJob(Arc::new(job))
575            }
576            Self::Galley(galley) => Self::Galley(galley),
577        }
578    }
579
580    #[inline]
581    pub fn is_empty(&self) -> bool {
582        match self {
583            Self::Text(text) => text.is_empty(),
584            Self::RichText(text) => text.is_empty(),
585            Self::LayoutJob(job) => job.is_empty(),
586            Self::Galley(galley) => galley.is_empty(),
587        }
588    }
589
590    #[inline]
591    pub fn text(&self) -> &str {
592        match self {
593            Self::Text(text) => text,
594            Self::RichText(text) => text.text(),
595            Self::LayoutJob(job) => &job.text,
596            Self::Galley(galley) => galley.text(),
597        }
598    }
599
600    /// Map the contents based on the provided closure.
601    ///
602    /// - [`Self::Text`] => convert to [`RichText`] and call f
603    /// - [`Self::RichText`] => call f
604    /// - else do nothing
605    #[must_use]
606    fn map_rich_text<F>(self, f: F) -> Self
607    where
608        F: FnOnce(RichText) -> RichText,
609    {
610        match self {
611            Self::Text(text) => Self::RichText(Arc::new(f(RichText::new(text)))),
612            Self::RichText(text) => Self::RichText(Arc::new(f(Arc::unwrap_or_clone(text)))),
613            other => other,
614        }
615    }
616
617    /// Override the [`TextStyle`] if, and only if, this is a [`RichText`].
618    ///
619    /// Prefer using [`RichText`] directly!
620    #[inline]
621    pub fn text_style(self, text_style: TextStyle) -> Self {
622        self.map_rich_text(|text| text.text_style(text_style))
623    }
624
625    /// Set the [`TextStyle`] unless it has already been set
626    ///
627    /// Prefer using [`RichText`] directly!
628    #[inline]
629    pub fn fallback_text_style(self, text_style: TextStyle) -> Self {
630        self.map_rich_text(|text| text.fallback_text_style(text_style))
631    }
632
633    /// Override text color if, and only if, this is a [`RichText`].
634    ///
635    /// Prefer using [`RichText`] directly!
636    #[inline]
637    pub fn color(self, color: impl Into<Color32>) -> Self {
638        self.map_rich_text(|text| text.color(color))
639    }
640
641    /// Prefer using [`RichText`] directly!
642    #[inline]
643    pub fn heading(self) -> Self {
644        self.map_rich_text(|text| text.heading())
645    }
646
647    /// Prefer using [`RichText`] directly!
648    #[inline]
649    pub fn monospace(self) -> Self {
650        self.map_rich_text(|text| text.monospace())
651    }
652
653    /// Prefer using [`RichText`] directly!
654    #[inline]
655    pub fn code(self) -> Self {
656        self.map_rich_text(|text| text.code())
657    }
658
659    /// Prefer using [`RichText`] directly!
660    #[inline]
661    pub fn strong(self) -> Self {
662        self.map_rich_text(|text| text.strong())
663    }
664
665    /// Prefer using [`RichText`] directly!
666    #[inline]
667    pub fn weak(self) -> Self {
668        self.map_rich_text(|text| text.weak())
669    }
670
671    /// Prefer using [`RichText`] directly!
672    #[inline]
673    pub fn underline(self) -> Self {
674        self.map_rich_text(|text| text.underline())
675    }
676
677    /// Prefer using [`RichText`] directly!
678    #[inline]
679    pub fn strikethrough(self) -> Self {
680        self.map_rich_text(|text| text.strikethrough())
681    }
682
683    /// Prefer using [`RichText`] directly!
684    #[inline]
685    pub fn italics(self) -> Self {
686        self.map_rich_text(|text| text.italics())
687    }
688
689    /// Prefer using [`RichText`] directly!
690    #[inline]
691    pub fn small(self) -> Self {
692        self.map_rich_text(|text| text.small())
693    }
694
695    /// Prefer using [`RichText`] directly!
696    #[inline]
697    pub fn small_raised(self) -> Self {
698        self.map_rich_text(|text| text.small_raised())
699    }
700
701    /// Prefer using [`RichText`] directly!
702    #[inline]
703    pub fn raised(self) -> Self {
704        self.map_rich_text(|text| text.raised())
705    }
706
707    /// Prefer using [`RichText`] directly!
708    #[inline]
709    pub fn background_color(self, background_color: impl Into<Color32>) -> Self {
710        self.map_rich_text(|text| text.background_color(background_color))
711    }
712
713    pub fn into_layout_job(
714        self,
715        style: &Style,
716        fallback_font: FontSelection,
717        default_valign: Align,
718    ) -> Arc<LayoutJob> {
719        match self {
720            Self::Text(text) => Arc::new(LayoutJob::simple_format(
721                text,
722                TextFormat {
723                    font_id: FontSelection::Default.resolve(style),
724                    color: crate::Color32::PLACEHOLDER,
725                    valign: default_valign,
726                    ..Default::default()
727                },
728            )),
729            Self::RichText(text) => Arc::new(Arc::unwrap_or_clone(text).into_layout_job(
730                style,
731                fallback_font,
732                default_valign,
733            )),
734            Self::LayoutJob(job) => job,
735            Self::Galley(galley) => Arc::clone(&galley.job),
736        }
737    }
738
739    /// Layout with wrap mode based on the containing [`Ui`].
740    ///
741    /// `wrap_mode`: override for [`Ui::wrap_mode`]
742    pub fn into_galley(
743        self,
744        ui: &Ui,
745        wrap_mode: Option<TextWrapMode>,
746        available_width: f32,
747        fallback_font: impl Into<FontSelection>,
748    ) -> Arc<Galley> {
749        let valign = ui.text_valign();
750        let style = ui.style();
751
752        let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());
753        let text_wrapping = TextWrapping::from_wrap_mode_and_width(wrap_mode, available_width);
754
755        self.into_galley_impl(ui.ctx(), style, text_wrapping, fallback_font.into(), valign)
756    }
757
758    pub fn into_galley_impl(
759        self,
760        ctx: &crate::Context,
761        style: &Style,
762        text_wrapping: TextWrapping,
763        fallback_font: FontSelection,
764        default_valign: Align,
765    ) -> Arc<Galley> {
766        match self {
767            Self::Text(text) => {
768                let color = style
769                    .visuals
770                    .override_text_color
771                    .unwrap_or(crate::Color32::PLACEHOLDER);
772
773                // We want the style overrides to take precedence over the fallback font
774                let font_id = FontSelection::default().resolve_with_fallback(style, fallback_font);
775                let line_height = ctx
776                    .fonts_mut(|f| f.row_height(&font_id) + style.spacing.extra_text_line_spacing);
777
778                let mut layout_job = LayoutJob::simple_format(
779                    text,
780                    TextFormat {
781                        font_id,
782                        color,
783                        valign: default_valign,
784                        line_height: Some(line_height),
785                        ..Default::default()
786                    },
787                );
788                layout_job.wrap = text_wrapping;
789                ctx.fonts_mut(|f| f.layout_job(layout_job))
790            }
791            Self::RichText(text) => {
792                let mut layout_job = Arc::unwrap_or_clone(text).into_layout_job(
793                    style,
794                    fallback_font,
795                    default_valign,
796                );
797                layout_job.wrap = text_wrapping;
798                ctx.fonts_mut(|f| f.layout_job(layout_job))
799            }
800            Self::LayoutJob(job) => {
801                let mut job = Arc::unwrap_or_clone(job);
802                job.wrap = text_wrapping;
803                ctx.fonts_mut(|f| f.layout_job(job))
804            }
805            Self::Galley(galley) => galley,
806        }
807    }
808}
809
810impl From<&str> for WidgetText {
811    #[inline]
812    fn from(text: &str) -> Self {
813        Self::Text(text.to_owned())
814    }
815}
816
817impl From<&String> for WidgetText {
818    #[inline]
819    fn from(text: &String) -> Self {
820        Self::Text(text.clone())
821    }
822}
823
824impl From<String> for WidgetText {
825    #[inline]
826    fn from(text: String) -> Self {
827        Self::Text(text)
828    }
829}
830
831impl From<&Box<str>> for WidgetText {
832    #[inline]
833    fn from(text: &Box<str>) -> Self {
834        Self::Text(text.to_string())
835    }
836}
837
838impl From<Box<str>> for WidgetText {
839    #[inline]
840    fn from(text: Box<str>) -> Self {
841        Self::Text(text.into())
842    }
843}
844
845impl From<Cow<'_, str>> for WidgetText {
846    #[inline]
847    fn from(text: Cow<'_, str>) -> Self {
848        Self::Text(text.into_owned())
849    }
850}
851
852impl From<RichText> for WidgetText {
853    #[inline]
854    fn from(rich_text: RichText) -> Self {
855        Self::RichText(Arc::new(rich_text))
856    }
857}
858
859impl From<Arc<RichText>> for WidgetText {
860    #[inline]
861    fn from(rich_text: Arc<RichText>) -> Self {
862        Self::RichText(rich_text)
863    }
864}
865
866impl From<LayoutJob> for WidgetText {
867    #[inline]
868    fn from(layout_job: LayoutJob) -> Self {
869        Self::LayoutJob(Arc::new(layout_job))
870    }
871}
872
873impl From<Arc<LayoutJob>> for WidgetText {
874    #[inline]
875    fn from(layout_job: Arc<LayoutJob>) -> Self {
876        Self::LayoutJob(layout_job)
877    }
878}
879
880impl From<Arc<Galley>> for WidgetText {
881    #[inline]
882    fn from(galley: Arc<Galley>) -> Self {
883        Self::Galley(galley)
884    }
885}
886
887#[cfg(test)]
888mod tests {
889    use crate::WidgetText;
890
891    #[test]
892    fn ensure_small_widget_text() {
893        assert_eq!(size_of::<WidgetText>(), size_of::<String>());
894    }
895}