iced_core/text/
paragraph.rs

1//! Draw paragraphs.
2use crate::alignment;
3use crate::text::{Alignment, Difference, Hit, Span, Text};
4use crate::{Point, Rectangle, Size};
5
6/// A text paragraph.
7pub trait Paragraph: Sized + Default {
8    /// The font of this [`Paragraph`].
9    type Font: Copy + PartialEq;
10
11    /// Creates a new [`Paragraph`] laid out with the given [`Text`].
12    fn with_text(text: Text<&str, Self::Font>) -> Self;
13
14    /// Creates a new [`Paragraph`] laid out with the given [`Text`].
15    fn with_spans<Link>(
16        text: Text<&[Span<'_, Link, Self::Font>], Self::Font>,
17    ) -> Self;
18
19    /// Lays out the [`Paragraph`] with some new boundaries.
20    fn resize(&mut self, new_bounds: Size);
21
22    /// Compares the [`Paragraph`] with some desired [`Text`] and returns the
23    /// [`Difference`].
24    fn compare(&self, text: Text<(), Self::Font>) -> Difference;
25
26    /// Returns the horizontal alignment of the [`Paragraph`].
27    fn align_x(&self) -> Alignment;
28
29    /// Returns the vertical alignment of the [`Paragraph`].
30    fn align_y(&self) -> alignment::Vertical;
31
32    /// Returns the minimum boundaries that can fit the contents of the
33    /// [`Paragraph`].
34    fn min_bounds(&self) -> Size;
35
36    /// Tests whether the provided point is within the boundaries of the
37    /// [`Paragraph`], returning information about the nearest character.
38    fn hit_test(&self, point: Point) -> Option<Hit>;
39
40    /// Tests whether the provided point is within the boundaries of a
41    /// [`Span`] in the [`Paragraph`], returning the index of the [`Span`]
42    /// that was hit.
43    fn hit_span(&self, point: Point) -> Option<usize>;
44
45    /// Returns all bounds for the provided [`Span`] index of the [`Paragraph`].
46    /// A [`Span`] can have multiple bounds for each line it's on.
47    fn span_bounds(&self, index: usize) -> Vec<Rectangle>;
48
49    /// Returns the distance to the given grapheme index in the [`Paragraph`].
50    fn grapheme_position(&self, line: usize, index: usize) -> Option<Point>;
51
52    /// Returns the minimum width that can fit the contents of the [`Paragraph`].
53    fn min_width(&self) -> f32 {
54        self.min_bounds().width
55    }
56
57    /// Returns the minimum height that can fit the contents of the [`Paragraph`].
58    fn min_height(&self) -> f32 {
59        self.min_bounds().height
60    }
61}
62
63/// A [`Paragraph`] of plain text.
64#[derive(Debug, Clone, Default)]
65pub struct Plain<P: Paragraph> {
66    raw: P,
67    content: String,
68}
69
70impl<P: Paragraph> Plain<P> {
71    /// Creates a new [`Plain`] paragraph.
72    pub fn new(text: Text<&str, P::Font>) -> Self {
73        let content = text.content.to_owned();
74
75        Self {
76            raw: P::with_text(text),
77            content,
78        }
79    }
80
81    /// Updates the plain [`Paragraph`] to match the given [`Text`], if needed.
82    pub fn update(&mut self, text: Text<&str, P::Font>) {
83        if self.content != text.content {
84            text.content.clone_into(&mut self.content);
85            self.raw = P::with_text(text);
86            return;
87        }
88
89        match self.raw.compare(Text {
90            content: (),
91            bounds: text.bounds,
92            size: text.size,
93            line_height: text.line_height,
94            font: text.font,
95            align_x: text.align_x,
96            align_y: text.align_y,
97            shaping: text.shaping,
98            wrapping: text.wrapping,
99        }) {
100            Difference::None => {}
101            Difference::Bounds => {
102                self.raw.resize(text.bounds);
103            }
104            Difference::Shape => {
105                self.raw = P::with_text(text);
106            }
107        }
108    }
109
110    /// Returns the horizontal alignment of the [`Paragraph`].
111    pub fn align_x(&self) -> Alignment {
112        self.raw.align_x()
113    }
114
115    /// Returns the vertical alignment of the [`Paragraph`].
116    pub fn align_y(&self) -> alignment::Vertical {
117        self.raw.align_y()
118    }
119
120    /// Returns the minimum boundaries that can fit the contents of the
121    /// [`Paragraph`].
122    pub fn min_bounds(&self) -> Size {
123        self.raw.min_bounds()
124    }
125
126    /// Returns the minimum width that can fit the contents of the
127    /// [`Paragraph`].
128    pub fn min_width(&self) -> f32 {
129        self.raw.min_width()
130    }
131
132    /// Returns the minimum height that can fit the contents of the
133    /// [`Paragraph`].
134    pub fn min_height(&self) -> f32 {
135        self.raw.min_height()
136    }
137
138    /// Returns the cached [`Paragraph`].
139    pub fn raw(&self) -> &P {
140        &self.raw
141    }
142}