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}