television_screen/
remote_control.rs1use rustc_hash::FxHashMap;
2
3use crate::colors::{Colorscheme, GeneralColorscheme};
4use crate::logo::build_remote_logo_paragraph;
5use crate::mode::{mode_color, Mode};
6use crate::results::build_results_list;
7use television_channels::entry::Entry;
8use television_utils::input::Input;
9
10use color_eyre::eyre::Result;
11use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
12use ratatui::prelude::Style;
13use ratatui::style::{Color, Stylize};
14use ratatui::text::{Line, Span};
15use ratatui::widgets::{
16 Block, BorderType, Borders, ListDirection, ListState, Padding, Paragraph,
17};
18use ratatui::Frame;
19
20#[allow(clippy::too_many_arguments)]
21pub fn draw_remote_control(
22 f: &mut Frame,
23 rect: Rect,
24 entries: &[Entry],
25 use_nerd_font_icons: bool,
26 picker_state: &mut ListState,
27 input_state: &mut Input,
28 icon_color_cache: &mut FxHashMap<String, Color>,
29 mode: &Mode,
30 colorscheme: &Colorscheme,
31) -> Result<()> {
32 let layout = Layout::default()
33 .direction(Direction::Vertical)
34 .constraints(
35 [
36 Constraint::Min(3),
37 Constraint::Length(3),
38 Constraint::Length(20),
39 ]
40 .as_ref(),
41 )
42 .split(rect);
43 draw_rc_channels(
44 f,
45 layout[0],
46 entries,
47 use_nerd_font_icons,
48 picker_state,
49 icon_color_cache,
50 colorscheme,
51 );
52 draw_rc_input(f, layout[1], input_state, colorscheme)?;
53 draw_rc_logo(
54 f,
55 layout[2],
56 mode_color(*mode, &colorscheme.mode),
57 &colorscheme.general,
58 );
59 Ok(())
60}
61
62fn draw_rc_channels(
63 f: &mut Frame,
64 area: Rect,
65 entries: &[Entry],
66 use_nerd_font_icons: bool,
67 picker_state: &mut ListState,
68 icon_color_cache: &mut FxHashMap<String, Color>,
69 colorscheme: &Colorscheme,
70) {
71 let rc_block = Block::default()
72 .borders(Borders::ALL)
73 .border_type(BorderType::Rounded)
74 .border_style(Style::default().fg(colorscheme.general.border_fg))
75 .style(
76 Style::default()
77 .bg(colorscheme.general.background.unwrap_or_default()),
78 )
79 .padding(Padding::right(1));
80
81 let channel_list = build_results_list(
82 rc_block,
83 entries,
84 None,
85 ListDirection::TopToBottom,
86 use_nerd_font_icons,
87 icon_color_cache,
88 &colorscheme.results,
89 );
90
91 f.render_stateful_widget(channel_list, area, picker_state);
92}
93
94fn draw_rc_input(
95 f: &mut Frame,
96 area: Rect,
97 input: &mut Input,
98 colorscheme: &Colorscheme,
99) -> Result<()> {
100 let input_block = Block::default()
101 .title_top(Line::from("Remote Control").alignment(Alignment::Center))
102 .borders(Borders::ALL)
103 .border_type(BorderType::Rounded)
104 .border_style(Style::default().fg(colorscheme.general.border_fg))
105 .style(
106 Style::default()
107 .bg(colorscheme.general.background.unwrap_or_default()),
108 );
109
110 let input_block_inner = input_block.inner(area);
111
112 f.render_widget(input_block, area);
113
114 let inner_input_chunks = Layout::default()
116 .direction(Direction::Horizontal)
117 .constraints([
118 Constraint::Length(2),
120 Constraint::Fill(1),
122 ])
123 .split(input_block_inner);
124
125 let prompt_symbol_block = Block::default();
126 let arrow = Paragraph::new(Span::styled(
127 "> ",
128 Style::default().fg(colorscheme.input.input_fg).bold(),
129 ))
130 .block(prompt_symbol_block);
131 f.render_widget(arrow, inner_input_chunks[0]);
132
133 let interactive_input_block = Block::default();
134 let width = inner_input_chunks[1].width.max(3) - 3;
136 let scroll = input.visual_scroll(width as usize);
137 let input_paragraph = Paragraph::new(input.value())
138 .scroll((0, u16::try_from(scroll)?))
139 .block(interactive_input_block)
140 .style(
141 Style::default()
142 .fg(colorscheme.input.input_fg)
143 .bold()
144 .italic(),
145 )
146 .alignment(Alignment::Left);
147 f.render_widget(input_paragraph, inner_input_chunks[1]);
148
149 f.set_cursor_position((
152 inner_input_chunks[1].x
154 + u16::try_from(input.visual_cursor().max(scroll) - scroll)?,
155 inner_input_chunks[1].y,
157 ));
158 Ok(())
159}
160fn draw_rc_logo(
161 f: &mut Frame,
162 area: Rect,
163 mode_color: Color,
164 colorscheme: &GeneralColorscheme,
165) {
166 let logo_block = Block::default().style(
167 Style::default()
168 .fg(mode_color)
169 .bg(colorscheme.background.unwrap_or_default()),
170 );
171
172 let logo_paragraph = build_remote_logo_paragraph()
173 .alignment(Alignment::Center)
174 .block(logo_block);
175
176 f.render_widget(logo_paragraph, area);
177}