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