1use super::*;
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
4pub enum TextWrap {
5 None,
6 Glyph,
7 Word,
8 #[default]
9 WordOrGlyph,
10}
11
12impl TextWrap {
13 pub(in crate::ui) fn into_cosmic(self) -> Wrap {
14 match self {
15 Self::None => Wrap::None,
16 Self::Glyph => Wrap::Glyph,
17 Self::Word => Wrap::Word,
18 Self::WordOrGlyph => Wrap::WordOrGlyph,
19 }
20 }
21}
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub enum TextOverflow {
25 #[default]
26 Clip,
27 Ellipsis,
28}
29
30#[derive(Clone, Debug, PartialEq)]
31pub struct TextStyle {
32 pub color: Paint,
33 pub gradient: GradientDirection,
34 pub size: u32,
35 pub family: Option<String>,
36 pub bold: bool,
37 pub italic: bool,
38 pub underline: bool,
39 pub strikethrough: bool,
40 pub overline: bool,
41}
42
43impl Default for TextStyle {
44 fn default() -> Self {
45 Self {
46 color: Paint::solid(Color::WHITE),
47 gradient: GradientDirection::default(),
48 size: 14,
49 family: None,
50 bold: false,
51 italic: false,
52 underline: false,
53 strikethrough: false,
54 overline: false,
55 }
56 }
57}
58
59#[derive(Clone, Debug, PartialEq)]
60pub struct Text {
61 pub content: Arc<str>,
62 pub style: TextStyle,
63 pub align: Align,
64 pub vertical_align: Align,
65 pub wrap: TextWrap,
66 pub overflow: TextOverflow,
67 pub max_lines: Option<u32>,
68 pub emoji_family: Option<String>,
69}
70
71impl Text {
72 pub fn new(content: impl Into<Arc<str>>) -> Self {
73 Self {
74 content: content.into(),
75 ..Self::default()
76 }
77 }
78}
79
80impl Default for Text {
81 fn default() -> Self {
82 Self {
83 content: Arc::from(""),
84 style: TextStyle::default(),
85 align: Align::Start,
86 vertical_align: Align::Start,
87 wrap: TextWrap::default(),
88 overflow: TextOverflow::default(),
89 max_lines: None,
90 emoji_family: Some("Noto Color Emoji".into()),
91 }
92 }
93}
94
95#[derive(Clone, Debug, PartialEq)]
96pub struct TextRun {
97 pub content: Arc<str>,
98 pub style: TextStyle,
99}
100
101impl TextRun {
102 pub fn new(content: impl Into<Arc<str>>, style: TextStyle) -> Self {
103 Self {
104 content: content.into(),
105 style,
106 }
107 }
108}
109
110#[derive(Clone, Debug, PartialEq)]
111pub struct RichText {
112 pub runs: Arc<[TextRun]>,
113 pub align: Align,
114 pub vertical_align: Align,
115 pub wrap: TextWrap,
116 pub overflow: TextOverflow,
117 pub max_lines: Option<u32>,
118 pub emoji_family: Option<String>,
119}
120
121impl RichText {
122 pub fn new(runs: impl Into<Arc<[TextRun]>>) -> Self {
123 Self {
124 runs: runs.into(),
125 ..Self::default()
126 }
127 }
128}
129
130impl Default for RichText {
131 fn default() -> Self {
132 Self {
133 runs: Arc::from([]),
134 align: Align::Start,
135 vertical_align: Align::Start,
136 wrap: TextWrap::default(),
137 overflow: TextOverflow::default(),
138 max_lines: None,
139 emoji_family: Some("Noto Color Emoji".into()),
140 }
141 }
142}
143
144#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
145pub enum ImageFit {
146 #[default]
147 None,
148 Fill,
149 Contain,
150 Cover,
151}
152
153#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
154pub enum ImageFilter {
155 #[default]
156 Nearest,
157 Linear,
158}
159
160pub trait RgbaImageSource: fmt::Debug + Send + Sync {
161 fn rgba(&self) -> &[u8];
162}
163
164#[derive(Clone)]
165pub enum ImagePixels {
166 Owned(Arc<[u8]>),
167 Source(Arc<dyn RgbaImageSource>),
168}
169
170impl ImagePixels {
171 pub fn as_rgba(&self) -> &[u8] {
172 match self {
173 Self::Owned(pixels) => pixels,
174 Self::Source(source) => source.rgba(),
175 }
176 }
177}
178
179impl fmt::Debug for ImagePixels {
180 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
181 formatter
182 .debug_struct("ImagePixels")
183 .field("len", &self.as_rgba().len())
184 .finish_non_exhaustive()
185 }
186}
187
188impl PartialEq for ImagePixels {
189 fn eq(&self, other: &Self) -> bool {
190 self.as_rgba() == other.as_rgba()
191 }
192}
193
194impl Eq for ImagePixels {}
195
196impl From<Arc<[u8]>> for ImagePixels {
197 fn from(pixels: Arc<[u8]>) -> Self {
198 Self::Owned(pixels)
199 }
200}
201
202impl From<Vec<u8>> for ImagePixels {
203 fn from(pixels: Vec<u8>) -> Self {
204 Self::Owned(pixels.into())
205 }
206}
207
208impl<const N: usize> From<[u8; N]> for ImagePixels {
209 fn from(pixels: [u8; N]) -> Self {
210 Self::Owned(Arc::from(pixels))
211 }
212}
213
214impl RgbaImageSource for Vec<u8> {
215 fn rgba(&self) -> &[u8] {
216 self
217 }
218}
219
220impl RgbaImageSource for Box<[u8]> {
221 fn rgba(&self) -> &[u8] {
222 self
223 }
224}
225
226impl RgbaImageSource for Arc<[u8]> {
227 fn rgba(&self) -> &[u8] {
228 self
229 }
230}
231
232#[derive(Clone, Debug, PartialEq, Eq)]
233pub struct Image {
234 pub width: u32,
235 pub height: u32,
236 pub rgba: ImagePixels,
237 pub stride: u32,
238 pub fit: ImageFit,
239 pub filter: ImageFilter,
240 pub align: Align,
241 pub vertical_align: Align,
242}
243
244impl Image {
245 pub fn new(width: u32, height: u32, rgba: impl Into<ImagePixels>) -> Self {
246 Self {
247 width,
248 height,
249 rgba: rgba.into(),
250 stride: width.saturating_mul(4),
251 fit: ImageFit::default(),
252 filter: ImageFilter::default(),
253 align: Align::Start,
254 vertical_align: Align::Start,
255 }
256 }
257
258 pub fn with_stride(mut self, stride: u32) -> Self {
259 self.stride = stride;
260 self
261 }
262
263 pub fn from_source(width: u32, height: u32, source: Arc<dyn RgbaImageSource>) -> Self {
264 Self::new(width, height, ImagePixels::Source(source))
265 }
266
267 pub fn from_source_with_stride(
268 width: u32,
269 height: u32,
270 stride: u32,
271 source: Arc<dyn RgbaImageSource>,
272 ) -> Self {
273 Self::from_source(width, height, source).with_stride(stride)
274 }
275
276 pub fn rgba(&self) -> &[u8] {
277 self.rgba.as_rgba()
278 }
279}
280
281#[derive(Clone, Debug, PartialEq)]
282pub enum Content {
283 Text(Text),
284 RichText(RichText),
285 Image(Image),
286}
287
288impl From<Text> for Content {
289 fn from(text: Text) -> Self {
290 Self::Text(text)
291 }
292}
293
294impl From<RichText> for Content {
295 fn from(text: RichText) -> Self {
296 Self::RichText(text)
297 }
298}
299
300impl From<Image> for Content {
301 fn from(image: Image) -> Self {
302 Self::Image(image)
303 }
304}