ascii_canvas/
style.rs

1//! The `Style` type is a simplified view of the various
2//! attributes offered by the `term` library. These are
3//! enumerated as bits so they can be easily or'd together
4//! etc.
5
6use std::default::Default;
7use term::{self, Terminal};
8
9#[derive(Copy, Clone, Default, PartialEq, Eq)]
10pub struct Style {
11    bits: u64,
12}
13
14macro_rules! declare_styles {
15    ($($style:ident,)*) => {
16        #[derive(Copy, Clone)]
17        #[allow(non_camel_case_types)]
18        #[allow(clippy::upper_case_acronyms)]
19        enum StyleBit {
20            $($style,)*
21        }
22
23        $(
24            pub const $style: Style = Style { bits: 1 << (StyleBit::$style as u64) };
25        )*
26    }
27}
28
29pub const DEFAULT: Style = Style { bits: 0 };
30
31declare_styles! {
32    // Foreground colors:
33    FG_BLACK,
34    FG_BLUE,
35    FG_BRIGHT_BLACK,
36    FG_BRIGHT_BLUE,
37    FG_BRIGHT_CYAN,
38    FG_BRIGHT_GREEN,
39    FG_BRIGHT_MAGENTA,
40    FG_BRIGHT_RED,
41    FG_BRIGHT_WHITE,
42    FG_BRIGHT_YELLOW,
43    FG_CYAN,
44    FG_GREEN,
45    FG_MAGENTA,
46    FG_RED,
47    FG_WHITE,
48    FG_YELLOW,
49
50    // Background colors:
51    BG_BLACK,
52    BG_BLUE,
53    BG_BRIGHT_BLACK,
54    BG_BRIGHT_BLUE,
55    BG_BRIGHT_CYAN,
56    BG_BRIGHT_GREEN,
57    BG_BRIGHT_MAGENTA,
58    BG_BRIGHT_RED,
59    BG_BRIGHT_WHITE,
60    BG_BRIGHT_YELLOW,
61    BG_CYAN,
62    BG_GREEN,
63    BG_MAGENTA,
64    BG_RED,
65    BG_WHITE,
66    BG_YELLOW,
67
68    // Other:
69    BOLD,
70    DIM,
71    ITALIC,
72    UNDERLINE,
73    BLINK,
74    STANDOUT,
75    REVERSE,
76    SECURE,
77}
78
79impl Style {
80    pub fn new() -> Style {
81        Style::default()
82    }
83
84    pub fn with(self, other_style: Style) -> Style {
85        Style {
86            bits: self.bits | other_style.bits,
87        }
88    }
89
90    pub fn contains(self, other_style: Style) -> bool {
91        self.with(other_style) == self
92    }
93
94    /// Attempts to apply the given style to the given terminal. If
95    /// the style is not supported, either there is no effect or else
96    /// a similar, substitute style may be applied.
97    pub fn apply<T: Terminal + ?Sized>(self, term: &mut T) -> term::Result<()> {
98        term.reset()?;
99
100        macro_rules! fg_color {
101            ($color:expr, $term_color:ident) => {
102                if self.contains($color) {
103                    if term.supports_color() {
104                        term.fg(term::color::$term_color)?;
105                    }
106                }
107            };
108        }
109
110        fg_color!(FG_BLACK, BLACK);
111        fg_color!(FG_BLUE, BLUE);
112        fg_color!(FG_BRIGHT_BLACK, BRIGHT_BLACK);
113        fg_color!(FG_BRIGHT_BLUE, BRIGHT_BLUE);
114        fg_color!(FG_BRIGHT_CYAN, BRIGHT_CYAN);
115        fg_color!(FG_BRIGHT_GREEN, BRIGHT_GREEN);
116        fg_color!(FG_BRIGHT_MAGENTA, BRIGHT_MAGENTA);
117        fg_color!(FG_BRIGHT_RED, BRIGHT_RED);
118        fg_color!(FG_BRIGHT_WHITE, BRIGHT_WHITE);
119        fg_color!(FG_BRIGHT_YELLOW, BRIGHT_YELLOW);
120        fg_color!(FG_CYAN, CYAN);
121        fg_color!(FG_GREEN, GREEN);
122        fg_color!(FG_MAGENTA, MAGENTA);
123        fg_color!(FG_RED, RED);
124        fg_color!(FG_WHITE, WHITE);
125        fg_color!(FG_YELLOW, YELLOW);
126
127        macro_rules! bg_color {
128            ($color:expr, $term_color:ident) => {
129                if self.contains($color) {
130                    if term.supports_color() {
131                        term.bg(term::color::$term_color)?;
132                    }
133                }
134            };
135        }
136
137        bg_color!(BG_BLACK, BLACK);
138        bg_color!(BG_BLUE, BLUE);
139        bg_color!(BG_BRIGHT_BLACK, BRIGHT_BLACK);
140        bg_color!(BG_BRIGHT_BLUE, BRIGHT_BLUE);
141        bg_color!(BG_BRIGHT_CYAN, BRIGHT_CYAN);
142        bg_color!(BG_BRIGHT_GREEN, BRIGHT_GREEN);
143        bg_color!(BG_BRIGHT_MAGENTA, BRIGHT_MAGENTA);
144        bg_color!(BG_BRIGHT_RED, BRIGHT_RED);
145        bg_color!(BG_BRIGHT_WHITE, BRIGHT_WHITE);
146        bg_color!(BG_BRIGHT_YELLOW, BRIGHT_YELLOW);
147        bg_color!(BG_CYAN, CYAN);
148        bg_color!(BG_GREEN, GREEN);
149        bg_color!(BG_MAGENTA, MAGENTA);
150        bg_color!(BG_RED, RED);
151        bg_color!(BG_WHITE, WHITE);
152        bg_color!(BG_YELLOW, YELLOW);
153
154        macro_rules! attr {
155            ($attr:expr, $term_attr:expr) => {
156                if self.contains($attr) {
157                    let attr = $term_attr;
158                    if term.supports_attr(attr) {
159                        term.attr(attr)?;
160                    }
161                }
162            };
163        }
164
165        attr!(BOLD, term::Attr::Bold);
166        attr!(DIM, term::Attr::Dim);
167        attr!(ITALIC, term::Attr::Italic(true));
168        attr!(UNDERLINE, term::Attr::Underline(true));
169        attr!(BLINK, term::Attr::Blink);
170        attr!(STANDOUT, term::Attr::Standout(true));
171        attr!(REVERSE, term::Attr::Reverse);
172        attr!(SECURE, term::Attr::Secure);
173
174        Ok(())
175    }
176}
177
178///////////////////////////////////////////////////////////////////////////
179
180pub struct StyleCursor<'term, T: ?Sized + Terminal> {
181    current_style: Style,
182    term: &'term mut T,
183}
184
185impl<'term, T: ?Sized + Terminal> StyleCursor<'term, T> {
186    pub fn new(term: &'term mut T) -> term::Result<StyleCursor<'term, T>> {
187        let current_style = Style::default();
188        current_style.apply(term)?;
189        Ok(StyleCursor {
190            current_style,
191            term,
192        })
193    }
194
195    pub fn term(&mut self) -> &mut T {
196        self.term
197    }
198
199    pub fn set_style(&mut self, style: Style) -> term::Result<()> {
200        if style != self.current_style {
201            style.apply(self.term)?;
202            self.current_style = style;
203        }
204        Ok(())
205    }
206}