feat: Initial support for custom user-defined themes
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||
@@ -15,6 +16,7 @@ use crate::app::App;
|
||||
use crate::models::{HorizontallyScrollableText, Route, TabState};
|
||||
use crate::ui::radarr_ui::RadarrUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::theme::Theme;
|
||||
use crate::ui::utils::{
|
||||
background_block, borderless_block, centered_rect, logo_block, title_block, title_block_centered,
|
||||
};
|
||||
@@ -24,10 +26,14 @@ use crate::ui::widgets::popup::Size;
|
||||
mod radarr_ui;
|
||||
mod sonarr_ui;
|
||||
mod styles;
|
||||
pub mod theme;
|
||||
mod utils;
|
||||
mod widgets;
|
||||
|
||||
static HIGHLIGHT_SYMBOL: &str = "=> ";
|
||||
thread_local! {
|
||||
pub static THEME: Cell<Theme> = Cell::new(Theme::default());
|
||||
}
|
||||
|
||||
pub trait DrawUi {
|
||||
fn accepts(route: Route) -> bool;
|
||||
|
||||
+17
-19
@@ -1,8 +1,6 @@
|
||||
use ratatui::prelude::Color;
|
||||
use crate::ui::THEME;
|
||||
use ratatui::style::{Styled, Stylize};
|
||||
|
||||
pub const COLOR_ORANGE: Color = Color::Rgb(255, 170, 66);
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "styles_tests.rs"]
|
||||
mod styles_tests;
|
||||
@@ -42,31 +40,31 @@ where
|
||||
}
|
||||
|
||||
fn awaiting_import(self) -> T {
|
||||
self.fg(COLOR_ORANGE)
|
||||
THEME.with(|theme| self.fg(theme.get().awaiting_import.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn indeterminate(self) -> T {
|
||||
self.fg(COLOR_ORANGE)
|
||||
THEME.with(|theme| self.fg(theme.get().indeterminate.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn default(self) -> T {
|
||||
self.white()
|
||||
THEME.with(|theme| self.fg(theme.get().default.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn downloaded(self) -> T {
|
||||
self.green()
|
||||
THEME.with(|theme| self.fg(theme.get().downloaded.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn downloading(self) -> T {
|
||||
self.magenta()
|
||||
THEME.with(|theme| self.fg(theme.get().downloading.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn failure(self) -> T {
|
||||
self.red()
|
||||
THEME.with(|theme| self.fg(theme.get().failure.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn help(self) -> T {
|
||||
self.light_blue()
|
||||
THEME.with(|theme| self.fg(theme.get().help.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn highlight(self) -> T {
|
||||
@@ -74,38 +72,38 @@ where
|
||||
}
|
||||
|
||||
fn missing(self) -> T {
|
||||
self.red()
|
||||
THEME.with(|theme| self.fg(theme.get().missing.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn primary(self) -> T {
|
||||
self.cyan()
|
||||
THEME.with(|theme| self.fg(theme.get().primary.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn secondary(self) -> T {
|
||||
self.yellow()
|
||||
THEME.with(|theme| self.fg(theme.get().secondary.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn success(self) -> T {
|
||||
self.green()
|
||||
THEME.with(|theme| self.fg(theme.get().success.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn system_function(self) -> T {
|
||||
self.yellow()
|
||||
THEME.with(|theme| self.fg(theme.get().system_function.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn unmonitored(self) -> T {
|
||||
self.gray()
|
||||
THEME.with(|theme| self.fg(theme.get().unmonitored.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn unmonitored_missing(self) -> T {
|
||||
self.yellow()
|
||||
THEME.with(|theme| self.fg(theme.get().unmonitored_missing.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn unreleased(self) -> T {
|
||||
self.light_cyan()
|
||||
THEME.with(|theme| self.fg(theme.get().unreleased.unwrap().color.unwrap()))
|
||||
}
|
||||
|
||||
fn warning(self) -> T {
|
||||
self.magenta()
|
||||
THEME.with(|theme| self.fg(theme.get().warning.unwrap().color.unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ui::styles::{ManagarrStyle, COLOR_ORANGE};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::prelude::Modifier;
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::style::{Color, Style, Stylize};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
@@ -14,13 +14,16 @@ mod test {
|
||||
fn test_style_awaiting_import() {
|
||||
assert_eq!(
|
||||
Style::new().awaiting_import(),
|
||||
Style::new().fg(COLOR_ORANGE)
|
||||
Style::new().fg(Color::Rgb(255, 170, 66))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_indeterminate() {
|
||||
assert_eq!(Style::new().indeterminate(), Style::new().fg(COLOR_ORANGE));
|
||||
assert_eq!(
|
||||
Style::new().indeterminate(),
|
||||
Style::new().fg(Color::Rgb(255, 170, 66))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
use anyhow::Result;
|
||||
use derivative::Derivative;
|
||||
use ratatui::style::Color;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "theme_tests.rs"]
|
||||
mod theme_tests;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Derivative)]
|
||||
#[derivative(Default)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct Background {
|
||||
#[serde(
|
||||
deserialize_with = "deserialize_color_str",
|
||||
serialize_with = "serialize_color_str",
|
||||
default = "default_background_color"
|
||||
)]
|
||||
#[derivative(Default(value = "Some(Color::Rgb(35, 50, 55))"))]
|
||||
pub color: Option<Color>,
|
||||
#[derivative(Default(value = "Some(true)"))]
|
||||
#[serde(default = "default_background_enabled")]
|
||||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, Copy)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct Style {
|
||||
#[serde(
|
||||
deserialize_with = "deserialize_color_str",
|
||||
serialize_with = "serialize_color_str"
|
||||
)]
|
||||
pub color: Option<Color>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Derivative)]
|
||||
#[derivative(Default)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct Theme {
|
||||
#[serde(default = "default_background")]
|
||||
#[derivative(Default(
|
||||
value = "Some(Background { color: Some(Color::Rgb(35, 50, 55)), enabled: Some(true) })"
|
||||
))]
|
||||
pub background: Option<Background>,
|
||||
#[serde(default = "default_awaiting_import_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Rgb(255, 170, 66)) })"))]
|
||||
pub awaiting_import: Option<Style>,
|
||||
#[serde(default = "default_indeterminate_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Rgb(255, 170, 66)) })"))]
|
||||
pub indeterminate: Option<Style>,
|
||||
#[serde(default = "default_default_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::White) })"))]
|
||||
pub default: Option<Style>,
|
||||
#[serde(default = "default_downloaded_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Green) })"))]
|
||||
pub downloaded: Option<Style>,
|
||||
#[serde(default = "default_downloading_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Magenta) })"))]
|
||||
pub downloading: Option<Style>,
|
||||
#[serde(default = "default_failure_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Red) })"))]
|
||||
pub failure: Option<Style>,
|
||||
#[serde(default = "default_help_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::LightBlue) })"))]
|
||||
pub help: Option<Style>,
|
||||
#[serde(default = "default_missing_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Red) })"))]
|
||||
pub missing: Option<Style>,
|
||||
#[serde(default = "default_primary_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Cyan) })"))]
|
||||
pub primary: Option<Style>,
|
||||
#[serde(default = "default_secondary_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Yellow) })"))]
|
||||
pub secondary: Option<Style>,
|
||||
#[serde(default = "default_success_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Green) })"))]
|
||||
pub success: Option<Style>,
|
||||
#[serde(default = "default_system_function_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Yellow) })"))]
|
||||
pub system_function: Option<Style>,
|
||||
#[serde(default = "default_unmonitored_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Gray) })"))]
|
||||
pub unmonitored: Option<Style>,
|
||||
#[serde(default = "default_unmonitored_missing_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Yellow) })"))]
|
||||
pub unmonitored_missing: Option<Style>,
|
||||
#[serde(default = "default_unreleased_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::LightCyan) })"))]
|
||||
pub unreleased: Option<Style>,
|
||||
#[serde(default = "default_warning_style")]
|
||||
#[derivative(Default(value = "Some(Style { color: Some(Color::Magenta) })"))]
|
||||
pub warning: Option<Style>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct ThemeDefinition {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
fn default_background_color() -> Option<Color> {
|
||||
Some(Color::Rgb(35, 50, 55))
|
||||
}
|
||||
|
||||
fn default_background_enabled() -> Option<bool> {
|
||||
Some(true)
|
||||
}
|
||||
|
||||
fn default_background() -> Option<Background> {
|
||||
Some(Background {
|
||||
color: default_background_color(),
|
||||
enabled: Some(true),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_awaiting_import_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Rgb(255, 170, 66)),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_indeterminate_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Rgb(255, 170, 66)),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_default_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::White),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_downloaded_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Green),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_downloading_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Magenta),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_failure_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Red),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_help_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::LightBlue),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_missing_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Red),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_primary_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Cyan),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_secondary_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Yellow),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_success_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Green),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_system_function_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Yellow),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_unmonitored_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Gray),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_unmonitored_missing_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Yellow),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_unreleased_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::LightCyan),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_warning_style() -> Option<Style> {
|
||||
Some(Style {
|
||||
color: Some(Color::Magenta),
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_color_str<'de, D>(deserializer: D) -> Result<Option<Color>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s: Option<String> = Option::deserialize(deserializer)?;
|
||||
match s {
|
||||
Some(s) => Color::from_str(&s)
|
||||
.map_err(serde::de::Error::custom)
|
||||
.map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_color_str<S>(color: &Option<Color>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&color.unwrap().to_string())
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
mod tests {
|
||||
use crate::ui::theme::{Background, Style, Theme, ThemeDefinition};
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::style::Color;
|
||||
|
||||
#[test]
|
||||
fn test_background_default() {
|
||||
let expected_background = Background {
|
||||
enabled: Some(true),
|
||||
color: Some(Color::Rgb(35, 50, 55)),
|
||||
};
|
||||
|
||||
assert_eq!(Background::default(), expected_background);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_theme_default() {
|
||||
let expected_theme = Theme {
|
||||
background: Some(Background {
|
||||
enabled: Some(true),
|
||||
color: Some(Color::Rgb(35, 50, 55)),
|
||||
}),
|
||||
awaiting_import: Some(Style {
|
||||
color: Some(Color::Rgb(255, 170, 66)),
|
||||
}),
|
||||
indeterminate: Some(Style {
|
||||
color: Some(Color::Rgb(255, 170, 66)),
|
||||
}),
|
||||
default: Some(Style {
|
||||
color: Some(Color::White),
|
||||
}),
|
||||
downloaded: Some(Style {
|
||||
color: Some(Color::Green),
|
||||
}),
|
||||
downloading: Some(Style {
|
||||
color: Some(Color::Magenta),
|
||||
}),
|
||||
failure: Some(Style {
|
||||
color: Some(Color::Red),
|
||||
}),
|
||||
help: Some(Style {
|
||||
color: Some(Color::LightBlue),
|
||||
}),
|
||||
missing: Some(Style {
|
||||
color: Some(Color::Red),
|
||||
}),
|
||||
primary: Some(Style {
|
||||
color: Some(Color::Cyan),
|
||||
}),
|
||||
secondary: Some(Style {
|
||||
color: Some(Color::Yellow),
|
||||
}),
|
||||
success: Some(Style {
|
||||
color: Some(Color::Green),
|
||||
}),
|
||||
system_function: Some(Style {
|
||||
color: Some(Color::Yellow),
|
||||
}),
|
||||
unmonitored: Some(Style {
|
||||
color: Some(Color::Gray),
|
||||
}),
|
||||
unmonitored_missing: Some(Style {
|
||||
color: Some(Color::Yellow),
|
||||
}),
|
||||
unreleased: Some(Style {
|
||||
color: Some(Color::LightCyan),
|
||||
}),
|
||||
warning: Some(Style {
|
||||
color: Some(Color::Magenta),
|
||||
}),
|
||||
};
|
||||
|
||||
assert_eq!(Theme::default(), expected_theme);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_theme_definition() {
|
||||
let expected_theme_definition = ThemeDefinition {
|
||||
name: String::new(),
|
||||
theme: Theme::default(),
|
||||
};
|
||||
|
||||
assert_eq!(ThemeDefinition::default(), expected_theme_definition);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialization_defaults_to_using_default_theme_values_when_missing() {
|
||||
let theme_yaml = r#""#;
|
||||
let theme: Theme = serde_yaml::from_str(theme_yaml).unwrap();
|
||||
|
||||
assert_eq!(theme, Theme::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialization_does_not_overwrite_non_empty_fields_with_default_values() {
|
||||
let theme_yaml = r###"
|
||||
background:
|
||||
enabled: false
|
||||
color: "#000000"
|
||||
awaiting_import:
|
||||
color: "#000000"
|
||||
indeterminate:
|
||||
color: "#000000"
|
||||
default:
|
||||
color: "#000000"
|
||||
downloaded:
|
||||
color: "#000000"
|
||||
downloading:
|
||||
color: "#000000"
|
||||
failure:
|
||||
color: "#000000"
|
||||
help:
|
||||
color: "#000000"
|
||||
missing:
|
||||
color: "#000000"
|
||||
primary:
|
||||
color: "#000000"
|
||||
secondary:
|
||||
color: "#000000"
|
||||
success:
|
||||
color: "#000000"
|
||||
system_function:
|
||||
color: "#000000"
|
||||
unmonitored:
|
||||
color: "#000000"
|
||||
unmonitored_missing:
|
||||
color: "#000000"
|
||||
unreleased:
|
||||
color: "#000000"
|
||||
warning:
|
||||
color: "#000000"
|
||||
"###;
|
||||
let theme: Theme = serde_yaml::from_str(theme_yaml).unwrap();
|
||||
let expected_theme = Theme {
|
||||
background: Some(Background {
|
||||
enabled: Some(false),
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
awaiting_import: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
indeterminate: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
default: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
downloaded: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
downloading: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
failure: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
help: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
missing: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
primary: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
secondary: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
success: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
system_function: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
unmonitored: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
unmonitored_missing: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
unreleased: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
warning: Some(Style {
|
||||
color: Some(Color::Rgb(0, 0, 0)),
|
||||
}),
|
||||
};
|
||||
|
||||
assert_eq!(theme, expected_theme);
|
||||
}
|
||||
}
|
||||
+17
-9
@@ -1,22 +1,30 @@
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::THEME;
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::style::{Color, Style, Stylize};
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::symbols;
|
||||
use ratatui::text::{Line, Span, Text};
|
||||
use ratatui::widgets::{Block, BorderType, Borders, LineGauge, ListItem, Paragraph, Wrap};
|
||||
|
||||
pub const COLOR_TEAL: Color = Color::Rgb(35, 50, 55);
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "utils_tests.rs"]
|
||||
mod utils_tests;
|
||||
|
||||
pub fn background_block<'a>() -> Block<'a> {
|
||||
Block::new().white().bg(COLOR_TEAL)
|
||||
THEME.with(|theme| {
|
||||
let background = theme.get().background.unwrap();
|
||||
|
||||
if background.enabled.unwrap() {
|
||||
Block::new().white().bg(background.color.unwrap())
|
||||
} else {
|
||||
Block::new().white()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn layout_block<'a>() -> Block<'a> {
|
||||
Block::new()
|
||||
.default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
}
|
||||
@@ -30,11 +38,11 @@ pub fn layout_block_top_border_with_title(title_span: Span<'_>) -> Block<'_> {
|
||||
}
|
||||
|
||||
pub fn layout_block_top_border<'a>() -> Block<'a> {
|
||||
Block::new().borders(Borders::TOP)
|
||||
Block::new().borders(Borders::TOP).default()
|
||||
}
|
||||
|
||||
pub fn layout_block_bottom_border<'a>() -> Block<'a> {
|
||||
Block::new().borders(Borders::BOTTOM)
|
||||
Block::new().borders(Borders::BOTTOM).default()
|
||||
}
|
||||
|
||||
pub fn layout_paragraph_borderless(string: &str) -> Paragraph<'_> {
|
||||
@@ -47,7 +55,7 @@ pub fn layout_paragraph_borderless(string: &str) -> Paragraph<'_> {
|
||||
}
|
||||
|
||||
pub fn borderless_block<'a>() -> Block<'a> {
|
||||
Block::new()
|
||||
Block::new().default()
|
||||
}
|
||||
|
||||
pub fn style_block_highlight(is_selected: bool) -> Style {
|
||||
@@ -98,7 +106,7 @@ pub fn centered_rect(percent_x: u16, percent_y: u16, area: Rect) -> Rect {
|
||||
pub fn line_gauge_with_title(title: &str, ratio: f64) -> LineGauge<'_> {
|
||||
LineGauge::new()
|
||||
.block(Block::new().title(title))
|
||||
.filled_style(Style::new().cyan())
|
||||
.filled_style(Style::new().primary())
|
||||
.line_set(symbols::line::THICK)
|
||||
.ratio(ratio)
|
||||
.label(Line::from(format!("{:.0}%", ratio * 100.0)))
|
||||
@@ -107,7 +115,7 @@ pub fn line_gauge_with_title(title: &str, ratio: f64) -> LineGauge<'_> {
|
||||
pub fn line_gauge_with_label(title: &str, ratio: f64) -> LineGauge<'_> {
|
||||
LineGauge::new()
|
||||
.block(Block::new())
|
||||
.filled_style(Style::new().cyan())
|
||||
.filled_style(Style::new().primary())
|
||||
.line_set(symbols::line::THICK)
|
||||
.ratio(ratio)
|
||||
.label(Line::from(format!("{title}: {:.0}%", ratio * 100.0)))
|
||||
|
||||
+24
-21
@@ -1,5 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, centered_rect, convert_to_minutes_hours_days, decorate_peer_style,
|
||||
get_width_from_percentage, layout_block, layout_block_bottom_border, layout_block_top_border,
|
||||
@@ -17,7 +18,8 @@ mod test {
|
||||
fn test_layout_block() {
|
||||
assert_eq!(
|
||||
layout_block(),
|
||||
Block::default()
|
||||
Block::new()
|
||||
.default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
);
|
||||
@@ -27,11 +29,12 @@ mod test {
|
||||
fn test_layout_block_with_title() {
|
||||
let title_span = Span::styled(
|
||||
"title",
|
||||
Style::default()
|
||||
Style::new()
|
||||
.fg(Color::DarkGray)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
);
|
||||
let expected_block = Block::default()
|
||||
let expected_block = Block::new()
|
||||
.default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.title(title_span.clone());
|
||||
@@ -43,11 +46,12 @@ mod test {
|
||||
fn test_layout_block_top_border_with_title() {
|
||||
let title_span = Span::styled(
|
||||
"title",
|
||||
Style::default()
|
||||
Style::new()
|
||||
.fg(Color::DarkGray)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
);
|
||||
let expected_block = Block::default()
|
||||
let expected_block = Block::new()
|
||||
.default()
|
||||
.borders(Borders::TOP)
|
||||
.title(title_span.clone());
|
||||
|
||||
@@ -61,7 +65,7 @@ mod test {
|
||||
fn test_layout_block_top_border() {
|
||||
assert_eq!(
|
||||
layout_block_top_border(),
|
||||
Block::default().borders(Borders::TOP)
|
||||
Block::new().borders(Borders::TOP).default()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,48 +73,45 @@ mod test {
|
||||
fn test_layout_block_bottom_border() {
|
||||
assert_eq!(
|
||||
layout_block_bottom_border(),
|
||||
Block::default().borders(Borders::BOTTOM)
|
||||
Block::new().borders(Borders::BOTTOM).default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borderless_block() {
|
||||
assert_eq!(borderless_block(), Block::default());
|
||||
assert_eq!(borderless_block(), Block::new().default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_button_highlight_selected() {
|
||||
let expected_style = Style::default()
|
||||
.fg(Color::Yellow)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let expected_style = Style::new().fg(Color::Yellow).add_modifier(Modifier::BOLD);
|
||||
|
||||
assert_eq!(style_block_highlight(true), expected_style);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_button_highlight_unselected() {
|
||||
let expected_style = Style::default()
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let expected_style = Style::new().fg(Color::White).add_modifier(Modifier::BOLD);
|
||||
|
||||
assert_eq!(style_block_highlight(false), expected_style);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_title_style() {
|
||||
let expected_span = Span::styled(" test ", Style::default().add_modifier(Modifier::BOLD));
|
||||
let expected_span = Span::styled(" test ", Style::new().add_modifier(Modifier::BOLD));
|
||||
|
||||
assert_eq!(title_style("test"), expected_span);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_title_block() {
|
||||
let expected_block = Block::default()
|
||||
let expected_block = Block::new()
|
||||
.default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.title(Span::styled(
|
||||
" test ",
|
||||
Style::default().add_modifier(Modifier::BOLD),
|
||||
Style::new().add_modifier(Modifier::BOLD),
|
||||
));
|
||||
|
||||
assert_eq!(title_block("test"), expected_block);
|
||||
@@ -118,12 +119,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_title_block_centered() {
|
||||
let expected_block = Block::default()
|
||||
let expected_block = Block::new()
|
||||
.default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.title(Span::styled(
|
||||
" test ",
|
||||
Style::default().add_modifier(Modifier::BOLD),
|
||||
Style::new().add_modifier(Modifier::BOLD),
|
||||
))
|
||||
.title_alignment(Alignment::Center);
|
||||
|
||||
@@ -132,12 +134,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_logo_block() {
|
||||
let expected_block = Block::default()
|
||||
let expected_block = Block::new()
|
||||
.default()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.title(Span::styled(
|
||||
" Managarr - A Servarr management TUI ",
|
||||
Style::default()
|
||||
Style::new()
|
||||
.fg(Color::Magenta)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.add_modifier(Modifier::ITALIC),
|
||||
|
||||
Reference in New Issue
Block a user