summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2019-02-17 21:47:40 +0100
committerCanop <cano.petrole@gmail.com>2019-02-17 21:47:40 +0100
commit6534cf0a4a4aadc4d9881d8cb99a750d0e473340 (patch)
tree0c171b759457ebf9d79aa3ae92f578a4fef85ccb
parent06b2e9dfa7b31daec9a0ad1c858c8a570f76452b (diff)
All colors of broot can be defined in config
-rw-r--r--documentation.md52
-rw-r--r--img/20190217-custom-colors-help.pngbin0 -> 35463 bytes
-rw-r--r--img/20190217-custom-colors-tree.pngbin0 -> 38191 bytes
-rw-r--r--src/app_context.rs1
-rw-r--r--src/browser_states.rs15
-rw-r--r--src/errors.rs2
-rw-r--r--src/help_states.rs28
-rw-r--r--src/input.rs4
-rw-r--r--src/screen_text.rs82
-rw-r--r--src/screens.rs10
-rw-r--r--src/skin.rs23
-rw-r--r--src/skin_conf.rs19
-rw-r--r--src/spinner.rs9
-rw-r--r--src/status.rs4
-rw-r--r--src/tree_views.rs50
15 files changed, 168 insertions, 131 deletions
diff --git a/documentation.md b/documentation.md
index 99a1c04..7d60c3a 100644
--- a/documentation.md
+++ b/documentation.md
@@ -185,3 +185,55 @@ Example:
![dcd ruleset](img/20190122-dcd_rulset.png)
+## Custom Colors
+
+You can change all colors by adding a `[skin]` section in your `conf.toml` file.
+
+For example:
+
+ [skin]
+ status_normal_fg = "grayscale(18)"
+ status_normal_bg = "grayscale(3)"
+ status_error_fg = "red"
+ status_error_bg = "yellow"
+ tree_fg = "red"
+ selected_line_bg = "grayscale(7)"
+ permissions_fg = "grayscale(12)"
+ size_bar_full_bg = "red"
+ size_bar_void_bg = "black"
+ directory_fg = "lightyellow"
+ input_fg = "cyan"
+ flag_value_fg = "lightyellow"
+ table_border_fg = "red"
+ code_fg = "lightyellow"
+
+which would look like this:
+
+![custom colors tree](img/20190217-custom-colors-tree.png)
+
+![custom colors help](img/20190217-custom-colors-help.png)
+
+Complete list of keys (expected to change before the v1 of broot):
+
+ char_match
+ code
+ directory
+ file
+ file_error
+ flag_label
+ flag_value
+ input
+ link
+ permissions
+ selected_line
+ size_bar_full
+ size_bar_void
+ size_text
+ spinner
+ status_error
+ status_normal
+ table_border
+ tree
+ unlisted
+
+Add `_fg` for a foreground color while `_bg` is for background colors.
diff --git a/img/20190217-custom-colors-help.png b/img/20190217-custom-colors-help.png
new file mode 100644
index 0000000..729effc
--- /dev/null
+++ b/img/20190217-custom-colors-help.png
Binary files differ
diff --git a/img/20190217-custom-colors-tree.png b/img/20190217-custom-colors-tree.png
new file mode 100644
index 0000000..c09f41a
--- /dev/null
+++ b/img/20190217-custom-colors-tree.png
Binary files differ
diff --git a/src/app_context.rs b/src/app_context.rs
index d9c4426..3ed3093 100644
--- a/src/app_context.rs
+++ b/src/app_context.rs
@@ -1,6 +1,5 @@
use crate::cli::AppLaunchArgs;
use crate::verbs::VerbStore;
-use crate::skin::Skin;
/// The immutable container that can be passed around to provide
/// the configuration things
diff --git a/src/browser_states.rs b/src/browser_states.rs
index 6869b43..6d279ef 100644
--- a/src/browser_states.rs
+++ b/src/browser_states.rs
@@ -5,7 +5,6 @@ use std::io::{self, Write};
use std::path::PathBuf;
use std::result::Result;
use std::time::Instant;
-use termion::color;
use crate::app::{AppState, AppStateCmdResult};
use crate::app_context::AppContext;
@@ -312,19 +311,23 @@ impl AppState for BrowserState {
let total_char_size = 9;
write!(
screen.stdout,
- "{}{}{}{} h:{} gi:{}{}{}",
+ "{}{}{}{} h:{}{}{}{}{} gi:{}{}{}",
termion::cursor::Goto(screen.w - total_char_size, screen.h),
- color::Bg(color::AnsiValue::grayscale(1)),
+ screen.skin.flag_label.fg,
+ screen.skin.flag_label.bg,
termion::clear::UntilNewline,
- color::Fg(color::AnsiValue::grayscale(15)),
+ screen.skin.flag_value.fg,
+ screen.skin.flag_value.bg,
if tree.options.show_hidden { 'y' } else { 'n' },
+ screen.skin.flag_label.fg,
+ screen.skin.flag_label.bg,
+ screen.skin.flag_value.fg,
+ screen.skin.flag_value.bg,
match tree.options.respect_git_ignore {
OptionBool::Auto => 'a',
OptionBool::Yes => 'y',
OptionBool::No => 'n',
},
- color::Bg(color::Reset),
- color::Fg(color::Reset),
)?;
Ok(())
}
diff --git a/src/errors.rs b/src/errors.rs
index df88266..6f0e8b7 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -2,8 +2,6 @@
use custom_error::custom_error;
use std::io;
-use crate::conf;
-
custom_error! {pub TreeBuildError
NotADirectory { path: String } = "Not a directory: {}",
FileNotFound { path: String } = "File not found: {}",
diff --git a/src/help_states.rs b/src/help_states.rs
index 1a7c1b4..fa396c6 100644
--- a/src/help_states.rs
+++ b/src/help_states.rs
@@ -8,6 +8,7 @@ use crate::commands::{Action, Command};
use crate::conf::Conf;
use crate::screen_text::{Text, TextTable};
use crate::screens::{Screen, ScreenArea};
+use crate::skin::Skin;
use crate::status::Status;
use crate::task_sync::TaskLifetime;
use crate::verbs::{PrefixSearchResult, Verb, VerbExecutor};
@@ -24,19 +25,6 @@ impl HelpState {
state.resize_area(screen);
state
}
- fn build_verbs_table(&self, text: &mut Text, con: &AppContext) {
- let mut tbl: TextTable<Verb> = TextTable::new();
- tbl.add_col("name", &|verb| &verb.name);
- tbl.add_col("shortcut", &|verb| {
- if let Some(sk) = &verb.short_key {
- &sk
- } else {
- ""
- }
- });
- tbl.add_col("description", &|verb: &Verb| &verb.description);
- tbl.write(&con.verb_store.verbs, text);
- }
fn resize_area(&mut self, screen: &Screen) {
self.area.bottom = screen.h - 2;
self.area.width = screen.w;
@@ -78,7 +66,7 @@ impl AppState for HelpState {
}
fn display(&mut self, screen: &mut Screen, con: &AppContext) -> io::Result<()> {
- let mut text = Text::new();
+ let mut text = Text::new(&screen.skin);
text.md("");
text.md(r#" **broot** lets you explore directory trees and launch commands."#);
text.md(r#" site: https://github.com/Canop/broot."#);
@@ -90,7 +78,16 @@ impl AppState for HelpState {
text.md("");
text.md(r#" To execute a verb, type a space or `:` then start of its name or shortcut."#);
text.md(" Verbs:");
- self.build_verbs_table(&mut text, con);
+ let mut tbl: TextTable<Verb> = TextTable::new(&screen.skin);
+ tbl.add_col("name", &|verb| &verb.name);
+ tbl.add_col("shortcut", &|verb| {
+ if let Some(sk) = &verb.short_key {
+ &sk
+ } else {
+ ""
+ }
+ });
+ tbl.write(&con.verb_store.verbs, &mut text);
text.md("");
text.md(&format!(
" Verb can be configured in {:?}.",
@@ -109,6 +106,7 @@ impl AppState for HelpState {
text.md(" When gitignore is auto, .gitignore rules are respected if");
text.md(" the displayed root is a git repository or in one.");
self.area.content_length = text.height() as i32;
+ screen.reset_colors()?;
text.write(screen, &self.area)?;
Ok(())
}
diff --git a/src/input.rs b/src/input.rs
index 8a730f3..9503888 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -13,8 +13,10 @@ impl Input for Screen {
fn write_input(&mut self, cmd: &Command) -> io::Result<()> {
write!(
self.stdout,
- "{}{}{}{} {}",
+ "{}{}{}{}{}{} {}",
termion::cursor::Goto(1, self.h),
+ self.skin.input.fg,
+ self.skin.input.bg,
termion::clear::CurrentLine,
cmd.raw,
termion::style::Invert,
diff --git a/src/screen_text.rs b/src/screen_text.rs
index 97d5d13..10fbd96 100644
--- a/src/screen_text.rs
+++ b/src/screen_text.rs
@@ -5,9 +5,10 @@
use regex::Regex;
use std::io::{self, Write};
-use termion::{color, style};
+use termion::style;
use crate::screens::{Screen, ScreenArea};
+use crate::skin::Skin;
// write a string, taking exactly w chars, either
// by cutting or by padding
@@ -18,38 +19,46 @@ fn append_pad(dest: &mut String, s: &str, w: usize) {
}
}
-// do some simple markdown-like formatting to ease text generation:
-// - bold: **bold text**
-// - code: `some code`
-fn md(s: &str) -> String {
- lazy_static! {
- static ref bold_regex: Regex = Regex::new(r"\*\*([^*]+)\*\*").unwrap();
- static ref bold_repl: String = format!("{}$1{}", style::Bold, style::Reset);
- static ref code_regex: Regex = Regex::new(r"`([^`]+)`").unwrap();
- static ref code_repl: String = format!(
- "{} $1 {}",
- color::Bg(color::AnsiValue::grayscale(2)),
- color::Bg(color::Reset)
- );
- }
- let s = bold_regex.replace_all(s, &*bold_repl as &str); // TODO how to avoid this complex casting ?
- let s = code_regex.replace_all(&s, &*code_repl as &str);
- s.to_string()
-}
-
/// A Text is a vec of lines
pub struct Text {
lines: Vec<String>,
+ md_bold_repl: String,
+ md_code_repl: String,
}
impl Text {
- pub fn new() -> Text {
- Text { lines: Vec::new() }
+ pub fn new(skin: &Skin) -> Text {
+ let md_code_repl = format!(
+ "{}{} $1 {}{}",
+ skin.code.fg,
+ skin.code.bg,
+ skin.reset.fg,
+ skin.reset.bg,
+ );
+ let md_bold_repl = format!("{}$1{}", style::Bold, style::Reset);
+ Text {
+ lines: Vec::new(),
+ md_bold_repl,
+ md_code_repl,
+ }
}
pub fn height(&self) -> usize {
self.lines.len()
}
+ // do some simple markdown-like formatting to ease text generation:
+ // - bold: **bold text**
+ // - code: `some code`
+ pub fn md_to_tty(&self, raw: &str) -> String {
+ lazy_static! {
+ static ref bold_regex: Regex = Regex::new(r"\*\*([^*]+)\*\*").unwrap();
+ static ref code_regex: Regex = Regex::new(r"`([^`]+)`").unwrap();
+ }
+ let s = bold_regex.replace_all(raw, &*self.md_bold_repl);
+ let s = code_regex.replace_all(&s, &*self.md_code_repl);
+ s.to_string()
+ }
+ // add a line from the line interpreted as "markdown"
pub fn md(&mut self, line: &str) {
- self.lines.push(md(line));
+ self.lines.push(self.md_to_tty(line));
}
pub fn push(&mut self, line: String) {
self.lines.push(line);
@@ -93,11 +102,20 @@ impl<'a, R> TextCol<'a, R> {}
/// A small utility to format some data in a tabular way on screen
pub struct TextTable<'a, R> {
cols: Vec<TextCol<'a, R>>,
+ bar: String, // according to skin
}
impl<'a, R> TextTable<'a, R> {
- pub fn new() -> TextTable<'a, R> {
- TextTable { cols: Vec::new() }
+ pub fn new(skin: &Skin) -> TextTable<'a, R> {
+ let bar = format!(
+ " {}│{} ",
+ skin.table_border.fg,
+ skin.reset.fg,
+ );
+ TextTable {
+ cols: Vec::new(),
+ bar,
+ }
}
pub fn add_col(&mut self, title: &str, extract: &'a Fn(&'a R) -> &str) {
let width = title.len(); // initial value, will change
@@ -119,18 +137,10 @@ impl<'a, R> TextTable<'a, R> {
// Right now, to ease width computations, md transformation is done
// only in the last column
pub fn write(&mut self, rows: &'a [R], text: &mut Text) {
- lazy_static! {
- static ref bar: String = format!(
- " {}│{} ",
- color::Fg(color::AnsiValue::grayscale(8)),
- color::Fg(color::Reset),
- )
- .to_string();
- }
self.compute_col_widths(&rows);
let mut header = String::new();
for col in &self.cols {
- header.push_str(&*bar);
+ header.push_str(&self.bar);
// we're lazy here:
// we add some bold, and add 4 for the width because we know the * won't
// show up on screen.
@@ -140,10 +150,10 @@ impl<'a, R> TextTable<'a, R> {
for row in rows {
let mut line = String::new();
for (i, col) in self.cols.iter().enumerate() {
- line.push_str(&*bar);
+ line.push_str(&self.bar);
let s = (col.extract)(row);
if i == self.cols.len() - 1 {
- line.push_str(&md(s));
+ line.push_str(&text.md_to_tty(s));
} else {
append_pad(&mut line, s, col.width);
}
diff --git a/src/screens.rs b/src/screens.rs
index 66fad51..4b106e1 100644
--- a/src/screens.rs
+++ b/src/screens.rs
@@ -1,6 +1,7 @@
use std::io::{self, stdout, Write};
use termion::raw::{IntoRawMode, RawTerminal};
use termion::screen::AlternateScreen;
+use termion::color;
use crate::skin::{Skin, SkinEntry};
pub struct Screen {
@@ -38,8 +39,13 @@ impl Screen {
self.h = h;
Ok(())
}
- pub fn apply_skin_entry(&mut self, ske: &SkinEntry) -> io::Result<()> {
- write!(self.stdout, "{}{}", ske.fg, ske.bg)
+ pub fn reset_colors(&mut self) -> io::Result<()> {
+ write!(
+ self.stdout,
+ "{}{}",
+ color::Fg(color::Reset),
+ color::Bg(color::Reset),
+ )
}
}
diff --git a/src/skin.rs b/src/skin.rs
index 9aa29e1..40d9cec 100644
--- a/src/skin.rs
+++ b/src/skin.rs
@@ -1,6 +1,6 @@
use std::io::{self, Write};
-use crate::screens::{Screen, ScreenArea};
+use crate::screens::Screen;
use std::collections::HashMap;
use termion::color::{self, *};
@@ -11,9 +11,6 @@ pub struct SkinEntry {
}
impl SkinEntry {
- pub fn apply(&self, screen: &mut Screen) -> io::Result<()> {
- write!(screen.stdout, "{}{}", self.fg, self.bg)
- }
pub fn fgbg(&self) -> String {
format!("{}{}", self.fg, self.bg)
}
@@ -48,12 +45,6 @@ macro_rules! Skin {
}
}
-// TODO none instead of Reset
-// -> and alert when a none is used
-// TODO allow default in skin conf
-// TODO fonction pour générer un nouveau fichier de skin ?
-// TODO utiliser le même format exactement ici et dans toml ?
-// TODO autoriser plusieurs skins avec des noms ?
Skin! {
status_normal: White, AnsiValue::grayscale(2)
status_error: Red, AnsiValue::grayscale(2)
@@ -63,6 +54,18 @@ Skin! {
size_text: AnsiValue::grayscale(15), Reset
size_bar_full: Reset, Magenta
size_bar_void: Reset, AnsiValue::grayscale(2)
+ file: White, Reset
+ directory: LightBlue, Reset
+ char_match: Green, Reset
+ link: LightMagenta, Reset
+ file_error: Red, Reset
+ unlisted: AnsiValue::grayscale(13), Reset
+ input: White, Reset
+ flag_label: AnsiValue::grayscale(14), AnsiValue::grayscale(1)
+ flag_value: AnsiValue::grayscale(16), AnsiValue::grayscale(1)
+ code: Reset, AnsiValue::grayscale(2)
+ table_border: AnsiValue::grayscale(8), Reset
+ spinner: AnsiValue::grayscale(10), AnsiValue::grayscale(2)
}
diff --git a/src/skin_conf.rs b/src/skin_conf.rs
index ba66795..94871e1 100644
--- a/src/skin_conf.rs
+++ b/src/skin_conf.rs
@@ -5,9 +5,8 @@
/// The code is ugly. Really. I know.
/// I found no clean way to deal with termion colors.
use std::result::Result;
-use std::ascii::AsciiExt;
use regex::Regex;
-use termion::color::{self, *};
+use termion::color::*;
use crate::errors::ConfError;
fn parse_gray(raw: &str) -> Result<Option<u8>, ConfError> {
@@ -92,19 +91,3 @@ make_parseurs! {
Yellow,
}
-//pub fn parse_entry(raw: &str) -> Result<String, ConfError> {
-// let mut tokens = raw.split_whitespace();
-// let fg = match tokens.next() {
-// Some(c) => parse_fg(c)?,
-// None => {
-// return Err(ConfError::InvalidSkinEntry{reason:"Missing foreground in skin".to_string()});
-// }
-// };
-// let bg = match tokens.next() {
-// Some(c) => parse_bg(c)?,
-// None => {
-// return Err(ConfError::InvalidSkinEntry{reason:"Missing background in skin".to_string()});
-// }
-// };
-// Ok(format!("{}{}", fg, bg))
-//}
diff --git a/src/spinner.rs b/src/spinner.rs
index d523b8a..8f0c6ac 100644
--- a/src/spinner.rs
+++ b/src/spinner.rs
@@ -2,7 +2,6 @@
//! Executed during the do_pending_tasks of the states
use std::io::{self, Write};
-use termion::color;
use crate::screens::Screen;
@@ -15,13 +14,11 @@ impl Spinner for Screen {
let y = self.h - 1;
write!(
self.stdout,
- "{}{}{}{}{}{}",
+ "{}{}{}{}",
termion::cursor::Goto(1, y),
- color::Bg(color::AnsiValue::grayscale(2)),
- color::Fg(color::AnsiValue::grayscale(10)),
+ self.skin.spinner.fg,
+ self.skin.spinner.bg,
if spinning { "⌛" } else { " " },
- color::Bg(color::Reset),
- color::Fg(color::Reset),
)?;
self.stdout.flush()?;
Ok(())
diff --git a/src/status.rs b/src/status.rs
index 34d570d..4c27ad3 100644
--- a/src/status.rs
+++ b/src/status.rs
@@ -23,8 +23,6 @@ impl Status for Screen {
termion::cursor::Goto(2, y),
self.skin.status_error.fg,
self.skin.status_error.bg,
- //color::Bg(color::AnsiValue::grayscale(2)),
- //color::Fg(color::Red),
termion::clear::CurrentLine,
text,
self.skin.reset.bg,
@@ -42,11 +40,9 @@ impl Status for Screen {
termion::cursor::Goto(2, y),
self.skin.status_normal.fg,
self.skin.status_normal.bg,
- //color::Bg(color::AnsiValue::grayscale(2)),
termion::clear::CurrentLine,
text,
self.skin.reset.bg,
- //color::Bg(color::Reset),
)?;
self.stdout.flush()?;
Ok(())
diff --git a/src/tree_views.rs b/src/tree_views.rs
index 5fa4ed3..e0e40a8 100644
--- a/src/tree_views.rs
+++ b/src/tree_views.rs
@@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::io::{self, Write};
use std::sync::Mutex;
-use termion::{color, style};
+use termion::style;
use users::{Groups, Users, UsersCache};
use crate::file_sizes::Size;
@@ -124,7 +124,6 @@ impl TreeView for Screen {
write!(self.stdout, "{}▐", termion::cursor::Goto(self.w, y),)?;
}
}
- //write!(self.stdout, "{}", color::Fg(color::Reset),)?;
}
self.stdout.flush()?;
Ok(())
@@ -167,15 +166,15 @@ impl TreeView for Screen {
write!(
self.stdout,
"{}{} ",
- color::Bg(color::Reset),
- color::Fg(color::Reset),
+ self.skin.reset.fg,
+ self.skin.reset.bg,
)
} else {
write!(
self.stdout,
"{}────────{} ",
- color::Fg(color::AnsiValue::grayscale(5)),
- color::Fg(color::Reset),
+ self.skin.tree.fg,
+ self.skin.reset.fg,
)
}
}
@@ -186,15 +185,6 @@ impl TreeView for Screen {
idx: usize,
pattern: &Pattern,
) -> io::Result<()> {
- lazy_static! {
- static ref fg_reset: String = format!("{}", color::Fg(color::White)).to_string();
- static ref fg_dir: String = format!("{}", color::Fg(color::LightBlue)).to_string();
- static ref fg_err: String = format!("{}", color::Fg(color::Red)).to_string();
- static ref fg_link: String = format!("{}", color::Fg(color::LightMagenta)).to_string();
- static ref fg_match: String = format!("{}", color::Fg(color::Green)).to_string();
- static ref fg_reset_dir: String = format!("{}{}", &*fg_reset, &*fg_dir).to_string();
- static ref fg_reset_link: String = format!("{}{}", &*fg_reset, &*fg_link).to_string();
- }
// TODO draw in red lines with has_error
match &line.line_type {
LineType::Dir => {
@@ -203,7 +193,7 @@ impl TreeView for Screen {
self.stdout,
"{}{}{}",
style::Bold,
- &*fg_dir,
+ &self.skin.directory.fg,
&line.path.to_string_lossy(),
)?;
} else {
@@ -211,8 +201,8 @@ impl TreeView for Screen {
self.stdout,
"{}{}{}",
style::Bold,
- &*fg_dir,
- decorated_name(&line.name, pattern, &*fg_match, &*fg_reset_dir),
+ &self.skin.directory.fg,
+ decorated_name(&line.name, pattern, &self.skin.char_match.fg, &self.skin.directory.fg),
)?;
if line.unlisted > 0 {
write!(self.stdout, " …",)?;
@@ -223,18 +213,18 @@ impl TreeView for Screen {
write!(
self.stdout,
"{}{}",
- &*fg_reset,
- decorated_name(&line.name, pattern, &*fg_match, &*fg_reset),
+ &self.skin.file.fg,
+ decorated_name(&line.name, pattern, &self.skin.char_match.fg, &self.skin.file.fg),
)?;
}
LineType::SymLinkToFile(target) => {
write!(
self.stdout,
"{}{} {}->{} {}",
- &*fg_link,
- decorated_name(&line.name, pattern, &*fg_match, &*fg_reset_link),
- if line.has_error { &*fg_err } else { &*fg_link },
- &*fg_reset,
+ &self.skin.link.fg,
+ decorated_name(&line.name, pattern, &self.skin.char_match.fg, &self.skin.link.fg),
+ if line.has_error { &self.skin.file_error.fg } else { &self.skin.link.fg },
+ &self.skin.file.fg,
&target,
)?;
}
@@ -242,11 +232,11 @@ impl TreeView for Screen {
write!(
self.stdout,
"{}{} {}->{}{} {}",
- &*fg_link,
- decorated_name(&line.name, pattern, &*fg_match, &*fg_reset_link),
- if line.has_error { &*fg_err } else { &*fg_link },
+ &self.skin.link.fg,
+ decorated_name(&line.name, pattern, &self.skin.char_match.fg, &self.skin.link.fg),
+ if line.has_error { &self.skin.file_error.fg } else { &self.skin.link.fg },
style::Bold,
- &*fg_reset_dir,
+ &self.skin.directory.fg,
&target,
)?;
}
@@ -254,8 +244,8 @@ impl TreeView for Screen {
write!(
self.stdout,
//"{}{}… {} unlisted", still not sure whether I want this '…'
- "{}{} {} unlisted",
- color::Fg(color::AnsiValue::grayscale(13)),
+ "{}{}{} unlisted",
+ self.skin.unlisted.fg,
style::Italic,
&line.unlisted,
)?;