Skip to main content

requestty_ui/
style.rs

1//! A module to control the looks of text.
2
3use std::{fmt::Display, io};
4
5/// Some content with a particular style applied.
6///
7/// See also [`write_styled`] and [`Stylize`].
8///
9/// [`write_styled`]: crate::backend::DisplayBackend::write_styled
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct Styled<T: ?Sized> {
12    fg: Option<Color>,
13    bg: Option<Color>,
14    attributes: Attributes,
15    content: T,
16}
17
18impl<T: Display> Styled<T> {
19    /// Creates a new [`Styled`]
20    pub fn new(content: T) -> Self {
21        Self {
22            fg: None,
23            bg: None,
24            attributes: Attributes::empty(),
25            content,
26        }
27    }
28}
29
30impl<T: Display + ?Sized> Styled<T> {
31    pub(super) fn write<B: crate::backend::DisplayBackend + ?Sized>(
32        &self,
33        backend: &mut B,
34    ) -> io::Result<()> {
35        if let Some(fg) = self.fg {
36            backend.set_fg(fg)?;
37        }
38        if let Some(bg) = self.bg {
39            backend.set_bg(bg)?;
40        }
41        if !self.attributes.is_empty() {
42            backend.set_attributes(self.attributes)?;
43        }
44
45        write!(backend, "{}", &self.content)?;
46
47        if self.fg.is_some() {
48            backend.set_fg(Color::Reset)?;
49        }
50        if self.bg.is_some() {
51            backend.set_bg(Color::Reset)?;
52        }
53        if !self.attributes.is_empty() {
54            backend.set_attributes(Attributes::empty())?;
55        }
56        Ok(())
57    }
58}
59
60impl<T: Display> From<T> for Styled<T> {
61    fn from(content: T) -> Self {
62        Self::new(content)
63    }
64}
65
66/// Represents a color. See the underlying terminal library documentation for information on
67/// terminal compatibility.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69#[allow(missing_docs)]
70pub enum Color {
71    Reset,
72    Black,
73    Red,
74    Green,
75    Yellow,
76    Blue,
77    Magenta,
78    Cyan,
79    Grey,
80    DarkGrey,
81    LightRed,
82    LightGreen,
83    LightYellow,
84    LightBlue,
85    LightMagenta,
86    LightCyan,
87    White,
88    Rgb(u8, u8, u8),
89    Ansi(u8),
90}
91
92bitflags::bitflags! {
93    /// Attributes change the way a piece of text is displayed.
94    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
95    pub struct Attributes: u16 {
96        /// Increases the text intensity.
97        const BOLD              = 0b0000_0000_0001;
98        /// Decreases the text intensity.
99        const DIM               = 0b0000_0000_0010;
100        /// Emphasises the text.
101        const ITALIC            = 0b0000_0000_0100;
102        /// Underlines the text.
103        const UNDERLINED        = 0b0000_0000_1000;
104        /// Makes the text blinking (< 150 per minute).
105        const SLOW_BLINK        = 0b0000_0001_0000;
106        /// Makes the text blinking (>= 150 per minute).
107        const RAPID_BLINK       = 0b0000_0010_0000;
108        /// Swaps foreground and background colors.
109        const REVERSED          = 0b0000_0100_0000;
110        /// Hides the text (also known as Conceal).
111        const HIDDEN            = 0b0000_1000_0000;
112        /// Crosses the text.
113        const CROSSED_OUT       = 0b0001_0000_0000;
114    }
115}
116
117impl Attributes {
118    /// Gets the attribute diff that needs to be applied to transition from the attributes in `self`
119    /// to the attributes in `to`.
120    pub fn diff(self, to: Attributes) -> AttributeDiff {
121        let diff = self ^ to;
122        AttributeDiff {
123            to_remove: diff & self,
124            to_add: diff & to,
125        }
126    }
127}
128
129/// The change in attributes. Use [`Attributes::diff`] to create one.
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131#[allow(missing_docs)]
132pub struct AttributeDiff {
133    pub to_add: Attributes,
134    pub to_remove: Attributes,
135}
136
137/// Provides a set of methods to set the colors and attributes.
138///
139/// Every method with the `on_` prefix sets the background color. Other color methods set the
140/// foreground color.
141///
142/// Method names correspond to the [`Color`] enum variants and [`Attributes`] names.
143///
144/// See also [`Styled`] and [`write_styled`].
145///
146/// [`write_styled`]: crate::backend::DisplayBackend::write_styled
147#[allow(missing_docs)]
148pub trait Stylize<T> {
149    fn black(self) -> Styled<T>;
150    fn dark_grey(self) -> Styled<T>;
151    fn light_red(self) -> Styled<T>;
152    fn red(self) -> Styled<T>;
153    fn light_green(self) -> Styled<T>;
154    fn green(self) -> Styled<T>;
155    fn light_yellow(self) -> Styled<T>;
156    fn yellow(self) -> Styled<T>;
157    fn light_blue(self) -> Styled<T>;
158    fn blue(self) -> Styled<T>;
159    fn light_magenta(self) -> Styled<T>;
160    fn magenta(self) -> Styled<T>;
161    fn light_cyan(self) -> Styled<T>;
162    fn cyan(self) -> Styled<T>;
163    fn white(self) -> Styled<T>;
164    fn grey(self) -> Styled<T>;
165    fn rgb(self, r: u8, g: u8, b: u8) -> Styled<T>;
166    fn ansi(self, ansi: u8) -> Styled<T>;
167
168    fn on_black(self) -> Styled<T>;
169    fn on_dark_grey(self) -> Styled<T>;
170    fn on_light_red(self) -> Styled<T>;
171    fn on_red(self) -> Styled<T>;
172    fn on_light_green(self) -> Styled<T>;
173    fn on_green(self) -> Styled<T>;
174    fn on_light_yellow(self) -> Styled<T>;
175    fn on_yellow(self) -> Styled<T>;
176    fn on_light_blue(self) -> Styled<T>;
177    fn on_blue(self) -> Styled<T>;
178    fn on_light_magenta(self) -> Styled<T>;
179    fn on_magenta(self) -> Styled<T>;
180    fn on_light_cyan(self) -> Styled<T>;
181    fn on_cyan(self) -> Styled<T>;
182    fn on_white(self) -> Styled<T>;
183    fn on_grey(self) -> Styled<T>;
184    fn on_rgb(self, r: u8, g: u8, b: u8) -> Styled<T>;
185    fn on_ansi(self, ansi: u8) -> Styled<T>;
186
187    fn bold(self) -> Styled<T>;
188    fn underlined(self) -> Styled<T>;
189    fn reverse(self) -> Styled<T>;
190    fn dim(self) -> Styled<T>;
191    fn italic(self) -> Styled<T>;
192    fn slow_blink(self) -> Styled<T>;
193    fn rapid_blink(self) -> Styled<T>;
194    fn hidden(self) -> Styled<T>;
195    fn crossed_out(self) -> Styled<T>;
196}
197
198impl<T, I: Into<Styled<T>>> Stylize<T> for I {
199    fn black(self) -> Styled<T> {
200        let mut styled = self.into();
201        styled.fg = Some(Color::Black);
202        styled
203    }
204    fn dark_grey(self) -> Styled<T> {
205        let mut styled = self.into();
206        styled.fg = Some(Color::DarkGrey);
207        styled
208    }
209    fn light_red(self) -> Styled<T> {
210        let mut styled = self.into();
211        styled.fg = Some(Color::LightRed);
212        styled
213    }
214    fn red(self) -> Styled<T> {
215        let mut styled = self.into();
216        styled.fg = Some(Color::Red);
217        styled
218    }
219    fn light_green(self) -> Styled<T> {
220        let mut styled = self.into();
221        styled.fg = Some(Color::LightGreen);
222        styled
223    }
224    fn green(self) -> Styled<T> {
225        let mut styled = self.into();
226        styled.fg = Some(Color::Green);
227        styled
228    }
229    fn light_yellow(self) -> Styled<T> {
230        let mut styled = self.into();
231        styled.fg = Some(Color::LightYellow);
232        styled
233    }
234    fn yellow(self) -> Styled<T> {
235        let mut styled = self.into();
236        styled.fg = Some(Color::Yellow);
237        styled
238    }
239    fn light_blue(self) -> Styled<T> {
240        let mut styled = self.into();
241        styled.fg = Some(Color::LightBlue);
242        styled
243    }
244    fn blue(self) -> Styled<T> {
245        let mut styled = self.into();
246        styled.fg = Some(Color::Blue);
247        styled
248    }
249    fn light_magenta(self) -> Styled<T> {
250        let mut styled = self.into();
251        styled.fg = Some(Color::LightMagenta);
252        styled
253    }
254    fn magenta(self) -> Styled<T> {
255        let mut styled = self.into();
256        styled.fg = Some(Color::Magenta);
257        styled
258    }
259    fn light_cyan(self) -> Styled<T> {
260        let mut styled = self.into();
261        styled.fg = Some(Color::LightCyan);
262        styled
263    }
264    fn cyan(self) -> Styled<T> {
265        let mut styled = self.into();
266        styled.fg = Some(Color::Cyan);
267        styled
268    }
269    fn white(self) -> Styled<T> {
270        let mut styled = self.into();
271        styled.fg = Some(Color::White);
272        styled
273    }
274    fn grey(self) -> Styled<T> {
275        let mut styled = self.into();
276        styled.fg = Some(Color::Grey);
277        styled
278    }
279    fn rgb(self, r: u8, g: u8, b: u8) -> Styled<T> {
280        let mut styled = self.into();
281        styled.fg = Some(Color::Rgb(r, g, b));
282        styled
283    }
284    fn ansi(self, ansi: u8) -> Styled<T> {
285        let mut styled = self.into();
286        styled.fg = Some(Color::Ansi(ansi));
287        styled
288    }
289
290    fn on_black(self) -> Styled<T> {
291        let mut styled = self.into();
292        styled.bg = Some(Color::Black);
293        styled
294    }
295    fn on_dark_grey(self) -> Styled<T> {
296        let mut styled = self.into();
297        styled.bg = Some(Color::DarkGrey);
298        styled
299    }
300    fn on_light_red(self) -> Styled<T> {
301        let mut styled = self.into();
302        styled.bg = Some(Color::LightRed);
303        styled
304    }
305    fn on_red(self) -> Styled<T> {
306        let mut styled = self.into();
307        styled.bg = Some(Color::Red);
308        styled
309    }
310    fn on_light_green(self) -> Styled<T> {
311        let mut styled = self.into();
312        styled.bg = Some(Color::LightGreen);
313        styled
314    }
315    fn on_green(self) -> Styled<T> {
316        let mut styled = self.into();
317        styled.bg = Some(Color::Green);
318        styled
319    }
320    fn on_light_yellow(self) -> Styled<T> {
321        let mut styled = self.into();
322        styled.bg = Some(Color::LightYellow);
323        styled
324    }
325    fn on_yellow(self) -> Styled<T> {
326        let mut styled = self.into();
327        styled.bg = Some(Color::Yellow);
328        styled
329    }
330    fn on_light_blue(self) -> Styled<T> {
331        let mut styled = self.into();
332        styled.bg = Some(Color::LightBlue);
333        styled
334    }
335    fn on_blue(self) -> Styled<T> {
336        let mut styled = self.into();
337        styled.bg = Some(Color::Blue);
338        styled
339    }
340    fn on_light_magenta(self) -> Styled<T> {
341        let mut styled = self.into();
342        styled.bg = Some(Color::LightMagenta);
343        styled
344    }
345    fn on_magenta(self) -> Styled<T> {
346        let mut styled = self.into();
347        styled.bg = Some(Color::Magenta);
348        styled
349    }
350    fn on_light_cyan(self) -> Styled<T> {
351        let mut styled = self.into();
352        styled.bg = Some(Color::LightCyan);
353        styled
354    }
355    fn on_cyan(self) -> Styled<T> {
356        let mut styled = self.into();
357        styled.bg = Some(Color::Cyan);
358        styled
359    }
360    fn on_white(self) -> Styled<T> {
361        let mut styled = self.into();
362        styled.bg = Some(Color::White);
363        styled
364    }
365    fn on_grey(self) -> Styled<T> {
366        let mut styled = self.into();
367        styled.bg = Some(Color::Grey);
368        styled
369    }
370    fn on_rgb(self, r: u8, g: u8, b: u8) -> Styled<T> {
371        let mut styled = self.into();
372        styled.bg = Some(Color::Rgb(r, g, b));
373        styled
374    }
375    fn on_ansi(self, ansi: u8) -> Styled<T> {
376        let mut styled = self.into();
377        styled.bg = Some(Color::Ansi(ansi));
378        styled
379    }
380
381    fn bold(self) -> Styled<T> {
382        let mut styled = self.into();
383        styled.attributes |= Attributes::BOLD;
384        styled
385    }
386    fn underlined(self) -> Styled<T> {
387        let mut styled = self.into();
388        styled.attributes |= Attributes::UNDERLINED;
389        styled
390    }
391    fn reverse(self) -> Styled<T> {
392        let mut styled = self.into();
393        styled.attributes |= Attributes::REVERSED;
394        styled
395    }
396    fn dim(self) -> Styled<T> {
397        let mut styled = self.into();
398        styled.attributes |= Attributes::DIM;
399        styled
400    }
401    fn italic(self) -> Styled<T> {
402        let mut styled = self.into();
403        styled.attributes |= Attributes::ITALIC;
404        styled
405    }
406    fn slow_blink(self) -> Styled<T> {
407        let mut styled = self.into();
408        styled.attributes |= Attributes::SLOW_BLINK;
409        styled
410    }
411    fn rapid_blink(self) -> Styled<T> {
412        let mut styled = self.into();
413        styled.attributes |= Attributes::RAPID_BLINK;
414        styled
415    }
416    fn hidden(self) -> Styled<T> {
417        let mut styled = self.into();
418        styled.attributes |= Attributes::HIDDEN;
419        styled
420    }
421    fn crossed_out(self) -> Styled<T> {
422        let mut styled = self.into();
423        styled.attributes |= Attributes::CROSSED_OUT;
424        styled
425    }
426}