feat(ui): new design

This commit is contained in:
Aram Drevekenin 2021-02-09 17:36:07 +01:00 committed by GitHub
commit 380f056315
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,52 +1,294 @@
use colored::*;
use mosaic_tile::*;
use std::fmt::{Display, Formatter, Error};
// for more of these, copy paste from: https://en.wikipedia.org/wiki/Box-drawing_character
static ARROW_SEPARATOR: &str = "";
static MORE_MSG: &str = " ... ";
#[derive(Default)]
struct State {
lines: Vec<String>,
page: usize,
}
struct State {}
register_tile!(State);
struct LinePart {
part: String,
len: usize,
}
impl Display for LinePart {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.part)
}
}
fn prefix(help: &Help) -> LinePart {
let prefix_text = " Mosaic ";
let part = match (&help.mode, help.mode_is_persistent) {
(InputMode::Command, false) => {
let prefix = prefix_text.bold().white().on_black();
let separator = ARROW_SEPARATOR.black().on_magenta();
format!("{}{}", prefix, separator)
}
(_, true) => {
let prefix = prefix_text.bold().white().on_black();
let separator = ARROW_SEPARATOR.black().on_yellow();
format!("{}{}", prefix, separator)
}
(InputMode::Normal, _) => {
let prefix = prefix_text.bold().white().on_black();
let separator = ARROW_SEPARATOR.black().on_green();
format!("{}{}", prefix, separator)
}
_ => {
let prefix = prefix_text.bold().white().on_black();
let separator = ARROW_SEPARATOR.black().on_magenta();
format!("{}{}", prefix, separator)
}
};
let len = prefix_text.chars().count() + ARROW_SEPARATOR.chars().count();
LinePart {
part,
len
}
}
fn key_path(help: &Help) -> LinePart {
let superkey_text = "<Ctrl-g> ";
let locked_text = "LOCKED ";
let (part, len) = match (&help.mode, help.mode_is_persistent) {
(InputMode::Command, false) => {
let key_path = superkey_text.bold().on_magenta();
let first_separator = ARROW_SEPARATOR.magenta().on_black();
let len = superkey_text.chars().count() + ARROW_SEPARATOR.chars().count() + ARROW_SEPARATOR.chars().count();
(format!("{}{}", key_path, first_separator), len)
}
(InputMode::Command, true) => {
let locked = locked_text.bold().white().on_yellow();
let locked_separator = ARROW_SEPARATOR.yellow().on_magenta();
let key_path = superkey_text.bold().on_magenta();
let superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let len = superkey_text.chars().count() + ARROW_SEPARATOR.chars().count() + locked_text.chars().count();
(format!("{}{}{}{}", locked, locked_separator, key_path, superkey_separator), len)
}
(InputMode::Resize, false) => {
let mode_shortcut_text = "r ";
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}", superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Resize, true) => {
let mode_shortcut_text = "r ";
let locked = locked_text.white().bold().on_yellow();
let locked_separator = ARROW_SEPARATOR.yellow().on_magenta();
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = locked_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}{}{}", locked, locked_separator, superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Pane, false) => {
let mode_shortcut_text = "p ";
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}", superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Pane, true) => {
let mode_shortcut_text = "p ";
let locked = locked_text.white().bold().on_yellow();
let locked_separator = ARROW_SEPARATOR.yellow().on_magenta();
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = locked_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}{}{}", locked, locked_separator, superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Tab, false) => {
let mode_shortcut_text = "t ";
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}", superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Tab, true) => {
let mode_shortcut_text = "t ";
let locked = locked_text.white().bold().on_yellow();
let locked_separator = ARROW_SEPARATOR.yellow().on_magenta();
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = locked_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}{}{}", locked, locked_separator, superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Scroll, false) => {
let mode_shortcut_text = "s ";
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}", superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Scroll, true) => {
let mode_shortcut_text = "s ";
let locked = locked_text.white().bold().on_yellow();
let locked_separator = ARROW_SEPARATOR.yellow().on_magenta();
let superkey = superkey_text.bold().on_magenta();
let first_superkey_separator = ARROW_SEPARATOR.magenta().on_black();
let second_superkey_separator = ARROW_SEPARATOR.black().on_magenta();
let mode_shortcut = mode_shortcut_text.white().bold().on_magenta();
let mode_shortcut_separator = ARROW_SEPARATOR.magenta().on_black();
let len = locked_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
superkey_text.chars().count() +
ARROW_SEPARATOR.chars().count() +
ARROW_SEPARATOR.chars().count() +
mode_shortcut_text.chars().count() +
ARROW_SEPARATOR.chars().count();
(format!("{}{}{}{}{}{}{}", locked, locked_separator, superkey, first_superkey_separator, second_superkey_separator, mode_shortcut, mode_shortcut_separator), len)
}
(InputMode::Normal, _) | _ => {
let key_path = superkey_text.on_green();
let separator = ARROW_SEPARATOR.green().on_black();
(format!("{}{}", key_path, separator), superkey_text.chars().count() + ARROW_SEPARATOR.chars().count())
}
};
LinePart {
part,
len
}
}
fn keybinds(help: &Help, max_width: usize) -> LinePart {
let mut keybinds = String::new();
let mut len = 0;
let full_keybinds_len = help.keybinds.iter().enumerate().fold(0, |acc, (i, (shortcut, description))| {
let shortcut_length = shortcut.chars().count() + 3; // 2 for <>'s around shortcut, 1 for the space
let description_length = description.chars().count() + 2;
let (_separator, separator_len) = if i > 0 { (" / ", 3) } else { ("", 0) };
acc + shortcut_length + description_length + separator_len
});
if full_keybinds_len < max_width {
for (i, (shortcut, description)) in help.keybinds.iter().enumerate() {
let separator = if i > 0 { " / " } else { "" };
let shortcut_len = shortcut.chars().count();
let shortcut = match help.mode {
InputMode::Normal => shortcut.cyan(),
_ => shortcut.white().bold(),
};
keybinds = format!("{}{}<{}> {}", keybinds, separator, shortcut, description);
len += shortcut_len + separator.chars().count();
}
} else {
for (i, (shortcut, description)) in help.keybinds.iter().enumerate() {
let description_first_word = description.split(' ').next().unwrap_or("");
let current_length = keybinds.chars().count();
let shortcut_length = shortcut.chars().count() + 3; // 2 for <>'s around shortcut, 1 for the space
let description_first_word_length = description_first_word.chars().count();
let (separator, separator_len) = if i > 0 { (" / ", 3) } else { ("", 0) };
let shortcut = match help.mode {
InputMode::Normal => shortcut.cyan(),
_ => shortcut.white().bold(),
};
if current_length + shortcut_length + description_first_word_length + separator_len + MORE_MSG.chars().count() < max_width {
keybinds = format!("{}{}<{}> {}", keybinds, separator, shortcut, description_first_word);
len += shortcut_length + description_first_word_length + separator_len;
} else if current_length + shortcut_length + separator_len + MORE_MSG.chars().count() < max_width {
keybinds = format!("{}{}<{}>", keybinds, separator, shortcut);
len += shortcut_length + separator_len;
} else {
keybinds = format!("{}{}", keybinds, MORE_MSG);
len += MORE_MSG.chars().count();
break;
}
}
}
LinePart {
part: keybinds,
len,
}
}
impl MosaicTile for State {
fn init(&mut self) {
set_selectable(false);
set_invisible_borders(true);
set_max_height(1);
}
fn draw(&mut self, _rows: usize, cols: usize) {
let more_msg = ", <?> More";
self.lines = vec![String::new()];
for item in get_help() {
let width = self.lines.last().unwrap().len();
if width + item.len() + 2 > cols - more_msg.len() {
self.lines.last_mut().unwrap().push_str(more_msg);
self.lines.push(item);
} else {
let line = self.lines.last_mut().unwrap();
if !line.is_empty() {
line.push_str(", ");
}
line.push_str(&item);
}
}
self.page %= self.lines.len();
let line = format!(
"{}{}",
self.lines[self.page],
if self.page > 0 && self.lines.len() == self.page + 1 {
", <?> Back"
} else {
""
}
);
println!("{}", line.italic());
}
fn handle_global_key(&mut self, key: Key) {
if let Key::Char('?') = key {
self.page += 1;
}
let help = get_help();
let line_prefix = prefix(&help);
let key_path = key_path(&help);
let line_len_before_keybinds = line_prefix.len + key_path.len;
let status_bar = if line_len_before_keybinds + MORE_MSG.chars().count() < cols {
let keybinds = keybinds(&help, cols - line_len_before_keybinds);
let keybinds = keybinds.part.cyan().on_black();
format!("{}{}{}", line_prefix, key_path, keybinds)
} else if line_len_before_keybinds < cols {
format!("{}{}", line_prefix, key_path)
} else if line_prefix.len < cols {
format!("{}", line_prefix)
} else {
// sorry, too small :(
format!("")
};
// 40m is black background, 0K is so that it fills the rest of the line,
// I could not find a way to do this with colored and did not want to have to
// manually fill the line with spaces to achieve the same
println!("{}\u{1b}[40m\u{1b}[0K", status_bar);
}
}