Skip to main content

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