Skip to main content

simple_render/ui/types/
content.rs

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}