diff --git a/benches/main/ui.rs b/benches/main/ui.rs index c7e6435..514d5ae 100644 --- a/benches/main/ui.rs +++ b/benches/main/ui.rs @@ -7,7 +7,9 @@ use ratatui::layout::Alignment; use ratatui::layout::Rect; use ratatui::prelude::{Line, Style}; use ratatui::style::Color; -use ratatui::widgets::{Block, BorderType, Borders, ListDirection, Padding}; +use ratatui::widgets::{ + Block, BorderType, Borders, ListDirection, ListState, Padding, +}; use television::channels::prototypes::ChannelPrototype; use television::picker::Movement; use television::{ @@ -390,9 +392,7 @@ pub fn draw_results_list(c: &mut Criterion) { ]; let colorscheme = ResultsColorscheme { - result_name_fg: Color::Indexed(222), - result_preview_fg: Color::Indexed(222), - result_line_number_fg: Color::Indexed(222), + result_fg: Color::Indexed(222), result_selected_fg: Color::Indexed(222), result_selected_bg: Color::Indexed(222), match_foreground_color: Color::Indexed(222), @@ -411,6 +411,7 @@ pub fn draw_results_list(c: &mut Criterion) { .style(Style::default()) .padding(Padding::right(1)), &entries, + &ListState::default(), ListDirection::BottomToTop, false, &colorscheme, diff --git a/television/config/themes.rs b/television/config/themes.rs index b40266a..25467e3 100644 --- a/television/config/themes.rs +++ b/television/config/themes.rs @@ -477,9 +477,7 @@ impl Into for &Theme { impl Into for &Theme { fn into(self) -> ResultsColorscheme { ResultsColorscheme { - result_name_fg: (&self.result_name_fg).into(), - result_preview_fg: (&self.result_value_fg).into(), - result_line_number_fg: (&self.result_line_number_fg).into(), + result_fg: (&self.result_name_fg).into(), result_selected_bg: (&self.selection_bg).into(), result_selected_fg: (&self.selection_fg).into(), match_foreground_color: (&self.match_fg).into(), diff --git a/television/screen/colors.rs b/television/screen/colors.rs index 84e55e1..ffb0177 100644 --- a/television/screen/colors.rs +++ b/television/screen/colors.rs @@ -24,9 +24,7 @@ pub struct HelpColorscheme { #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct ResultsColorscheme { - pub result_name_fg: Color, - pub result_preview_fg: Color, - pub result_line_number_fg: Color, + pub result_fg: Color, pub result_selected_bg: Color, pub result_selected_fg: Color, pub match_foreground_color: Color, diff --git a/television/screen/remote_control.rs b/television/screen/remote_control.rs index 950956a..b2cb62a 100644 --- a/television/screen/remote_control.rs +++ b/television/screen/remote_control.rs @@ -255,6 +255,7 @@ fn draw_rc_channels( let channel_list = result_item::build_results_list( rc_block, entries, + picker_state, ListDirection::TopToBottom, use_nerd_font_icons, &colorscheme.results, diff --git a/television/screen/result_item.rs b/television/screen/result_item.rs index 1c29b57..c1f06a9 100644 --- a/television/screen/result_item.rs +++ b/television/screen/result_item.rs @@ -13,7 +13,7 @@ use devicons::FileIcon; use ratatui::{ prelude::{Color, Line, Span, Style}, style::Stylize, - widgets::{Block, List, ListDirection}, + widgets::{Block, List, ListDirection, ListState}, }; use std::str::FromStr; use unicode_width::UnicodeWidthStr; @@ -42,12 +42,17 @@ pub trait ResultItem { /// Build a single `Line` for a [`ResultItem`]. #[allow(clippy::too_many_arguments)] #[allow(clippy::cast_possible_truncation)] +// TODO: pass the right colors directly as arguments and make the +// calling function responsible for the colors used for each line. pub fn build_result_line<'a, T: ResultItem + ?Sized>( item: &'a T, use_icons: bool, - colorscheme: &ResultsColorscheme, + selection_fg: Color, + result_fg: Color, + match_fg: Color, area_width: u16, - prefix: Option, // Some(true)=selected ●, Some(false)=unselected, None=no prefix + // Some(true)=selected ●, Some(false)=unselected, None=no prefix + prefix: Option, ) -> Line<'a> { // PERF: Pre-allocate spans vector with estimated capacity let mut spans = Vec::>::with_capacity(16); @@ -57,7 +62,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( if selected { spans.push(Span::styled( SELECTED_SYMBOL, - Style::default().fg(colorscheme.result_selected_fg), + Style::default().fg(selection_fg), )); } else { spans.push(Span::raw(DESELECTED_SYMBOL)); @@ -111,10 +116,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( // PERF: Early return for empty match ranges - common case optimization if match_ranges.is_empty() { - spans.push(Span::styled( - entry_name, - Style::default().fg(colorscheme.result_name_fg), - )); + spans.push(Span::styled(entry_name, Style::default().fg(result_fg))); } else { // PERF: Collect chars once to avoid repeated Unicode parsing let chars: Vec = entry_name.chars().collect(); @@ -131,7 +133,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( if !text.is_empty() { spans.push(Span::styled( text, - Style::default().fg(colorscheme.result_name_fg), + Style::default().fg(result_fg), )); } } @@ -143,8 +145,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( if !text.is_empty() { spans.push(Span::styled( text, - Style::default() - .fg(colorscheme.match_foreground_color), + Style::default().fg(match_fg), )); } } @@ -156,10 +157,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( if last_end < name_len { let text: String = chars[last_end..].iter().collect(); if !text.is_empty() { - spans.push(Span::styled( - text, - Style::default().fg(colorscheme.result_name_fg), - )); + spans.push(Span::styled(text, Style::default().fg(result_fg))); } } } @@ -170,7 +168,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( match binding { Binding::SingleKey(k) => spans.push(Span::styled( k.to_string(), - Style::default().fg(colorscheme.match_foreground_color), + Style::default().fg(match_fg), )), Binding::MultipleKeys(keys) => { for (i, k) in keys.iter().enumerate() { @@ -179,8 +177,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( } spans.push(Span::styled( k.to_string(), - Style::default() - .fg(colorscheme.match_foreground_color), + Style::default().fg(match_fg), )); } } @@ -195,6 +192,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>( pub fn build_results_list<'a, 'b, T, F>( block: Block<'b>, entries: &'a [T], + relative_picker_state: &ListState, list_direction: ListDirection, use_icons: bool, colorscheme: &ResultsColorscheme, @@ -206,10 +204,22 @@ where T: ResultItem, F: FnMut(&T) -> Option, { - use ratatui::widgets::List; - List::new(entries.iter().map(|e| { + List::new(entries.iter().enumerate().map(|(i, e)| { let prefix = prefix_fn(e); - build_result_line(e, use_icons, colorscheme, area_width, prefix) + let result_fg = if relative_picker_state.selected() == Some(i) { + colorscheme.result_selected_fg + } else { + colorscheme.result_fg + }; + build_result_line( + e, + use_icons, + colorscheme.result_selected_fg, + result_fg, + colorscheme.match_foreground_color, + area_width, + prefix, + ) })) .direction(list_direction) .highlight_style( @@ -223,7 +233,6 @@ where mod tests { use super::*; use crate::channels::entry::Entry; - use crate::screen::colors::ResultsColorscheme; use ratatui::prelude::{Color, Span}; use ratatui::text::Line; @@ -234,7 +243,9 @@ mod tests { let line = build_result_line( &entry, false, - &ResultsColorscheme::default(), + Color::Reset, + Color::Reset, + Color::Reset, 200, None, ); @@ -258,7 +269,9 @@ mod tests { let line = build_result_line( &entry, false, - &ResultsColorscheme::default(), + Color::Reset, + Color::Reset, + Color::Reset, 20, // small width None, ); diff --git a/television/screen/results.rs b/television/screen/results.rs index d843f49..175dc92 100644 --- a/television/screen/results.rs +++ b/television/screen/results.rs @@ -44,6 +44,7 @@ pub fn draw_results_list( let results_list = result_item::build_results_list( results_block, entries, + relative_picker_state, list_direction, use_nerd_font_icons, &colorscheme.results, diff --git a/television/screen/status_bar.rs b/television/screen/status_bar.rs index b6226fd..c518c2a 100644 --- a/television/screen/status_bar.rs +++ b/television/screen/status_bar.rs @@ -69,7 +69,7 @@ pub fn draw_status_bar(f: &mut Frame<'_>, area: Rect, ctx: &Ctx) { // Add channel-specific info in Channel mode if ctx.tv_state.mode == Mode::Channel { let name_style = Style::default() - .fg(ctx.colorscheme.results.result_name_fg) + .fg(ctx.colorscheme.results.result_fg) .add_modifier(Modifier::BOLD); // Channel name @@ -89,7 +89,7 @@ pub fn draw_status_bar(f: &mut Frame<'_>, area: Rect, ctx: &Ctx) { Span::styled( format!("{} selected", selected_count), Style::default() - .fg(ctx.colorscheme.results.result_name_fg) + .fg(ctx.colorscheme.results.result_fg) .add_modifier(Modifier::ITALIC), ), ]); @@ -191,7 +191,7 @@ pub fn draw_status_bar(f: &mut Frame<'_>, area: Rect, ctx: &Ctx) { let right_spans = vec![Span::styled( format!("v{} ", ctx.app_metadata.version), Style::default() - .fg(ctx.colorscheme.results.result_name_fg) + .fg(ctx.colorscheme.results.result_fg) .add_modifier(Modifier::ITALIC), )]; diff --git a/themes/catppuccin.toml b/themes/catppuccin.toml index 53625ce..c69259c 100644 --- a/themes/catppuccin.toml +++ b/themes/catppuccin.toml @@ -20,4 +20,3 @@ channel_mode_fg = '#1e1e2e' channel_mode_bg = '#f5c2e7' remote_control_mode_fg = '#1e1e2e' remote_control_mode_bg = '#a6e3a1' -send_to_channel_mode_fg = '#89dceb' diff --git a/themes/dracula.toml b/themes/dracula.toml index 76ea1a2..ad894f5 100644 --- a/themes/dracula.toml +++ b/themes/dracula.toml @@ -16,6 +16,8 @@ match_fg = '#FF5555' # preview preview_title_fg = '#50FA7B' # modes -channel_mode_fg = '#8BE9FD' -remote_control_mode_fg = '#FFB86C' +channel_mode_fg = '#282A36' +channel_mode_bg = '#8BE9FD' +remote_control_mode_fg = '#282A36' +remote_control_mode_bg = '#FFB86C' send_to_channel_mode_fg = '#FF79C6' diff --git a/themes/gruvbox-dark.toml b/themes/gruvbox-dark.toml index e3d71a9..940a6d2 100644 --- a/themes/gruvbox-dark.toml +++ b/themes/gruvbox-dark.toml @@ -20,4 +20,3 @@ channel_mode_fg = '#282828' channel_mode_bg = '#b16286' remote_control_mode_fg = '#282828' remote_control_mode_bg = '#8ec07c' -send_to_channel_mode_fg = '#458588' diff --git a/themes/gruvbox-light.toml b/themes/gruvbox-light.toml index 1e4872c..37ea6f4 100644 --- a/themes/gruvbox-light.toml +++ b/themes/gruvbox-light.toml @@ -16,7 +16,7 @@ match_fg = '#af3a03' # preview preview_title_fg = '#98971a' # modes -channel_mode_fg = '#d65d0e' -remote_control_mode_fg = '#689d6a' -send_to_channel_mode_fg = '#458588' - +channel_mode_fg = '#504945' +channel_mode_bg = '#d65d0e' +remote_control_mode_fg = '#504945' +remote_control_mode_bg = '#d79921' diff --git a/themes/monokai.toml b/themes/monokai.toml index 780d0ef..8f4da36 100644 --- a/themes/monokai.toml +++ b/themes/monokai.toml @@ -16,6 +16,7 @@ match_fg = '#f92672' # preview preview_title_fg = '#fd971f' # modes -channel_mode_fg = '#fd971f' -remote_control_mode_fg = '#a6e22e' -send_to_channel_mode_fg = '#66d9ef' +channel_mode_fg = '#2e2e2e' +channel_mode_bg = '#fd971f' +remote_control_mode_fg = '#2e2e2e' +remote_control_mode_bg = '#a6e22e' diff --git a/themes/nord-dark.toml b/themes/nord-dark.toml index 87ca0d2..1b35a23 100644 --- a/themes/nord-dark.toml +++ b/themes/nord-dark.toml @@ -16,6 +16,7 @@ match_fg = '#bf616a' # preview preview_title_fg = '#8fbcbb' # modes -channel_mode_fg = '#b48ead' -remote_control_mode_fg = '#a3be8c' -send_to_channel_mode_fg = '#d08770' +channel_mode_fg = '#2e3440' +channel_mode_bg = '#b48ead' +remote_control_mode_fg = '#2e3440' +remote_control_mode_bg = '#a3be8c' diff --git a/themes/onedark.toml b/themes/onedark.toml index d24198e..06d20d2 100644 --- a/themes/onedark.toml +++ b/themes/onedark.toml @@ -16,6 +16,7 @@ match_fg = '#e06c75' # preview preview_title_fg = '#61afef' # modes -channel_mode_fg = '#61afef' -remote_control_mode_fg = '#98c379' -send_to_channel_mode_fg = '#c678dd' +channel_mode_fg = '#2c323c' +channel_mode_bg = '#61afef' +remote_control_mode_fg = '#2c323c' +remote_control_mode_bg = '#98c379' diff --git a/themes/solarized-dark.toml b/themes/solarized-dark.toml index 619b8e0..68c2834 100644 --- a/themes/solarized-dark.toml +++ b/themes/solarized-dark.toml @@ -16,6 +16,7 @@ match_fg = '#cb4b16' # preview preview_title_fg = '#859900' # modes -channel_mode_fg = '#2aa198' -remote_control_mode_fg = '#859900' -send_to_channel_mode_fg = '#dc322f' +channel_mode_fg = '#002b36' +channel_mode_bg = '#2aa198' +remote_control_mode_fg = '#002b36' +remote_control_mode_bg = '#b58900' diff --git a/themes/solarized-light.toml b/themes/solarized-light.toml index ce50b52..eb5be0c 100644 --- a/themes/solarized-light.toml +++ b/themes/solarized-light.toml @@ -16,6 +16,7 @@ match_fg = '#cb4b16' # preview preview_title_fg = '#859900' # modes -channel_mode_fg = '#2aa198' -remote_control_mode_fg = '#859900' -send_to_channel_mode_fg = '#dc322f' +channel_mode_fg = '#fdf6e3' +channel_mode_bg = '#2aa198' +remote_control_mode_fg = '#fdf6e3' +remote_control_mode_bg = '#859900' diff --git a/themes/television.toml b/themes/television.toml index e522371..c1974e1 100644 --- a/themes/television.toml +++ b/themes/television.toml @@ -17,6 +17,7 @@ match_fg = '#d2788c' # preview preview_title_fg = '#d2a374' # modes -channel_mode_fg = '#8faf77' -remote_control_mode_fg = '#d2a374' -send_to_channel_mode_fg = '#d2788c' +channel_mode_fg = '#18191a' +channel_mode_bg = '#8faf77' +remote_control_mode_fg = '#18191a' +remote_control_mode_bg = '#d2a374' diff --git a/themes/tokyonight.toml b/themes/tokyonight.toml index 5e35251..c9f901e 100644 --- a/themes/tokyonight.toml +++ b/themes/tokyonight.toml @@ -16,7 +16,7 @@ match_fg = '#f7768e' # preview preview_title_fg = '#bb9af7' # modes -channel_mode_fg = '#faba4a' -remote_control_mode_fg = '#9ece6a' -send_to_channel_mode_fg = '#a4daff' - +channel_mode_fg = '#1a1b26' +channel_mode_bg = '#faba4a' +remote_control_mode_fg = '#1a1b26' +remote_control_mode_bg = '#9ece6a'