mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
More ncurses isolation
Also some renaming in the theme module
This commit is contained in:
parent
4dcb2ea901
commit
985009e51c
@ -6,24 +6,24 @@ borders = "simple" # Alternatives are "none" and "outset"
|
|||||||
# Base colors are red, green, blue,
|
# Base colors are red, green, blue,
|
||||||
# cyan, magenta, yellow, white and black.
|
# cyan, magenta, yellow, white and black.
|
||||||
[colors]
|
[colors]
|
||||||
background = ["#923456", "magenta"]
|
background = ["454", "#923456", "401", "magenta"]
|
||||||
# If the value is an array, the first valid color will be used.
|
# If the value is an array, the first valid color will be used.
|
||||||
# If the terminal doesn't support custom color,
|
# If the terminal doesn't support custom color,
|
||||||
# non-base colors will be skipped.
|
# non-base colors will be skipped.
|
||||||
shadow = ["#222222", "blue"]
|
shadow = ["#222288", "blue"]
|
||||||
view = "#888888"
|
view = "111"
|
||||||
|
|
||||||
# Array and simple values have the same effect.
|
# Array and simple values have the same effect.
|
||||||
primary = ["#111111"]
|
primary = ["white"]
|
||||||
secondary = "#EEEEEE"
|
secondary = "#EEEEEE"
|
||||||
tertiary = "#444444"
|
tertiary = "#444444"
|
||||||
|
|
||||||
# Hex values can use lower or uppercase.
|
# Hex values can use lower or uppercase.
|
||||||
# (base color MUST be lowercase)
|
# (base color MUST be lowercase)
|
||||||
title_primary = "#883333"
|
title_primary = "yellow"
|
||||||
title_secondary = "#ffff55"
|
title_secondary = "#ffff55"
|
||||||
|
|
||||||
# Lower precision values can use only 3 digits.
|
# Lower precision values can use only 3 digits.
|
||||||
highlight = "#833"
|
highlight = "#F88"
|
||||||
highlight_inactive = "#5555FF"
|
highlight_inactive = "#5555FF"
|
||||||
|
|
||||||
|
@ -1,31 +1,15 @@
|
|||||||
|
use backend;
|
||||||
use event;
|
use event;
|
||||||
use theme;
|
use theme;
|
||||||
use utf8;
|
use utf8;
|
||||||
|
|
||||||
use ncurses;
|
use ncurses;
|
||||||
|
|
||||||
pub trait Backend {
|
|
||||||
fn init();
|
|
||||||
fn finish();
|
|
||||||
|
|
||||||
fn clear();
|
|
||||||
fn refresh();
|
|
||||||
|
|
||||||
fn print_at((usize, usize), &str);
|
|
||||||
|
|
||||||
fn poll_event() -> event::Event;
|
|
||||||
fn set_refresh_rate(fps: u32);
|
|
||||||
fn screen_size() -> (usize, usize);
|
|
||||||
|
|
||||||
fn with_color<F: FnOnce()>(color: theme::ColorPair, f: F);
|
|
||||||
fn with_effect<F: FnOnce()>(effect: theme::Effect, f: F);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub struct NcursesBackend;
|
pub struct NcursesBackend;
|
||||||
|
|
||||||
impl Backend for NcursesBackend {
|
impl backend::Backend for NcursesBackend {
|
||||||
fn init() {
|
fn init() {
|
||||||
|
::std::env::set_var("ESCDELAY", "25");
|
||||||
ncurses::setlocale(ncurses::LcCategory::all, "");
|
ncurses::setlocale(ncurses::LcCategory::all, "");
|
||||||
ncurses::initscr();
|
ncurses::initscr();
|
||||||
ncurses::keypad(ncurses::stdscr, true);
|
ncurses::keypad(ncurses::stdscr, true);
|
||||||
@ -34,7 +18,7 @@ impl Backend for NcursesBackend {
|
|||||||
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::wbkgd(ncurses::stdscr,
|
||||||
ncurses::COLOR_PAIR(theme::ColorPair::Background.ncurses_id()));
|
ncurses::COLOR_PAIR(theme::ColorStyle::Background.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_size() -> (usize, usize) {
|
fn screen_size() -> (usize, usize) {
|
||||||
@ -44,16 +28,31 @@ impl Backend for NcursesBackend {
|
|||||||
(x as usize, y as usize)
|
(x as usize, y as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_colors() -> bool {
|
||||||
|
ncurses::has_colors()
|
||||||
|
}
|
||||||
|
|
||||||
fn finish() {
|
fn finish() {
|
||||||
ncurses::endwin();
|
ncurses::endwin();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_color<F: FnOnce()>(color: theme::ColorPair, f: F) {
|
|
||||||
|
fn init_color_style(style: theme::ColorStyle,
|
||||||
|
foreground: &theme::Color,
|
||||||
|
background: &theme::Color) {
|
||||||
|
// TODO: build the color on the spot
|
||||||
|
|
||||||
|
ncurses::init_pair(style.id(),
|
||||||
|
find_closest(foreground) as i16,
|
||||||
|
find_closest(background) as i16);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_color<F: FnOnce()>(color: theme::ColorStyle, f: F) {
|
||||||
let mut current_style: ncurses::attr_t = 0;
|
let mut current_style: ncurses::attr_t = 0;
|
||||||
let mut current_color: i16 = 0;
|
let mut current_color: i16 = 0;
|
||||||
ncurses::attr_get(&mut current_style, &mut current_color);
|
ncurses::attr_get(&mut current_style, &mut current_color);
|
||||||
|
|
||||||
let style = ncurses::COLOR_PAIR(color.ncurses_id());
|
let style = ncurses::COLOR_PAIR(color.id());
|
||||||
ncurses::attron(style);
|
ncurses::attron(style);
|
||||||
f();
|
f();
|
||||||
// ncurses::attroff(style);
|
// ncurses::attroff(style);
|
||||||
@ -218,3 +217,25 @@ fn parse_ncurses_char(ch: i32) -> event::Key {
|
|||||||
_ => event::Key::Unknown(ch),
|
_ => event::Key::Unknown(ch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_closest(color: &theme::Color) -> u8 {
|
||||||
|
match *color {
|
||||||
|
theme::Color::Black => 0,
|
||||||
|
theme::Color::Red => 1,
|
||||||
|
theme::Color::Green => 2,
|
||||||
|
theme::Color::Yellow => 3,
|
||||||
|
theme::Color::Blue => 4,
|
||||||
|
theme::Color::Magenta => 5,
|
||||||
|
theme::Color::Cyan => 6,
|
||||||
|
theme::Color::White => 7,
|
||||||
|
theme::Color::Rgb(r, g, b) => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
theme::Color::RgbLowRes(r, g, b) => {
|
||||||
|
(16 + 36 * r + 6 * g + b) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/backend/mod.rs
Normal file
31
src/backend/mod.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use event;
|
||||||
|
use theme;
|
||||||
|
|
||||||
|
// Module is not named `ncurses` to avoir naming conflict
|
||||||
|
mod curses;
|
||||||
|
|
||||||
|
pub use self::curses::NcursesBackend;
|
||||||
|
|
||||||
|
pub trait Backend {
|
||||||
|
|
||||||
|
fn init();
|
||||||
|
fn finish();
|
||||||
|
|
||||||
|
fn clear();
|
||||||
|
fn refresh();
|
||||||
|
|
||||||
|
fn has_colors() -> bool;
|
||||||
|
|
||||||
|
fn init_color_style(style: theme::ColorStyle,
|
||||||
|
foreground: &theme::Color,
|
||||||
|
background: &theme::Color);
|
||||||
|
|
||||||
|
fn print_at((usize, usize), &str);
|
||||||
|
|
||||||
|
fn poll_event() -> event::Event;
|
||||||
|
fn set_refresh_rate(fps: u32);
|
||||||
|
fn screen_size() -> (usize, usize);
|
||||||
|
|
||||||
|
fn with_color<F: FnOnce()>(color: theme::ColorStyle, f: F);
|
||||||
|
fn with_effect<F: FnOnce()>(effect: theme::Effect, f: F);
|
||||||
|
}
|
@ -90,13 +90,11 @@ impl Cursive {
|
|||||||
/// Creates a new Cursive root, and initialize ncurses.
|
/// Creates a new Cursive root, and initialize ncurses.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Default delay is way too long. 25 is imperceptible yet works fine.
|
// Default delay is way too long. 25 is imperceptible yet works fine.
|
||||||
std::env::set_var("ESCDELAY", "25");
|
|
||||||
B::init();
|
B::init();
|
||||||
|
|
||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
// let theme = theme::load_theme("assets/style.toml").unwrap();
|
||||||
|
|
||||||
|
|
||||||
let mut res = Cursive {
|
let mut res = Cursive {
|
||||||
theme: theme,
|
theme: theme,
|
||||||
screens: Vec::new(),
|
screens: Vec::new(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use menu::*;
|
use menu::*;
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
use printer::Printer;
|
use printer::Printer;
|
||||||
use event::*;
|
use event::*;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ impl Menubar {
|
|||||||
|
|
||||||
pub fn draw(&mut self, printer: &Printer) {
|
pub fn draw(&mut self, printer: &Printer) {
|
||||||
// Draw the bar at the top
|
// Draw the bar at the top
|
||||||
printer.with_color(ColorPair::Primary, |printer| {
|
printer.with_color(ColorStyle::Primary, |printer| {
|
||||||
printer.print_hline((0, 0), printer.size.x, " ");
|
printer.print_hline((0, 0), printer.size.x, " ");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use backend::Backend;
|
|||||||
|
|
||||||
use B;
|
use B;
|
||||||
|
|
||||||
use theme::{ColorPair, Theme, Effect};
|
use theme::{ColorStyle, Theme, Effect};
|
||||||
use vec::{ToVec2, Vec2};
|
use vec::{ToVec2, Vec2};
|
||||||
|
|
||||||
/// Convenient interface to draw on a subset of the screen.
|
/// Convenient interface to draw on a subset of the screen.
|
||||||
@ -94,11 +94,11 @@ impl Printer {
|
|||||||
/// # use cursive::printer::Printer;
|
/// # use cursive::printer::Printer;
|
||||||
/// # use cursive::theme;
|
/// # use cursive::theme;
|
||||||
/// # let printer = Printer::new((6,4), theme::load_default());
|
/// # let printer = Printer::new((6,4), theme::load_default());
|
||||||
/// printer.with_color(theme::ColorPair::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!");
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_color<F>(&self, c: ColorPair, f: F)
|
pub fn with_color<F>(&self, c: ColorStyle, f: F)
|
||||||
where F: FnOnce(&Printer)
|
where F: FnOnce(&Printer)
|
||||||
{
|
{
|
||||||
B::with_color(c, || f(self));
|
B::with_color(c, || f(self));
|
||||||
|
349
src/theme.rs
349
src/theme.rs
@ -5,17 +5,24 @@ use std::io::Read;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use ncurses;
|
use backend::Backend;
|
||||||
|
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
|
use B;
|
||||||
|
|
||||||
pub enum Effect {
|
pub enum Effect {
|
||||||
Simple,
|
Simple,
|
||||||
Reverse,
|
Reverse,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the color of a character and its background.
|
/// Possible color style for a cell.
|
||||||
|
///
|
||||||
|
/// 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)]
|
||||||
pub enum ColorPair {
|
pub enum ColorStyle {
|
||||||
/// Application background, where no view is present.
|
/// Application background, where no view is present.
|
||||||
Background,
|
Background,
|
||||||
/// Color used by view shadows. Only background matters.
|
/// Color used by view shadows. Only background matters.
|
||||||
@ -36,19 +43,19 @@ pub enum ColorPair {
|
|||||||
HighlightInactive,
|
HighlightInactive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorPair {
|
impl ColorStyle {
|
||||||
/// Returns the ncurses pair ID associated with this color pair.
|
/// Returns the ncurses pair ID associated with this color pair.
|
||||||
pub fn ncurses_id(self) -> i16 {
|
pub fn id(self) -> i16 {
|
||||||
match self {
|
match self {
|
||||||
ColorPair::Background => 1,
|
ColorStyle::Background => 1,
|
||||||
ColorPair::Shadow => 2,
|
ColorStyle::Shadow => 2,
|
||||||
ColorPair::Primary => 3,
|
ColorStyle::Primary => 3,
|
||||||
ColorPair::Secondary => 4,
|
ColorStyle::Secondary => 4,
|
||||||
ColorPair::Tertiary => 5,
|
ColorStyle::Tertiary => 5,
|
||||||
ColorPair::TitlePrimary => 6,
|
ColorStyle::TitlePrimary => 6,
|
||||||
ColorPair::TitleSecondary => 7,
|
ColorStyle::TitleSecondary => 7,
|
||||||
ColorPair::Highlight => 8,
|
ColorStyle::Highlight => 8,
|
||||||
ColorPair::HighlightInactive => 9,
|
ColorStyle::HighlightInactive => 9,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,29 +68,31 @@ pub struct Theme {
|
|||||||
/// How view borders should be drawn.
|
/// How view borders should be drawn.
|
||||||
pub borders: BorderStyle,
|
pub borders: BorderStyle,
|
||||||
/// What colors should be used through the application?
|
/// What colors should be used through the application?
|
||||||
pub colors: ColorStyle,
|
pub colors: Palette,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Default for Theme {
|
||||||
fn default() -> Theme {
|
fn default() -> Self {
|
||||||
Theme {
|
Theme {
|
||||||
shadow: true,
|
shadow: true,
|
||||||
borders: BorderStyle::Simple,
|
borders: BorderStyle::Simple,
|
||||||
colors: ColorStyle {
|
colors: Palette {
|
||||||
background: Color::blue(),
|
background: Color::Blue,
|
||||||
shadow: Color::black(),
|
shadow: Color::Black,
|
||||||
view: Color::white(),
|
view: Color::White,
|
||||||
primary: Color::black(),
|
primary: Color::Black,
|
||||||
secondary: Color::blue(),
|
secondary: Color::Blue,
|
||||||
tertiary: Color::white(),
|
tertiary: Color::White,
|
||||||
title_primary: Color::red(),
|
title_primary: Color::Red,
|
||||||
title_secondary: Color::yellow(),
|
title_secondary: Color::Yellow,
|
||||||
highlight: Color::red(),
|
highlight: Color::Red,
|
||||||
highlight_inactive: Color::blue(),
|
highlight_inactive: Color::Blue,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Theme {
|
||||||
fn load(&mut self, table: &toml::Table) {
|
fn load(&mut self, table: &toml::Table) {
|
||||||
if let Some(&toml::Value::Boolean(shadow)) = table.get("shadow") {
|
if let Some(&toml::Value::Boolean(shadow)) = table.get("shadow") {
|
||||||
self.shadow = shadow;
|
self.shadow = shadow;
|
||||||
@ -100,45 +109,48 @@ impl Theme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self) {
|
fn activate(&self) {
|
||||||
Theme::apply_color(ColorPair::Background,
|
// Initialize each color with the backend
|
||||||
&self.colors.view,
|
B::init_color_style(ColorStyle::Background,
|
||||||
&self.colors.background);
|
&self.colors.view,
|
||||||
Theme::apply_color(ColorPair::Shadow, &self.colors.shadow, &self.colors.shadow);
|
&self.colors.background);
|
||||||
Theme::apply_color(ColorPair::Primary, &self.colors.primary, &self.colors.view);
|
B::init_color_style(ColorStyle::Shadow,
|
||||||
Theme::apply_color(ColorPair::Secondary,
|
&self.colors.shadow,
|
||||||
&self.colors.secondary,
|
&self.colors.shadow);
|
||||||
&self.colors.view);
|
B::init_color_style(ColorStyle::Primary,
|
||||||
Theme::apply_color(ColorPair::Tertiary,
|
&self.colors.primary,
|
||||||
&self.colors.tertiary,
|
&self.colors.view);
|
||||||
&self.colors.view);
|
B::init_color_style(ColorStyle::Secondary,
|
||||||
Theme::apply_color(ColorPair::TitlePrimary,
|
&self.colors.secondary,
|
||||||
&self.colors.title_primary,
|
&self.colors.view);
|
||||||
&self.colors.view);
|
B::init_color_style(ColorStyle::Tertiary,
|
||||||
Theme::apply_color(ColorPair::TitleSecondary,
|
&self.colors.tertiary,
|
||||||
&self.colors.title_secondary,
|
&self.colors.view);
|
||||||
&self.colors.view);
|
B::init_color_style(ColorStyle::TitlePrimary,
|
||||||
Theme::apply_color(ColorPair::Highlight,
|
&self.colors.title_primary,
|
||||||
&self.colors.view,
|
&self.colors.view);
|
||||||
&self.colors.highlight);
|
B::init_color_style(ColorStyle::TitleSecondary,
|
||||||
Theme::apply_color(ColorPair::HighlightInactive,
|
&self.colors.title_secondary,
|
||||||
&self.colors.view,
|
&self.colors.view);
|
||||||
&self.colors.highlight_inactive);
|
B::init_color_style(ColorStyle::Highlight,
|
||||||
}
|
&self.colors.view,
|
||||||
|
&self.colors.highlight);
|
||||||
fn apply_color(pair: ColorPair, front: &Color, back: &Color) {
|
B::init_color_style(ColorStyle::HighlightInactive,
|
||||||
ncurses::init_pair(pair.ncurses_id(), front.id, back.id);
|
&self.colors.view,
|
||||||
|
&self.colors.highlight_inactive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies how View borders should be drawn.
|
/// Specifies how some borders should be drawn.
|
||||||
|
///
|
||||||
|
/// Borders are used around Dialogs, select popups, and panels.
|
||||||
#[derive(Clone,Copy,Debug)]
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub enum BorderStyle {
|
pub enum BorderStyle {
|
||||||
/// Don't draw any border.
|
/// Don't draw any border.
|
||||||
NoBorder,
|
NoBorder,
|
||||||
/// Simple borders.
|
/// Simple borders.
|
||||||
Simple,
|
Simple,
|
||||||
/// Outset borders with a 3d effect.
|
/// Outset borders with a simple 3d effect.
|
||||||
Outset,
|
Outset,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +168,11 @@ impl BorderStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the colors the application will use in various situations.
|
/// Color configuration for the application.
|
||||||
|
///
|
||||||
|
/// Assign each color role an actual color.
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct ColorStyle {
|
pub struct Palette {
|
||||||
/// Color used for the application background.
|
/// Color used for the application background.
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
/// Color used for View shadows.
|
/// Color used for View shadows.
|
||||||
@ -181,39 +195,57 @@ pub struct ColorStyle {
|
|||||||
pub highlight_inactive: Color,
|
pub highlight_inactive: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorStyle {
|
impl Palette {
|
||||||
fn load(&mut self, table: &toml::Table) {
|
fn load(&mut self, table: &toml::Table) {
|
||||||
let mut new_id = 16;
|
|
||||||
|
|
||||||
self.background.load(table, "background", &mut new_id);
|
load_color(&mut self.background, table.get("background"));
|
||||||
self.shadow.load(table, "shadow", &mut new_id);
|
load_color(&mut self.shadow, table.get("shadow"));
|
||||||
self.view.load(table, "view", &mut new_id);
|
load_color(&mut self.view, table.get("view"));
|
||||||
self.primary.load(table, "primary", &mut new_id);
|
load_color(&mut self.primary, table.get("primary"));
|
||||||
self.secondary.load(table, "secondary", &mut new_id);
|
load_color(&mut self.secondary, table.get("secondary"));
|
||||||
self.tertiary.load(table, "tertiary", &mut new_id);
|
load_color(&mut self.tertiary, table.get("tertiary"));
|
||||||
self.title_primary.load(table, "title_primary", &mut new_id);
|
load_color(&mut self.title_primary, table.get("title_primary"));
|
||||||
self.title_secondary.load(table, "title_secondary", &mut new_id);
|
load_color(&mut self.title_secondary, table.get("title_secondary"));
|
||||||
self.highlight.load(table, "highlight", &mut new_id);
|
load_color(&mut self.highlight, table.get("highlight"));
|
||||||
self.highlight_inactive.load(table, "highlight_inactive", &mut new_id);
|
load_color(&mut self.highlight_inactive, table.get("highlight_inactive"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_color(target: &mut Color, value: Option<&toml::Value>) -> bool {
|
||||||
|
if let Some(value) = value {
|
||||||
|
match *value {
|
||||||
|
toml::Value::String(ref value) => if let Some(color) = Color::parse(value) {
|
||||||
|
*target = color;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
toml::Value::Array(ref array) => array.iter().any(|item| load_color(target, Some(item))),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a color used by the theme.
|
/// Represents a color used by the theme.
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct Color {
|
pub enum Color {
|
||||||
/// Color ID used by ncurses.
|
/// Color ID used by ncurses.
|
||||||
pub id: i16,
|
Black,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Yellow,
|
||||||
|
Blue,
|
||||||
|
Magenta,
|
||||||
|
Cyan,
|
||||||
|
White,
|
||||||
|
// 24-bit color
|
||||||
|
Rgb(u8, u8, u8),
|
||||||
|
RgbLowRes(u8, u8, u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
/// Return the rgb values used by the color.
|
|
||||||
pub fn rgb(&self) -> (i16, i16, i16) {
|
|
||||||
let (mut r, mut g, mut b) = (0, 0, 0);
|
|
||||||
|
|
||||||
ncurses::color_content(self.id, &mut r, &mut g, &mut b);
|
|
||||||
|
|
||||||
(r, g, b)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possible error returned when loading a theme.
|
/// Possible error returned when loading a theme.
|
||||||
@ -232,115 +264,47 @@ impl From<io::Error> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
fn parse(value: &str, new_id: &mut i16) -> Option<Self> {
|
fn parse(value: &str) -> Option<Self> {
|
||||||
let color = if value == "black" {
|
Some(match value {
|
||||||
Color::black()
|
"black" => Color::Black,
|
||||||
} else if value == "red" {
|
"red" => Color::Red,
|
||||||
Color::red()
|
"green" => Color::Green,
|
||||||
} else if value == "green" {
|
"yellow" => Color::Yellow,
|
||||||
Color::green()
|
"blue" => Color::Blue,
|
||||||
} else if value == "yellow" {
|
"magenta" => Color::Magenta,
|
||||||
Color::yellow()
|
"cyan" => Color::Cyan,
|
||||||
} else if value == "blue" {
|
"white" => Color::White,
|
||||||
Color::blue()
|
value => return Color::parse_special(value),
|
||||||
} else if value == "magenta" {
|
})
|
||||||
Color::magenta()
|
}
|
||||||
} else if value == "cyan" {
|
|
||||||
Color::cyan()
|
fn parse_special(value: &str) -> Option<Color> {
|
||||||
} else if value == "white" {
|
if value.starts_with('#') {
|
||||||
Color::white()
|
|
||||||
|
let value = &value[1..];
|
||||||
|
// Compute per-color length, and amplitude
|
||||||
|
let (l, multiplier) = match value.len() {
|
||||||
|
6 => (2, 1),
|
||||||
|
3 => (1, 17),
|
||||||
|
_ => panic!("Cannot parse color: {}", value),
|
||||||
|
};
|
||||||
|
let r = load_hex(&value[0 * l..1 * l]) * multiplier;
|
||||||
|
let g = load_hex(&value[1 * l..2 * l]) * multiplier;
|
||||||
|
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
|
||||||
|
Some(Color::Rgb(r as u8, g as u8, b as u8))
|
||||||
|
} else if value.len() == 3 {
|
||||||
|
// RGB values between 0 and 5 maybe?
|
||||||
|
let rgb: Vec<_> = value.chars().map(|c| c as i16 - '0' as i16).collect();
|
||||||
|
println!("{:?}", rgb);
|
||||||
|
if rgb.iter().all(|&i| i >= 0 && i < 6) {
|
||||||
|
Some(Color::RgbLowRes(rgb[0] as u8, rgb[1] as u8, rgb[2] as u8))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Let's make a new color
|
None
|
||||||
return Color::make_new(value, new_id);
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load(&mut self, table: &toml::Table, key: &str, new_id: &mut i16) {
|
|
||||||
match table.get(key) {
|
|
||||||
Some(&toml::Value::String(ref value)) => {
|
|
||||||
self.load_value(value, new_id);
|
|
||||||
}
|
|
||||||
Some(&toml::Value::Array(ref array)) => {
|
|
||||||
for color in array.iter() {
|
|
||||||
if let toml::Value::String(ref color) = *color {
|
|
||||||
if self.load_value(color, new_id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_value(&mut self, value: &str, new_id: &mut i16) -> bool {
|
|
||||||
match Color::parse(value, new_id) {
|
|
||||||
Some(color) => self.id = color.id,
|
|
||||||
None => return false,
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_new(value: &str, new_id: &mut i16) -> Option<Self> {
|
|
||||||
// if !ncurses::can_change_color() {
|
|
||||||
if !ncurses::has_colors() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !value.starts_with('#') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if *new_id >= ncurses::COLORS as i16 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let s = &value[1..];
|
|
||||||
let (l, max) = match s.len() {
|
|
||||||
6 => (2, 255),
|
|
||||||
3 => (1, 15),
|
|
||||||
_ => panic!("Cannot parse color: {}", s),
|
|
||||||
};
|
|
||||||
|
|
||||||
let r = (load_hex(&s[0 * l..1 * l]) as i32 * 1000 / max) as i16;
|
|
||||||
let g = (load_hex(&s[1 * l..2 * l]) as i32 * 1000 / max) as i16;
|
|
||||||
let b = (load_hex(&s[2 * l..3 * l]) as i32 * 1000 / max) as i16;
|
|
||||||
|
|
||||||
ncurses::init_color(*new_id, r, g, b);
|
|
||||||
|
|
||||||
let color = Color { id: *new_id };
|
|
||||||
*new_id += 1;
|
|
||||||
|
|
||||||
Some(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn black() -> Self {
|
|
||||||
Color { id: 0 }
|
|
||||||
}
|
|
||||||
pub fn red() -> Self {
|
|
||||||
Color { id: 1 }
|
|
||||||
}
|
|
||||||
pub fn green() -> Self {
|
|
||||||
Color { id: 2 }
|
|
||||||
}
|
|
||||||
pub fn yellow() -> Self {
|
|
||||||
Color { id: 3 }
|
|
||||||
}
|
|
||||||
pub fn blue() -> Self {
|
|
||||||
Color { id: 4 }
|
|
||||||
}
|
|
||||||
pub fn magenta() -> Self {
|
|
||||||
Color { id: 5 }
|
|
||||||
}
|
|
||||||
pub fn cyan() -> Self {
|
|
||||||
Color { id: 6 }
|
|
||||||
}
|
|
||||||
pub fn white() -> Self {
|
|
||||||
Color { id: 7 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -379,6 +343,7 @@ impl Color {
|
|||||||
/// highlight_inactive = "#5555FF"
|
/// highlight_inactive = "#5555FF"
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
|
/// Loads a theme and sets it as active.
|
||||||
pub fn load_theme<P: AsRef<Path>>(filename: P) -> Result<Theme, Error> {
|
pub fn load_theme<P: AsRef<Path>>(filename: P) -> Result<Theme, Error> {
|
||||||
let content = {
|
let content = {
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
@ -395,7 +360,7 @@ pub fn load_theme<P: AsRef<Path>>(filename: P) -> Result<Theme, Error> {
|
|||||||
|
|
||||||
let mut theme = Theme::default();
|
let mut theme = Theme::default();
|
||||||
theme.load(&table);
|
theme.load(&table);
|
||||||
theme.apply();
|
theme.activate();
|
||||||
|
|
||||||
Ok(theme)
|
Ok(theme)
|
||||||
}
|
}
|
||||||
@ -403,12 +368,12 @@ pub fn load_theme<P: AsRef<Path>>(filename: P) -> Result<Theme, Error> {
|
|||||||
/// Loads the default theme, and returns its representation.
|
/// Loads the default theme, and returns its representation.
|
||||||
pub fn load_default() -> Theme {
|
pub fn load_default() -> Theme {
|
||||||
let theme = Theme::default();
|
let theme = Theme::default();
|
||||||
theme.apply();
|
theme.activate();
|
||||||
theme
|
theme
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a hexadecimal code
|
/// Loads a hexadecimal code
|
||||||
fn load_hex(s: &str) -> i16 {
|
fn load_hex(s: &str) -> u16 {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
sum *= 16;
|
sum *= 16;
|
||||||
@ -420,5 +385,5 @@ fn load_hex(s: &str) -> i16 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
sum
|
sum as u16
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
use Cursive;
|
use Cursive;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{SizeRequest, View};
|
use view::{SizeRequest, View};
|
||||||
@ -29,9 +29,9 @@ impl Button {
|
|||||||
impl View for Button {
|
impl View for Button {
|
||||||
fn draw(&mut self, printer: &Printer) {
|
fn draw(&mut self, printer: &Printer) {
|
||||||
let style = if !printer.focused {
|
let style = if !printer.focused {
|
||||||
ColorPair::Primary
|
ColorStyle::Primary
|
||||||
} else {
|
} else {
|
||||||
ColorPair::Highlight
|
ColorStyle::Highlight
|
||||||
};
|
};
|
||||||
let x = printer.size.x - 1;
|
let x = printer.size.x - 1;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use std::any::Any;
|
|||||||
use Cursive;
|
use Cursive;
|
||||||
use align::*;
|
use align::*;
|
||||||
use event::*;
|
use event::*;
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
use view::{DimensionRequest, Selector, SizeRequest, TextView, View};
|
use view::{DimensionRequest, Selector, SizeRequest, TextView, View};
|
||||||
use view::{Button, SizedView};
|
use view::{Button, SizedView};
|
||||||
use vec::{ToVec4, Vec2, Vec4};
|
use vec::{ToVec4, Vec2, Vec4};
|
||||||
@ -152,7 +152,7 @@ impl View for Dialog {
|
|||||||
printer.print((x - 2, 0), "┤ ");
|
printer.print((x - 2, 0), "┤ ");
|
||||||
printer.print((x + len, 0), " ├");
|
printer.print((x + len, 0), " ├");
|
||||||
|
|
||||||
printer.with_color(ColorPair::TitlePrimary, |p| p.print((x, 0), &self.title));
|
printer.with_color(ColorStyle::TitlePrimary, |p| p.print((x, 0), &self.title));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use theme::{ColorPair, Effect};
|
use theme::{ColorStyle, Effect};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{IdView, SizeRequest, View};
|
use view::{IdView, SizeRequest, View};
|
||||||
use event::*;
|
use event::*;
|
||||||
@ -85,7 +85,7 @@ impl View for EditView {
|
|||||||
fn draw(&mut self, printer: &Printer) {
|
fn draw(&mut self, printer: &Printer) {
|
||||||
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
|
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
|
||||||
let len = self.content.chars().count();
|
let len = self.content.chars().count();
|
||||||
printer.with_color(ColorPair::Secondary, |printer| {
|
printer.with_color(ColorStyle::Secondary, |printer| {
|
||||||
printer.with_effect(Effect::Reverse, |printer| {
|
printer.with_effect(Effect::Reverse, |printer| {
|
||||||
if len < self.last_length {
|
if len < self.last_length {
|
||||||
printer.print((0, 0), &self.content);
|
printer.print((0, 0), &self.content);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use printer::Printer;
|
use printer::Printer;
|
||||||
|
|
||||||
@ -129,9 +129,9 @@ impl ScrollBase {
|
|||||||
let start = steps * self.start_line / (1 + self.content_height - self.view_height);
|
let start = steps * self.start_line / (1 + self.content_height - self.view_height);
|
||||||
|
|
||||||
let color = if printer.focused {
|
let color = if printer.focused {
|
||||||
ColorPair::Highlight
|
ColorStyle::Highlight
|
||||||
} else {
|
} else {
|
||||||
ColorPair::HighlightInactive
|
ColorStyle::HighlightInactive
|
||||||
};
|
};
|
||||||
|
|
||||||
printer.print_vline((printer.size.x - 1, 0), printer.size.y, "|");
|
printer.print_vline((printer.size.x - 1, 0), printer.size.y, "|");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
use Cursive;
|
use Cursive;
|
||||||
use align::*;
|
use align::*;
|
||||||
use view::{DimensionRequest, IdView, SizeRequest, View};
|
use view::{DimensionRequest, IdView, SizeRequest, View};
|
||||||
@ -133,12 +133,12 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
self.scrollbase.draw(printer, |printer, i| {
|
self.scrollbase.draw(printer, |printer, i| {
|
||||||
let style = if i == self.focus {
|
let style = if i == self.focus {
|
||||||
if printer.focused {
|
if printer.focused {
|
||||||
ColorPair::Highlight
|
ColorStyle::Highlight
|
||||||
} else {
|
} else {
|
||||||
ColorPair::HighlightInactive
|
ColorStyle::HighlightInactive
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ColorPair::Primary
|
ColorStyle::Primary
|
||||||
};
|
};
|
||||||
printer.with_color(style, |printer| {
|
printer.with_color(style, |printer| {
|
||||||
let l = self.items[i].label.chars().count();
|
let l = self.items[i].label.chars().count();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use view::{SizeRequest, View, ViewWrapper};
|
use view::{SizeRequest, View, ViewWrapper};
|
||||||
use printer::Printer;
|
use printer::Printer;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
|
|
||||||
/// Wrapper view that adds a shadow.
|
/// Wrapper view that adds a shadow.
|
||||||
///
|
///
|
||||||
@ -40,7 +40,7 @@ impl<T: View> ViewWrapper for ShadowView<T> {
|
|||||||
let h = printer.size.y - 1;
|
let h = printer.size.y - 1;
|
||||||
let w = printer.size.x - 1;
|
let w = printer.size.x - 1;
|
||||||
|
|
||||||
printer.with_color(ColorPair::Shadow, |printer| {
|
printer.with_color(ColorStyle::Shadow, |printer| {
|
||||||
printer.print_hline((2, h), w - 1, " ");
|
printer.print_hline((2, h), w - 1, " ");
|
||||||
printer.print_vline((w, 2), h - 1, " ");
|
printer.print_vline((w, 2), h - 1, " ");
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,7 @@ use vec::Vec2;
|
|||||||
use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View};
|
use view::{DimensionRequest, Selector, ShadowView, SizeRequest, View};
|
||||||
use event::{Event, EventResult};
|
use event::{Event, EventResult};
|
||||||
use printer::Printer;
|
use printer::Printer;
|
||||||
use theme::ColorPair;
|
use theme::ColorStyle;
|
||||||
|
|
||||||
/// Simple stack of views.
|
/// Simple stack of views.
|
||||||
/// Only the top-most view is active and can receive input.
|
/// Only the top-most view is active and can receive input.
|
||||||
@ -49,7 +49,7 @@ impl StackView {
|
|||||||
impl View for StackView {
|
impl View for StackView {
|
||||||
fn draw(&mut self, printer: &Printer) {
|
fn draw(&mut self, printer: &Printer) {
|
||||||
let last = self.layers.len();
|
let last = self.layers.len();
|
||||||
printer.with_color(ColorPair::Primary, |printer| {
|
printer.with_color(ColorStyle::Primary, |printer| {
|
||||||
for (i, v) in self.layers.iter_mut().enumerate() {
|
for (i, v) in self.layers.iter_mut().enumerate() {
|
||||||
// Center the view
|
// Center the view
|
||||||
let size = v.size;
|
let size = v.size;
|
||||||
|
Loading…
Reference in New Issue
Block a user