This commit is contained in:
Jeevitha Kannan K S 2024-11-10 16:18:17 +00:00 committed by GitHub
commit e1df2bee62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 163 additions and 175 deletions

2
Cargo.lock generated
View File

@ -449,13 +449,13 @@ dependencies = [
"portable-pty", "portable-pty",
"rand", "rand",
"ratatui", "ratatui",
"regex",
"temp-dir", "temp-dir",
"textwrap", "textwrap",
"time", "time",
"tree-sitter-bash", "tree-sitter-bash",
"tree-sitter-highlight", "tree-sitter-highlight",
"tui-term", "tui-term",
"unicode-width 0.2.0",
"zips", "zips",
] ]

View File

@ -21,7 +21,6 @@ ratatui = "0.29.0"
tui-term = "0.2.0" tui-term = "0.2.0"
temp-dir = "0.1.14" temp-dir = "0.1.14"
time = { version = "0.3.36", features = ["local-offset", "macros", "formatting"] } time = { version = "0.3.36", features = ["local-offset", "macros", "formatting"] }
unicode-width = "0.2.0"
rand = { version = "0.8.5", optional = true } rand = { version = "0.8.5", optional = true }
linutil_core = { path = "../core", version = "24.9.28" } linutil_core = { path = "../core", version = "24.9.28" }
tree-sitter-highlight = "0.24.3" tree-sitter-highlight = "0.24.3"
@ -30,6 +29,7 @@ textwrap = "0.16.1"
anstyle = "1.0.8" anstyle = "1.0.8"
ansi-to-tui = "7.0.0" ansi-to-tui = "7.0.0"
zips = "0.1.7" zips = "0.1.7"
regex = { version = "1.3", default-features = false, features = ["std"] }
[[bin]] [[bin]]
name = "linutil" name = "linutil"

View File

@ -1,13 +1,11 @@
use std::borrow::Cow; use crate::{float::FloatContent, hint::Shortcut, theme};
use crate::{float::FloatContent, hint::Shortcut};
use ratatui::{ use ratatui::{
crossterm::event::{KeyCode, KeyEvent}, crossterm::event::{KeyCode, KeyEvent},
layout::Alignment, layout::Alignment,
prelude::*, prelude::*,
widgets::{Block, Borders, Clear, List}, widgets::{Block, Borders, Clear, List},
}; };
use std::borrow::Cow;
pub enum ConfirmStatus { pub enum ConfirmStatus {
Confirm, Confirm,
@ -16,35 +14,30 @@ pub enum ConfirmStatus {
} }
pub struct ConfirmPrompt { pub struct ConfirmPrompt {
pub names: Box<[String]>, inner_area_height: usize,
pub status: ConfirmStatus, names: Box<[String]>,
scroll: usize, scroll: usize,
pub status: ConfirmStatus,
} }
impl ConfirmPrompt { impl ConfirmPrompt {
pub fn new(names: &[&str]) -> Self { pub fn new(names: Vec<&str>) -> Self {
let max_count_str = format!("{}", names.len());
let names = names let names = names
.iter() .iter()
.zip(1..) .zip(1..)
.map(|(name, n)| { .map(|(name, n)| format!(" {n}. {name}"))
let count_str = format!("{n}");
let space_str = (0..(max_count_str.len() - count_str.len()))
.map(|_| ' ')
.collect::<String>();
format!("{space_str}{n}. {name}")
})
.collect(); .collect();
Self { Self {
inner_area_height: 0,
names, names,
status: ConfirmStatus::None,
scroll: 0, scroll: 0,
status: ConfirmStatus::None,
} }
} }
pub fn scroll_down(&mut self) { pub fn scroll_down(&mut self) {
if self.scroll < self.names.len() - 1 { if self.scroll + self.inner_area_height < self.names.len() - 1 {
self.scroll += 1; self.scroll += 1;
} }
} }
@ -57,19 +50,28 @@ impl ConfirmPrompt {
} }
impl FloatContent for ConfirmPrompt { impl FloatContent for ConfirmPrompt {
fn draw(&mut self, frame: &mut Frame, area: Rect) { fn draw(&mut self, frame: &mut Frame, area: Rect, theme: &theme::Theme) {
let block = Block::default() let block = Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_set(ratatui::symbols::border::ROUNDED) .border_set(ratatui::symbols::border::ROUNDED)
.title(" Confirm selections ") .title(" Confirm selections ")
.title_bottom(" [y] to continue, [n] to abort ") .title_bottom(Line::from(vec![
Span::styled(" [", Style::default()),
Span::styled("y", Style::default().fg(theme.success_color())),
Span::styled("] to continue ", Style::default()),
Span::styled("[", Style::default()),
Span::styled("n", Style::default().fg(theme.fail_color())),
Span::styled("] to abort ", Style::default()),
]))
.title_alignment(Alignment::Center) .title_alignment(Alignment::Center)
.title_style(Style::default().bold()) .title_style(Style::default().bold())
.style(Style::default()); .style(Style::default());
frame.render_widget(block.clone(), area);
let inner_area = block.inner(area); let inner_area = block.inner(area);
self.inner_area_height = inner_area.height as usize;
frame.render_widget(Clear, area);
frame.render_widget(block, area);
let paths_text = self let paths_text = self
.names .names
@ -81,26 +83,25 @@ impl FloatContent for ConfirmPrompt {
}) })
.collect::<Text>(); .collect::<Text>();
frame.render_widget(Clear, inner_area);
frame.render_widget(List::new(paths_text), inner_area); frame.render_widget(List::new(paths_text), inner_area);
} }
fn handle_key_event(&mut self, key: &KeyEvent) -> bool { fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
use KeyCode::*; use ConfirmStatus::*;
use KeyCode::{Char, Down, Esc, Up};
self.status = match key.code { self.status = match key.code {
Char('y') | Char('Y') => ConfirmStatus::Confirm, Char('y') | Char('Y') => Confirm,
Char('n') | Char('N') | Esc | Char('q') => ConfirmStatus::Abort, Char('n') | Char('N') | Esc | Char('q') => Abort,
Char('j') => { Char('j') | Char('J') | Down => {
self.scroll_down(); self.scroll_down();
ConfirmStatus::None None
} }
Char('k') => { Char('k') | Char('K') | Up => {
self.scroll_up(); self.scroll_up();
ConfirmStatus::None None
} }
_ => ConfirmStatus::None, _ => None,
}; };
false false
} }
@ -118,8 +119,8 @@ impl FloatContent for ConfirmPrompt {
Box::new([ Box::new([
Shortcut::new("Continue", ["Y", "y"]), Shortcut::new("Continue", ["Y", "y"]),
Shortcut::new("Abort", ["N", "n", "q", "Esc"]), Shortcut::new("Abort", ["N", "n", "q", "Esc"]),
Shortcut::new("Scroll up", ["k"]), Shortcut::new("Scroll up", ["k", "Up"]),
Shortcut::new("Scroll down", ["j"]), Shortcut::new("Scroll down", ["j", "Down"]),
Shortcut::new("Close linutil", ["CTRL-c"]), Shortcut::new("Close linutil", ["CTRL-c"]),
]), ]),
) )

View File

@ -3,12 +3,12 @@ use linutil_core::{ego_tree::NodeId, Tab};
use ratatui::{ use ratatui::{
crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
layout::{Position, Rect}, layout::{Position, Rect},
style::{Color, Style}, style::Style,
text::Span, text::Span,
widgets::{Block, Borders, Paragraph}, widgets::{Block, Borders, Paragraph},
Frame, Frame,
}; };
use unicode_width::UnicodeWidthChar; use regex::RegexBuilder;
pub enum SearchAction { pub enum SearchAction {
None, None,
@ -17,7 +17,7 @@ pub enum SearchAction {
} }
pub struct Filter { pub struct Filter {
search_input: Vec<char>, search_input: String,
in_search_mode: bool, in_search_mode: bool,
input_position: usize, input_position: usize,
items: Vec<ListEntry>, items: Vec<ListEntry>,
@ -27,7 +27,7 @@ pub struct Filter {
impl Filter { impl Filter {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
search_input: vec![], search_input: String::new(),
in_search_mode: false, in_search_mode: false,
input_position: 0, input_position: 0,
items: vec![], items: vec![],
@ -62,47 +62,45 @@ impl Filter {
.collect(); .collect();
} else { } else {
self.items.clear(); self.items.clear();
if let Ok(regex) = self.regex_builder(&regex::escape(&self.search_input)) {
let query_lower = self.search_input.iter().collect::<String>().to_lowercase(); for tab in tabs {
for tab in tabs.iter() {
let mut stack = vec![tab.tree.root().id()]; let mut stack = vec![tab.tree.root().id()];
while let Some(node_id) = stack.pop() { while let Some(node_id) = stack.pop() {
let node = tab.tree.get(node_id).unwrap(); let node = tab.tree.get(node_id).unwrap();
if regex.is_match(&node.value().name) && !node.has_children() {
if node.value().name.to_lowercase().contains(&query_lower)
&& !node.has_children()
{
self.items.push(ListEntry { self.items.push(ListEntry {
node: node.value().clone(), node: node.value().clone(),
id: node.id(), id: node.id(),
has_children: false, has_children: false,
}); });
} }
stack.extend(node.children().map(|child| child.id())); stack.extend(node.children().map(|child| child.id()));
} }
} }
self.items.sort_by(|a, b| a.node.name.cmp(&b.node.name)); self.items
.sort_unstable_by(|a, b| a.node.name.cmp(&b.node.name));
} else {
self.search_input.clear();
}
} }
self.update_completion_preview(); self.update_completion_preview();
} }
fn update_completion_preview(&mut self) { fn update_completion_preview(&mut self) {
if self.search_input.is_empty() { self.completion_preview = if self.items.is_empty() || self.search_input.is_empty() {
self.completion_preview = None; None
return; } else {
} let pattern = format!("(?i)^{}", regex::escape(&self.search_input));
if let Ok(regex) = self.regex_builder(&pattern) {
let input = self.search_input.iter().collect::<String>().to_lowercase(); self.items.iter().find_map(|item| {
self.completion_preview = self.items.iter().find_map(|item| { regex
let item_name_lower = item.node.name.to_lowercase(); .find(&item.node.name)
if item_name_lower.starts_with(&input) { .map(|mat| item.node.name[mat.end()..].to_string())
Some(item_name_lower[input.len()..].to_string()) })
} else { } else {
None None
} }
}); }
} }
pub fn draw_searchbar(&self, frame: &mut Frame, area: Rect, theme: &Theme) { pub fn draw_searchbar(&self, frame: &mut Frame, area: Rect, theme: &Theme) {
@ -110,8 +108,10 @@ impl Filter {
let display_text = if !self.in_search_mode && self.search_input.is_empty() { let display_text = if !self.in_search_mode && self.search_input.is_empty() {
Span::raw("Press / to search") Span::raw("Press / to search")
} else { } else {
let input_text = self.search_input.iter().collect::<String>(); Span::styled(
Span::styled(input_text, Style::default().fg(theme.focused_color())) &self.search_input,
Style::default().fg(theme.focused_color()),
)
}; };
let search_color = if self.in_search_mode { let search_color = if self.in_search_mode {
@ -135,24 +135,16 @@ impl Filter {
// Render cursor in search bar // Render cursor in search bar
if self.in_search_mode { if self.in_search_mode {
let cursor_position: usize = self.search_input[..self.input_position] let x = area.x + self.input_position as u16 + 1;
.iter()
.map(|c| c.width().unwrap_or(1))
.sum();
let x = area.x + cursor_position as u16 + 1;
let y = area.y + 1; let y = area.y + 1;
frame.set_cursor_position(Position::new(x, y)); frame.set_cursor_position(Position::new(x, y));
if let Some(preview) = &self.completion_preview { if let Some(preview) = &self.completion_preview {
let preview_span = Span::styled(preview, Style::default().fg(Color::DarkGray)); let preview_x = area.x + self.search_input.len() as u16 + 1;
let preview_paragraph = Paragraph::new(preview_span).style(Style::default()); let preview_span =
let preview_area = Rect::new( Span::styled(preview, Style::default().fg(theme.search_preview_color()));
x, let preview_area = Rect::new(preview_x, y, preview.len() as u16, 1);
y, frame.render_widget(Paragraph::new(preview_span), preview_area);
(preview.len() as u16).min(area.width - cursor_position as u16 - 1),
1,
);
frame.render_widget(preview_paragraph, preview_area);
} }
} }
} }
@ -219,16 +211,37 @@ impl Filter {
} }
} }
fn regex_builder(&self, pattern: &str) -> Result<regex::Regex, regex::Error> {
RegexBuilder::new(pattern).case_insensitive(true).build()
}
fn complete_search(&mut self) -> SearchAction { fn complete_search(&mut self) -> SearchAction {
if let Some(completion) = self.completion_preview.take() { if self.completion_preview.is_none() {
self.search_input.extend(completion.chars()); SearchAction::None
} else {
let pattern = format!("(?i)^{}", self.search_input);
if let Ok(regex) = self.regex_builder(&pattern) {
self.search_input = self
.items
.iter()
.find_map(|item| {
if regex.is_match(&item.node.name) {
Some(item.node.name.clone())
} else {
None
}
})
.unwrap_or_default();
self.completion_preview = None;
self.input_position = self.search_input.len(); self.input_position = self.search_input.len();
self.update_completion_preview();
SearchAction::Update SearchAction::Update
} else { } else {
SearchAction::None SearchAction::None
} }
} }
}
pub fn clear_search(&mut self) { pub fn clear_search(&mut self) {
self.search_input.clear(); self.search_input.clear();

View File

@ -1,13 +1,12 @@
use crate::{hint::Shortcut, theme::Theme};
use ratatui::{ use ratatui::{
crossterm::event::{KeyCode, KeyEvent}, crossterm::event::{KeyCode, KeyEvent},
layout::{Constraint, Direction, Layout, Rect}, layout::{Constraint, Direction, Layout, Rect},
Frame, Frame,
}; };
use crate::hint::Shortcut;
pub trait FloatContent { pub trait FloatContent {
fn draw(&mut self, frame: &mut Frame, area: Rect); fn draw(&mut self, frame: &mut Frame, area: Rect, theme: &Theme);
fn handle_key_event(&mut self, key: &KeyEvent) -> bool; fn handle_key_event(&mut self, key: &KeyEvent) -> bool;
fn is_finished(&self) -> bool; fn is_finished(&self) -> bool;
fn get_shortcut_list(&self) -> (&str, Box<[Shortcut]>); fn get_shortcut_list(&self) -> (&str, Box<[Shortcut]>);
@ -48,9 +47,9 @@ impl<Content: FloatContent + ?Sized> Float<Content> {
.split(hor_float)[1] .split(hor_float)[1]
} }
pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) { pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect, theme: &Theme) {
let popup_area = self.floating_window(parent_area); let popup_area = self.floating_window(parent_area);
self.content.draw(frame, popup_area); self.content.draw(frame, popup_area, theme);
} }
// Returns true if the floating window is finished. // Returns true if the floating window is finished.

View File

@ -1,13 +1,6 @@
use std::{ use crate::{float::FloatContent, hint::Shortcut, theme::Theme};
borrow::Cow, use ansi_to_tui::IntoText;
collections::VecDeque,
io::{Cursor, Read as _, Seek, SeekFrom, Write as _},
};
use crate::{float::FloatContent, hint::Shortcut};
use linutil_core::Command; use linutil_core::Command;
use ratatui::{ use ratatui::{
crossterm::event::{KeyCode, KeyEvent}, crossterm::event::{KeyCode, KeyEvent},
layout::Rect, layout::Rect,
@ -16,9 +9,11 @@ use ratatui::{
widgets::{Block, Borders, Clear, List}, widgets::{Block, Borders, Clear, List},
Frame, Frame,
}; };
use std::{
use ansi_to_tui::IntoText; borrow::Cow,
collections::VecDeque,
io::{Cursor, Read as _, Seek, SeekFrom, Write as _},
};
use textwrap::wrap; use textwrap::wrap;
use tree_sitter_bash as hl_bash; use tree_sitter_bash as hl_bash;
use tree_sitter_highlight::{self as hl, HighlightEvent}; use tree_sitter_highlight::{self as hl, HighlightEvent};
@ -209,7 +204,7 @@ impl FloatingText {
} }
impl FloatContent for FloatingText { impl FloatContent for FloatingText {
fn draw(&mut self, frame: &mut Frame, area: Rect) { fn draw(&mut self, frame: &mut Frame, area: Rect, _theme: &Theme) {
self.frame_height = area.height as usize; self.frame_height = area.height as usize;
// Define the Block with a border and background color // Define the Block with a border and background color

View File

@ -1,9 +1,8 @@
use std::borrow::Cow;
use ratatui::{ use ratatui::{
style::{Style, Stylize}, style::{Style, Stylize},
text::{Line, Span}, text::{Line, Span},
}; };
use std::borrow::Cow;
pub struct Shortcut { pub struct Shortcut {
pub key_sequences: Vec<Span<'static>>, pub key_sequences: Vec<Span<'static>>,

View File

@ -7,15 +7,8 @@ mod running_command;
pub mod state; pub mod state;
mod theme; mod theme;
use std::{
io::{self, stdout},
path::PathBuf,
time::Duration,
};
use crate::theme::Theme; use crate::theme::Theme;
use clap::Parser; use clap::Parser;
use ratatui::{ use ratatui::{
backend::CrosstermBackend, backend::CrosstermBackend,
crossterm::{ crossterm::{
@ -27,6 +20,11 @@ use ratatui::{
Terminal, Terminal,
}; };
use state::AppState; use state::AppState;
use std::{
io::{self, stdout},
path::PathBuf,
time::Duration,
};
// Linux utility toolbox // Linux utility toolbox
#[derive(Debug, Parser)] #[derive(Debug, Parser)]

View File

@ -1,4 +1,4 @@
use crate::{float::FloatContent, hint::Shortcut}; use crate::{float::FloatContent, hint::Shortcut, theme::Theme};
use linutil_core::Command; use linutil_core::Command;
use oneshot::{channel, Receiver}; use oneshot::{channel, Receiver};
use portable_pty::{ use portable_pty::{
@ -7,7 +7,7 @@ use portable_pty::{
use ratatui::{ use ratatui::{
crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
layout::{Rect, Size}, layout::{Rect, Size},
style::{Color, Style, Stylize}, style::{Style, Stylize},
text::{Line, Span}, text::{Line, Span},
widgets::{Block, Borders}, widgets::{Block, Borders},
Frame, Frame,
@ -22,6 +22,7 @@ use tui_term::{
vt100::{self, Screen}, vt100::{self, Screen},
widget::PseudoTerminal, widget::PseudoTerminal,
}; };
pub struct RunningCommand { pub struct RunningCommand {
/// A buffer to save all the command output (accumulates, until the command exits) /// A buffer to save all the command output (accumulates, until the command exits)
buffer: Arc<Mutex<Vec<u8>>>, buffer: Arc<Mutex<Vec<u8>>>,
@ -42,7 +43,7 @@ pub struct RunningCommand {
} }
impl FloatContent for RunningCommand { impl FloatContent for RunningCommand {
fn draw(&mut self, frame: &mut Frame, area: Rect) { fn draw(&mut self, frame: &mut Frame, area: Rect, theme: &Theme) {
// Calculate the inner size of the terminal area, considering borders // Calculate the inner size of the terminal area, considering borders
let inner_size = Size { let inner_size = Size {
width: area.width - 2, // Adjust for border width width: area.width - 2, // Adjust for border width
@ -64,13 +65,13 @@ impl FloatContent for RunningCommand {
Line::from( Line::from(
Span::default() Span::default()
.content("SUCCESS!") .content("SUCCESS!")
.style(Style::default().fg(Color::Green).reversed()), .style(Style::default().fg(theme.success_color()).reversed()),
) )
} else { } else {
Line::from( Line::from(
Span::default() Span::default()
.content("FAILED!") .content("FAILED!")
.style(Style::default().fg(Color::Red).reversed()), .style(Style::default().fg(theme.fail_color()).reversed()),
) )
}; };

View File

@ -137,7 +137,7 @@ impl AppState {
.map(|node| node.name.as_str()) .map(|node| node.name.as_str())
.collect(); .collect();
let prompt = ConfirmPrompt::new(&cmd_names); let prompt = ConfirmPrompt::new(cmd_names);
self.focus = Focus::ConfirmationPrompt(Float::new(Box::new(prompt), 40, 40)); self.focus = Focus::ConfirmationPrompt(Float::new(Box::new(prompt), 40, 40));
} }
} }
@ -229,7 +229,7 @@ impl AppState {
MIN_HEIGHT, MIN_HEIGHT,
)) ))
.alignment(Alignment::Center) .alignment(Alignment::Center)
.style(Style::default().fg(ratatui::style::Color::Red).bold()) .style(Style::default().fg(self.theme.fail_color()).bold())
.wrap(ratatui::widgets::Wrap { trim: true }); .wrap(ratatui::widgets::Wrap { trim: true });
let centered_layout = Layout::default() let centered_layout = Layout::default()
@ -461,8 +461,8 @@ impl AppState {
frame.render_stateful_widget(disclaimer_list, list_chunks[1], &mut self.selection); frame.render_stateful_widget(disclaimer_list, list_chunks[1], &mut self.selection);
match &mut self.focus { match &mut self.focus {
Focus::FloatingWindow(float) => float.draw(frame, chunks[1]), Focus::FloatingWindow(float) => float.draw(frame, chunks[1], &self.theme),
Focus::ConfirmationPrompt(prompt) => prompt.draw(frame, chunks[1]), Focus::ConfirmationPrompt(prompt) => prompt.draw(frame, chunks[1], &self.theme),
_ => {} _ => {}
} }
@ -798,7 +798,7 @@ impl AppState {
.map(|node| node.name.as_str()) .map(|node| node.name.as_str())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let prompt = ConfirmPrompt::new(&cmd_names[..]); let prompt = ConfirmPrompt::new(cmd_names);
self.focus = Focus::ConfirmationPrompt(Float::new(Box::new(prompt), 40, 40)); self.focus = Focus::ConfirmationPrompt(Float::new(Box::new(prompt), 40, 40));
} }
} }

View File

@ -14,88 +14,70 @@ pub enum Theme {
} }
impl Theme { impl Theme {
pub fn dir_color(&self) -> Color { fn get_color_variant(&self, default: Color, compatible: Color) -> Color {
match self { match self {
Theme::Default => Color::Blue, Theme::Default => default,
Theme::Compatible => Color::Blue, Theme::Compatible => compatible,
} }
} }
fn get_icon_variant(&self, default: &'static str, compatible: &'static str) -> &'static str {
match self {
Theme::Default => default,
Theme::Compatible => compatible,
}
}
pub fn dir_color(&self) -> Color {
self.get_color_variant(Color::Blue, Color::Blue)
}
pub fn cmd_color(&self) -> Color { pub fn cmd_color(&self) -> Color {
match self { self.get_color_variant(Color::Rgb(204, 224, 208), Color::LightGreen)
Theme::Default => Color::Rgb(204, 224, 208),
Theme::Compatible => Color::LightGreen,
}
} }
pub fn multi_select_disabled_color(&self) -> Color { pub fn multi_select_disabled_color(&self) -> Color {
match self { self.get_color_variant(Color::DarkGray, Color::DarkGray)
Theme::Default => Color::DarkGray,
Theme::Compatible => Color::DarkGray,
}
} }
pub fn tab_color(&self) -> Color { pub fn tab_color(&self) -> Color {
match self { self.get_color_variant(Color::Rgb(255, 255, 85), Color::Yellow)
Theme::Default => Color::Rgb(255, 255, 85),
Theme::Compatible => Color::Yellow,
}
} }
pub fn dir_icon(&self) -> &'static str { pub fn dir_icon(&self) -> &'static str {
match self { self.get_icon_variant("", "[DIR]")
Theme::Default => "",
Theme::Compatible => "[DIR]",
}
} }
pub fn cmd_icon(&self) -> &'static str { pub fn cmd_icon(&self) -> &'static str {
match self { self.get_icon_variant("", "[CMD]")
Theme::Default => "",
Theme::Compatible => "[CMD]",
}
} }
pub fn tab_icon(&self) -> &'static str { pub fn tab_icon(&self) -> &'static str {
match self { self.get_icon_variant("", ">> ")
Theme::Default => "",
Theme::Compatible => ">> ",
}
} }
pub fn multi_select_icon(&self) -> &'static str { pub fn multi_select_icon(&self) -> &'static str {
match self { self.get_icon_variant("", "*")
Theme::Default => "",
Theme::Compatible => "*",
}
} }
pub fn success_color(&self) -> Color { pub fn success_color(&self) -> Color {
match self { self.get_color_variant(Color::Rgb(5, 255, 55), Color::Green)
Theme::Default => Color::Rgb(199, 55, 44),
Theme::Compatible => Color::Green,
}
} }
pub fn fail_color(&self) -> Color { pub fn fail_color(&self) -> Color {
match self { self.get_color_variant(Color::Rgb(199, 55, 44), Color::Red)
Theme::Default => Color::Rgb(5, 255, 55),
Theme::Compatible => Color::Red,
}
} }
pub fn focused_color(&self) -> Color { pub fn focused_color(&self) -> Color {
match self { self.get_color_variant(Color::LightBlue, Color::LightBlue)
Theme::Default => Color::LightBlue,
Theme::Compatible => Color::LightBlue,
} }
pub fn search_preview_color(&self) -> Color {
self.get_color_variant(Color::DarkGray, Color::DarkGray)
} }
pub fn unfocused_color(&self) -> Color { pub fn unfocused_color(&self) -> Color {
match self { self.get_color_variant(Color::Gray, Color::Gray)
Theme::Default => Color::Gray,
Theme::Compatible => Color::Gray,
}
} }
} }