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