ratatui_widgets/
logo.rs

1//! The [`RatatuiLogo`] widget renders the Ratatui logo.
2use indoc::indoc;
3use ratatui_core::buffer::Buffer;
4use ratatui_core::layout::Rect;
5use ratatui_core::text::Text;
6use ratatui_core::widgets::Widget;
7
8/// A widget that renders the Ratatui logo
9///
10/// The Ratatui logo takes up two lines of text and comes in two sizes: `Tiny` and `Small`. This may
11/// be used in an application's help or about screen to show that it is powered by Ratatui.
12///
13/// # Examples
14///
15/// The [Ratatui-logo] example demonstrates how to use the `RatatuiLogo` widget. This can be run by
16/// cloning the Ratatui repository and then running the following command with an optional size
17/// argument:
18///
19/// ```shell
20/// cargo run --example logo [size]
21/// ```
22///
23/// [Ratatui-logo]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples/logo.rs
24///
25/// ## Tiny (default, 2x15 characters)
26///
27/// ```
28/// use ratatui::widgets::RatatuiLogo;
29///
30/// # fn draw(frame: &mut ratatui::Frame) {
31/// frame.render_widget(RatatuiLogo::tiny(), frame.area());
32/// # }
33/// ```
34///
35/// Renders:
36///
37/// ```text
38/// ▛▚▗▀▖▜▘▞▚▝▛▐ ▌▌
39/// ▛▚▐▀▌▐ ▛▜ ▌▝▄▘▌
40/// ```
41///
42/// ## Small (2x27 characters)
43///
44/// ```
45/// use ratatui::widgets::RatatuiLogo;
46///
47/// # fn draw(frame: &mut ratatui::Frame) {
48/// frame.render_widget(RatatuiLogo::small(), frame.area());
49/// # }
50/// ```
51///
52/// Renders:
53///
54/// ```text
55/// █▀▀▄ ▄▀▀▄▝▜▛▘▄▀▀▄▝▜▛▘█  █ █
56/// █▀▀▄ █▀▀█ ▐▌ █▀▀█ ▐▌ ▀▄▄▀ █
57/// ```
58#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
59pub struct RatatuiLogo {
60    size: Size,
61}
62
63/// The size of the logo
64#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
65#[non_exhaustive]
66pub enum Size {
67    /// A tiny logo
68    ///
69    /// The default size of the logo (2x15 characters)
70    ///
71    /// ```text
72    /// ▛▚▗▀▖▜▘▞▚▝▛▐ ▌▌
73    /// ▛▚▐▀▌▐ ▛▜ ▌▝▄▘▌
74    /// ```
75    #[default]
76    Tiny,
77    /// A small logo
78    ///
79    /// A slightly larger version of the logo (2x27 characters)
80    ///
81    /// ```text
82    /// █▀▀▄ ▄▀▀▄▝▜▛▘▄▀▀▄▝▜▛▘█  █ █
83    /// █▀▀▄ █▀▀█ ▐▌ █▀▀█ ▐▌ ▀▄▄▀ █
84    /// ```
85    Small,
86}
87
88impl RatatuiLogo {
89    /// Create a new Ratatui logo widget
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// use ratatui::widgets::{RatatuiLogo, RatatuiLogoSize};
95    ///
96    /// let logo = RatatuiLogo::new(RatatuiLogoSize::Tiny);
97    /// ```
98    pub const fn new(size: Size) -> Self {
99        Self { size }
100    }
101
102    /// Set the size of the logo
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// use ratatui::widgets::{RatatuiLogo, RatatuiLogoSize};
108    ///
109    /// let logo = RatatuiLogo::default().size(RatatuiLogoSize::Small);
110    /// ```
111    #[must_use]
112    pub const fn size(self, size: Size) -> Self {
113        let _ = self;
114        Self { size }
115    }
116
117    /// Create a new Ratatui logo widget with a tiny size
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use ratatui::widgets::RatatuiLogo;
123    ///
124    /// let logo = RatatuiLogo::tiny();
125    /// ```
126    pub const fn tiny() -> Self {
127        Self::new(Size::Tiny)
128    }
129
130    /// Create a new Ratatui logo widget with a small size
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use ratatui::widgets::RatatuiLogo;
136    ///
137    /// let logo = RatatuiLogo::small();
138    /// ```
139    pub const fn small() -> Self {
140        Self::new(Size::Small)
141    }
142}
143
144impl Widget for RatatuiLogo {
145    fn render(self, area: Rect, buf: &mut Buffer) {
146        let logo = self.size.as_str();
147        Text::raw(logo).render(area, buf);
148    }
149}
150
151impl Size {
152    const fn as_str(self) -> &'static str {
153        match self {
154            Self::Tiny => Self::tiny(),
155            Self::Small => Self::small(),
156        }
157    }
158
159    const fn tiny() -> &'static str {
160        indoc! {"
161            ▛▚▗▀▖▜▘▞▚▝▛▐ ▌▌
162            ▛▚▐▀▌▐ ▛▜ ▌▝▄▘▌
163        "}
164    }
165
166    const fn small() -> &'static str {
167        indoc! {"
168            █▀▀▄ ▄▀▀▄▝▜▛▘▄▀▀▄▝▜▛▘█  █ █
169            █▀▀▄ █▀▀█ ▐▌ █▀▀█ ▐▌ ▀▄▄▀ █
170        "}
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use rstest::rstest;
177
178    use super::*;
179
180    #[rstest]
181    #[case::tiny(Size::Tiny)]
182    #[case::small(Size::Small)]
183    fn new_size(#[case] size: Size) {
184        let logo = RatatuiLogo::new(size);
185        assert_eq!(logo.size, size);
186    }
187
188    #[test]
189    fn default_logo_is_tiny() {
190        let logo = RatatuiLogo::default();
191        assert_eq!(logo.size, Size::Tiny);
192    }
193
194    #[test]
195    fn set_logo_size_to_small() {
196        let logo = RatatuiLogo::default().size(Size::Small);
197        assert_eq!(logo.size, Size::Small);
198    }
199
200    #[test]
201    fn tiny_logo_constant() {
202        let logo = RatatuiLogo::tiny();
203        assert_eq!(logo.size, Size::Tiny);
204    }
205
206    #[test]
207    fn small_logo_constant() {
208        let logo = RatatuiLogo::small();
209        assert_eq!(logo.size, Size::Small);
210    }
211
212    #[test]
213    #[rustfmt::skip]
214    fn render_tiny() {
215        let mut buf = Buffer::empty(Rect::new(0, 0, 15, 2));
216        RatatuiLogo::tiny().render(buf.area, &mut buf);
217        assert_eq!(
218            buf,
219            Buffer::with_lines([
220                "▛▚▗▀▖▜▘▞▚▝▛▐ ▌▌",
221                "▛▚▐▀▌▐ ▛▜ ▌▝▄▘▌",
222            ])
223        );
224    }
225
226    #[test]
227    #[rustfmt::skip]
228    fn render_small() {
229        let mut buf = Buffer::empty(Rect::new(0, 0, 27, 2));
230        RatatuiLogo::small().render(buf.area, &mut buf);
231        assert_eq!(
232            buf,
233            Buffer::with_lines([
234                "█▀▀▄ ▄▀▀▄▝▜▛▘▄▀▀▄▝▜▛▘█  █ █",
235                "█▀▀▄ █▀▀█ ▐▌ █▀▀█ ▐▌ ▀▄▄▀ █",
236            ])
237        );
238    }
239
240    #[rstest]
241    #[case::tiny(Size::Tiny, Buffer::with_lines(["▛"]))]
242    #[case::small(Size::Small, Buffer::with_lines(["█"]))]
243    fn render_in_minimal_buffer(#[case] size: Size, #[case] expected: Buffer) {
244        let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
245        let logo = RatatuiLogo::new(size);
246        // This should not panic, even if the buffer is too small to render the logo.
247        logo.render(buffer.area, &mut buffer);
248        assert_eq!(buffer, expected);
249    }
250
251    #[rstest]
252    #[case::tiny(Size::Tiny)]
253    #[case::small(Size::Small)]
254    fn render_in_zero_size_buffer(#[case] size: Size) {
255        let mut buffer = Buffer::empty(Rect::ZERO);
256        let logo = RatatuiLogo::new(size);
257        // This should not panic, even if the buffer has zero size.
258        logo.render(buffer.area, &mut buffer);
259    }
260}