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}