From b799d830779d232ba25eb5004aafb5a24d3f48fe Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 9 Apr 2017 13:30:24 -0700 Subject: [PATCH 01/11] Add `ColorStyle::Custom` for theme-independent coloring Moved `color_id` to curses backend. --- src/backend/curses/mod.rs | 18 ++++++++++- src/backend/curses/n.rs | 8 ++--- src/backend/curses/pan.rs | 8 ++--- src/theme.rs | 64 ++++++++++++++++----------------------- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index fa5a6df..e0ec9fc 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -1,4 +1,4 @@ -use theme::{BaseColor, Color}; +use theme::{BaseColor, Color, ColorStyle}; #[cfg(feature = "ncurses")] mod n; @@ -38,3 +38,19 @@ fn find_closest(color: &Color) -> u8 { Color::RgbLowRes(r, g, b) => (16 + 36 * r + 6 * g + b) as u8, } } + +fn color_id(style: ColorStyle) -> i16 { + match style { + ColorStyle::Background => 1, + ColorStyle::Shadow => 2, + ColorStyle::Primary => 3, + ColorStyle::Secondary => 4, + ColorStyle::Tertiary => 5, + ColorStyle::TitlePrimary => 6, + ColorStyle::TitleSecondary => 7, + ColorStyle::Highlight => 8, + ColorStyle::HighlightInactive => 9, + ColorStyle::Custom { front, back } => 10, + + } +} diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 467a6c2..0b04e47 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -1,7 +1,7 @@ extern crate ncurses; -use self::super::find_closest; +use self::super::{color_id, find_closest}; use backend; use event::{Event, Key}; use theme::{Color, ColorStyle, Effect}; @@ -23,7 +23,7 @@ impl backend::Backend for Concrete { ncurses::start_color(); ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE); ncurses::wbkgd(ncurses::stdscr(), - ncurses::COLOR_PAIR(ColorStyle::Background.id())); + ncurses::COLOR_PAIR(color_id(ColorStyle::Background))); Concrete } @@ -48,7 +48,7 @@ impl backend::Backend for Concrete { background: &Color) { // TODO: build the color on the spot - ncurses::init_pair(style.id(), + ncurses::init_pair(color_id(style), find_closest(foreground) as i16, find_closest(background) as i16); } @@ -58,7 +58,7 @@ impl backend::Backend for Concrete { let mut current_color: i16 = 0; ncurses::attr_get(&mut current_style, &mut current_color); - let style = ncurses::COLOR_PAIR(color.id()); + let style = ncurses::COLOR_PAIR(color_id(color)); ncurses::attron(style); f(); // ncurses::attroff(style); diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 062df26..786d544 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -2,7 +2,7 @@ extern crate pancurses; -use self::super::find_closest; +use self::super::{color_id, find_closest}; use backend; use event::{Event, Key}; use theme::{Color, ColorStyle, Effect}; @@ -21,7 +21,7 @@ impl backend::Backend for Concrete { pancurses::cbreak(); pancurses::start_color(); pancurses::curs_set(0); - window.bkgd(pancurses::ColorPair(ColorStyle::Background.id() as u8)); + window.bkgd(pancurses::ColorPair(color_id(ColorStyle::Background) as u8)); Concrete { window: window } } @@ -41,14 +41,14 @@ impl backend::Backend for Concrete { fn init_color_style(&mut self, style: ColorStyle, foreground: &Color, background: &Color) { - pancurses::init_pair(style.id(), + pancurses::init_pair(color_id(style), find_closest(foreground) as i16, find_closest(background) as i16); } fn with_color(&self, color: ColorStyle, f: F) { let (_, current_color_pair) = self.window.attrget(); - let color_attribute = pancurses::ColorPair(color.id() as u8); + let color_attribute = pancurses::ColorPair(color_id(color) as u8); self.window.attron(color_attribute); f(); diff --git a/src/theme.rs b/src/theme.rs index 5b4f0e4..803bad6 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -158,23 +158,13 @@ pub enum ColorStyle { Highlight, /// Highlight color for inactive views (not in focus). HighlightInactive, -} - -impl ColorStyle { - /// Returns the ncurses pair ID associated with this color pair. - pub fn id(self) -> i16 { - match self { - ColorStyle::Background => 1, - ColorStyle::Shadow => 2, - ColorStyle::Primary => 3, - ColorStyle::Secondary => 4, - ColorStyle::Tertiary => 5, - ColorStyle::TitlePrimary => 6, - ColorStyle::TitleSecondary => 7, - ColorStyle::Highlight => 8, - ColorStyle::HighlightInactive => 9, - } - } + /// Directly specifies colors, independently of the theme. + Custom { + /// Foreground color + front: Color, + /// Background color + back: Color + }, } /// Represents the style a Cursive application will use. @@ -288,7 +278,7 @@ impl BorderStyle { /// Color configuration for the application. /// /// Assign each color role an actual color. -#[derive(Clone,Debug)] +#[derive(Copy,Clone,Debug)] pub struct Palette { /// Color used for the application background. pub background: Color, @@ -405,8 +395,6 @@ pub enum Color { RgbLowRes(u8, u8, u8), } -impl Color {} - /// Possible error returned when loading a theme. #[derive(Debug)] pub enum Error { @@ -431,24 +419,24 @@ impl From for Error { impl Color { fn parse(value: &str) -> Option { Some(match value { - "black" => Color::Dark(BaseColor::Black), - "red" => Color::Dark(BaseColor::Red), - "green" => Color::Dark(BaseColor::Green), - "yellow" => Color::Dark(BaseColor::Yellow), - "blue" => Color::Dark(BaseColor::Blue), - "magenta" => Color::Dark(BaseColor::Magenta), - "cyan" => Color::Dark(BaseColor::Cyan), - "white" => Color::Dark(BaseColor::White), - "light black" => Color::Light(BaseColor::Black), - "light red" => Color::Light(BaseColor::Red), - "light green" => Color::Light(BaseColor::Green), - "light yellow" => Color::Light(BaseColor::Yellow), - "light blue" => Color::Light(BaseColor::Blue), - "light magenta" => Color::Light(BaseColor::Magenta), - "light cyan" => Color::Light(BaseColor::Cyan), - "light white" => Color::Light(BaseColor::White), - value => return Color::parse_special(value), - }) + "black" => Color::Dark(BaseColor::Black), + "red" => Color::Dark(BaseColor::Red), + "green" => Color::Dark(BaseColor::Green), + "yellow" => Color::Dark(BaseColor::Yellow), + "blue" => Color::Dark(BaseColor::Blue), + "magenta" => Color::Dark(BaseColor::Magenta), + "cyan" => Color::Dark(BaseColor::Cyan), + "white" => Color::Dark(BaseColor::White), + "light black" => Color::Light(BaseColor::Black), + "light red" => Color::Light(BaseColor::Red), + "light green" => Color::Light(BaseColor::Green), + "light yellow" => Color::Light(BaseColor::Yellow), + "light blue" => Color::Light(BaseColor::Blue), + "light magenta" => Color::Light(BaseColor::Magenta), + "light cyan" => Color::Light(BaseColor::Cyan), + "light white" => Color::Light(BaseColor::White), + value => return Color::parse_special(value), + }) } fn parse_special(value: &str) -> Option { From 9e1a83f7cc6483d420ade446c4595f8a0afe0293 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sun, 11 Jun 2017 15:01:35 -0700 Subject: [PATCH 02/11] Set up Custom color in termion and ncurses backends --- examples/theme_manual.rs | 35 ++++++++++++++++++------------- src/backend/curses/mod.rs | 3 +-- src/backend/curses/n.rs | 35 ++++++++++++++++++++++--------- src/backend/termion.rs | 44 ++++++++++++++++++--------------------- src/theme.rs | 8 +++---- 5 files changed, 71 insertions(+), 54 deletions(-) diff --git a/examples/theme_manual.rs b/examples/theme_manual.rs index b7b5e57..6229994 100644 --- a/examples/theme_manual.rs +++ b/examples/theme_manual.rs @@ -1,26 +1,33 @@ extern crate cursive; use cursive::Cursive; -use cursive::views::Dialog; -use cursive::theme::BorderStyle; +use cursive::theme::{ColorStyle, BaseColor, Color, BorderStyle}; +use cursive::views::{EditView, LinearLayout, Dialog, TextView}; fn main() { let mut siv = Cursive::new(); - siv.add_layer(Dialog::text("This is a dynamic theme example!") - .button("Change", |s| { - let mut theme = s.current_theme().clone(); + let layout = LinearLayout::vertical() + .child(TextView::new("This is a dynamic theme example!")) + .child(EditView::new().content("Woo! colors!").style(ColorStyle::Custom { + front: Color::Rgb(200, 150, 150), + back: Color::Dark(BaseColor::Blue), + })); - theme.shadow = !theme.shadow; - theme.borders = match theme.borders { - Some(BorderStyle::Simple) => Some(BorderStyle::Outset), - Some(BorderStyle::Outset) => None, - None => Some(BorderStyle::Simple), - }; + siv.add_layer(Dialog::around(layout) + .button("Change", |s| { + let mut theme = s.current_theme().clone(); - s.set_theme(theme); - }) - .button("Quit", Cursive::quit)); + theme.shadow = !theme.shadow; + theme.borders = match theme.borders { + Some(BorderStyle::Simple) => Some(BorderStyle::Outset), + Some(BorderStyle::Outset) => None, + None => Some(BorderStyle::Simple), + }; + + s.set_theme(theme); + }) + .button("Quit", Cursive::quit)); siv.run(); } diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index e0ec9fc..fb23906 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -50,7 +50,6 @@ fn color_id(style: ColorStyle) -> i16 { ColorStyle::TitleSecondary => 7, ColorStyle::Highlight => 8, ColorStyle::HighlightInactive => 9, - ColorStyle::Custom { front, back } => 10, - + ColorStyle::Custom { .. } => 10, } } diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index 0b04e47..a385836 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -4,10 +4,13 @@ extern crate ncurses; use self::super::{color_id, find_closest}; use backend; use event::{Event, Key}; +use std::cell::Cell; use theme::{Color, ColorStyle, Effect}; use utf8; -pub struct Concrete; +pub struct Concrete { + current_style: Cell, +} impl backend::Backend for Concrete { fn init() -> Self { @@ -25,7 +28,7 @@ impl backend::Backend for Concrete { ncurses::wbkgd(ncurses::stdscr(), ncurses::COLOR_PAIR(color_id(ColorStyle::Background))); - Concrete + Concrete { current_style: Cell::new(ColorStyle::Background) } } fn screen_size(&self) -> (usize, usize) { @@ -46,7 +49,6 @@ impl backend::Backend for Concrete { fn init_color_style(&mut self, style: ColorStyle, foreground: &Color, background: &Color) { - // TODO: build the color on the spot ncurses::init_pair(color_id(style), find_closest(foreground) as i16, @@ -54,15 +56,13 @@ impl backend::Backend for Concrete { } fn with_color(&self, color: ColorStyle, f: F) { - let mut current_style: ncurses::attr_t = 0; - let mut current_color: i16 = 0; - ncurses::attr_get(&mut current_style, &mut current_color); + let current = self.current_style.get(); - let style = ncurses::COLOR_PAIR(color_id(color)); - ncurses::attron(style); + self.current_style.set(color); + set_colorstyle(color); f(); - // ncurses::attroff(style); - ncurses::attron(current_style); + set_colorstyle(current); + self.current_style.set(current); } fn with_effect(&self, effect: Effect, f: F) { @@ -109,6 +109,21 @@ impl backend::Backend for Concrete { } } +fn set_colorstyle(style: ColorStyle) { + if let ColorStyle::Custom { + ref front, + ref back, + } = style { + + println_stderr!("Redifining..."); + ncurses::init_pair(color_id(style), + find_closest(front) as i16, + find_closest(back) as i16); + } + let style = ncurses::COLOR_PAIR(color_id(style)); + ncurses::attron(style); +} + /// Returns the Key enum corresponding to the given ncurses event. fn parse_ncurses_char(ch: i32) -> Event { match ch { diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 8776fe3..f909b77 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -13,7 +13,7 @@ use backend; use chan; use event::{Event, Key}; use std::cell::Cell; -use std::collections::BTreeMap; +use std::collections::HashMap; use std::fmt; use std::io::Write; use std::thread; @@ -23,7 +23,7 @@ use theme; pub struct Concrete { terminal: AlternateScreen>, current_style: Cell, - colors: BTreeMap, Box)>, + colors: HashMap, Box)>, input: chan::Receiver, resize: chan::Receiver, @@ -35,20 +35,6 @@ trait Effectable { fn off(&self); } -struct ColorRef<'a>(&'a tcolor::Color); - -impl<'a> tcolor::Color for ColorRef<'a> { - #[inline] - fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.write_fg(f) - } - - #[inline] - fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.write_bg(f) - } -} - impl Effectable for theme::Effect { fn on(&self) { match *self { @@ -66,13 +52,20 @@ impl Effectable for theme::Effect { } fn apply_colors(fg: &tcolor::Color, bg: &tcolor::Color) { - print!("{}{}", tcolor::Fg(ColorRef(fg)), tcolor::Bg(ColorRef(bg))); + print!("{}{}", tcolor::Fg(fg), tcolor::Bg(bg)); } impl Concrete { fn apply_colorstyle(&self, color_style: theme::ColorStyle) { - let (ref fg, ref bg) = self.colors[&color_style.id()]; - apply_colors(&**fg, &**bg); + if let theme::ColorStyle::Custom { front, back } = color_style { + let fg = colour_to_termion_colour(&front); + let bg = colour_to_termion_colour(&back); + apply_colors(&*fg, &*bg); + } else { + let (ref fg, ref bg) = self.colors[&color_style]; + apply_colors(&**fg, &**bg); + } + } } @@ -82,7 +75,9 @@ impl backend::Backend for Concrete { let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]); - let terminal = AlternateScreen::from(::std::io::stdout().into_raw_mode().unwrap()); + let terminal = AlternateScreen::from(::std::io::stdout() + .into_raw_mode() + .unwrap()); let (sender, receiver) = chan::async(); thread::spawn(move || for key in ::std::io::stdin().events() { @@ -94,7 +89,7 @@ impl backend::Backend for Concrete { let backend = Concrete { terminal: terminal, current_style: Cell::new(theme::ColorStyle::Background), - colors: BTreeMap::new(), + colors: HashMap::new(), input: receiver, resize: resize, timeout: None, @@ -114,9 +109,10 @@ impl backend::Backend for Concrete { fn init_color_style(&mut self, style: theme::ColorStyle, foreground: &theme::Color, background: &theme::Color) { // Step 1: convert foreground and background into proper termion Color - self.colors.insert(style.id(), - (colour_to_termion_colour(foreground), - colour_to_termion_colour(background))); + self.colors + .insert(style, + (colour_to_termion_colour(foreground), + colour_to_termion_colour(background))); } fn with_color(&self, color: theme::ColorStyle, f: F) { diff --git a/src/theme.rs b/src/theme.rs index 803bad6..73a73db 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -129,7 +129,7 @@ pub enum Effect { /// No effect Simple, /// Reverses foreground and background colors - Reverse, + Reverse, // TODO: bold, italic, underline } @@ -138,7 +138,7 @@ pub enum Effect { /// Represents a color pair role to use when printing something. /// /// The current theme will assign each role a foreground and background color. -#[derive(Clone,Copy)] +#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)] pub enum ColorStyle { /// Application background, where no view is present. Background, @@ -342,7 +342,7 @@ fn load_color(target: &mut Color, value: Option<&toml::Value>) -> bool { } /// One of the 8 base colors. -#[derive(Clone,Copy,Debug)] +#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)] pub enum BaseColor { /// Black color /// @@ -379,7 +379,7 @@ pub enum BaseColor { } /// Represents a color used by the theme. -#[derive(Clone,Copy,Debug)] +#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)] pub enum Color { /// One of the 8 base colors. Dark(BaseColor), From 5c42a599540aceddd1de59713be07d201bb4da7b Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 12 Jun 2017 11:59:33 -0700 Subject: [PATCH 03/11] Refactor colors management Do not register pairs with backend. Let backend cache color pairs if needed. --- examples/theme_manual.rs | 6 +- src/backend/blt.rs | 7 +- src/backend/curses/n.rs | 82 ++++++++++++--------- src/backend/curses/pan.rs | 20 +++--- src/backend/mod.rs | 5 +- src/backend/termion.rs | 3 +- src/lib.rs | 11 ++- src/printer.rs | 16 ++--- src/theme.rs | 145 ++++++++++++++++++++++++++------------ 9 files changed, 182 insertions(+), 113 deletions(-) diff --git a/examples/theme_manual.rs b/examples/theme_manual.rs index 6229994..ea5f9aa 100644 --- a/examples/theme_manual.rs +++ b/examples/theme_manual.rs @@ -20,9 +20,9 @@ fn main() { theme.shadow = !theme.shadow; theme.borders = match theme.borders { - Some(BorderStyle::Simple) => Some(BorderStyle::Outset), - Some(BorderStyle::Outset) => None, - None => Some(BorderStyle::Simple), + BorderStyle::Simple => BorderStyle::Outset, + BorderStyle::Outset => BorderStyle::None, + BorderStyle::None => BorderStyle::Simple, }; s.set_theme(theme); diff --git a/src/backend/blt.rs b/src/backend/blt.rs index d8c021c..341fa9f 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -26,9 +26,10 @@ impl backend::Backend for Concrete { fn init_color_style(&mut self, style: ColorStyle, foreground: &Color, background: &Color) { - self.colours.insert(style.id(), - (colour_to_blt_colour(foreground), - colour_to_blt_colour(background))); + self.colours + .insert(style.id(), + (colour_to_blt_colour(foreground), + colour_to_blt_colour(background))); } fn with_color(&self, color: ColorStyle, f: F) { diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index a385836..eabf489 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -1,15 +1,54 @@ extern crate ncurses; - use self::super::{color_id, find_closest}; use backend; use event::{Event, Key}; -use std::cell::Cell; -use theme::{Color, ColorStyle, Effect}; +use std::cell::{RefCell, Cell}; +use std::collections::HashMap; +use theme::{ColorPair, ColorStyle, Effect}; use utf8; pub struct Concrete { - current_style: Cell, + current_style: Cell, + pairs: RefCell>, +} + +impl Concrete { + fn insert_color(&self, pairs: &mut HashMap, pair: ColorPair) -> i16 { + + let n = 1 + pairs.len() as i16; + let target = if ncurses::COLOR_PAIRS() > n as i32 { + // We still have plenty of space for everyone. + n + } else { + // The world is too small for both of us. + let target = n - 1; + // Remove the mapping to n-1 + pairs.retain(|_, &mut v| v != target); + target + }; + pairs.insert(pair, target); + ncurses::init_pair(target, + find_closest(&pair.front) as i16, + find_closest(&pair.back) as i16); + target + } + fn set_colorstyle(&self, pair: ColorPair) { + self.current_style.set(pair); + + let mut pairs = self.pairs.borrow_mut(); + + // Find if we have this color in stock + let i = if pairs.contains_key(&pair) { + // We got it! + pairs[&pair] + } else { + self.insert_color(&mut *pairs, pair) + }; + + let style = ncurses::COLOR_PAIR(i); + ncurses::attron(style); + } } impl backend::Backend for Concrete { @@ -28,7 +67,10 @@ impl backend::Backend for Concrete { ncurses::wbkgd(ncurses::stdscr(), ncurses::COLOR_PAIR(color_id(ColorStyle::Background))); - Concrete { current_style: Cell::new(ColorStyle::Background) } + Concrete { + current_style: Cell::new(ColorPair::from_256colors(0, 0)), + pairs: RefCell::new(HashMap::new()), + } } fn screen_size(&self) -> (usize, usize) { @@ -47,21 +89,12 @@ impl backend::Backend for Concrete { } - fn init_color_style(&mut self, style: ColorStyle, foreground: &Color, - background: &Color) { - - ncurses::init_pair(color_id(style), - find_closest(foreground) as i16, - find_closest(background) as i16); - } - - fn with_color(&self, color: ColorStyle, f: F) { + fn with_color(&self, colors: ColorPair, f: F) { let current = self.current_style.get(); - self.current_style.set(color); - set_colorstyle(color); + self.set_colorstyle(colors); f(); - set_colorstyle(current); + self.set_colorstyle(current); self.current_style.set(current); } @@ -109,21 +142,6 @@ impl backend::Backend for Concrete { } } -fn set_colorstyle(style: ColorStyle) { - if let ColorStyle::Custom { - ref front, - ref back, - } = style { - - println_stderr!("Redifining..."); - ncurses::init_pair(color_id(style), - find_closest(front) as i16, - find_closest(back) as i16); - } - let style = ncurses::COLOR_PAIR(color_id(style)); - ncurses::attron(style); -} - /// Returns the Key enum corresponding to the given ncurses event. fn parse_ncurses_char(ch: i32) -> Event { match ch { diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 786d544..5a11a5a 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -21,7 +21,8 @@ impl backend::Backend for Concrete { pancurses::cbreak(); pancurses::start_color(); pancurses::curs_set(0); - window.bkgd(pancurses::ColorPair(color_id(ColorStyle::Background) as u8)); + window.bkgd(pancurses::ColorPair(color_id(ColorStyle::Background) as + u8)); Concrete { window: window } } @@ -52,7 +53,8 @@ impl backend::Backend for Concrete { self.window.attron(color_attribute); f(); - self.window.attron(pancurses::ColorPair(current_color_pair as u8)); + self.window + .attron(pancurses::ColorPair(current_color_pair as u8)); } fn with_effect(&self, effect: Effect, f: F) { @@ -94,12 +96,14 @@ impl backend::Backend for Concrete { pancurses::Input::Character(c) if 32 <= (c as u32) && (c as u32) <= 255 => { Event::Char(utf8::read_char(c as u8, || { - self.window.getch().and_then(|i| match i { - pancurses::Input::Character(c) => { - Some(c as u8) - } - _ => None, - }) + self.window + .getch() + .and_then(|i| match i { + pancurses::Input::Character(c) => { + Some(c as u8) + } + _ => None, + }) }) .unwrap()) } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index f8289e1..3d4fa19 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -25,9 +25,6 @@ pub trait Backend { fn has_colors(&self) -> bool; - fn init_color_style(&mut self, style: theme::ColorStyle, - foreground: &theme::Color, background: &theme::Color); - fn print_at(&self, (usize, usize), &str); fn poll_event(&self) -> event::Event; @@ -35,6 +32,6 @@ pub trait Backend { fn screen_size(&self) -> (usize, usize); // TODO: unify those into a single method? - fn with_color(&self, color: theme::ColorStyle, f: F); + fn with_color(&self, colors: theme::ColorPair, f: F); fn with_effect(&self, effect: theme::Effect, f: F); } diff --git a/src/backend/termion.rs b/src/backend/termion.rs index f909b77..72ee325 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -23,7 +23,8 @@ use theme; pub struct Concrete { terminal: AlternateScreen>, current_style: Cell, - colors: HashMap, Box)>, + colors: + HashMap, Box)>, input: chan::Receiver, resize: chan::Receiver, diff --git a/src/lib.rs b/src/lib.rs index 705048a..7c1fe2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,10 +169,10 @@ new_default!(Cursive); impl Cursive { /// Creates a new Cursive root, and initialize the back-end. pub fn new() -> Self { - let mut backend = backend::Concrete::init(); + let backend = backend::Concrete::init(); let theme = theme::load_default(); - theme.activate(&mut backend); + // theme.activate(&mut backend); // let theme = theme::load_theme("assets/style.toml").unwrap(); let (tx, rx) = mpsc::channel(); @@ -286,7 +286,7 @@ impl Cursive { /// Sets the current theme. pub fn set_theme(&mut self, theme: theme::Theme) { self.theme = theme; - self.theme.activate(&mut self.backend); + // self.theme.activate(&mut self.backend); self.backend.clear(); } @@ -533,9 +533,6 @@ impl Cursive { } fn draw(&mut self) { - // TODO: don't clone the theme - // Reference it or something - let sizes = self.screen().layer_sizes(); if self.last_sizes != sizes { self.clear(); @@ -543,7 +540,7 @@ impl Cursive { } let printer = Printer::new(self.screen_size(), - self.theme.clone(), + &self.theme, &self.backend); // Draw the currently active screen diff --git a/src/printer.rs b/src/printer.rs index d8e5f55..d7c9942 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -21,7 +21,7 @@ pub struct Printer<'a> { /// Whether the view to draw is currently focused or not. pub focused: bool, /// Currently used theme - pub theme: Theme, + pub theme: &'a Theme, /// `true` if nothing has been drawn yet. new: Rc>, @@ -34,7 +34,7 @@ impl<'a> Printer<'a> { /// /// But nobody needs to know that. #[doc(hidden)] - pub fn new>(size: T, theme: Theme, + pub fn new>(size: T, theme: &'a Theme, backend: &'a backend::Concrete) -> Self { Printer { @@ -131,7 +131,7 @@ impl<'a> Printer<'a> { pub fn with_color(&self, c: ColorStyle, f: F) where F: FnOnce(&Printer) { - self.backend.with_color(c, || f(self)); + self.backend.with_color(c.resolve(self.theme), || f(self)); } /// Same as `with_color`, but apply a ncurses style instead, @@ -195,8 +195,8 @@ impl<'a> Printer<'a> { where F: FnOnce(&Printer) { let color = match self.theme.borders { - None => return, - Some(BorderStyle::Outset) if !invert => ColorStyle::Tertiary, + BorderStyle::None => return, + BorderStyle::Outset if !invert => ColorStyle::Tertiary, _ => ColorStyle::Primary, }; @@ -213,8 +213,8 @@ impl<'a> Printer<'a> { where F: FnOnce(&Printer) { let color = match self.theme.borders { - None => return, - Some(BorderStyle::Outset) if invert => ColorStyle::Tertiary, + BorderStyle::None => return, + BorderStyle::Outset if invert => ColorStyle::Tertiary, _ => ColorStyle::Primary, }; @@ -260,7 +260,7 @@ impl<'a> Printer<'a> { // We can't be larger than what remains size: Vec2::min(self.size - offset, size), focused: self.focused && focused, - theme: self.theme.clone(), + theme: self.theme, backend: self.backend, new: self.new.clone(), } diff --git a/src/theme.rs b/src/theme.rs index 73a73db..f8c8607 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -114,8 +114,6 @@ //! highlight_inactive = "#5555FF" //! ``` - -use backend::{self, Backend}; use std::fs::File; use std::io; use std::io::Read; @@ -133,6 +131,35 @@ pub enum Effect { // TODO: bold, italic, underline } +/// Combines a front and back color. +#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)] +pub struct ColorPair { + /// Color used for the foreground. + pub front: Color, + /// Color used for the background. + pub back: Color, +} + +impl ColorPair { + /// Return an inverted color pair. + /// + /// With swapped front abd back color. + pub fn invert(&self) -> Self { + ColorPair { + front: self.back, + back: self.front, + } + } + + /// Creates a new color pair from color IDs. + pub fn from_256colors(front: u8, back: u8) -> Self { + Self { + front: Color::from_256colors(front), + back: Color::from_256colors(back), + } + } +} + /// Possible color style for a cell. /// /// Represents a color pair role to use when printing something. @@ -163,17 +190,39 @@ pub enum ColorStyle { /// Foreground color front: Color, /// Background color - back: Color + back: Color, }, } +impl ColorStyle { + /// Return the color pair that this style represents. + /// + /// Returns `(front, back)`. + pub fn resolve(&self, theme: &Theme) -> ColorPair { + let c = &theme.colors; + let (front, back) = match *self { + ColorStyle::Background => (c.view, c.background), + ColorStyle::Shadow => (c.shadow, c.shadow), + ColorStyle::Primary => (c.primary, c.view), + ColorStyle::Secondary => (c.secondary, c.view), + ColorStyle::Tertiary => (c.tertiary, c.view), + ColorStyle::TitlePrimary => (c.title_primary, c.view), + ColorStyle::TitleSecondary => (c.title_secondary, c.view), + ColorStyle::Highlight => (c.view, c.highlight), + ColorStyle::HighlightInactive => (c.view, c.highlight_inactive), + ColorStyle::Custom { front, back } => (front, back), + }; + ColorPair { front, back } + } +} + /// Represents the style a Cursive application will use. #[derive(Clone,Debug)] pub struct Theme { /// Whether views in a StackView should have shadows. pub shadow: bool, /// How view borders should be drawn. - pub borders: Option, + pub borders: BorderStyle, /// What colors should be used through the application? pub colors: Palette, } @@ -182,7 +231,7 @@ impl Default for Theme { fn default() -> Self { Theme { shadow: true, - borders: Some(BorderStyle::Simple), + borders: BorderStyle::Simple, colors: Palette { background: Color::Dark(BaseColor::Blue), shadow: Color::Dark(BaseColor::Black), @@ -213,64 +262,29 @@ impl Theme { self.colors.load(table); } } - - /// Sets a theme as active. - /// - /// **Don't use this directly.** Uses [`Cursive::set_theme`] instead. - /// - /// [`Cursive::set_theme`]: ../struct.Cursive.html#method.set_theme - pub fn activate(&self, backend: &mut backend::Concrete) { - // Initialize each color with the backend - backend.init_color_style(ColorStyle::Background, - &self.colors.view, - &self.colors.background); - backend.init_color_style(ColorStyle::Shadow, - &self.colors.shadow, - &self.colors.shadow); - backend.init_color_style(ColorStyle::Primary, - &self.colors.primary, - &self.colors.view); - backend.init_color_style(ColorStyle::Secondary, - &self.colors.secondary, - &self.colors.view); - backend.init_color_style(ColorStyle::Tertiary, - &self.colors.tertiary, - &self.colors.view); - backend.init_color_style(ColorStyle::TitlePrimary, - &self.colors.title_primary, - &self.colors.view); - backend.init_color_style(ColorStyle::TitleSecondary, - &self.colors.title_secondary, - &self.colors.view); - backend.init_color_style(ColorStyle::Highlight, - &self.colors.view, - &self.colors.highlight); - backend.init_color_style(ColorStyle::HighlightInactive, - &self.colors.view, - &self.colors.highlight_inactive); - backend.clear(); - } } /// Specifies how some borders should be drawn. /// /// Borders are used around Dialogs, select popups, and panels. -#[derive(Clone,Copy,Debug)] +#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)] pub enum BorderStyle { /// Simple borders. Simple, /// Outset borders with a simple 3d effect. Outset, + /// No borders. + None, } impl BorderStyle { - fn from(s: &str) -> Option { + fn from(s: &str) -> Self { if s == "simple" { - Some(BorderStyle::Simple) + BorderStyle::Simple } else if s == "outset" { - Some(BorderStyle::Outset) + BorderStyle::Outset } else { - None + BorderStyle::None } } } @@ -378,6 +392,22 @@ pub enum BaseColor { White, } +impl From for BaseColor { + fn from(n: u8) -> Self { + match n % 8 { + 0 => BaseColor::Black, + 1 => BaseColor::Red, + 2 => BaseColor::Green, + 3 => BaseColor::Yellow, + 4 => BaseColor::Blue, + 5 => BaseColor::Magenta, + 6 => BaseColor::Cyan, + 7 => BaseColor::White, + _ => unreachable!(), + } + } +} + /// Represents a color used by the theme. #[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)] pub enum Color { @@ -417,6 +447,27 @@ impl From for Error { } impl Color { + /// Creates a color from its ID in the 256 colors list. + /// + /// * Colors 0-7 are base dark colors. + /// * Colors 8-15 are base light colors. + /// * Colors 16-255 are rgb colors with 6 values per channel. + pub fn from_256colors(n: u8) -> Self { + if n < 8 { + Color::Dark(BaseColor::from(n)) + } else if n < 16 { + Color::Light(BaseColor::from(n)) + } else { + let n = n - 16; + + let r = n / 36; + let g = (n % 36) / 6; + let b = n % 6; + + Color::RgbLowRes(r, g, b) + } + } + fn parse(value: &str) -> Option { Some(match value { "black" => Color::Dark(BaseColor::Black), From 9f5ce65e34509928b25f53fb4d6d2b20f41f9c01 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 12 Jun 2017 18:03:52 -0700 Subject: [PATCH 04/11] Fix doc comment --- src/printer.rs | 6 ++++-- src/view/scroll.rs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index d7c9942..66f702e 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -123,7 +123,8 @@ impl<'a> Printer<'a> { /// # use cursive::theme; /// # use cursive::backend::{self, Backend}; /// # let b = backend::Concrete::init(); - /// # let printer = Printer::new((6,4), theme::load_default(), &b); + /// # let t = theme::load_default(); + /// # let printer = Printer::new((6,4), &t, &b); /// printer.with_color(theme::ColorStyle::Highlight, |printer| { /// printer.print((0,0), "This text is highlighted!"); /// }); @@ -156,7 +157,8 @@ impl<'a> Printer<'a> { /// # use cursive::theme; /// # use cursive::backend::{self, Backend}; /// # let b = backend::Concrete::init(); - /// # let printer = Printer::new((6,4), theme::load_default(), &b); + /// # let t = theme::load_default(); + /// # let printer = Printer::new((6,4), &t, &b); /// printer.print_box((0,0), (6,4), false); /// ``` pub fn print_box, S: Into>(&self, start: T, size: S, diff --git a/src/view/scroll.rs b/src/view/scroll.rs index 2e417e9..ff5c872 100644 --- a/src/view/scroll.rs +++ b/src/view/scroll.rs @@ -159,7 +159,8 @@ impl ScrollBase { /// # use cursive::backend::{self, Backend}; /// # let scrollbase = ScrollBase::new(); /// # let b = backend::Concrete::init(); - /// # let printer = Printer::new((5,1), theme::load_default(), &b); + /// # let t = theme::load_default(); + /// # let printer = Printer::new((5,1), &t, &b); /// # let printer = &printer; /// let lines = ["Line 1", "Line number 2"]; /// scrollbase.draw(printer, |printer, i| { From 059812f42784bea06711eb3159ee155090fa5e86 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 12 Jun 2017 18:31:08 -0700 Subject: [PATCH 05/11] Update termion backend --- src/backend/termion.rs | 112 ++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 69 deletions(-) diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 72ee325..e79e243 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -22,10 +22,7 @@ use theme; pub struct Concrete { terminal: AlternateScreen>, - current_style: Cell, - colors: - HashMap, Box)>, - + current_style: Cell, input: chan::Receiver, resize: chan::Receiver, timeout: Option, @@ -57,16 +54,9 @@ fn apply_colors(fg: &tcolor::Color, bg: &tcolor::Color) { } impl Concrete { - fn apply_colorstyle(&self, color_style: theme::ColorStyle) { - if let theme::ColorStyle::Custom { front, back } = color_style { - let fg = colour_to_termion_colour(&front); - let bg = colour_to_termion_colour(&back); - apply_colors(&*fg, &*bg); - } else { - let (ref fg, ref bg) = self.colors[&color_style]; - apply_colors(&**fg, &**bg); - } - + fn apply_colorstyle(&self, colors: theme::ColorPair) { + with_color(&colors.front, |c| print!("{}", tcolor::Fg(c))); + with_color(&colors.back, |c| print!("{}", tcolor::Bg(c))); } } @@ -89,8 +79,7 @@ impl backend::Backend for Concrete { let backend = Concrete { terminal: terminal, - current_style: Cell::new(theme::ColorStyle::Background), - colors: HashMap::new(), + current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)), input: receiver, resize: resize, timeout: None, @@ -107,16 +96,7 @@ impl backend::Backend for Concrete { termion::clear::All); } - fn init_color_style(&mut self, style: theme::ColorStyle, - foreground: &theme::Color, background: &theme::Color) { - // Step 1: convert foreground and background into proper termion Color - self.colors - .insert(style, - (colour_to_termion_colour(foreground), - colour_to_termion_colour(background))); - } - - fn with_color(&self, color: theme::ColorStyle, f: F) { + fn with_color(&self, color: theme::ColorPair, f: F) { let current_style = self.current_style.get(); self.apply_colorstyle(color); @@ -145,7 +125,7 @@ impl backend::Backend for Concrete { } fn clear(&self) { - self.apply_colorstyle(theme::ColorStyle::Background); + // self.apply_colorstyle(theme::ColorStyle::Background); print!("{}", termion::clear::All); } @@ -211,49 +191,43 @@ fn map_key(event: TEvent) -> Event { } -fn colour_to_termion_colour(clr: &theme::Color) -> Box { +fn with_color(clr: &theme::Color, f: F) -> R + where F: FnOnce(&tcolor::Color) -> R +{ + match *clr { - theme::Color::Dark(theme::BaseColor::Black) => Box::new(tcolor::Black), - theme::Color::Dark(theme::BaseColor::Red) => Box::new(tcolor::Red), - theme::Color::Dark(theme::BaseColor::Green) => Box::new(tcolor::Green), - theme::Color::Dark(theme::BaseColor::Yellow) => { - Box::new(tcolor::Yellow) - } - theme::Color::Dark(theme::BaseColor::Blue) => Box::new(tcolor::Blue), - theme::Color::Dark(theme::BaseColor::Magenta) => { - Box::new(tcolor::Magenta) - } - theme::Color::Dark(theme::BaseColor::Cyan) => Box::new(tcolor::Cyan), - theme::Color::Dark(theme::BaseColor::White) => Box::new(tcolor::White), + theme::Color::Dark(theme::BaseColor::Black) => f(&tcolor::Black), + theme::Color::Dark(theme::BaseColor::Red) => f(&tcolor::Red), + theme::Color::Dark(theme::BaseColor::Green) => f(&tcolor::Green), + theme::Color::Dark(theme::BaseColor::Yellow) => f(&tcolor::Yellow), + theme::Color::Dark(theme::BaseColor::Blue) => f(&tcolor::Blue), + theme::Color::Dark(theme::BaseColor::Magenta) => f(&tcolor::Magenta), + theme::Color::Dark(theme::BaseColor::Cyan) => f(&tcolor::Cyan), + theme::Color::Dark(theme::BaseColor::White) => f(&tcolor::White), - theme::Color::Light(theme::BaseColor::Black) => { - Box::new(tcolor::LightBlack) - } - theme::Color::Light(theme::BaseColor::Red) => { - Box::new(tcolor::LightRed) - } - theme::Color::Light(theme::BaseColor::Green) => { - Box::new(tcolor::LightGreen) - } - theme::Color::Light(theme::BaseColor::Yellow) => { - Box::new(tcolor::LightYellow) - } - theme::Color::Light(theme::BaseColor::Blue) => { - Box::new(tcolor::LightBlue) - } - theme::Color::Light(theme::BaseColor::Magenta) => { - Box::new(tcolor::LightMagenta) - } - theme::Color::Light(theme::BaseColor::Cyan) => { - Box::new(tcolor::LightCyan) - } - theme::Color::Light(theme::BaseColor::White) => { - Box::new(tcolor::LightWhite) - } + theme::Color::Light(theme::BaseColor::Black) => { + f(&tcolor::LightBlack) + } + theme::Color::Light(theme::BaseColor::Red) => f(&tcolor::LightRed), + theme::Color::Light(theme::BaseColor::Green) => { + f(&tcolor::LightGreen) + } + theme::Color::Light(theme::BaseColor::Yellow) => { + f(&tcolor::LightYellow) + } + theme::Color::Light(theme::BaseColor::Blue) => f(&tcolor::LightBlue), + theme::Color::Light(theme::BaseColor::Magenta) => { + f(&tcolor::LightMagenta) + } + theme::Color::Light(theme::BaseColor::Cyan) => f(&tcolor::LightCyan), + theme::Color::Light(theme::BaseColor::White) => { + f(&tcolor::LightWhite) + } - theme::Color::Rgb(r, g, b) => Box::new(tcolor::Rgb(r, g, b)), - theme::Color::RgbLowRes(r, g, b) => { - Box::new(tcolor::AnsiValue::rgb(r, g, b)) - } - } + theme::Color::Rgb(r, g, b) => f(&tcolor::Rgb(r, g, b)), + theme::Color::RgbLowRes(r, g, b) => { + f(&tcolor::AnsiValue::rgb(r, g, b)) + } + + } } From dea07d29cf995383212715a4dcbe718a0655d006 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 12 Jun 2017 23:29:26 -0700 Subject: [PATCH 06/11] Give the background color to the clear method. --- src/backend/blt.rs | 29 +++++++++++------------------ src/backend/curses/mod.rs | 17 +---------------- src/backend/curses/n.rs | 34 ++++++++++++++++++++++++---------- src/backend/mod.rs | 15 +++++++++------ src/backend/termion.rs | 7 +++++-- src/lib.rs | 6 +++--- src/printer.rs | 2 +- 7 files changed, 54 insertions(+), 56 deletions(-) diff --git a/src/backend/blt.rs b/src/backend/blt.rs index 341fa9f..7f56a21 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -6,7 +6,7 @@ use self::bear_lib_terminal::terminal::{self, Event as BltEvent, KeyCode}; use backend; use event::{Event, Key}; use std::collections::BTreeMap; -use theme::{BaseColor, Color, ColorStyle, Effect}; +use theme::{BaseColor, Color, ColorPair, Effect}; pub struct Concrete { colours: BTreeMap, @@ -24,16 +24,9 @@ impl backend::Backend for Concrete { terminal::close(); } - fn init_color_style(&mut self, style: ColorStyle, foreground: &Color, - background: &Color) { - self.colours - .insert(style.id(), - (colour_to_blt_colour(foreground), - colour_to_blt_colour(background))); - } - - fn with_color(&self, color: ColorStyle, f: F) { - let (fg, bg) = self.colours[&color.id()]; + fn with_color(&self, color: ColorPair, f: F) { + let fg = colour_to_blt_colour(color.front); + let bg = colour_to_blt_colour(color.back); terminal::with_colors(fg, bg, f); } @@ -61,7 +54,7 @@ impl backend::Backend for Concrete { (width as usize, height as usize) } - fn clear(&self) { + fn clear(&self, color: Color) { terminal::clear(None); } @@ -104,8 +97,8 @@ impl backend::Backend for Concrete { } } -fn colour_to_blt_colour(clr: &Color) -> BltColor { - let (r, g, b) = match *clr { +fn colour_to_blt_colour(clr: Color) -> BltColor { + let (r, g, b) = match clr { // Colours taken from // https://en.wikipedia.org/wiki/ANSI_escape_code#Colors Color::Dark(BaseColor::Black) => (0, 0, 0), @@ -141,7 +134,7 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event { KeyCode::F1 | KeyCode::F2 | KeyCode::F3 | KeyCode::F4 | KeyCode::F5 | KeyCode::F6 | KeyCode::F7 | KeyCode::F8 | KeyCode::F9 | KeyCode::F10 | KeyCode::F11 | KeyCode::F12 | - KeyCode::Enter | KeyCode::Escape | KeyCode::Backspace | + KeyCode::NumEnter | KeyCode::Enter | KeyCode::Escape | KeyCode::Backspace | KeyCode::Tab | KeyCode::Pause | KeyCode::Insert | KeyCode::Home | KeyCode::PageUp | KeyCode::Delete | KeyCode::End | KeyCode::PageDown | KeyCode::Right | KeyCode::Left | @@ -169,7 +162,7 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event { KeyCode::Apostrophe | KeyCode::Comma | KeyCode::Period | KeyCode::Slash | KeyCode::Space | KeyCode::NumDivide | KeyCode::NumMultiply | KeyCode::NumMinus | KeyCode::NumPlus | - KeyCode::NumEnter | KeyCode::NumPeriod | KeyCode::Num1 | + KeyCode::NumPeriod | KeyCode::Num1 | KeyCode::Num2 | KeyCode::Num3 | KeyCode::Num4 | KeyCode::Num5 | KeyCode::Num6 | KeyCode::Num7 | KeyCode::Num8 | KeyCode::Num9 | KeyCode::Num0 => { @@ -247,7 +240,7 @@ fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char { KeyCode::Num8 => '8', KeyCode::Num9 => '9', KeyCode::Num0 => '0', - _ => unreachable!(), + _ => { println_stderr!("{:?}", kc); unreachable!() }, } } @@ -265,7 +258,7 @@ fn blt_keycode_to_key(kc: KeyCode) -> Key { KeyCode::F10 => Key::F10, KeyCode::F11 => Key::F11, KeyCode::F12 => Key::F12, - KeyCode::Enter => Key::Enter, + KeyCode::NumEnter | KeyCode::Enter => Key::Enter, KeyCode::Escape => Key::Esc, KeyCode::Backspace => Key::Backspace, KeyCode::Tab => Key::Tab, diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index fb23906..fa5a6df 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -1,4 +1,4 @@ -use theme::{BaseColor, Color, ColorStyle}; +use theme::{BaseColor, Color}; #[cfg(feature = "ncurses")] mod n; @@ -38,18 +38,3 @@ fn find_closest(color: &Color) -> u8 { Color::RgbLowRes(r, g, b) => (16 + 36 * r + 6 * g + b) as u8, } } - -fn color_id(style: ColorStyle) -> i16 { - match style { - ColorStyle::Background => 1, - ColorStyle::Shadow => 2, - ColorStyle::Primary => 3, - ColorStyle::Secondary => 4, - ColorStyle::Tertiary => 5, - ColorStyle::TitlePrimary => 6, - ColorStyle::TitleSecondary => 7, - ColorStyle::Highlight => 8, - ColorStyle::HighlightInactive => 9, - ColorStyle::Custom { .. } => 10, - } -} diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index eabf489..a449596 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -1,11 +1,11 @@ extern crate ncurses; -use self::super::{color_id, find_closest}; +use self::super::find_closest; use backend; use event::{Event, Key}; use std::cell::{RefCell, Cell}; use std::collections::HashMap; -use theme::{ColorPair, ColorStyle, Effect}; +use theme::{Color, ColorPair, Effect}; use utf8; pub struct Concrete { @@ -14,7 +14,10 @@ pub struct Concrete { } impl Concrete { - fn insert_color(&self, pairs: &mut HashMap, pair: ColorPair) -> i16 { + /// Save a new color pair. + fn insert_color(&self, pairs: &mut HashMap, + pair: ColorPair) + -> i16 { let n = 1 + pairs.len() as i16; let target = if ncurses::COLOR_PAIRS() > n as i32 { @@ -33,19 +36,26 @@ impl Concrete { find_closest(&pair.back) as i16); target } - fn set_colorstyle(&self, pair: ColorPair) { - self.current_style.set(pair); + + /// Checks the pair in the cache, or re-define a color if needed. + fn get_or_create(&self, pair: ColorPair) -> i16 { let mut pairs = self.pairs.borrow_mut(); // Find if we have this color in stock - let i = if pairs.contains_key(&pair) { + if pairs.contains_key(&pair) { // We got it! pairs[&pair] } else { self.insert_color(&mut *pairs, pair) - }; + } + } + fn set_colorstyle(&self, pair: ColorPair) { + + let i = self.get_or_create(pair); + + self.current_style.set(pair); let style = ncurses::COLOR_PAIR(i); ncurses::attron(style); } @@ -64,8 +74,6 @@ impl backend::Backend for Concrete { ncurses::cbreak(); ncurses::start_color(); ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE); - ncurses::wbkgd(ncurses::stdscr(), - ncurses::COLOR_PAIR(color_id(ColorStyle::Background))); Concrete { current_style: Cell::new(ColorPair::from_256colors(0, 0)), @@ -108,7 +116,13 @@ impl backend::Backend for Concrete { ncurses::attroff(style); } - fn clear(&self) { + fn clear(&self, color: Color) { + let id = self.get_or_create(ColorPair { + front: color, + back: color, + }); + ncurses::wbkgd(ncurses::stdscr(), ncurses::COLOR_PAIR(id)); + ncurses::clear(); } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 3d4fa19..41ff8a2 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -18,19 +18,22 @@ pub use self::termion::*; pub trait Backend { fn init() -> Self; // TODO: take `self` by value? + // Or implement Drop? fn finish(&mut self); - fn clear(&self); fn refresh(&mut self); fn has_colors(&self) -> bool; - - fn print_at(&self, (usize, usize), &str); - - fn poll_event(&self) -> event::Event; - fn set_refresh_rate(&mut self, fps: u32); fn screen_size(&self) -> (usize, usize); + /// Main input method + fn poll_event(&self) -> event::Event; + + /// Main method used for printing + fn print_at(&self, (usize, usize), &str); + fn clear(&self, color: theme::Color); + + fn set_refresh_rate(&mut self, fps: u32); // TODO: unify those into a single method? fn with_color(&self, colors: theme::ColorPair, f: F); fn with_effect(&self, effect: theme::Effect, f: F); diff --git a/src/backend/termion.rs b/src/backend/termion.rs index e79e243..5889a02 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -124,8 +124,11 @@ impl backend::Backend for Concrete { (x as usize, y as usize) } - fn clear(&self) { - // self.apply_colorstyle(theme::ColorStyle::Background); + fn clear(&self, color: theme::Color) { + self.apply_colorstyle(theme::ColorPair { + front: color, + back: color, + }); print!("{}", termion::clear::All); } diff --git a/src/lib.rs b/src/lib.rs index 7c1fe2e..1c11f38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,14 +287,14 @@ impl Cursive { pub fn set_theme(&mut self, theme: theme::Theme) { self.theme = theme; // self.theme.activate(&mut self.backend); - self.backend.clear(); + self.clear(); } /// Clears the screen. /// /// Users rarely have to call this directly. pub fn clear(&self) { - self.backend.clear(); + self.backend.clear(self.theme.colors.background); } /// Loads a theme from the given file. @@ -617,7 +617,7 @@ impl Cursive { } if event == Event::WindowResize { - self.backend.clear(); + self.clear(); } // Event dispatch order: diff --git a/src/printer.rs b/src/printer.rs index 66f702e..71f5124 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -53,7 +53,7 @@ impl<'a> Printer<'a> { /// /// Users rarely need to call this directly. pub fn clear(&self) { - self.backend.clear(); + self.backend.clear(self.theme.colors.background); } /// Returns `true` if nothing has been printed yet. From 98be066b4df0e6a085bf2423ee573a7e835cb029 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 12 Jun 2017 23:51:41 -0700 Subject: [PATCH 07/11] Don't re-apply current color --- src/backend/termion.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 5889a02..5b4a029 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -49,12 +49,8 @@ impl Effectable for theme::Effect { } } -fn apply_colors(fg: &tcolor::Color, bg: &tcolor::Color) { - print!("{}{}", tcolor::Fg(fg), tcolor::Bg(bg)); -} - impl Concrete { - fn apply_colorstyle(&self, colors: theme::ColorPair) { + fn apply_colors(&self, colors: theme::ColorPair) { with_color(&colors.front, |c| print!("{}", tcolor::Fg(c))); with_color(&colors.back, |c| print!("{}", tcolor::Bg(c))); } @@ -99,13 +95,17 @@ impl backend::Backend for Concrete { fn with_color(&self, color: theme::ColorPair, f: F) { let current_style = self.current_style.get(); - self.apply_colorstyle(color); + if current_style != color { + self.apply_colors(color); + self.current_style.set(color); + } - self.current_style.set(color); f(); - self.current_style.set(current_style); - self.apply_colorstyle(current_style); + if current_style != color { + self.current_style.set(current_style); + self.apply_colors(current_style); + } } fn with_effect(&self, effect: theme::Effect, f: F) { @@ -125,7 +125,7 @@ impl backend::Backend for Concrete { } fn clear(&self, color: theme::Color) { - self.apply_colorstyle(theme::ColorPair { + self.apply_colors(theme::ColorPair { front: color, back: color, }); From 2cdf546455435eab9db6e708d491acf27df868b3 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 13 Jun 2017 23:30:55 -0700 Subject: [PATCH 08/11] Fix pancurses backend --- src/backend/curses/mod.rs | 6 +-- src/backend/curses/n.rs | 16 ++++--- src/backend/curses/pan.rs | 96 +++++++++++++++++++++++++++++++-------- src/backend/termion.rs | 2 - 4 files changed, 89 insertions(+), 31 deletions(-) diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index fa5a6df..9cfbf43 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -11,7 +11,7 @@ mod pan; pub use self::pan::*; -fn find_closest(color: &Color) -> u8 { +fn find_closest(color: &Color) -> i16 { match *color { Color::Dark(BaseColor::Black) => 0, Color::Dark(BaseColor::Red) => 1, @@ -33,8 +33,8 @@ fn find_closest(color: &Color) -> u8 { let r = 6 * r as u16 / 256; let g = 6 * g as u16 / 256; let b = 6 * b as u16 / 256; - (16 + 36 * r + 6 * g + b) as u8 + (16 + 36 * r + 6 * g + b) as i16 } - Color::RgbLowRes(r, g, b) => (16 + 36 * r + 6 * g + b) as u8, + Color::RgbLowRes(r, g, b) => (16 + 36 * r + 6 * g + b) as i16, } } diff --git a/src/backend/curses/n.rs b/src/backend/curses/n.rs index a449596..e06677d 100644 --- a/src/backend/curses/n.rs +++ b/src/backend/curses/n.rs @@ -32,8 +32,8 @@ impl Concrete { }; pairs.insert(pair, target); ncurses::init_pair(target, - find_closest(&pair.front) as i16, - find_closest(&pair.back) as i16); + find_closest(&pair.front), + find_closest(&pair.back)); target } @@ -51,7 +51,7 @@ impl Concrete { } } - fn set_colorstyle(&self, pair: ColorPair) { + fn set_colors(&self, pair: ColorPair) { let i = self.get_or_create(pair); @@ -99,11 +99,15 @@ impl backend::Backend for Concrete { fn with_color(&self, colors: ColorPair, f: F) { let current = self.current_style.get(); + if current != colors { + self.set_colors(colors); + } - self.set_colorstyle(colors); f(); - self.set_colorstyle(current); - self.current_style.set(current); + + if current != colors { + self.set_colors(current); + } } fn with_effect(&self, effect: Effect, f: F) { diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 5a11a5a..6d64d8f 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -1,17 +1,69 @@ extern crate pancurses; - - -use self::super::{color_id, find_closest}; +use self::super::find_closest; use backend; use event::{Event, Key}; -use theme::{Color, ColorStyle, Effect}; +use std::cell::{RefCell, Cell}; +use std::collections::HashMap; +use theme::{Color, ColorStyle, ColorPair, Effect}; use utf8; pub struct Concrete { + current_style: Cell, + pairs: RefCell>, window: pancurses::Window, } +impl Concrete { + /// Save a new color pair. + fn insert_color(&self, pairs: &mut HashMap, + pair: ColorPair) + -> i32 { + + let n = 1 + pairs.len() as i32; + + // TODO: when COLORS_PAIRS is available... + let target = if 16 > n { + // We still have plenty of space for everyone. + n + } else { + // The world is too small for both of us. + let target = n - 1; + // Remove the mapping to n-1 + pairs.retain(|_, &mut v| v != target); + target + }; + pairs.insert(pair, target); + pancurses::init_pair(target as i16, + find_closest(&pair.front), + find_closest(&pair.back)); + target + } + + /// Checks the pair in the cache, or re-define a color if needed. + fn get_or_create(&self, pair: ColorPair) -> i32 { + + let mut pairs = self.pairs.borrow_mut(); + + // Find if we have this color in stock + if pairs.contains_key(&pair) { + // We got it! + pairs[&pair] + } else { + self.insert_color(&mut *pairs, pair) + } + } + + fn set_colors(&self, pair: ColorPair) { + + let i = self.get_or_create(pair); + + self.current_style.set(pair); + let style = pancurses::COLOR_PAIR(i as u32); + self.window.attron(style); + } +} + impl backend::Backend for Concrete { fn init() -> Self { ::std::env::set_var("ESCDELAY", "25"); @@ -21,10 +73,12 @@ impl backend::Backend for Concrete { pancurses::cbreak(); pancurses::start_color(); pancurses::curs_set(0); - window.bkgd(pancurses::ColorPair(color_id(ColorStyle::Background) as - u8)); - Concrete { window: window } + Concrete { + current_style: Cell::new(ColorPair::from_256colors(0, 0)), + pairs: RefCell::new(HashMap::new()), + window: window, + } } fn screen_size(&self) -> (usize, usize) { @@ -40,21 +94,18 @@ impl backend::Backend for Concrete { pancurses::endwin(); } - fn init_color_style(&mut self, style: ColorStyle, foreground: &Color, - background: &Color) { - pancurses::init_pair(color_id(style), - find_closest(foreground) as i16, - find_closest(background) as i16); - } + fn with_color(&self, colors: ColorPair, f: F) { + let current = self.current_style.get(); - fn with_color(&self, color: ColorStyle, f: F) { - let (_, current_color_pair) = self.window.attrget(); - let color_attribute = pancurses::ColorPair(color_id(color) as u8); + if current != colors { + self.set_colors(colors); + } - self.window.attron(color_attribute); f(); - self.window - .attron(pancurses::ColorPair(current_color_pair as u8)); + + if current != colors { + self.set_colors(current); + } } fn with_effect(&self, effect: Effect, f: F) { @@ -67,7 +118,12 @@ impl backend::Backend for Concrete { self.window.attroff(style); } - fn clear(&self) { + fn clear(&self, color: Color) { + let id = self.get_or_create(ColorPair { + front: color, + back: color, + }); + self.window.bkgd(pancurses::ColorPair(id as u8)); self.window.clear(); } diff --git a/src/backend/termion.rs b/src/backend/termion.rs index 5b4a029..567e564 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -13,8 +13,6 @@ use backend; use chan; use event::{Event, Key}; use std::cell::Cell; -use std::collections::HashMap; -use std::fmt; use std::io::Write; use std::thread; From 88d3d81e8803f073873cf22a62bb6bafd65cdc68 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 13 Jun 2017 23:49:55 -0700 Subject: [PATCH 09/11] Use pancurses::chtype directly --- src/backend/curses/pan.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/curses/pan.rs b/src/backend/curses/pan.rs index 6d64d8f..24ebab0 100644 --- a/src/backend/curses/pan.rs +++ b/src/backend/curses/pan.rs @@ -5,7 +5,7 @@ use backend; use event::{Event, Key}; use std::cell::{RefCell, Cell}; use std::collections::HashMap; -use theme::{Color, ColorStyle, ColorPair, Effect}; +use theme::{Color, ColorPair, Effect}; use utf8; pub struct Concrete { @@ -59,7 +59,7 @@ impl Concrete { let i = self.get_or_create(pair); self.current_style.set(pair); - let style = pancurses::COLOR_PAIR(i as u32); + let style = pancurses::COLOR_PAIR(i as pancurses::chtype); self.window.attron(style); } } From 7a7ccbf45c4be65a5642e828303249fcec21c7c4 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Wed, 14 Jun 2017 00:08:58 -0700 Subject: [PATCH 10/11] Add colors example --- examples/colors.rs | 30 ++++++++++++++++++++++++++++++ src/backend/blt.rs | 23 +++++++++++++---------- src/vec.rs | 5 +++++ 3 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 examples/colors.rs diff --git a/examples/colors.rs b/examples/colors.rs new file mode 100644 index 0000000..7985421 --- /dev/null +++ b/examples/colors.rs @@ -0,0 +1,30 @@ +extern crate cursive; + +use cursive::Cursive; +use cursive::theme::{ColorStyle, Color}; +use cursive::view::Boxable; +use cursive::views::Canvas; + +fn main() { + let mut siv = Cursive::new(); + + siv.add_layer(Canvas::new(()) + .with_draw(|printer, _| for x in 0..20 { + for y in 0..10 { + printer.with_color(ColorStyle::Custom { + front: Color::Rgb(x * 12, + y * 25, + (x + 2 * y) * 6), + back: Color::Rgb(255 - x * 12, + 255 - y * 25, + 128 + (40 - x - 2 * y) * 3), + }, + |printer| { printer.print((x, y), "+"); }); + } + }) + .fixed_size((20, 10))); + + siv.add_global_callback('q', |s| s.quit()); + + siv.run(); +} diff --git a/src/backend/blt.rs b/src/backend/blt.rs index 7f56a21..a097d88 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -55,6 +55,7 @@ impl backend::Backend for Concrete { } fn clear(&self, color: Color) { + terminal::set_background(colour_to_blt_colour(color)); terminal::clear(None); } @@ -134,11 +135,11 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event { KeyCode::F1 | KeyCode::F2 | KeyCode::F3 | KeyCode::F4 | KeyCode::F5 | KeyCode::F6 | KeyCode::F7 | KeyCode::F8 | KeyCode::F9 | KeyCode::F10 | KeyCode::F11 | KeyCode::F12 | - KeyCode::NumEnter | KeyCode::Enter | KeyCode::Escape | KeyCode::Backspace | - KeyCode::Tab | KeyCode::Pause | KeyCode::Insert | KeyCode::Home | - KeyCode::PageUp | KeyCode::Delete | KeyCode::End | - KeyCode::PageDown | KeyCode::Right | KeyCode::Left | - KeyCode::Down | KeyCode::Up => { + KeyCode::NumEnter | KeyCode::Enter | KeyCode::Escape | + KeyCode::Backspace | KeyCode::Tab | KeyCode::Pause | + KeyCode::Insert | KeyCode::Home | KeyCode::PageUp | + KeyCode::Delete | KeyCode::End | KeyCode::PageDown | + KeyCode::Right | KeyCode::Left | KeyCode::Down | KeyCode::Up => { match (shift, ctrl) { (true, true) => Event::CtrlShift(blt_keycode_to_key(kc)), (true, false) => Event::Shift(blt_keycode_to_key(kc)), @@ -162,10 +163,9 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event { KeyCode::Apostrophe | KeyCode::Comma | KeyCode::Period | KeyCode::Slash | KeyCode::Space | KeyCode::NumDivide | KeyCode::NumMultiply | KeyCode::NumMinus | KeyCode::NumPlus | - KeyCode::NumPeriod | KeyCode::Num1 | - KeyCode::Num2 | KeyCode::Num3 | KeyCode::Num4 | KeyCode::Num5 | - KeyCode::Num6 | KeyCode::Num7 | KeyCode::Num8 | KeyCode::Num9 | - KeyCode::Num0 => { + KeyCode::NumPeriod | KeyCode::Num1 | KeyCode::Num2 | + KeyCode::Num3 | KeyCode::Num4 | KeyCode::Num5 | KeyCode::Num6 | + KeyCode::Num7 | KeyCode::Num8 | KeyCode::Num9 | KeyCode::Num0 => { if ctrl { Event::CtrlChar(blt_keycode_to_char(kc, shift)) } else { @@ -240,7 +240,10 @@ fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char { KeyCode::Num8 => '8', KeyCode::Num9 => '9', KeyCode::Num0 => '0', - _ => { println_stderr!("{:?}", kc); unreachable!() }, + _ => { + println_stderr!("{:?}", kc); + unreachable!() + } } } diff --git a/src/vec.rs b/src/vec.rs index 4a8c316..83aa383 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -114,6 +114,11 @@ impl From<(u32, u32)> for XY { } } +impl From<(u8, u8)> for XY { + fn from((x, y): (u8, u8)) -> Self { + (x as usize, y as usize).into() + } +} impl, O: Into>> Add for XY { type Output = Self; From 9089d9fd88833d066f24959fba7d90a5d2d35eb3 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Wed, 14 Jun 2017 00:43:03 -0700 Subject: [PATCH 11/11] Cleaner colors example --- examples/colors.rs | 47 ++++++++++++++++++++++++++++++---------------- src/backend/blt.rs | 2 +- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/examples/colors.rs b/examples/colors.rs index 7985421..8b2e834 100644 --- a/examples/colors.rs +++ b/examples/colors.rs @@ -1,6 +1,6 @@ extern crate cursive; -use cursive::Cursive; +use cursive::{Cursive, Printer}; use cursive::theme::{ColorStyle, Color}; use cursive::view::Boxable; use cursive::views::Canvas; @@ -8,23 +8,38 @@ use cursive::views::Canvas; fn main() { let mut siv = Cursive::new(); - siv.add_layer(Canvas::new(()) - .with_draw(|printer, _| for x in 0..20 { - for y in 0..10 { - printer.with_color(ColorStyle::Custom { - front: Color::Rgb(x * 12, - y * 25, - (x + 2 * y) * 6), - back: Color::Rgb(255 - x * 12, - 255 - y * 25, - 128 + (40 - x - 2 * y) * 3), - }, - |printer| { printer.print((x, y), "+"); }); - } - }) - .fixed_size((20, 10))); + siv.add_layer(Canvas::new(()).with_draw(draw).fixed_size((20, 10))); siv.add_global_callback('q', |s| s.quit()); siv.run(); } + +fn front_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color { + Color::Rgb(x * (255 / x_max), + y * (255 / y_max), + (x + 2 * y) * (255 / (x_max + 2 * y_max))) +} + +fn back_color(x: u8, y: u8, x_max: u8, y_max: u8) -> Color { + + Color::Rgb(128 + (2 * y_max + x - 2 * y) * (128 / (x_max + 2 * y_max)), + 255 - y * (255 / y_max), + 255 - x * (255 / x_max)) +} + +fn draw(p: &Printer, _: &()) { + let x_max = p.size.x as u8; + let y_max = p.size.y as u8; + + for x in 0..x_max { + for y in 0..y_max { + let style = ColorStyle::Custom { + front: front_color(x, y, x_max, y_max), + back: back_color(x, y, x_max, y_max), + }; + + p.with_color(style, |printer| { printer.print((x, y), "+"); }); + } + } +} diff --git a/src/backend/blt.rs b/src/backend/blt.rs index a097d88..28dd1df 100644 --- a/src/backend/blt.rs +++ b/src/backend/blt.rs @@ -241,7 +241,7 @@ fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char { KeyCode::Num9 => '9', KeyCode::Num0 => '0', _ => { - println_stderr!("{:?}", kc); + println_stderr!("Found unknown input: {:?}", kc); unreachable!() } }