1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
//! Draw paragraphs.
use crate::alignment;
use crate::text::{
Alignment, Difference, Ellipsis, Hit, LineHeight, Shaping, Span, Target, Text, Wrapping,
};
use crate::{Pixels, Point, Rectangle, Size};
/// A text paragraph.
pub trait Paragraph: Sized + Default {
/// The font of this [`Paragraph`].
type Font: Copy + PartialEq;
/// Creates a new [`Paragraph`] laid out with the given [`Text`].
fn with_text(text: Text<&str, Self::Font>) -> Self;
/// Creates a new [`Paragraph`] laid out with the given [`Text`].
fn with_spans<Link>(text: Text<&[Span<'_, Link, Self::Font>], Self::Font>) -> Self;
/// Lays out the [`Paragraph`] with some new boundaries.
fn resize(&mut self, new_bounds: Size);
/// Compares the [`Paragraph`] with some desired [`Text`] and returns the
/// [`Difference`].
fn compare(&self, text: Text<(), Self::Font>) -> Difference;
/// Returns the text size of the [`Paragraph`] in [`Pixels`].
fn size(&self) -> Pixels;
/// Returns the hint factor of the [`Paragraph`].
fn hint_factor(&self) -> Option<f32>;
/// Returns the letter spacing of the [`Paragraph`], if any.
fn letter_spacing(&self) -> Option<f32>;
/// Returns the font of the [`Paragraph`].
fn font(&self) -> Self::Font;
/// Returns the [`LineHeight`] of the [`Paragraph`].
fn line_height(&self) -> LineHeight;
/// Returns the horizontal alignment of the [`Paragraph`].
fn align_x(&self) -> Alignment;
/// Returns the vertical alignment of the [`Paragraph`].
fn align_y(&self) -> alignment::Vertical;
/// Returns the [`Wrapping`] strategy of the [`Paragraph`]>
fn wrapping(&self) -> Wrapping;
/// Returns the [`Ellipsis`] strategy of the [`Paragraph`]>
fn ellipsis(&self) -> Ellipsis;
/// Returns the [`Shaping`] strategy of the [`Paragraph`]>
fn shaping(&self) -> Shaping;
/// Returns the available bounds used to layout the [`Paragraph`].
fn bounds(&self) -> Size;
/// Returns the minimum boundaries that can fit the contents of the
/// [`Paragraph`].
fn min_bounds(&self) -> Size;
/// Tests whether the provided point is within the boundaries of the
/// [`Paragraph`], returning information about the nearest character.
fn hit_test(&self, point: Point) -> Option<Hit>;
/// Tests whether the provided point is within the boundaries of a
/// [`Span`] in the [`Paragraph`], returning the index of the [`Span`]
/// that was hit.
fn hit_span(&self, point: Point) -> Option<usize>;
/// Returns all bounds for the provided [`Span`] index of the [`Paragraph`].
/// A [`Span`] can have multiple bounds for each line it's on.
fn span_bounds(&self, index: usize) -> Vec<Rectangle>;
/// Returns the visual bounds for a character range selection.
/// Returns multiple rectangles if the selection spans multiple lines.
fn selection_bounds(&self, start: usize, end: usize) -> Vec<Rectangle>;
/// Returns the distance to the given grapheme index in the [`Paragraph`].
fn grapheme_position(&self, line: usize, index: usize) -> Option<Point>;
/// Returns the minimum width that can fit the contents of the [`Paragraph`].
fn min_width(&self) -> f32 {
self.min_bounds().width
}
/// Returns the minimum height that can fit the contents of the [`Paragraph`].
fn min_height(&self) -> f32 {
self.min_bounds().height
}
/// Returns whether the text was truncated by ellipsis.
///
/// `true` when the full text would exceed the allocated bounds and the
/// [`Ellipsis`] strategy caused visible truncation.
fn is_truncated(&self) -> bool;
/// Selects the text between two positions relative to the origin of the
/// [`Paragraph`], as a drag from `start` to `end` would.
///
/// The [`Target`] grows the selection around `start` to whole words or
/// lines. A paragraph that keeps no selection ignores this.
fn select(&mut self, _start: Point, _end: Point, _target: Target) {}
/// Selects all the text of the [`Paragraph`].
fn select_all(&mut self) {}
/// Clears the selection of the [`Paragraph`].
fn deselect(&mut self) {}
/// Returns the regions of the current selection, one per line, relative
/// to the origin of the [`Paragraph`].
fn selection(&self) -> &[Rectangle] {
&[]
}
/// Returns the selected text, if any.
fn copy(&mut self) -> Option<String> {
None
}
}
/// A [`Paragraph`] of plain text.
#[derive(Debug, Clone, Default)]
pub struct Plain<P: Paragraph> {
raw: P,
content: String,
}
impl<P: Paragraph> Plain<P> {
/// Creates a new [`Plain`] paragraph.
pub fn new(text: Text<String, P::Font>) -> Self {
Self {
raw: P::with_text(text.as_ref()),
content: text.content,
}
}
/// Updates the plain [`Paragraph`] to match the given [`Text`], if needed.
///
/// Returns true if the [`Paragraph`] changed.
pub fn update(&mut self, text: Text<&str, P::Font>) -> bool {
if self.content != text.content {
text.content.clone_into(&mut self.content);
self.raw = P::with_text(text);
return true;
}
match self.raw.compare(text.with_content(())) {
Difference::None => false,
Difference::Bounds => {
self.raw.resize(text.bounds);
true
}
Difference::Shape => {
self.raw = P::with_text(text);
true
}
}
}
/// Returns the horizontal alignment of the [`Paragraph`].
pub fn align_x(&self) -> Alignment {
self.raw.align_x()
}
/// Returns the vertical alignment of the [`Paragraph`].
pub fn align_y(&self) -> alignment::Vertical {
self.raw.align_y()
}
/// Returns the minimum boundaries that can fit the contents of the
/// [`Paragraph`].
pub fn min_bounds(&self) -> Size {
self.raw.min_bounds()
}
/// Returns the minimum width that can fit the contents of the
/// [`Paragraph`].
pub fn min_width(&self) -> f32 {
self.raw.min_width()
}
/// Returns the minimum height that can fit the contents of the
/// [`Paragraph`].
pub fn min_height(&self) -> f32 {
self.raw.min_height()
}
/// Returns whether the text was truncated by ellipsis.
pub fn is_truncated(&self) -> bool {
self.raw.is_truncated()
}
/// Returns the cached [`Paragraph`].
pub fn raw(&self) -> &P {
&self.raw
}
/// Returns the cached [`Paragraph`], mutably.
pub fn raw_mut(&mut self) -> &mut P {
&mut self.raw
}
/// Returns the current content of the plain [`Paragraph`].
pub fn content(&self) -> &str {
&self.content
}
/// Returns the [`Paragraph`] as a [`Text`] definition.
pub fn as_text(&self) -> Text<&str, P::Font> {
Text {
content: &self.content,
bounds: self.raw.bounds(),
size: self.raw.size(),
line_height: self.raw.line_height(),
font: self.raw.font(),
align_x: self.raw.align_x(),
align_y: self.raw.align_y(),
shaping: self.raw.shaping(),
wrapping: self.raw.wrapping(),
ellipsis: self.raw.ellipsis(),
hint_factor: self.raw.hint_factor(),
letter_spacing: self.raw.letter_spacing(),
}
}
}