mirror of
https://github.com/orhun/systeroid
synced 2024-07-23 19:34:58 +00:00
feat(tui): support custom terminal colors
This commit is contained in:
parent
f40b76738e
commit
95a8f2e5ef
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -100,6 +100,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorsys"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dfdf9179d546b55ff3f88c9d93ecfaa3e9760163da5a1080af5243230dbbb70"
|
||||
|
||||
[[package]]
|
||||
name = "copypasta"
|
||||
version = "0.7.1"
|
||||
|
@ -734,6 +740,7 @@ dependencies = [
|
|||
name = "systeroid-tui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"colorsys",
|
||||
"copypasta-ext",
|
||||
"getopts",
|
||||
"systeroid-core",
|
||||
|
|
|
@ -16,6 +16,7 @@ unicode-width = "0.1.9"
|
|||
thiserror = "1.0.30"
|
||||
getopts = "0.2.21"
|
||||
copypasta-ext = { version = "0.3.7", optional = true }
|
||||
colorsys = "0.6.5"
|
||||
|
||||
[dependencies.systeroid-core]
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::color::Colors;
|
||||
use getopts::Options;
|
||||
use std::path::PathBuf;
|
||||
use systeroid_core::sysctl::section::Section;
|
||||
|
@ -23,6 +24,8 @@ pub struct Args {
|
|||
pub section: Option<Section>,
|
||||
/// Query to search on startup.
|
||||
pub search_query: Option<String>,
|
||||
/// Background/foreground colors.
|
||||
pub colors: Colors,
|
||||
/// Do not parse/show Linux kernel documentation.
|
||||
pub no_docs: bool,
|
||||
}
|
||||
|
@ -45,6 +48,18 @@ impl Args {
|
|||
);
|
||||
opts.optopt("s", "section", "set the section to filter", "<section>");
|
||||
opts.optopt("q", "query", "set the query to search", "<query>");
|
||||
opts.optopt(
|
||||
"",
|
||||
"bg-color",
|
||||
"set the background color [default: black]",
|
||||
"<color>",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"fg-color",
|
||||
"set the foreground color [default: white]",
|
||||
"<color>",
|
||||
);
|
||||
opts.optflag("n", "no-docs", "do not show the kernel documentation");
|
||||
opts.optflag("h", "help", "display this help and exit");
|
||||
opts.optflag("V", "version", "output version information and exit");
|
||||
|
@ -79,6 +94,12 @@ impl Args {
|
|||
kernel_docs: matches.opt_str("D").map(PathBuf::from),
|
||||
section: matches.opt_str("s").map(Section::from),
|
||||
search_query: matches.opt_str("q"),
|
||||
colors: Colors::new(
|
||||
matches.opt_str("bg-color").as_deref().unwrap_or("black"),
|
||||
matches.opt_str("fg-color").as_deref().unwrap_or("white"),
|
||||
)
|
||||
.map_err(|e| eprintln!("error: `{}`", e))
|
||||
.ok()?,
|
||||
no_docs: matches.opt_present("n"),
|
||||
})
|
||||
}
|
||||
|
|
108
systeroid-tui/src/color.rs
Normal file
108
systeroid-tui/src/color.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
use crate::error::Result;
|
||||
use colorsys::{ParseError, Rgb};
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::FromStr;
|
||||
use tui::style::Color as TuiColor;
|
||||
|
||||
/// Color configuration.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Colors {
|
||||
/// Background color.
|
||||
pub bg: Color,
|
||||
/// Foreground color.
|
||||
pub fg: Color,
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
/// Constructs a new instance.
|
||||
pub fn new(background: &str, foreground: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
bg: Color::from_str(background)?,
|
||||
fg: Color::from_str(foreground)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for widget colors.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Color {
|
||||
/// Inner type.
|
||||
inner: TuiColor,
|
||||
}
|
||||
|
||||
impl Default for Color {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: TuiColor::Reset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
/// Returns the underlying [`Color`] type.
|
||||
///
|
||||
/// [`Color`]: tui::style::Color
|
||||
pub fn get(self) -> TuiColor {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Color {
|
||||
type Err = ParseError;
|
||||
fn from_str(s: &str) -> StdResult<Self, Self::Err> {
|
||||
Ok(Self {
|
||||
inner: match s.to_lowercase().as_ref() {
|
||||
"reset" => TuiColor::Reset,
|
||||
"black" => TuiColor::Black,
|
||||
"red" => TuiColor::Red,
|
||||
"green" => TuiColor::Green,
|
||||
"yellow" => TuiColor::Yellow,
|
||||
"blue" => TuiColor::Blue,
|
||||
"magenta" => TuiColor::Magenta,
|
||||
"cyan" => TuiColor::Cyan,
|
||||
"gray" => TuiColor::Gray,
|
||||
"darkgray" => TuiColor::DarkGray,
|
||||
"lightred" => TuiColor::LightRed,
|
||||
"lightgreen" => TuiColor::LightGreen,
|
||||
"lightyellow" => TuiColor::LightYellow,
|
||||
"lightblue" => TuiColor::LightBlue,
|
||||
"lightmagenta" => TuiColor::LightMagenta,
|
||||
"lightcyan" => TuiColor::LightCyan,
|
||||
"white" => TuiColor::White,
|
||||
_ => {
|
||||
let rgb = Rgb::from_hex_str(&format!("#{}", s))?;
|
||||
TuiColor::Rgb(rgb.red() as u8, rgb.green() as u8, rgb.blue() as u8)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_color() -> Result<()> {
|
||||
assert_eq!(TuiColor::Reset, Color::default().get());
|
||||
assert_eq!(TuiColor::Gray, Color::from_str("gray")?.get());
|
||||
assert_eq!(TuiColor::Black, Color::from_str("black")?.get());
|
||||
assert_eq!(TuiColor::Green, Color::from_str("green")?.get());
|
||||
assert_eq!(
|
||||
TuiColor::Rgb(152, 157, 69),
|
||||
Color::from_str("989D45")?.get()
|
||||
);
|
||||
assert_eq!(TuiColor::Rgb(18, 49, 47), Color::from_str("12312F")?.get());
|
||||
assert_eq!(
|
||||
TuiColor::Rgb(255, 242, 255),
|
||||
Color::from_str("FFF2FF")?.get()
|
||||
);
|
||||
assert_eq!(
|
||||
Colors {
|
||||
bg: Color::from_str("red")?,
|
||||
fg: Color::from_str("blue")?,
|
||||
},
|
||||
Colors::new("red", "blue")?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -12,6 +12,9 @@ pub enum Error {
|
|||
/// Error that may occur while getting/setting the contents of the clipboard.
|
||||
#[error("Clipboard error: `{0}`")]
|
||||
ClipboardError(String),
|
||||
/// Error that may occur while parsing a color.
|
||||
#[error(transparent)]
|
||||
ColorParseError(#[from] colorsys::ParseError),
|
||||
/// Error that may occur in the core library.
|
||||
#[error("{0}")]
|
||||
SysctlError(#[from] systeroid_core::error::Error),
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
pub mod app;
|
||||
/// Command-line argument parser.
|
||||
pub mod args;
|
||||
/// Color helper.
|
||||
pub mod color;
|
||||
/// Application commands.
|
||||
pub mod command;
|
||||
/// Error implementation.
|
||||
|
@ -57,7 +59,7 @@ pub fn run<B: Backend>(args: Args, backend: B) -> Result<()> {
|
|||
app.input = None;
|
||||
}
|
||||
while app.running {
|
||||
terminal.draw(|frame| ui::render(frame, &mut app))?;
|
||||
terminal.draw(|frame| ui::render(frame, &mut app, &args.colors))?;
|
||||
match event_handler.next()? {
|
||||
Event::KeyPress(key) => {
|
||||
let command = Command::parse(key, app.is_input_mode());
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use crate::app::{App, KeyBinding, HELP_TEXT};
|
||||
use crate::color::Colors;
|
||||
use crate::widgets::SelectableList;
|
||||
use tui::backend::Backend;
|
||||
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||
use tui::style::{Color, Style};
|
||||
use tui::style::Style;
|
||||
use tui::text::{Span, Text};
|
||||
use tui::widgets::{Block, BorderType, Borders, Cell, Clear, Paragraph, Row, Table, Wrap};
|
||||
use tui::Frame;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// Renders the user interface.
|
||||
pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App) {
|
||||
pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App, colors: &Colors) {
|
||||
let documentation = app
|
||||
.parameter_list
|
||||
.selected()
|
||||
|
@ -32,9 +33,9 @@ pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App) {
|
|||
[Constraint::Percentage(100), Constraint::Min(0)]
|
||||
})
|
||||
.split(chunks[0]);
|
||||
render_parameter_list(frame, chunks[0], app);
|
||||
render_parameter_list(frame, chunks[0], app, colors);
|
||||
if app.input.is_some() {
|
||||
render_input_prompt(frame, chunks[1], rect.height - 2, app);
|
||||
render_input_prompt(frame, chunks[1], rect.height - 2, app, colors);
|
||||
}
|
||||
}
|
||||
if let Some(documentation) = documentation {
|
||||
|
@ -43,15 +44,21 @@ pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App) {
|
|||
chunks[1],
|
||||
documentation,
|
||||
&mut app.docs_scroll_amount,
|
||||
colors,
|
||||
);
|
||||
}
|
||||
if app.show_help {
|
||||
render_help_text(frame, rect, &mut app.key_bindings);
|
||||
render_help_text(frame, rect, &mut app.key_bindings, colors);
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the list that contains the sysctl parameters.
|
||||
fn render_parameter_list<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, app: &mut App) {
|
||||
fn render_parameter_list<B: Backend>(
|
||||
frame: &mut Frame<'_, B>,
|
||||
rect: Rect,
|
||||
app: &mut App,
|
||||
colors: &Colors,
|
||||
) {
|
||||
let max_width = app
|
||||
.parameter_list
|
||||
.items
|
||||
|
@ -65,17 +72,17 @@ fn render_parameter_list<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, app:
|
|||
Row::new(if minimize_rows {
|
||||
vec![Cell::from(Span::styled(
|
||||
format!("{} = {}", item.name, item.value),
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))]
|
||||
} else {
|
||||
vec![
|
||||
Cell::from(Span::styled(
|
||||
item.name.clone(),
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
)),
|
||||
Cell::from(Span::styled(
|
||||
item.value.clone(),
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
)),
|
||||
]
|
||||
})
|
||||
|
@ -88,15 +95,15 @@ fn render_parameter_list<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, app:
|
|||
Block::default()
|
||||
.title(Span::styled(
|
||||
"Parameters",
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))
|
||||
.title_alignment(Alignment::Left)
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(Color::White))
|
||||
.border_style(Style::default().fg(colors.fg.get()))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
)
|
||||
.highlight_style(Style::default().bg(Color::White).fg(Color::Black))
|
||||
.highlight_style(Style::default().bg(colors.fg.get()).fg(colors.bg.get()))
|
||||
.widths(&if minimize_rows {
|
||||
[Constraint::Percentage(100), Constraint::Min(0)]
|
||||
} else {
|
||||
|
@ -117,17 +124,23 @@ fn render_parameter_list<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, app:
|
|||
.unwrap_or(0),
|
||||
app.parameter_list.items.len()
|
||||
),
|
||||
colors,
|
||||
);
|
||||
if let Some(section) = app.section_list.selected() {
|
||||
render_section_text(frame, rect, section);
|
||||
render_section_text(frame, rect, section, colors);
|
||||
}
|
||||
if let Some(options) = app.options.as_mut() {
|
||||
render_options_menu(frame, rect, options);
|
||||
render_options_menu(frame, rect, options, colors);
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the text for displaying the selected index.
|
||||
fn render_selection_text<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, selection_text: String) {
|
||||
fn render_selection_text<B: Backend>(
|
||||
frame: &mut Frame<'_, B>,
|
||||
rect: Rect,
|
||||
selection_text: String,
|
||||
colors: &Colors,
|
||||
) {
|
||||
let selection_text_width = u16::try_from(selection_text.width()).unwrap_or_default();
|
||||
if let Some(horizontal_area_width) = rect.width.checked_sub(selection_text_width + 2) {
|
||||
let vertical_area = Layout::default()
|
||||
|
@ -158,7 +171,7 @@ fn render_selection_text<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, selec
|
|||
Paragraph::new(selection_text).block(
|
||||
Block::default()
|
||||
.borders(Borders::NONE)
|
||||
.style(Style::default().bg(Color::White).fg(Color::Black)),
|
||||
.style(Style::default().bg(colors.fg.get()).fg(colors.bg.get())),
|
||||
),
|
||||
horizontal_area[1],
|
||||
);
|
||||
|
@ -167,7 +180,7 @@ fn render_selection_text<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, selec
|
|||
Paragraph::new(Text::default()).block(
|
||||
Block::default()
|
||||
.borders(Borders::NONE)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
),
|
||||
horizontal_area[2],
|
||||
);
|
||||
|
@ -175,7 +188,12 @@ fn render_selection_text<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, selec
|
|||
}
|
||||
|
||||
/// Renders the text for displaying the parameter section.
|
||||
fn render_section_text<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, section: &str) {
|
||||
fn render_section_text<B: Backend>(
|
||||
frame: &mut Frame<'_, B>,
|
||||
rect: Rect,
|
||||
section: &str,
|
||||
colors: &Colors,
|
||||
) {
|
||||
let section = format!("|{}|", section);
|
||||
let text_width: u16 = section.width().try_into().unwrap_or(1);
|
||||
let vertical_area = Layout::default()
|
||||
|
@ -205,10 +223,10 @@ fn render_section_text<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, section
|
|||
.split(vertical_area[0]);
|
||||
frame.render_widget(Clear, area[1]);
|
||||
frame.render_widget(
|
||||
Paragraph::new(Span::styled(section, Style::default().fg(Color::White))).block(
|
||||
Paragraph::new(Span::styled(section, Style::default().fg(colors.fg.get()))).block(
|
||||
Block::default()
|
||||
.borders(Borders::NONE)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
),
|
||||
area[1],
|
||||
);
|
||||
|
@ -219,6 +237,7 @@ fn render_help_text<B: Backend>(
|
|||
frame: &mut Frame<'_, B>,
|
||||
rect: Rect,
|
||||
key_bindings: &mut SelectableList<&KeyBinding>,
|
||||
colors: &Colors,
|
||||
) {
|
||||
let (percent_x, percent_y) = (50, 50);
|
||||
let popup_layout = Layout::default()
|
||||
|
@ -264,26 +283,32 @@ fn render_help_text<B: Backend>(
|
|||
.split(rect);
|
||||
frame.render_widget(Clear, area[0]);
|
||||
frame.render_widget(
|
||||
Paragraph::new(Text::styled(HELP_TEXT, Style::default().fg(Color::White)))
|
||||
.block(
|
||||
Block::default()
|
||||
.title(Span::styled("About", Style::default().fg(Color::White)))
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(Color::White))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
)
|
||||
.alignment(Alignment::Center)
|
||||
.wrap(Wrap { trim: false }),
|
||||
Paragraph::new(Text::styled(
|
||||
HELP_TEXT,
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))
|
||||
.block(
|
||||
Block::default()
|
||||
.title(Span::styled("About", Style::default().fg(colors.fg.get())))
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(colors.fg.get()))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
)
|
||||
.alignment(Alignment::Center)
|
||||
.wrap(Wrap { trim: false }),
|
||||
area[0],
|
||||
);
|
||||
frame.render_widget(Clear, area[1]);
|
||||
frame.render_stateful_widget(
|
||||
Table::new(key_bindings.items.iter().map(|item| {
|
||||
Row::new(vec![
|
||||
Cell::from(Span::styled(item.key, Style::default().fg(Color::White))),
|
||||
Cell::from(Span::styled(item.action, Style::default().fg(Color::White))),
|
||||
Cell::from(Span::styled(item.key, Style::default().fg(colors.fg.get()))),
|
||||
Cell::from(Span::styled(
|
||||
item.action,
|
||||
Style::default().fg(colors.fg.get()),
|
||||
)),
|
||||
])
|
||||
.height(1)
|
||||
.bottom_margin(0)
|
||||
|
@ -292,15 +317,15 @@ fn render_help_text<B: Backend>(
|
|||
Block::default()
|
||||
.title(Span::styled(
|
||||
"Key Bindings",
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(Color::White))
|
||||
.border_style(Style::default().fg(colors.fg.get()))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
)
|
||||
.highlight_style(Style::default().bg(Color::White).fg(Color::Black))
|
||||
.highlight_style(Style::default().bg(colors.fg.get()).fg(colors.bg.get()))
|
||||
.widths(&[Constraint::Percentage(50), Constraint::Percentage(50)]),
|
||||
area[1],
|
||||
&mut key_bindings.state,
|
||||
|
@ -312,6 +337,7 @@ fn render_options_menu<B: Backend>(
|
|||
frame: &mut Frame<'_, B>,
|
||||
rect: Rect,
|
||||
options: &mut SelectableList<&str>,
|
||||
colors: &Colors,
|
||||
) {
|
||||
let (length_x, length_y) = (
|
||||
25,
|
||||
|
@ -348,7 +374,7 @@ fn render_options_menu<B: Backend>(
|
|||
Table::new(options.items.iter().map(|item| {
|
||||
Row::new(vec![Cell::from(Span::styled(
|
||||
item.to_string(),
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))])
|
||||
.height(1)
|
||||
.bottom_margin(0)
|
||||
|
@ -357,15 +383,15 @@ fn render_options_menu<B: Backend>(
|
|||
Block::default()
|
||||
.title(Span::styled(
|
||||
"Copy to clipboard",
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(Color::White))
|
||||
.border_style(Style::default().fg(colors.fg.get()))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
)
|
||||
.highlight_style(Style::default().bg(Color::White).fg(Color::Black))
|
||||
.highlight_style(Style::default().bg(colors.fg.get()).fg(colors.bg.get()))
|
||||
.widths(&[Constraint::Percentage(100)]),
|
||||
rect,
|
||||
&mut options.state,
|
||||
|
@ -378,6 +404,7 @@ fn render_parameter_documentation<B: Backend>(
|
|||
rect: Rect,
|
||||
documentation: String,
|
||||
scroll_amount: &mut u16,
|
||||
colors: &Colors,
|
||||
) {
|
||||
match (documentation.lines().count() * 2).checked_sub(rect.height.into()) {
|
||||
Some(scroll_overflow) => {
|
||||
|
@ -392,19 +419,19 @@ fn render_parameter_documentation<B: Backend>(
|
|||
frame.render_widget(
|
||||
Paragraph::new(Text::styled(
|
||||
documentation,
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))
|
||||
.block(
|
||||
Block::default()
|
||||
.title(Span::styled(
|
||||
"Documentation",
|
||||
Style::default().fg(Color::White),
|
||||
Style::default().fg(colors.fg.get()),
|
||||
))
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(Color::White))
|
||||
.border_style(Style::default().fg(colors.fg.get()))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
)
|
||||
.scroll((*scroll_amount, 0))
|
||||
.wrap(Wrap { trim: false }),
|
||||
|
@ -413,7 +440,13 @@ fn render_parameter_documentation<B: Backend>(
|
|||
}
|
||||
|
||||
/// Renders the input prompt for running commands.
|
||||
fn render_input_prompt<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, cursor_y: u16, app: &App) {
|
||||
fn render_input_prompt<B: Backend>(
|
||||
frame: &mut Frame<'_, B>,
|
||||
rect: Rect,
|
||||
cursor_y: u16,
|
||||
app: &App,
|
||||
colors: &Colors,
|
||||
) {
|
||||
let text = match app.input.clone() {
|
||||
Some(mut input) => {
|
||||
if app.input_time.is_some() {
|
||||
|
@ -447,12 +480,12 @@ fn render_input_prompt<B: Backend>(frame: &mut Frame<'_, B>, rect: Rect, cursor_
|
|||
None => String::new(),
|
||||
};
|
||||
frame.render_widget(
|
||||
Paragraph::new(Span::styled(text, Style::default().fg(Color::White))).block(
|
||||
Paragraph::new(Span::styled(text, Style::default().fg(colors.fg.get()))).block(
|
||||
Block::default()
|
||||
.borders(Borders::all())
|
||||
.border_style(Style::default().fg(Color::White))
|
||||
.border_style(Style::default().fg(colors.fg.get()))
|
||||
.border_type(BorderType::Rounded)
|
||||
.style(Style::default().bg(Color::Black)),
|
||||
.style(Style::default().bg(colors.bg.get())),
|
||||
),
|
||||
rect,
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@ use systeroid_core::sysctl::controller::Sysctl;
|
|||
use systeroid_core::sysctl::parameter::Parameter;
|
||||
use systeroid_core::sysctl::section::Section;
|
||||
use systeroid_tui::app::App;
|
||||
use systeroid_tui::color::Colors;
|
||||
use systeroid_tui::command::Command;
|
||||
use systeroid_tui::error::Result;
|
||||
use systeroid_tui::options::{Direction, ScrollArea};
|
||||
|
@ -61,9 +62,10 @@ fn test_render_tui() -> Result<()> {
|
|||
config: Config::default(),
|
||||
};
|
||||
let mut app = App::new(&mut sysctl);
|
||||
let colors = Colors::default();
|
||||
let backend = TestBackend::new(40, 10);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -83,7 +85,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.run_command(Command::Help)?;
|
||||
app.run_command(Command::Scroll(ScrollArea::List, Direction::Down, 1))?;
|
||||
app.run_command(Command::Scroll(ScrollArea::List, Direction::Up, 1))?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -102,7 +104,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.run_command(Command::Select)?;
|
||||
|
||||
app.run_command(Command::Select)?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -128,7 +130,7 @@ fn test_render_tui() -> Result<()> {
|
|||
.chars()
|
||||
.try_for_each(|c| app.run_command(Command::UpdateInput(c)))?;
|
||||
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -146,7 +148,7 @@ fn test_render_tui() -> Result<()> {
|
|||
)?;
|
||||
|
||||
app.run_command(Command::ProcessInput)?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -165,7 +167,7 @@ fn test_render_tui() -> Result<()> {
|
|||
|
||||
thread::sleep(Duration::from_millis(2000));
|
||||
app.tick();
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -185,7 +187,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.run_command(Command::Search)?;
|
||||
app.run_command(Command::Cancel)?;
|
||||
app.run_command(Command::Copy)?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -205,7 +207,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.run_command(Command::Scroll(ScrollArea::List, Direction::Down, 1))?;
|
||||
app.run_command(Command::Scroll(ScrollArea::List, Direction::Up, 1))?;
|
||||
app.run_command(Command::Select)?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──────────────────────|all|─╮",
|
||||
|
@ -226,7 +228,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.tick();
|
||||
app.run_command(Command::Scroll(ScrollArea::Section, Direction::Left, 1))?;
|
||||
app.run_command(Command::Scroll(ScrollArea::Section, Direction::Left, 1))?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters─────────────────────|user|─╮",
|
||||
|
@ -248,7 +250,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.input = Some(String::new());
|
||||
app.run_command(Command::Search)?;
|
||||
app.run_command(Command::UpdateInput('_'))?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──|all|─╮╭──Documentation───╮",
|
||||
|
@ -273,7 +275,7 @@ fn test_render_tui() -> Result<()> {
|
|||
5,
|
||||
))?;
|
||||
app.run_command(Command::Scroll(ScrollArea::Documentation, Direction::Up, 1))?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──|all|─╮╭──Documentation───╮",
|
||||
|
@ -297,7 +299,7 @@ fn test_render_tui() -> Result<()> {
|
|||
app.run_command(Command::Scroll(ScrollArea::List, Direction::Down, 1))?;
|
||||
app.run_command(Command::Scroll(ScrollArea::List, Direction::Down, 2))?;
|
||||
app.run_command(Command::Refresh)?;
|
||||
terminal.draw(|frame| render(frame, &mut app))?;
|
||||
terminal.draw(|frame| render(frame, &mut app, &colors))?;
|
||||
assert_buffer(
|
||||
Buffer::with_lines(vec![
|
||||
"╭Parameters──|all|─╮╭──Documentation───╮",
|
||||
|
|
Loading…
Reference in a new issue