Skip to content

Commit

Permalink
Remove style item if not specified i.e. if None
Browse files Browse the repository at this point in the history
  • Loading branch information
fritzrehde committed Nov 8, 2023
1 parent 8f7eefc commit c97399d
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 37 deletions.
5 changes: 3 additions & 2 deletions examples/ls.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ watched-command = "ls"
interval = 3.0

# all lines
bold = false
# bold = false

# cursor line
cursor-fg = "black"
cursor-bg = "blue"
cursor-bold = true
cursor-bold = false

# selected lines
selected-bg = "red"

# header lines
header-lines = 1
header-fg = "blue"
header-bold = false

[keybindings]
"esc" = [ "unselect-all", "help-hide" ]
Expand Down
7 changes: 3 additions & 4 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use indoc::indoc;
use serde::Deserialize;
use std::{fs::read_to_string, path::PathBuf, time::Duration};

// TODO: don't have public members

pub struct Config {
pub log_file: Option<PathBuf>,
pub watched_command: String,
Expand Down Expand Up @@ -188,12 +190,9 @@ impl From<ClapConfig> for TomlConfig {
impl Default for TomlConfig {
fn default() -> Self {
let toml = indoc! {r#"
"interval" = 5.0
"bold" = false
"cursor-fg" = "black"
"interval" = 3.0
"cursor-bg" = "blue"
"cursor-bold" = true
"header-fg" = "blue"
"selected-bg" = "magenta"
[keybindings]
Expand Down
40 changes: 24 additions & 16 deletions src/config/style.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use anyhow::{bail, Result};
use ratatui::style::{Color, Modifier, Style};

#[derive(Clone)]
// TODO: don't have public members

#[derive(Debug, Clone)]
pub struct Styles {
pub line: Style,
pub cursor: Style,
Expand All @@ -11,6 +13,7 @@ pub struct Styles {

impl Styles {
// TODO: clippy says too many arguments (and I agree)

pub fn parse(
fg: Option<String>,
bg: Option<String>,
Expand All @@ -23,12 +26,6 @@ impl Styles {
header_bold: Option<bool>,
selected_bg: Option<String>,
) -> Result<Self> {
let new_style = |fg, bg, bold| -> Result<Style> {
Ok(Style::reset()
.fg(parse_color(fg)?)
.bg(parse_color(bg)?)
.add_modifier(parse_bold(bold)))
};
Ok(Self {
line: new_style(fg, bg, bold)?,
cursor: new_style(cursor_fg, cursor_bg, cursor_bold)?,
Expand All @@ -38,18 +35,29 @@ impl Styles {
}
}

fn parse_bold(bold: Option<bool>) -> Modifier {
if let Some(true) = bold {
Modifier::BOLD
} else {
Modifier::empty()
fn new_style(fg: Option<String>, bg: Option<String>, bold: Option<bool>) -> Result<Style> {
let mut style = Style::default();

if let Some(fg) = parse_color(fg)? {
style = style.fg(fg);
}
if let Some(bg) = parse_color(bg)? {
style = style.bg(bg);
}
match bold {
Some(true) => style = style.add_modifier(Modifier::BOLD),
Some(false) => style = style.remove_modifier(Modifier::BOLD),
None => {}
}

Ok(style)
}

// TODO: create custom Color type and impl from_str and add parser directly into toml and clap structs
fn parse_color(src: Option<String>) -> Result<Color> {

fn parse_color(src: Option<String>) -> Result<Option<Color>> {
Ok(match src {
Some(color) => match color.to_lowercase().as_str() {
Some(color) => Some(match color.to_lowercase().as_str() {
"white" => Color::White,
"black" => Color::Black,
"red" => Color::Red,
Expand All @@ -67,7 +75,7 @@ fn parse_color(src: Option<String>) -> Result<Color> {
"light_magenta" => Color::LightMagenta,
"light_cyan" => Color::LightCyan,
invalid => bail!("Invalid color provided: \"{}\"", invalid),
},
_ => Color::Reset,
}),
None => None,
})
}
53 changes: 38 additions & 15 deletions src/ui/state/lines/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,72 @@ pub struct Line {
/// This string will be made available to the user's command's subshell
/// through an environment variable.
unformatted: String,
/// Widget containing the text string that will be displayed in the TUI.
/// The text string is formatted according to the user's field separator.
/// The text string has ANSI color codes converted to ratatui colors.
/// The text string that contains the string content, is formatted
/// according to the user's field separator, and is styled according to any
/// ANSI codes. Does not contain the user style. Immutable for the lifetime
/// of the line.
displayed_text: Text<'static>,
/// A cell containing the `displayed_text`, but with any user styles (style
/// settings that should apply to the whole line), provided at creation
/// and/or later, applied. If there is overlap in a setting between the
/// `displayed_text`s style and the user style, the user style is
/// prioritized.
displayed: Cell<'static>,
}

impl Line {
/// Create a new Line with a given `style`.
impl<'a> Line {
/// Create a new Line. Apply the `user_style` to the whole line.
/// The formatted string was formatted according to the user's field
/// separator.
/// The unformatted and formatted strings can both contain ANSI escape
/// codes, which will be converted to ratatui Style's.
/// The unformatted and formatted strings may both contain ANSI escape
/// codes, which will be converted incorporated into `displayed_text`.
pub fn new(
unformatted_ansi: String,
formatted_ansi: Option<String>,
style: Style,
user_style: Style,
) -> Result<Self> {
let formatted_or_unformatted = formatted_ansi.as_ref().unwrap_or(&unformatted_ansi);
let displayed = Self::format_line_content(formatted_or_unformatted).into_text()?;

let displayed_text = Self::format_line_content(formatted_or_unformatted).into_text()?;
let displayed = Self::build_displayed_style(&displayed_text, user_style);

let unformatted = unformatted_ansi.into_text()?.to_unformatted_string();

Ok(Self {
unformatted,
displayed: Cell::from(displayed).style(style),
displayed,
displayed_text,
})
}

/// Add one space before the line's content to create separation from the
/// left frame edge.
/// frame to the left.
fn format_line_content(line_content: &str) -> String {
format!(" {}", line_content)
}

/// Build the final style of the displayed cell, which consists of the
/// displayed text's inherent style and the user style. If any style
/// settings overlap, the user style is taken.
fn build_displayed_style(displayed_text: &Text<'a>, user_style: Style) -> Cell<'a> {
// We don't want to add the user style to the displayed text, so clone.
let mut displayed_text = displayed_text.clone();
// Merge the style from the displayed text and the user style, and
// prioritise the user style.
displayed_text.patch_style(user_style);
// Also apply user style to whole cell, so areas outside the text but
// still inside the cell are also styled.
Cell::from(displayed_text).style(user_style)
}

/// Draw the line.
pub fn draw(&self) -> Cell {
self.displayed.clone()
}

/// Update the style of the line.
/// Update the style of the whole line.
pub fn update_style(&mut self, new_style: Style) {
// TODO: ask ratatui to add a method to directly replace the style without having to clone
let displayed = self.displayed.clone();
self.displayed = displayed.style(new_style);
self.displayed = Self::build_displayed_style(&self.displayed_text, new_style);
}

/// Get the line as a &str.
Expand Down

0 comments on commit c97399d

Please sign in to comment.