mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Merge branch 'custom_color'
This commit is contained in:
commit
dad0016759
45
examples/colors.rs
Normal file
45
examples/colors.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
extern crate cursive;
|
||||||
|
|
||||||
|
use cursive::{Cursive, Printer};
|
||||||
|
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(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), "+"); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,33 @@
|
|||||||
extern crate cursive;
|
extern crate cursive;
|
||||||
|
|
||||||
use cursive::Cursive;
|
use cursive::Cursive;
|
||||||
use cursive::views::Dialog;
|
use cursive::theme::{ColorStyle, BaseColor, Color, BorderStyle};
|
||||||
use cursive::theme::BorderStyle;
|
use cursive::views::{EditView, LinearLayout, Dialog, TextView};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = Cursive::new();
|
let mut siv = Cursive::new();
|
||||||
|
|
||||||
siv.add_layer(Dialog::text("This is a dynamic theme example!")
|
let layout = LinearLayout::vertical()
|
||||||
.button("Change", |s| {
|
.child(TextView::new("This is a dynamic theme example!"))
|
||||||
let mut theme = s.current_theme().clone();
|
.child(EditView::new().content("Woo! colors!").style(ColorStyle::Custom {
|
||||||
|
front: Color::Rgb(200, 150, 150),
|
||||||
|
back: Color::Dark(BaseColor::Blue),
|
||||||
|
}));
|
||||||
|
|
||||||
theme.shadow = !theme.shadow;
|
siv.add_layer(Dialog::around(layout)
|
||||||
theme.borders = match theme.borders {
|
.button("Change", |s| {
|
||||||
Some(BorderStyle::Simple) => Some(BorderStyle::Outset),
|
let mut theme = s.current_theme().clone();
|
||||||
Some(BorderStyle::Outset) => None,
|
|
||||||
None => Some(BorderStyle::Simple),
|
|
||||||
};
|
|
||||||
|
|
||||||
s.set_theme(theme);
|
theme.shadow = !theme.shadow;
|
||||||
})
|
theme.borders = match theme.borders {
|
||||||
.button("Quit", Cursive::quit));
|
BorderStyle::Simple => BorderStyle::Outset,
|
||||||
|
BorderStyle::Outset => BorderStyle::None,
|
||||||
|
BorderStyle::None => BorderStyle::Simple,
|
||||||
|
};
|
||||||
|
|
||||||
|
s.set_theme(theme);
|
||||||
|
})
|
||||||
|
.button("Quit", Cursive::quit));
|
||||||
|
|
||||||
siv.run();
|
siv.run();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use self::bear_lib_terminal::terminal::{self, Event as BltEvent, KeyCode};
|
|||||||
use backend;
|
use backend;
|
||||||
use event::{Event, Key};
|
use event::{Event, Key};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use theme::{BaseColor, Color, ColorStyle, Effect};
|
use theme::{BaseColor, Color, ColorPair, Effect};
|
||||||
|
|
||||||
pub struct Concrete {
|
pub struct Concrete {
|
||||||
colours: BTreeMap<i16, (BltColor, BltColor)>,
|
colours: BTreeMap<i16, (BltColor, BltColor)>,
|
||||||
@ -24,15 +24,9 @@ impl backend::Backend for Concrete {
|
|||||||
terminal::close();
|
terminal::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
|
fn with_color<F: FnOnce()>(&self, color: ColorPair, f: F) {
|
||||||
background: &Color) {
|
let fg = colour_to_blt_colour(color.front);
|
||||||
self.colours.insert(style.id(),
|
let bg = colour_to_blt_colour(color.back);
|
||||||
(colour_to_blt_colour(foreground),
|
|
||||||
colour_to_blt_colour(background)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
|
|
||||||
let (fg, bg) = self.colours[&color.id()];
|
|
||||||
terminal::with_colors(fg, bg, f);
|
terminal::with_colors(fg, bg, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +54,8 @@ impl backend::Backend for Concrete {
|
|||||||
(width as usize, height as usize)
|
(width as usize, height as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&self) {
|
fn clear(&self, color: Color) {
|
||||||
|
terminal::set_background(colour_to_blt_colour(color));
|
||||||
terminal::clear(None);
|
terminal::clear(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +98,8 @@ impl backend::Backend for Concrete {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn colour_to_blt_colour(clr: &Color) -> BltColor {
|
fn colour_to_blt_colour(clr: Color) -> BltColor {
|
||||||
let (r, g, b) = match *clr {
|
let (r, g, b) = match clr {
|
||||||
// Colours taken from
|
// Colours taken from
|
||||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||||
Color::Dark(BaseColor::Black) => (0, 0, 0),
|
Color::Dark(BaseColor::Black) => (0, 0, 0),
|
||||||
@ -140,11 +135,11 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event {
|
|||||||
KeyCode::F1 | KeyCode::F2 | KeyCode::F3 | KeyCode::F4 |
|
KeyCode::F1 | KeyCode::F2 | KeyCode::F3 | KeyCode::F4 |
|
||||||
KeyCode::F5 | KeyCode::F6 | KeyCode::F7 | KeyCode::F8 |
|
KeyCode::F5 | KeyCode::F6 | KeyCode::F7 | KeyCode::F8 |
|
||||||
KeyCode::F9 | KeyCode::F10 | KeyCode::F11 | KeyCode::F12 |
|
KeyCode::F9 | KeyCode::F10 | KeyCode::F11 | KeyCode::F12 |
|
||||||
KeyCode::Enter | KeyCode::Escape | KeyCode::Backspace |
|
KeyCode::NumEnter | KeyCode::Enter | KeyCode::Escape |
|
||||||
KeyCode::Tab | KeyCode::Pause | KeyCode::Insert | KeyCode::Home |
|
KeyCode::Backspace | KeyCode::Tab | KeyCode::Pause |
|
||||||
KeyCode::PageUp | KeyCode::Delete | KeyCode::End |
|
KeyCode::Insert | KeyCode::Home | KeyCode::PageUp |
|
||||||
KeyCode::PageDown | KeyCode::Right | KeyCode::Left |
|
KeyCode::Delete | KeyCode::End | KeyCode::PageDown |
|
||||||
KeyCode::Down | KeyCode::Up => {
|
KeyCode::Right | KeyCode::Left | KeyCode::Down | KeyCode::Up => {
|
||||||
match (shift, ctrl) {
|
match (shift, ctrl) {
|
||||||
(true, true) => Event::CtrlShift(blt_keycode_to_key(kc)),
|
(true, true) => Event::CtrlShift(blt_keycode_to_key(kc)),
|
||||||
(true, false) => Event::Shift(blt_keycode_to_key(kc)),
|
(true, false) => Event::Shift(blt_keycode_to_key(kc)),
|
||||||
@ -168,10 +163,9 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event {
|
|||||||
KeyCode::Apostrophe | KeyCode::Comma | KeyCode::Period |
|
KeyCode::Apostrophe | KeyCode::Comma | KeyCode::Period |
|
||||||
KeyCode::Slash | KeyCode::Space | KeyCode::NumDivide |
|
KeyCode::Slash | KeyCode::Space | KeyCode::NumDivide |
|
||||||
KeyCode::NumMultiply | KeyCode::NumMinus | KeyCode::NumPlus |
|
KeyCode::NumMultiply | KeyCode::NumMinus | KeyCode::NumPlus |
|
||||||
KeyCode::NumEnter | KeyCode::NumPeriod | KeyCode::Num1 |
|
KeyCode::NumPeriod | KeyCode::Num1 | KeyCode::Num2 |
|
||||||
KeyCode::Num2 | KeyCode::Num3 | KeyCode::Num4 | KeyCode::Num5 |
|
KeyCode::Num3 | KeyCode::Num4 | KeyCode::Num5 | KeyCode::Num6 |
|
||||||
KeyCode::Num6 | KeyCode::Num7 | KeyCode::Num8 | KeyCode::Num9 |
|
KeyCode::Num7 | KeyCode::Num8 | KeyCode::Num9 | KeyCode::Num0 => {
|
||||||
KeyCode::Num0 => {
|
|
||||||
if ctrl {
|
if ctrl {
|
||||||
Event::CtrlChar(blt_keycode_to_char(kc, shift))
|
Event::CtrlChar(blt_keycode_to_char(kc, shift))
|
||||||
} else {
|
} else {
|
||||||
@ -246,7 +240,10 @@ fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char {
|
|||||||
KeyCode::Num8 => '8',
|
KeyCode::Num8 => '8',
|
||||||
KeyCode::Num9 => '9',
|
KeyCode::Num9 => '9',
|
||||||
KeyCode::Num0 => '0',
|
KeyCode::Num0 => '0',
|
||||||
_ => unreachable!(),
|
_ => {
|
||||||
|
println_stderr!("Found unknown input: {:?}", kc);
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +261,7 @@ fn blt_keycode_to_key(kc: KeyCode) -> Key {
|
|||||||
KeyCode::F10 => Key::F10,
|
KeyCode::F10 => Key::F10,
|
||||||
KeyCode::F11 => Key::F11,
|
KeyCode::F11 => Key::F11,
|
||||||
KeyCode::F12 => Key::F12,
|
KeyCode::F12 => Key::F12,
|
||||||
KeyCode::Enter => Key::Enter,
|
KeyCode::NumEnter | KeyCode::Enter => Key::Enter,
|
||||||
KeyCode::Escape => Key::Esc,
|
KeyCode::Escape => Key::Esc,
|
||||||
KeyCode::Backspace => Key::Backspace,
|
KeyCode::Backspace => Key::Backspace,
|
||||||
KeyCode::Tab => Key::Tab,
|
KeyCode::Tab => Key::Tab,
|
||||||
|
@ -11,7 +11,7 @@ mod pan;
|
|||||||
pub use self::pan::*;
|
pub use self::pan::*;
|
||||||
|
|
||||||
|
|
||||||
fn find_closest(color: &Color) -> u8 {
|
fn find_closest(color: &Color) -> i16 {
|
||||||
match *color {
|
match *color {
|
||||||
Color::Dark(BaseColor::Black) => 0,
|
Color::Dark(BaseColor::Black) => 0,
|
||||||
Color::Dark(BaseColor::Red) => 1,
|
Color::Dark(BaseColor::Red) => 1,
|
||||||
@ -33,8 +33,8 @@ fn find_closest(color: &Color) -> u8 {
|
|||||||
let r = 6 * r as u16 / 256;
|
let r = 6 * r as u16 / 256;
|
||||||
let g = 6 * g as u16 / 256;
|
let g = 6 * g as u16 / 256;
|
||||||
let b = 6 * b 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,65 @@
|
|||||||
extern crate ncurses;
|
extern crate ncurses;
|
||||||
|
|
||||||
|
|
||||||
use self::super::find_closest;
|
use self::super::find_closest;
|
||||||
use backend;
|
use backend;
|
||||||
use event::{Event, Key};
|
use event::{Event, Key};
|
||||||
use theme::{Color, ColorStyle, Effect};
|
use std::cell::{RefCell, Cell};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use theme::{Color, ColorPair, Effect};
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
pub struct Concrete;
|
pub struct Concrete {
|
||||||
|
current_style: Cell<ColorPair>,
|
||||||
|
pairs: RefCell<HashMap<ColorPair, i16>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Concrete {
|
||||||
|
/// Save a new color pair.
|
||||||
|
fn insert_color(&self, pairs: &mut HashMap<ColorPair, i16>,
|
||||||
|
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),
|
||||||
|
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) -> i16 {
|
||||||
|
|
||||||
|
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 = ncurses::COLOR_PAIR(i);
|
||||||
|
ncurses::attron(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Concrete {
|
impl backend::Backend for Concrete {
|
||||||
fn init() -> Self {
|
fn init() -> Self {
|
||||||
@ -22,10 +74,11 @@ impl backend::Backend for Concrete {
|
|||||||
ncurses::cbreak();
|
ncurses::cbreak();
|
||||||
ncurses::start_color();
|
ncurses::start_color();
|
||||||
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||||
ncurses::wbkgd(ncurses::stdscr(),
|
|
||||||
ncurses::COLOR_PAIR(ColorStyle::Background.id()));
|
|
||||||
|
|
||||||
Concrete
|
Concrete {
|
||||||
|
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
|
||||||
|
pairs: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_size(&self) -> (usize, usize) {
|
fn screen_size(&self) -> (usize, usize) {
|
||||||
@ -44,25 +97,17 @@ impl backend::Backend for Concrete {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
|
fn with_color<F: FnOnce()>(&self, colors: ColorPair, f: F) {
|
||||||
background: &Color) {
|
let current = self.current_style.get();
|
||||||
// TODO: build the color on the spot
|
if current != colors {
|
||||||
|
self.set_colors(colors);
|
||||||
|
}
|
||||||
|
|
||||||
ncurses::init_pair(style.id(),
|
|
||||||
find_closest(foreground) as i16,
|
|
||||||
find_closest(background) as i16);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_color<F: FnOnce()>(&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 style = ncurses::COLOR_PAIR(color.id());
|
|
||||||
ncurses::attron(style);
|
|
||||||
f();
|
f();
|
||||||
// ncurses::attroff(style);
|
|
||||||
ncurses::attron(current_style);
|
if current != colors {
|
||||||
|
self.set_colors(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
|
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
|
||||||
@ -75,7 +120,13 @@ impl backend::Backend for Concrete {
|
|||||||
ncurses::attroff(style);
|
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();
|
ncurses::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,69 @@
|
|||||||
extern crate pancurses;
|
extern crate pancurses;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use self::super::find_closest;
|
use self::super::find_closest;
|
||||||
use backend;
|
use backend;
|
||||||
use event::{Event, Key};
|
use event::{Event, Key};
|
||||||
use theme::{Color, ColorStyle, Effect};
|
use std::cell::{RefCell, Cell};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use theme::{Color, ColorPair, Effect};
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
pub struct Concrete {
|
pub struct Concrete {
|
||||||
|
current_style: Cell<ColorPair>,
|
||||||
|
pairs: RefCell<HashMap<ColorPair, i32>>,
|
||||||
window: pancurses::Window,
|
window: pancurses::Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Concrete {
|
||||||
|
/// Save a new color pair.
|
||||||
|
fn insert_color(&self, pairs: &mut HashMap<ColorPair, i32>,
|
||||||
|
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 pancurses::chtype);
|
||||||
|
self.window.attron(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Concrete {
|
impl backend::Backend for Concrete {
|
||||||
fn init() -> Self {
|
fn init() -> Self {
|
||||||
let window = pancurses::initscr();
|
let window = pancurses::initscr();
|
||||||
@ -21,9 +73,12 @@ impl backend::Backend for Concrete {
|
|||||||
pancurses::cbreak();
|
pancurses::cbreak();
|
||||||
pancurses::start_color();
|
pancurses::start_color();
|
||||||
pancurses::curs_set(0);
|
pancurses::curs_set(0);
|
||||||
window.bkgd(pancurses::ColorPair(ColorStyle::Background.id() 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) {
|
fn screen_size(&self) -> (usize, usize) {
|
||||||
@ -39,20 +94,18 @@ impl backend::Backend for Concrete {
|
|||||||
pancurses::endwin();
|
pancurses::endwin();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
|
fn with_color<F: FnOnce()>(&self, colors: ColorPair, f: F) {
|
||||||
background: &Color) {
|
let current = self.current_style.get();
|
||||||
pancurses::init_pair(style.id(),
|
|
||||||
find_closest(foreground) as i16,
|
|
||||||
find_closest(background) as i16);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
|
if current != colors {
|
||||||
let (_, current_color_pair) = self.window.attrget();
|
self.set_colors(colors);
|
||||||
let color_attribute = pancurses::ColorPair(color.id() as u8);
|
}
|
||||||
|
|
||||||
self.window.attron(color_attribute);
|
|
||||||
f();
|
f();
|
||||||
self.window.attron(pancurses::ColorPair(current_color_pair as u8));
|
|
||||||
|
if current != colors {
|
||||||
|
self.set_colors(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
|
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
|
||||||
@ -65,7 +118,12 @@ impl backend::Backend for Concrete {
|
|||||||
self.window.attroff(style);
|
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();
|
self.window.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +152,14 @@ impl backend::Backend for Concrete {
|
|||||||
pancurses::Input::Character(c) if 32 <= (c as u32) &&
|
pancurses::Input::Character(c) if 32 <= (c as u32) &&
|
||||||
(c as u32) <= 255 => {
|
(c as u32) <= 255 => {
|
||||||
Event::Char(utf8::read_char(c as u8, || {
|
Event::Char(utf8::read_char(c as u8, || {
|
||||||
self.window.getch().and_then(|i| match i {
|
self.window
|
||||||
pancurses::Input::Character(c) => {
|
.getch()
|
||||||
Some(c as u8)
|
.and_then(|i| match i {
|
||||||
}
|
pancurses::Input::Character(c) => {
|
||||||
_ => None,
|
Some(c as u8)
|
||||||
})
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.unwrap())
|
.unwrap())
|
||||||
}
|
}
|
||||||
|
@ -18,23 +18,23 @@ pub use self::termion::*;
|
|||||||
pub trait Backend {
|
pub trait Backend {
|
||||||
fn init() -> Self;
|
fn init() -> Self;
|
||||||
// TODO: take `self` by value?
|
// TODO: take `self` by value?
|
||||||
|
// Or implement Drop?
|
||||||
fn finish(&mut self);
|
fn finish(&mut self);
|
||||||
|
|
||||||
fn clear(&self);
|
|
||||||
fn refresh(&mut self);
|
fn refresh(&mut self);
|
||||||
|
|
||||||
fn has_colors(&self) -> bool;
|
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;
|
|
||||||
fn set_refresh_rate(&mut self, fps: u32);
|
|
||||||
fn screen_size(&self) -> (usize, usize);
|
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?
|
// TODO: unify those into a single method?
|
||||||
fn with_color<F: FnOnce()>(&self, color: theme::ColorStyle, f: F);
|
fn with_color<F: FnOnce()>(&self, colors: theme::ColorPair, f: F);
|
||||||
fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F);
|
fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F);
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ use backend;
|
|||||||
use chan;
|
use chan;
|
||||||
use event::{Event, Key};
|
use event::{Event, Key};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::fmt;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -22,9 +20,7 @@ use theme;
|
|||||||
|
|
||||||
pub struct Concrete {
|
pub struct Concrete {
|
||||||
terminal: AlternateScreen<termion::raw::RawTerminal<::std::io::Stdout>>,
|
terminal: AlternateScreen<termion::raw::RawTerminal<::std::io::Stdout>>,
|
||||||
current_style: Cell<theme::ColorStyle>,
|
current_style: Cell<theme::ColorPair>,
|
||||||
colors: BTreeMap<i16, (Box<tcolor::Color>, Box<tcolor::Color>)>,
|
|
||||||
|
|
||||||
input: chan::Receiver<Event>,
|
input: chan::Receiver<Event>,
|
||||||
resize: chan::Receiver<chan_signal::Signal>,
|
resize: chan::Receiver<chan_signal::Signal>,
|
||||||
timeout: Option<u32>,
|
timeout: Option<u32>,
|
||||||
@ -35,20 +31,6 @@ trait Effectable {
|
|||||||
fn off(&self);
|
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 {
|
impl Effectable for theme::Effect {
|
||||||
fn on(&self) {
|
fn on(&self) {
|
||||||
match *self {
|
match *self {
|
||||||
@ -65,14 +47,10 @@ impl Effectable for theme::Effect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_colors(fg: &tcolor::Color, bg: &tcolor::Color) {
|
|
||||||
print!("{}{}", tcolor::Fg(ColorRef(fg)), tcolor::Bg(ColorRef(bg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Concrete {
|
impl Concrete {
|
||||||
fn apply_colorstyle(&self, color_style: theme::ColorStyle) {
|
fn apply_colors(&self, colors: theme::ColorPair) {
|
||||||
let (ref fg, ref bg) = self.colors[&color_style.id()];
|
with_color(&colors.front, |c| print!("{}", tcolor::Fg(c)));
|
||||||
apply_colors(&**fg, &**bg);
|
with_color(&colors.back, |c| print!("{}", tcolor::Bg(c)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +60,9 @@ impl backend::Backend for Concrete {
|
|||||||
|
|
||||||
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
|
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();
|
let (sender, receiver) = chan::async();
|
||||||
|
|
||||||
thread::spawn(move || for key in ::std::io::stdin().events() {
|
thread::spawn(move || for key in ::std::io::stdin().events() {
|
||||||
@ -93,8 +73,7 @@ impl backend::Backend for Concrete {
|
|||||||
|
|
||||||
let backend = Concrete {
|
let backend = Concrete {
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
current_style: Cell::new(theme::ColorStyle::Background),
|
current_style: Cell::new(theme::ColorPair::from_256colors(0, 0)),
|
||||||
colors: BTreeMap::new(),
|
|
||||||
input: receiver,
|
input: receiver,
|
||||||
resize: resize,
|
resize: resize,
|
||||||
timeout: None,
|
timeout: None,
|
||||||
@ -111,24 +90,20 @@ impl backend::Backend for Concrete {
|
|||||||
termion::clear::All);
|
termion::clear::All);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_color_style(&mut self, style: theme::ColorStyle,
|
fn with_color<F: FnOnce()>(&self, color: theme::ColorPair, f: F) {
|
||||||
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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_color<F: FnOnce()>(&self, color: theme::ColorStyle, f: F) {
|
|
||||||
let current_style = self.current_style.get();
|
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();
|
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<F: FnOnce()>(&self, effect: theme::Effect, f: F) {
|
fn with_effect<F: FnOnce()>(&self, effect: theme::Effect, f: F) {
|
||||||
@ -147,8 +122,11 @@ impl backend::Backend for Concrete {
|
|||||||
(x as usize, y as usize)
|
(x as usize, y as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&self) {
|
fn clear(&self, color: theme::Color) {
|
||||||
self.apply_colorstyle(theme::ColorStyle::Background);
|
self.apply_colors(theme::ColorPair {
|
||||||
|
front: color,
|
||||||
|
back: color,
|
||||||
|
});
|
||||||
print!("{}", termion::clear::All);
|
print!("{}", termion::clear::All);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,49 +192,43 @@ fn map_key(event: TEvent) -> Event {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn colour_to_termion_colour(clr: &theme::Color) -> Box<tcolor::Color> {
|
fn with_color<F, R>(clr: &theme::Color, f: F) -> R
|
||||||
|
where F: FnOnce(&tcolor::Color) -> R
|
||||||
|
{
|
||||||
|
|
||||||
match *clr {
|
match *clr {
|
||||||
theme::Color::Dark(theme::BaseColor::Black) => Box::new(tcolor::Black),
|
theme::Color::Dark(theme::BaseColor::Black) => f(&tcolor::Black),
|
||||||
theme::Color::Dark(theme::BaseColor::Red) => Box::new(tcolor::Red),
|
theme::Color::Dark(theme::BaseColor::Red) => f(&tcolor::Red),
|
||||||
theme::Color::Dark(theme::BaseColor::Green) => Box::new(tcolor::Green),
|
theme::Color::Dark(theme::BaseColor::Green) => f(&tcolor::Green),
|
||||||
theme::Color::Dark(theme::BaseColor::Yellow) => {
|
theme::Color::Dark(theme::BaseColor::Yellow) => f(&tcolor::Yellow),
|
||||||
Box::new(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::Blue) => Box::new(tcolor::Blue),
|
theme::Color::Dark(theme::BaseColor::Cyan) => f(&tcolor::Cyan),
|
||||||
theme::Color::Dark(theme::BaseColor::Magenta) => {
|
theme::Color::Dark(theme::BaseColor::White) => f(&tcolor::White),
|
||||||
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::Light(theme::BaseColor::Black) => {
|
theme::Color::Light(theme::BaseColor::Black) => {
|
||||||
Box::new(tcolor::LightBlack)
|
f(&tcolor::LightBlack)
|
||||||
}
|
}
|
||||||
theme::Color::Light(theme::BaseColor::Red) => {
|
theme::Color::Light(theme::BaseColor::Red) => f(&tcolor::LightRed),
|
||||||
Box::new(tcolor::LightRed)
|
theme::Color::Light(theme::BaseColor::Green) => {
|
||||||
}
|
f(&tcolor::LightGreen)
|
||||||
theme::Color::Light(theme::BaseColor::Green) => {
|
}
|
||||||
Box::new(tcolor::LightGreen)
|
theme::Color::Light(theme::BaseColor::Yellow) => {
|
||||||
}
|
f(&tcolor::LightYellow)
|
||||||
theme::Color::Light(theme::BaseColor::Yellow) => {
|
}
|
||||||
Box::new(tcolor::LightYellow)
|
theme::Color::Light(theme::BaseColor::Blue) => f(&tcolor::LightBlue),
|
||||||
}
|
theme::Color::Light(theme::BaseColor::Magenta) => {
|
||||||
theme::Color::Light(theme::BaseColor::Blue) => {
|
f(&tcolor::LightMagenta)
|
||||||
Box::new(tcolor::LightBlue)
|
}
|
||||||
}
|
theme::Color::Light(theme::BaseColor::Cyan) => f(&tcolor::LightCyan),
|
||||||
theme::Color::Light(theme::BaseColor::Magenta) => {
|
theme::Color::Light(theme::BaseColor::White) => {
|
||||||
Box::new(tcolor::LightMagenta)
|
f(&tcolor::LightWhite)
|
||||||
}
|
}
|
||||||
theme::Color::Light(theme::BaseColor::Cyan) => {
|
|
||||||
Box::new(tcolor::LightCyan)
|
|
||||||
}
|
|
||||||
theme::Color::Light(theme::BaseColor::White) => {
|
|
||||||
Box::new(tcolor::LightWhite)
|
|
||||||
}
|
|
||||||
|
|
||||||
theme::Color::Rgb(r, g, b) => Box::new(tcolor::Rgb(r, g, b)),
|
theme::Color::Rgb(r, g, b) => f(&tcolor::Rgb(r, g, b)),
|
||||||
theme::Color::RgbLowRes(r, g, b) => {
|
theme::Color::RgbLowRes(r, g, b) => {
|
||||||
Box::new(tcolor::AnsiValue::rgb(r, g, b))
|
f(&tcolor::AnsiValue::rgb(r, g, b))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
17
src/lib.rs
17
src/lib.rs
@ -170,10 +170,10 @@ new_default!(Cursive);
|
|||||||
impl Cursive {
|
impl Cursive {
|
||||||
/// Creates a new Cursive root, and initialize the back-end.
|
/// Creates a new Cursive root, and initialize the back-end.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut backend = backend::Concrete::init();
|
let backend = backend::Concrete::init();
|
||||||
|
|
||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
theme.activate(&mut backend);
|
// theme.activate(&mut backend);
|
||||||
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
@ -287,15 +287,15 @@ impl Cursive {
|
|||||||
/// Sets the current theme.
|
/// Sets the current theme.
|
||||||
pub fn set_theme(&mut self, theme: theme::Theme) {
|
pub fn set_theme(&mut self, theme: theme::Theme) {
|
||||||
self.theme = theme;
|
self.theme = theme;
|
||||||
self.theme.activate(&mut self.backend);
|
// self.theme.activate(&mut self.backend);
|
||||||
self.backend.clear();
|
self.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the screen.
|
/// Clears the screen.
|
||||||
///
|
///
|
||||||
/// Users rarely have to call this directly.
|
/// Users rarely have to call this directly.
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.backend.clear();
|
self.backend.clear(self.theme.colors.background);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a theme from the given file.
|
/// Loads a theme from the given file.
|
||||||
@ -534,9 +534,6 @@ impl Cursive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self) {
|
fn draw(&mut self) {
|
||||||
// TODO: don't clone the theme
|
|
||||||
// Reference it or something
|
|
||||||
|
|
||||||
let sizes = self.screen().layer_sizes();
|
let sizes = self.screen().layer_sizes();
|
||||||
if self.last_sizes != sizes {
|
if self.last_sizes != sizes {
|
||||||
self.clear();
|
self.clear();
|
||||||
@ -544,7 +541,7 @@ impl Cursive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let printer = Printer::new(self.screen_size(),
|
let printer = Printer::new(self.screen_size(),
|
||||||
self.theme.clone(),
|
&self.theme,
|
||||||
&self.backend);
|
&self.backend);
|
||||||
|
|
||||||
// Draw the currently active screen
|
// Draw the currently active screen
|
||||||
@ -625,7 +622,7 @@ impl Cursive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if event == Event::WindowResize {
|
if event == Event::WindowResize {
|
||||||
self.backend.clear();
|
self.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event dispatch order:
|
// Event dispatch order:
|
||||||
|
@ -21,7 +21,7 @@ pub struct Printer<'a> {
|
|||||||
/// Whether the view to draw is currently focused or not.
|
/// Whether the view to draw is currently focused or not.
|
||||||
pub focused: bool,
|
pub focused: bool,
|
||||||
/// Currently used theme
|
/// Currently used theme
|
||||||
pub theme: Theme,
|
pub theme: &'a Theme,
|
||||||
|
|
||||||
/// `true` if nothing has been drawn yet.
|
/// `true` if nothing has been drawn yet.
|
||||||
new: Rc<Cell<bool>>,
|
new: Rc<Cell<bool>>,
|
||||||
@ -34,7 +34,7 @@ impl<'a> Printer<'a> {
|
|||||||
///
|
///
|
||||||
/// But nobody needs to know that.
|
/// But nobody needs to know that.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn new<T: Into<Vec2>>(size: T, theme: Theme,
|
pub fn new<T: Into<Vec2>>(size: T, theme: &'a Theme,
|
||||||
backend: &'a backend::Concrete)
|
backend: &'a backend::Concrete)
|
||||||
-> Self {
|
-> Self {
|
||||||
Printer {
|
Printer {
|
||||||
@ -53,7 +53,7 @@ impl<'a> Printer<'a> {
|
|||||||
///
|
///
|
||||||
/// Users rarely need to call this directly.
|
/// Users rarely need to call this directly.
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.backend.clear();
|
self.backend.clear(self.theme.colors.background);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if nothing has been printed yet.
|
/// Returns `true` if nothing has been printed yet.
|
||||||
@ -123,7 +123,8 @@ impl<'a> Printer<'a> {
|
|||||||
/// # use cursive::theme;
|
/// # use cursive::theme;
|
||||||
/// # use cursive::backend::{self, Backend};
|
/// # use cursive::backend::{self, Backend};
|
||||||
/// # let b = backend::Concrete::init();
|
/// # 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.with_color(theme::ColorStyle::Highlight, |printer| {
|
||||||
/// printer.print((0,0), "This text is highlighted!");
|
/// printer.print((0,0), "This text is highlighted!");
|
||||||
/// });
|
/// });
|
||||||
@ -131,7 +132,7 @@ impl<'a> Printer<'a> {
|
|||||||
pub fn with_color<F>(&self, c: ColorStyle, f: F)
|
pub fn with_color<F>(&self, c: ColorStyle, f: F)
|
||||||
where F: FnOnce(&Printer)
|
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,
|
/// Same as `with_color`, but apply a ncurses style instead,
|
||||||
@ -156,7 +157,8 @@ impl<'a> Printer<'a> {
|
|||||||
/// # use cursive::theme;
|
/// # use cursive::theme;
|
||||||
/// # use cursive::backend::{self, Backend};
|
/// # use cursive::backend::{self, Backend};
|
||||||
/// # let b = backend::Concrete::init();
|
/// # 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);
|
/// printer.print_box((0,0), (6,4), false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>(&self, start: T, size: S,
|
pub fn print_box<T: Into<Vec2>, S: Into<Vec2>>(&self, start: T, size: S,
|
||||||
@ -195,8 +197,8 @@ impl<'a> Printer<'a> {
|
|||||||
where F: FnOnce(&Printer)
|
where F: FnOnce(&Printer)
|
||||||
{
|
{
|
||||||
let color = match self.theme.borders {
|
let color = match self.theme.borders {
|
||||||
None => return,
|
BorderStyle::None => return,
|
||||||
Some(BorderStyle::Outset) if !invert => ColorStyle::Tertiary,
|
BorderStyle::Outset if !invert => ColorStyle::Tertiary,
|
||||||
_ => ColorStyle::Primary,
|
_ => ColorStyle::Primary,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -213,8 +215,8 @@ impl<'a> Printer<'a> {
|
|||||||
where F: FnOnce(&Printer)
|
where F: FnOnce(&Printer)
|
||||||
{
|
{
|
||||||
let color = match self.theme.borders {
|
let color = match self.theme.borders {
|
||||||
None => return,
|
BorderStyle::None => return,
|
||||||
Some(BorderStyle::Outset) if invert => ColorStyle::Tertiary,
|
BorderStyle::Outset if invert => ColorStyle::Tertiary,
|
||||||
_ => ColorStyle::Primary,
|
_ => ColorStyle::Primary,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -260,7 +262,7 @@ impl<'a> Printer<'a> {
|
|||||||
// We can't be larger than what remains
|
// We can't be larger than what remains
|
||||||
size: Vec2::min(self.size - offset, size),
|
size: Vec2::min(self.size - offset, size),
|
||||||
focused: self.focused && focused,
|
focused: self.focused && focused,
|
||||||
theme: self.theme.clone(),
|
theme: self.theme,
|
||||||
backend: self.backend,
|
backend: self.backend,
|
||||||
new: self.new.clone(),
|
new: self.new.clone(),
|
||||||
}
|
}
|
||||||
|
205
src/theme.rs
205
src/theme.rs
@ -114,8 +114,6 @@
|
|||||||
//! highlight_inactive = "#5555FF"
|
//! highlight_inactive = "#5555FF"
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
|
||||||
use backend::{self, Backend};
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@ -133,12 +131,41 @@ pub enum Effect {
|
|||||||
// TODO: bold, italic, underline
|
// 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.
|
/// Possible color style for a cell.
|
||||||
///
|
///
|
||||||
/// Represents a color pair role to use when printing something.
|
/// Represents a color pair role to use when printing something.
|
||||||
///
|
///
|
||||||
/// The current theme will assign each role a foreground and background color.
|
/// 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 {
|
pub enum ColorStyle {
|
||||||
/// Application background, where no view is present.
|
/// Application background, where no view is present.
|
||||||
Background,
|
Background,
|
||||||
@ -158,22 +185,34 @@ pub enum ColorStyle {
|
|||||||
Highlight,
|
Highlight,
|
||||||
/// Highlight color for inactive views (not in focus).
|
/// Highlight color for inactive views (not in focus).
|
||||||
HighlightInactive,
|
HighlightInactive,
|
||||||
|
/// Directly specifies colors, independently of the theme.
|
||||||
|
Custom {
|
||||||
|
/// Foreground color
|
||||||
|
front: Color,
|
||||||
|
/// Background color
|
||||||
|
back: Color,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorStyle {
|
impl ColorStyle {
|
||||||
/// Returns the ncurses pair ID associated with this color pair.
|
/// Return the color pair that this style represents.
|
||||||
pub fn id(self) -> i16 {
|
///
|
||||||
match self {
|
/// Returns `(front, back)`.
|
||||||
ColorStyle::Background => 1,
|
pub fn resolve(&self, theme: &Theme) -> ColorPair {
|
||||||
ColorStyle::Shadow => 2,
|
let c = &theme.colors;
|
||||||
ColorStyle::Primary => 3,
|
let (front, back) = match *self {
|
||||||
ColorStyle::Secondary => 4,
|
ColorStyle::Background => (c.view, c.background),
|
||||||
ColorStyle::Tertiary => 5,
|
ColorStyle::Shadow => (c.shadow, c.shadow),
|
||||||
ColorStyle::TitlePrimary => 6,
|
ColorStyle::Primary => (c.primary, c.view),
|
||||||
ColorStyle::TitleSecondary => 7,
|
ColorStyle::Secondary => (c.secondary, c.view),
|
||||||
ColorStyle::Highlight => 8,
|
ColorStyle::Tertiary => (c.tertiary, c.view),
|
||||||
ColorStyle::HighlightInactive => 9,
|
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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +222,7 @@ pub struct Theme {
|
|||||||
/// Whether views in a StackView should have shadows.
|
/// Whether views in a StackView should have shadows.
|
||||||
pub shadow: bool,
|
pub shadow: bool,
|
||||||
/// How view borders should be drawn.
|
/// How view borders should be drawn.
|
||||||
pub borders: Option<BorderStyle>,
|
pub borders: BorderStyle,
|
||||||
/// What colors should be used through the application?
|
/// What colors should be used through the application?
|
||||||
pub colors: Palette,
|
pub colors: Palette,
|
||||||
}
|
}
|
||||||
@ -192,7 +231,7 @@ impl Default for Theme {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Theme {
|
Theme {
|
||||||
shadow: true,
|
shadow: true,
|
||||||
borders: Some(BorderStyle::Simple),
|
borders: BorderStyle::Simple,
|
||||||
colors: Palette {
|
colors: Palette {
|
||||||
background: Color::Dark(BaseColor::Blue),
|
background: Color::Dark(BaseColor::Blue),
|
||||||
shadow: Color::Dark(BaseColor::Black),
|
shadow: Color::Dark(BaseColor::Black),
|
||||||
@ -223,64 +262,29 @@ impl Theme {
|
|||||||
self.colors.load(table);
|
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.
|
/// Specifies how some borders should be drawn.
|
||||||
///
|
///
|
||||||
/// Borders are used around Dialogs, select popups, and panels.
|
/// Borders are used around Dialogs, select popups, and panels.
|
||||||
#[derive(Clone,Copy,Debug)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)]
|
||||||
pub enum BorderStyle {
|
pub enum BorderStyle {
|
||||||
/// Simple borders.
|
/// Simple borders.
|
||||||
Simple,
|
Simple,
|
||||||
/// Outset borders with a simple 3d effect.
|
/// Outset borders with a simple 3d effect.
|
||||||
Outset,
|
Outset,
|
||||||
|
/// No borders.
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorderStyle {
|
impl BorderStyle {
|
||||||
fn from(s: &str) -> Option<Self> {
|
fn from(s: &str) -> Self {
|
||||||
if s == "simple" {
|
if s == "simple" {
|
||||||
Some(BorderStyle::Simple)
|
BorderStyle::Simple
|
||||||
} else if s == "outset" {
|
} else if s == "outset" {
|
||||||
Some(BorderStyle::Outset)
|
BorderStyle::Outset
|
||||||
} else {
|
} else {
|
||||||
None
|
BorderStyle::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,7 +292,7 @@ impl BorderStyle {
|
|||||||
/// Color configuration for the application.
|
/// Color configuration for the application.
|
||||||
///
|
///
|
||||||
/// Assign each color role an actual color.
|
/// Assign each color role an actual color.
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Copy,Clone,Debug)]
|
||||||
pub struct Palette {
|
pub struct Palette {
|
||||||
/// Color used for the application background.
|
/// Color used for the application background.
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
@ -352,7 +356,7 @@ fn load_color(target: &mut Color, value: Option<&toml::Value>) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// One of the 8 base colors.
|
/// One of the 8 base colors.
|
||||||
#[derive(Clone,Copy,Debug)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)]
|
||||||
pub enum BaseColor {
|
pub enum BaseColor {
|
||||||
/// Black color
|
/// Black color
|
||||||
///
|
///
|
||||||
@ -388,8 +392,24 @@ pub enum BaseColor {
|
|||||||
White,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u8> 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.
|
/// Represents a color used by the theme.
|
||||||
#[derive(Clone,Copy,Debug)]
|
#[derive(Clone,Copy,Debug,PartialEq,Eq,Hash)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
/// One of the 8 base colors.
|
/// One of the 8 base colors.
|
||||||
Dark(BaseColor),
|
Dark(BaseColor),
|
||||||
@ -405,8 +425,6 @@ pub enum Color {
|
|||||||
RgbLowRes(u8, u8, u8),
|
RgbLowRes(u8, u8, u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {}
|
|
||||||
|
|
||||||
/// Possible error returned when loading a theme.
|
/// Possible error returned when loading a theme.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -429,26 +447,47 @@ impl From<toml::de::Error> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
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<Self> {
|
fn parse(value: &str) -> Option<Self> {
|
||||||
Some(match value {
|
Some(match value {
|
||||||
"black" => Color::Dark(BaseColor::Black),
|
"black" => Color::Dark(BaseColor::Black),
|
||||||
"red" => Color::Dark(BaseColor::Red),
|
"red" => Color::Dark(BaseColor::Red),
|
||||||
"green" => Color::Dark(BaseColor::Green),
|
"green" => Color::Dark(BaseColor::Green),
|
||||||
"yellow" => Color::Dark(BaseColor::Yellow),
|
"yellow" => Color::Dark(BaseColor::Yellow),
|
||||||
"blue" => Color::Dark(BaseColor::Blue),
|
"blue" => Color::Dark(BaseColor::Blue),
|
||||||
"magenta" => Color::Dark(BaseColor::Magenta),
|
"magenta" => Color::Dark(BaseColor::Magenta),
|
||||||
"cyan" => Color::Dark(BaseColor::Cyan),
|
"cyan" => Color::Dark(BaseColor::Cyan),
|
||||||
"white" => Color::Dark(BaseColor::White),
|
"white" => Color::Dark(BaseColor::White),
|
||||||
"light black" => Color::Light(BaseColor::Black),
|
"light black" => Color::Light(BaseColor::Black),
|
||||||
"light red" => Color::Light(BaseColor::Red),
|
"light red" => Color::Light(BaseColor::Red),
|
||||||
"light green" => Color::Light(BaseColor::Green),
|
"light green" => Color::Light(BaseColor::Green),
|
||||||
"light yellow" => Color::Light(BaseColor::Yellow),
|
"light yellow" => Color::Light(BaseColor::Yellow),
|
||||||
"light blue" => Color::Light(BaseColor::Blue),
|
"light blue" => Color::Light(BaseColor::Blue),
|
||||||
"light magenta" => Color::Light(BaseColor::Magenta),
|
"light magenta" => Color::Light(BaseColor::Magenta),
|
||||||
"light cyan" => Color::Light(BaseColor::Cyan),
|
"light cyan" => Color::Light(BaseColor::Cyan),
|
||||||
"light white" => Color::Light(BaseColor::White),
|
"light white" => Color::Light(BaseColor::White),
|
||||||
value => return Color::parse_special(value),
|
value => return Color::parse_special(value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_special(value: &str) -> Option<Color> {
|
fn parse_special(value: &str) -> Option<Color> {
|
||||||
|
@ -114,6 +114,11 @@ impl From<(u32, u32)> for XY<usize> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(u8, u8)> for XY<usize> {
|
||||||
|
fn from((x, y): (u8, u8)) -> Self {
|
||||||
|
(x as usize, y as usize).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Add<Output = T>, O: Into<XY<T>>> Add<O> for XY<T> {
|
impl<T: Add<Output = T>, O: Into<XY<T>>> Add<O> for XY<T> {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
@ -159,7 +159,8 @@ impl ScrollBase {
|
|||||||
/// # use cursive::backend::{self, Backend};
|
/// # use cursive::backend::{self, Backend};
|
||||||
/// # let scrollbase = ScrollBase::new();
|
/// # let scrollbase = ScrollBase::new();
|
||||||
/// # let b = backend::Concrete::init();
|
/// # 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 printer = &printer;
|
||||||
/// let lines = ["Line 1", "Line number 2"];
|
/// let lines = ["Line 1", "Line number 2"];
|
||||||
/// scrollbase.draw(printer, |printer, i| {
|
/// scrollbase.draw(printer, |printer, i| {
|
||||||
|
Loading…
Reference in New Issue
Block a user