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