line_ui/element/
styled.rs

1/*
2 * Copyright (c) 2025 Jasmine Tai. All rights reserved.
3 */
4
5use crate::element::Element;
6use crate::render::RenderChunk;
7use crate::style::Style;
8
9/// An element that renders its content with a particular style.
10pub struct Styled<E> {
11    style: Style,
12    inner: E,
13}
14
15impl<E> Styled<E> {
16    /// Creates a new [`Styled`].
17    pub fn new(style: Style, inner: E) -> Self {
18        Styled { style, inner }
19    }
20}
21
22impl<'s, E: Element<'s>> Element<'s> for Styled<E> {
23    fn width(&self) -> usize {
24        self.inner.width()
25    }
26
27    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
28        self.inner.render().map(|mut item| {
29            item.style = item.style.or(self.style);
30            item
31        })
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use crate::Color;
38    use crate::element::Text;
39
40    use super::*;
41
42    const STYLE_1: Style = Style {
43        foreground: Some(Color::Ansi(42)),
44        ..Style::EMPTY
45    };
46
47    const STYLE_2: Style = Style {
48        foreground: Some(Color::Ansi(96)),
49        ..Style::EMPTY
50    };
51
52    const STYLE_3: Style = Style {
53        background: Some(Color::Ansi(1)),
54        ..Style::EMPTY
55    };
56
57    #[test]
58    fn basic() {
59        let element = Styled::new(STYLE_1, Text::from("Hello, world!"));
60        let render: Vec<_> = element.render().collect();
61        assert_eq!(render, [RenderChunk::new("Hello, world!", STYLE_1)]);
62    }
63
64    #[test]
65    fn nested() {
66        let element = Styled::new(STYLE_1, Styled::new(STYLE_2, Text::from("Hello, world!")));
67        let render: Vec<_> = element.render().collect();
68        assert_eq!(render, [RenderChunk::new("Hello, world!", STYLE_2)]);
69    }
70
71    #[test]
72    fn nested_merge() {
73        let element = Styled::new(STYLE_3, Styled::new(STYLE_2, Text::from("Hello, world!")));
74        let render: Vec<_> = element.render().collect();
75        assert_eq!(
76            render,
77            [RenderChunk::new(
78                "Hello, world!",
79                Style {
80                    foreground: Some(96.into()),
81                    background: Some(1.into()),
82                    ..Style::EMPTY
83                },
84            )],
85        );
86    }
87}