Skip to main content

nf_rated/render/components/
admin.rs

1use tui::{
2    backend::Backend,
3    layout::Constraint,
4    layout::Direction,
5    layout::Layout,
6    layout::Rect,
7    style::{Color, Style},
8    text::Span,
9    widgets::Block,
10    widgets::BorderType,
11    widgets::Borders,
12    widgets::Paragraph,
13    widgets::Row,
14    widgets::Table,
15    Frame,
16};
17
18use crate::{data::ItemType, render::App, render::QueryField};
19
20pub fn query_offset(query_field: &QueryField) -> u16 {
21    3 * match query_field {
22        QueryField::Genre => 0,
23        QueryField::Title => 1,
24        QueryField::Cast => 2,
25        QueryField::Country => 3,
26        QueryField::Language => 4,
27        QueryField::Plot => 5,
28    } + 2
29}
30
31pub fn render_admin<B>(f: &mut Frame<B>, app: &App, container: Rect)
32where
33    B: Backend,
34{
35    let chunks = Layout::default()
36        .direction(Direction::Vertical)
37        .constraints([Constraint::Percentage(100)].as_ref())
38        .split(container);
39    let config_container = chunks[0];
40
41    f.set_cursor(
42        // Put cursor past the end of the input text
43        config_container.x + app.get_query().len() as u16 + 1,
44        // Move one line down, from the border to the input line
45        config_container.y + query_offset(&app.query_field),
46    );
47
48    render_config(f, &app, config_container);
49}
50
51pub fn render_config<B>(f: &mut Frame<B>, app: &App, container: Rect)
52where
53    B: Backend,
54{
55    let chunks = Layout::default()
56        .direction(Direction::Vertical)
57        .constraints(
58            [
59                Constraint::Length(1),
60                Constraint::Length(3),
61                Constraint::Length(3),
62                Constraint::Length(3),
63                Constraint::Length(3),
64                Constraint::Length(3),
65                Constraint::Length(3),
66                Constraint::Length(10),
67                Constraint::Length(10),
68            ]
69            .as_ref(),
70        )
71        .split(container);
72
73    let item_type_container = chunks[0];
74    let genre_query_container = chunks[1];
75    let title_query_container = chunks[2];
76    let cast_query_container = chunks[3];
77    let country_query_container = chunks[4];
78    let language_query_container = chunks[5];
79    let plot_query_container = chunks[6];
80    let keyboard_shortcuts_container = chunks[7];
81    let queries_container = chunks[8];
82
83    let item_type_ui = render_item_type(&app.item_type);
84    f.render_widget(item_type_ui, item_type_container);
85
86    let genre_query_ui = render_query(
87        "Genre",
88        &app.genre_query,
89        &app.query_field == &QueryField::Genre,
90    );
91    f.render_widget(genre_query_ui, genre_query_container);
92
93    let title_query_ui = render_query(
94        "Title",
95        &app.title_query,
96        &app.query_field == &QueryField::Title,
97    );
98    f.render_widget(title_query_ui, title_query_container);
99
100    let cast_query_ui = render_query(
101        "Cast",
102        &app.cast_query,
103        &app.query_field == &QueryField::Cast,
104    );
105    f.render_widget(cast_query_ui, cast_query_container);
106
107    let country_query_ui = render_query(
108        "Country",
109        &app.country_query,
110        &app.query_field == &QueryField::Country,
111    );
112    f.render_widget(country_query_ui, country_query_container);
113
114    let language_query_ui = render_query(
115        "Language",
116        &app.language_query,
117        &app.query_field == &QueryField::Language,
118    );
119    f.render_widget(language_query_ui, language_query_container);
120
121    let plot_query_ui = render_query(
122        "Plot",
123        &app.plot_query,
124        &app.query_field == &QueryField::Plot,
125    );
126    f.render_widget(plot_query_ui, plot_query_container);
127
128    render_keyboard_shortcuts(f, keyboard_shortcuts_container);
129    render_queries_legend(f, queries_container);
130}
131
132fn render_keyboard_shortcuts<B>(f: &mut Frame<B>, container: Rect)
133where
134    B: Backend,
135{
136    let shortcut_style = Style::default().fg(Color::Gray);
137    let table_style = Style::default().fg(Color::DarkGray);
138
139    let header = ["Shortcut", "Description"];
140    let items = vec![
141        vec!["<Tab>", "Cycle Filters"],
142        vec!["↓/↑ | Ctrl-N/Ctrl-P", "Select Next/Previous Show"],
143        vec!["Ctrl-D/Ctrl-U", "Select Next/Previous Page"],
144        vec!["Ctrl-E", "Erase all Filters"],
145        vec!["Ctrl-O", "Change Type of Show"],
146    ];
147
148    let rows = items
149        .iter()
150        .map(|i| Row::StyledData(i.iter(), shortcut_style));
151    let t = Table::new(header.iter(), rows)
152        .style(table_style)
153        .block(Block::default().borders(Borders::ALL))
154        .widths(&[Constraint::Percentage(30), Constraint::Percentage(70)]);
155
156    f.render_widget(t, container);
157}
158
159fn render_queries_legend<B>(f: &mut Frame<B>, container: Rect)
160where
161    B: Backend,
162{
163    let shortcut_style = Style::default().fg(Color::Gray);
164    let table_style = Style::default().fg(Color::DarkGray);
165
166    let header = ["Query", "Description"];
167    let items = vec![
168        vec![
169            "drama adventure",
170            "Match all that has 'drama' or 'adventure'",
171        ],
172        vec!["!comedy", "Match all that does not have 'comedy'"],
173    ];
174
175    let rows = items
176        .iter()
177        .map(|i| Row::StyledData(i.iter(), shortcut_style));
178    let t = Table::new(header.iter(), rows)
179        .style(table_style)
180        .block(Block::default().borders(Borders::ALL))
181        .widths(&[Constraint::Percentage(30), Constraint::Percentage(70)]);
182
183    f.render_widget(t, container);
184}
185
186fn render_item_type(item_type: &ItemType) -> Paragraph {
187    let value = match item_type {
188        ItemType::Movie => "Movies",
189        ItemType::Series => "Series",
190        ItemType::Both => "Movies and Series",
191    };
192    let value_style = Style::default().fg(Color::LightBlue);
193    let value_span = Span::styled(value, value_style);
194
195    Paragraph::new(value_span)
196}
197
198fn render_query<'a>(label: &'a str, query: &'a str, selected: bool) -> Paragraph<'a> {
199    let label_style = Style::default().fg(Color::Gray);
200    let border_style = if selected {
201        Style::default().fg(Color::Yellow)
202    } else {
203        Style::default().fg(Color::White)
204    };
205
206    let query_style = Style::default().fg(Color::White);
207    let query_span = Span::styled(query, query_style);
208
209    Paragraph::new(query_span).block(
210        Block::default()
211            .borders(Borders::ALL)
212            .border_style(border_style)
213            .border_type(BorderType::Rounded)
214            .style(label_style)
215            .title(label),
216    )
217}