iced_core/text/
paragraph.rs

1//! Draw paragraphs.
2use crate::alignment;
3use crate::text::{Alignment, Difference, Hit, LineHeight, Shaping, Span, Text, Wrapping};
4use crate::{Pixels, 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>(text: Text<&[Span<'_, Link, Self::Font>], Self::Font>) -> Self;
16
17    /// Lays out the [`Paragraph`] with some new boundaries.
18    fn resize(&mut self, new_bounds: Size);
19
20    /// Compares the [`Paragraph`] with some desired [`Text`] and returns the
21    /// [`Difference`].
22    fn compare(&self, text: Text<(), Self::Font>) -> Difference;
23
24    /// Returns the text size of the [`Paragraph`] in [`Pixels`].
25    fn size(&self) -> Pixels;
26
27    /// Returns the font of the [`Paragraph`].
28    fn font(&self) -> Self::Font;
29
30    /// Returns the [`LineHeight`] of the [`Paragraph`].
31    fn line_height(&self) -> LineHeight;
32
33    /// Returns the horizontal alignment of the [`Paragraph`].
34    fn align_x(&self) -> Alignment;
35
36    /// Returns the vertical alignment of the [`Paragraph`].
37    fn align_y(&self) -> alignment::Vertical;
38
39    /// Returns the [`Wrapping`] strategy of the [`Paragraph`]>
40    fn wrapping(&self) -> Wrapping;
41
42    /// Returns the [`Shaping`] strategy of the [`Paragraph`]>
43    fn shaping(&self) -> Shaping;
44
45    /// Returns the available bounds used to layout the [`Paragraph`].
46    fn bounds(&self) -> Size;
47
48    /// Returns the minimum boundaries that can fit the contents of the
49    /// [`Paragraph`].
50    fn min_bounds(&self) -> Size;
51
52    /// Tests whether the provided point is within the boundaries of the
53    /// [`Paragraph`], returning information about the nearest character.
54    fn hit_test(&self, point: Point) -> Option<Hit>;
55
56    /// Tests whether the provided point is within the boundaries of a
57    /// [`Span`] in the [`Paragraph`], returning the index of the [`Span`]
58    /// that was hit.
59    fn hit_span(&self, point: Point) -> Option<usize>;
60
61    /// Returns all bounds for the provided [`Span`] index of the [`Paragraph`].
62    /// A [`Span`] can have multiple bounds for each line it's on.
63    fn span_bounds(&self, index: usize) -> Vec<Rectangle>;
64
65    /// Returns the distance to the given grapheme index in the [`Paragraph`].
66    fn grapheme_position(&self, line: usize, index: usize) -> Option<Point>;
67
68    /// Returns the minimum width that can fit the contents of the [`Paragraph`].
69    fn min_width(&self) -> f32 {
70        self.min_bounds().width
71    }
72
73    /// Returns the minimum height that can fit the contents of the [`Paragraph`].
74    fn min_height(&self) -> f32 {
75        self.min_bounds().height
76    }
77}
78
79/// A [`Paragraph`] of plain text.
80#[derive(Debug, Clone, Default)]
81pub struct Plain<P: Paragraph> {
82    raw: P,
83    content: String,
84}
85
86impl<P: Paragraph> Plain<P> {
87    /// Creates a new [`Plain`] paragraph.
88    pub fn new(text: Text<String, P::Font>) -> Self {
89        Self {
90            raw: P::with_text(text.as_ref()),
91            content: text.content,
92        }
93    }
94
95    /// Updates the plain [`Paragraph`] to match the given [`Text`], if needed.
96    ///
97    /// Returns true if the [`Paragraph`] changed.
98    pub fn update(&mut self, text: Text<&str, P::Font>) -> bool {
99        if self.content != text.content {
100            text.content.clone_into(&mut self.content);
101            self.raw = P::with_text(text);
102            return true;
103        }
104
105        match self.raw.compare(text.with_content(())) {
106            Difference::None => false,
107            Difference::Bounds => {
108                self.raw.resize(text.bounds);
109                true
110            }
111            Difference::Shape => {
112                self.raw = P::with_text(text);
113                true
114            }
115        }
116    }
117
118    /// Returns the horizontal alignment of the [`Paragraph`].
119    pub fn align_x(&self) -> Alignment {
120        self.raw.align_x()
121    }
122
123    /// Returns the vertical alignment of the [`Paragraph`].
124    pub fn align_y(&self) -> alignment::Vertical {
125        self.raw.align_y()
126    }
127
128    /// Returns the minimum boundaries that can fit the contents of the
129    /// [`Paragraph`].
130    pub fn min_bounds(&self) -> Size {
131        self.raw.min_bounds()
132    }
133
134    /// Returns the minimum width that can fit the contents of the
135    /// [`Paragraph`].
136    pub fn min_width(&self) -> f32 {
137        self.raw.min_width()
138    }
139
140    /// Returns the minimum height that can fit the contents of the
141    /// [`Paragraph`].
142    pub fn min_height(&self) -> f32 {
143        self.raw.min_height()
144    }
145
146    /// Returns the cached [`Paragraph`].
147    pub fn raw(&self) -> &P {
148        &self.raw
149    }
150
151    /// Returns the current content of the plain [`Paragraph`].
152    pub fn content(&self) -> &str {
153        &self.content
154    }
155
156    /// Returns the [`Paragraph`] as a [`Text`] definition.
157    pub fn as_text(&self) -> Text<&str, P::Font> {
158        Text {
159            content: &self.content,
160            bounds: self.raw.bounds(),
161            size: self.raw.size(),
162            line_height: self.raw.line_height(),
163            font: self.raw.font(),
164            align_x: self.raw.align_x(),
165            align_y: self.raw.align_y(),
166            shaping: self.raw.shaping(),
167            wrapping: self.raw.wrapping(),
168        }
169    }
170}