mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 11:35:25 +00:00
top menus
This commit is contained in:
parent
4bec64e245
commit
9eea37a5b5
@ -4,9 +4,7 @@ down = "SelectNextEntry"
|
|||||||
up = "SelectPrevEntry"
|
up = "SelectPrevEntry"
|
||||||
ctrl-n = "SelectNextEntry"
|
ctrl-n = "SelectNextEntry"
|
||||||
ctrl-p = "SelectPrevEntry"
|
ctrl-p = "SelectPrevEntry"
|
||||||
alt-down = "ScrollPreviewHalfPageDown"
|
|
||||||
ctrl-d = "ScrollPreviewHalfPageDown"
|
ctrl-d = "ScrollPreviewHalfPageDown"
|
||||||
alt-up = "ScrollPreviewHalfPageUp"
|
|
||||||
ctrl-u = "ScrollPreviewHalfPageUp"
|
ctrl-u = "ScrollPreviewHalfPageUp"
|
||||||
enter = "SelectEntry"
|
enter = "SelectEntry"
|
||||||
ctrl-enter = "SendToChannel"
|
ctrl-enter = "SendToChannel"
|
||||||
@ -20,4 +18,3 @@ ctrl-n = "SelectNextEntry"
|
|||||||
ctrl-p = "SelectPrevEntry"
|
ctrl-p = "SelectPrevEntry"
|
||||||
enter = "SelectEntry"
|
enter = "SelectEntry"
|
||||||
ctrl-s = "ToggleChannelSelection"
|
ctrl-s = "ToggleChannelSelection"
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
_______________
|
_______________
|
||||||
|,----------. |\
|
|,----------. |\
|
||||||
|| |=| |
|
|| |=| |
|
||||||
|| || | |
|
|| | | |
|
||||||
|| . _o| | |
|
|| |o| |
|
||||||
|`-----------' |/
|
|`-----------' |/
|
||||||
~~~~~~~~~~~~~~~
|
`--------------'
|
||||||
__ __ _ _
|
__ __ _ _
|
||||||
/ /____ / /__ _ __(_)__ (_)__ ___
|
/ /____ / /__ _ __(_)__ (_)__ ___
|
||||||
/ __/ -_) / -_) |/ / (_-</ / _ \/ _ \
|
/ __/ -_) / -_) |/ / (_-</ / _ \/ _ \
|
||||||
\__/\__/_/\__/|___/_/___/_/\___/_//_/
|
\__/\__/_/\__/|___/_/___/_/\___/_//_/
|
||||||
|
|
||||||
|
@ -14,12 +14,15 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
use strum::Display;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use crate::ui::input::Input;
|
|
||||||
use crate::ui::layout::{Dimensions, Layout};
|
|
||||||
use crate::ui::preview::DEFAULT_PREVIEW_TITLE_FG;
|
use crate::ui::preview::DEFAULT_PREVIEW_TITLE_FG;
|
||||||
use crate::ui::results::build_results_list;
|
use crate::ui::results::build_results_list;
|
||||||
|
use crate::ui::{
|
||||||
|
layout::{Dimensions, Layout},
|
||||||
|
logo::build_logo_paragraph,
|
||||||
|
};
|
||||||
use crate::utils::strings::EMPTY_STRING;
|
use crate::utils::strings::EMPTY_STRING;
|
||||||
use crate::{action::Action, config::Config};
|
use crate::{action::Action, config::Config};
|
||||||
use crate::{channels::tv_guide::TvGuide, ui::get_border_style};
|
use crate::{channels::tv_guide::TvGuide, ui::get_border_style};
|
||||||
@ -27,13 +30,16 @@ use crate::{channels::OnAir, utils::strings::shrink_with_ellipsis};
|
|||||||
use crate::{
|
use crate::{
|
||||||
channels::TelevisionChannel, ui::input::actions::InputActionHandler,
|
channels::TelevisionChannel, ui::input::actions::InputActionHandler,
|
||||||
};
|
};
|
||||||
|
use crate::{channels::UnitChannel, ui::input::Input};
|
||||||
use crate::{
|
use crate::{
|
||||||
entry::{Entry, ENTRY_PLACEHOLDER},
|
entry::{Entry, ENTRY_PLACEHOLDER},
|
||||||
ui::spinner::Spinner,
|
ui::spinner::Spinner,
|
||||||
};
|
};
|
||||||
use crate::{previewers::Previewer, ui::spinner::SpinnerState};
|
use crate::{previewers::Previewer, ui::spinner::SpinnerState};
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Hash, Eq, Debug, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
PartialEq, Copy, Clone, Hash, Eq, Debug, Serialize, Deserialize, Display,
|
||||||
|
)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
Channel,
|
Channel,
|
||||||
Guide,
|
Guide,
|
||||||
@ -98,6 +104,10 @@ impl Television {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_channel(&self) -> UnitChannel {
|
||||||
|
UnitChannel::from(&self.channel)
|
||||||
|
}
|
||||||
|
|
||||||
/// FIXME: this needs rework
|
/// FIXME: this needs rework
|
||||||
pub fn change_channel(&mut self, channel: TelevisionChannel) {
|
pub fn change_channel(&mut self, channel: TelevisionChannel) {
|
||||||
self.reset_preview_scroll();
|
self.reset_preview_scroll();
|
||||||
@ -304,19 +314,17 @@ impl Television {
|
|||||||
Action::ScrollPreviewUp => self.scroll_preview_up(1),
|
Action::ScrollPreviewUp => self.scroll_preview_up(1),
|
||||||
Action::ScrollPreviewHalfPageDown => self.scroll_preview_down(20),
|
Action::ScrollPreviewHalfPageDown => self.scroll_preview_down(20),
|
||||||
Action::ScrollPreviewHalfPageUp => self.scroll_preview_up(20),
|
Action::ScrollPreviewHalfPageUp => self.scroll_preview_up(20),
|
||||||
Action::ToggleChannelSelection => {
|
Action::ToggleChannelSelection => match self.mode {
|
||||||
match self.mode {
|
Mode::Channel => {
|
||||||
Mode::Channel => {
|
self.reset_screen();
|
||||||
self.reset_screen();
|
self.mode = Mode::Guide;
|
||||||
self.mode = Mode::Guide;
|
|
||||||
}
|
|
||||||
Mode::Guide => {
|
|
||||||
self.reset_screen();
|
|
||||||
self.mode = Mode::Channel;
|
|
||||||
}
|
|
||||||
Mode::SendToChannel => {}
|
|
||||||
}
|
}
|
||||||
}
|
Mode::Guide => {
|
||||||
|
self.reset_screen();
|
||||||
|
self.mode = Mode::Channel;
|
||||||
|
}
|
||||||
|
Mode::SendToChannel => {}
|
||||||
|
},
|
||||||
Action::SelectEntry => {
|
Action::SelectEntry => {
|
||||||
if let Some(entry) = self.get_selected_entry() {
|
if let Some(entry) = self.get_selected_entry() {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
@ -387,19 +395,38 @@ impl Television {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let help_block = Block::default()
|
let metadata_block = Block::default()
|
||||||
.borders(Borders::NONE)
|
.borders(Borders::ALL)
|
||||||
|
.border_type(BorderType::Rounded)
|
||||||
|
.border_style(Style::default().fg(Color::Blue))
|
||||||
|
.padding(Padding::horizontal(1))
|
||||||
|
.style(Style::default());
|
||||||
|
|
||||||
|
let metadata_table = self.build_metadata_table().block(metadata_block);
|
||||||
|
|
||||||
|
f.render_widget(metadata_table, layout.help_bar_left);
|
||||||
|
|
||||||
|
let keymaps_block = Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_type(BorderType::Rounded)
|
||||||
|
.border_style(Style::default().fg(Color::Blue))
|
||||||
.style(Style::default())
|
.style(Style::default())
|
||||||
.padding(Padding::uniform(1));
|
.padding(Padding::horizontal(1));
|
||||||
|
|
||||||
let help_text = self
|
let keymaps_table = self.build_help_table()?.block(keymaps_block);
|
||||||
.build_help_paragraph()?
|
|
||||||
.style(Style::default().fg(Color::DarkGray).italic())
|
|
||||||
.alignment(Alignment::Center)
|
|
||||||
.wrap(Wrap { trim: true })
|
|
||||||
.block(help_block);
|
|
||||||
|
|
||||||
f.render_widget(help_text, layout.help_bar);
|
f.render_widget(keymaps_table, layout.help_bar_middle);
|
||||||
|
|
||||||
|
let logo_block = Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_type(BorderType::Rounded)
|
||||||
|
.border_style(Style::default().fg(Color::Blue))
|
||||||
|
.style(Style::default().fg(Color::Yellow))
|
||||||
|
.padding(Padding::horizontal(1));
|
||||||
|
|
||||||
|
let logo_paragraph = build_logo_paragraph().block(logo_block);
|
||||||
|
|
||||||
|
f.render_widget(logo_paragraph, layout.help_bar_right);
|
||||||
|
|
||||||
self.results_area_height = u32::from(layout.results.height);
|
self.results_area_height = u32::from(layout.results.height);
|
||||||
if let Some(preview_window) = layout.preview_window {
|
if let Some(preview_window) = layout.preview_window {
|
||||||
@ -489,7 +516,7 @@ impl Television {
|
|||||||
"> ",
|
"> ",
|
||||||
Style::default().fg(DEFAULT_INPUT_FG).bold(),
|
Style::default().fg(DEFAULT_INPUT_FG).bold(),
|
||||||
))
|
))
|
||||||
.block(arrow_block);
|
.block(arrow_block);
|
||||||
f.render_widget(arrow, inner_input_chunks[0]);
|
f.render_widget(arrow, inner_input_chunks[0]);
|
||||||
|
|
||||||
let interactive_input_block = Block::default();
|
let interactive_input_block = Block::default();
|
||||||
@ -527,8 +554,8 @@ impl Television {
|
|||||||
),
|
),
|
||||||
Style::default().fg(DEFAULT_RESULTS_COUNT_FG).italic(),
|
Style::default().fg(DEFAULT_RESULTS_COUNT_FG).italic(),
|
||||||
))
|
))
|
||||||
.block(result_count_block)
|
.block(result_count_block)
|
||||||
.alignment(Alignment::Right);
|
.alignment(Alignment::Right);
|
||||||
f.render_widget(result_count_paragraph, inner_input_chunks[2]);
|
f.render_widget(result_count_paragraph, inner_input_chunks[2]);
|
||||||
|
|
||||||
// Make the cursor visible and ask tui-rs to put it at the
|
// Make the cursor visible and ask tui-rs to put it at the
|
||||||
@ -537,8 +564,8 @@ impl Television {
|
|||||||
// Put cursor past the end of the input text
|
// Put cursor past the end of the input text
|
||||||
inner_input_chunks[1].x
|
inner_input_chunks[1].x
|
||||||
+ u16::try_from(
|
+ u16::try_from(
|
||||||
self.input.visual_cursor().max(scroll) - scroll,
|
self.input.visual_cursor().max(scroll) - scroll,
|
||||||
)?,
|
)?,
|
||||||
// Move one line down, from the border to the input line
|
// Move one line down, from the border to the input line
|
||||||
inner_input_chunks[1].y,
|
inner_input_chunks[1].y,
|
||||||
));
|
));
|
||||||
|
@ -3,6 +3,8 @@ use ratatui::style::{Color, Style, Stylize};
|
|||||||
pub mod help;
|
pub mod help;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
pub mod logo;
|
||||||
|
pub mod metadata;
|
||||||
pub mod preview;
|
pub mod preview;
|
||||||
pub mod results;
|
pub mod results;
|
||||||
pub mod spinner;
|
pub mod spinner;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use color_eyre::eyre::{OptionExt, Result};
|
use color_eyre::eyre::{OptionExt, Result};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
layout::Constraint,
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::Paragraph,
|
widgets::{Cell, Row, Table},
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -12,142 +13,118 @@ use crate::{
|
|||||||
television::{Mode, Television},
|
television::{Mode, Television},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SEPARATOR: &str = " ";
|
|
||||||
const ACTION_COLOR: Color = Color::DarkGray;
|
const ACTION_COLOR: Color = Color::DarkGray;
|
||||||
const KEY_COLOR: Color = Color::LightYellow;
|
const KEY_COLOR: Color = Color::LightYellow;
|
||||||
|
|
||||||
impl Television {
|
impl Television {
|
||||||
pub fn build_help_paragraph<'a>(&self) -> Result<Paragraph<'a>> {
|
pub fn build_help_table<'a>(&self) -> Result<Table<'a>> {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Channel => self.build_help_paragraph_for_channel(),
|
Mode::Channel => self.build_help_table_for_channel(),
|
||||||
Mode::Guide => self.build_help_paragraph_for_channel_selection(),
|
Mode::Guide => self.build_help_table_for_channel_selection(),
|
||||||
Mode::SendToChannel => self.build_help_paragraph_for_channel(),
|
Mode::SendToChannel => self.build_help_table_for_channel(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_help_paragraph_for_channel<'a>(&self) -> Result<Paragraph<'a>> {
|
fn build_help_table_for_channel<'a>(&self) -> Result<Table<'a>> {
|
||||||
let keymap = self.keymap_for_mode()?;
|
let keymap = self.keymap_for_mode()?;
|
||||||
let mut lines = Vec::new();
|
|
||||||
|
|
||||||
// NAVIGATION and SELECTION line
|
|
||||||
let mut ns_line = Line::default();
|
|
||||||
|
|
||||||
// Results navigation
|
// Results navigation
|
||||||
let prev = keys_for_action(keymap, Action::SelectPrevEntry);
|
let prev = keys_for_action(keymap, &Action::SelectPrevEntry);
|
||||||
let next = keys_for_action(keymap, Action::SelectNextEntry);
|
let next = keys_for_action(keymap, &Action::SelectNextEntry);
|
||||||
let results_spans =
|
let results_row = Row::new(build_cells_for_key_groups(
|
||||||
build_spans_for_key_groups("↕ Results", vec![prev, next]);
|
"↕ Results navigation",
|
||||||
|
vec![prev, next],
|
||||||
ns_line.extend(results_spans);
|
));
|
||||||
ns_line.push_span(Span::styled(SEPARATOR, Style::default()));
|
|
||||||
|
|
||||||
// Preview navigation
|
// Preview navigation
|
||||||
let up_keys = keys_for_action(keymap, Action::ScrollPreviewHalfPageUp);
|
let up_keys =
|
||||||
|
keys_for_action(keymap, &Action::ScrollPreviewHalfPageUp);
|
||||||
let down_keys =
|
let down_keys =
|
||||||
keys_for_action(keymap, Action::ScrollPreviewHalfPageDown);
|
keys_for_action(keymap, &Action::ScrollPreviewHalfPageDown);
|
||||||
let preview_spans =
|
let preview_row = Row::new(build_cells_for_key_groups(
|
||||||
build_spans_for_key_groups("↕ Preview", vec![up_keys, down_keys]);
|
"↕ Preview navigation",
|
||||||
|
vec![up_keys, down_keys],
|
||||||
ns_line.extend(preview_spans);
|
));
|
||||||
ns_line.push_span(Span::styled(SEPARATOR, Style::default()));
|
|
||||||
|
|
||||||
// Select entry
|
// Select entry
|
||||||
let select_entry_keys = keys_for_action(keymap, Action::SelectEntry);
|
let select_entry_keys = keys_for_action(keymap, &Action::SelectEntry);
|
||||||
let select_entry_spans = build_spans_for_key_groups(
|
let select_entry_row = Row::new(build_cells_for_key_groups(
|
||||||
"Select entry",
|
"✓ Select entry",
|
||||||
vec![select_entry_keys],
|
vec![select_entry_keys],
|
||||||
);
|
));
|
||||||
|
|
||||||
ns_line.extend(select_entry_spans);
|
|
||||||
ns_line.push_span(Span::styled(SEPARATOR, Style::default()));
|
|
||||||
|
|
||||||
// Send to channel
|
// Send to channel
|
||||||
let send_to_channel_keys =
|
let send_to_channel_keys =
|
||||||
keys_for_action(keymap, Action::SendToChannel);
|
keys_for_action(keymap, &Action::SendToChannel);
|
||||||
// TODO: add send icon
|
let send_to_channel_row = Row::new(build_cells_for_key_groups(
|
||||||
let send_to_channel_spans =
|
"⇉ Send results to",
|
||||||
build_spans_for_key_groups("Send to", vec![send_to_channel_keys]);
|
vec![send_to_channel_keys],
|
||||||
|
));
|
||||||
ns_line.extend(send_to_channel_spans);
|
|
||||||
ns_line.push_span(Span::styled(SEPARATOR, Style::default()));
|
|
||||||
|
|
||||||
// Switch channels
|
// Switch channels
|
||||||
let switch_channels_keys =
|
let switch_channels_keys =
|
||||||
keys_for_action(keymap, Action::ToggleChannelSelection);
|
keys_for_action(keymap, &Action::ToggleChannelSelection);
|
||||||
let switch_channels_spans = build_spans_for_key_groups(
|
let switch_channels_row = Row::new(build_cells_for_key_groups(
|
||||||
"Switch channels",
|
"⨀ Switch channels",
|
||||||
vec![switch_channels_keys],
|
vec![switch_channels_keys],
|
||||||
);
|
));
|
||||||
|
|
||||||
ns_line.extend(switch_channels_spans);
|
|
||||||
lines.push(ns_line);
|
|
||||||
|
|
||||||
// MISC line (quit, help, etc.)
|
// MISC line (quit, help, etc.)
|
||||||
// let mut misc_line = Line::default();
|
// Quit ⏼
|
||||||
//
|
let quit_keys = keys_for_action(keymap, &Action::Quit);
|
||||||
// // Quit
|
let quit_row =
|
||||||
// let quit_keys = keys_for_action(keymap, Action::Quit);
|
Row::new(build_cells_for_key_groups("⏼ Quit", vec![quit_keys]));
|
||||||
// let quit_spans = build_spans_for_key_groups("Quit", vec![quit_keys]);
|
|
||||||
//
|
|
||||||
// misc_line.extend(quit_spans);
|
|
||||||
//
|
|
||||||
// lines.push(misc_line);
|
|
||||||
|
|
||||||
Ok(Paragraph::new(lines))
|
let widths = vec![Constraint::Fill(1), Constraint::Fill(2)];
|
||||||
|
|
||||||
|
Ok(Table::new(
|
||||||
|
vec![
|
||||||
|
results_row,
|
||||||
|
preview_row,
|
||||||
|
select_entry_row,
|
||||||
|
send_to_channel_row,
|
||||||
|
switch_channels_row,
|
||||||
|
quit_row,
|
||||||
|
],
|
||||||
|
widths,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_help_paragraph_for_channel_selection<'a>(
|
fn build_help_table_for_channel_selection<'a>(&self) -> Result<Table<'a>> {
|
||||||
&self,
|
|
||||||
) -> Result<Paragraph<'a>> {
|
|
||||||
let keymap = self.keymap_for_mode()?;
|
let keymap = self.keymap_for_mode()?;
|
||||||
let mut lines = Vec::new();
|
|
||||||
|
|
||||||
// NAVIGATION + SELECTION line
|
|
||||||
let mut ns_line = Line::default();
|
|
||||||
|
|
||||||
// Results navigation
|
// Results navigation
|
||||||
let prev = keys_for_action(keymap, Action::SelectPrevEntry);
|
let prev = keys_for_action(keymap, &Action::SelectPrevEntry);
|
||||||
let next = keys_for_action(keymap, Action::SelectNextEntry);
|
let next = keys_for_action(keymap, &Action::SelectNextEntry);
|
||||||
let results_spans =
|
let results_row = Row::new(build_cells_for_key_groups(
|
||||||
build_spans_for_key_groups("↕ Results", vec![prev, next]);
|
"↕ Results",
|
||||||
|
vec![prev, next],
|
||||||
ns_line.extend(results_spans);
|
));
|
||||||
ns_line.push_span(Span::styled(SEPARATOR, Style::default()));
|
|
||||||
|
|
||||||
// Select entry
|
// Select entry
|
||||||
let select_entry_keys = keys_for_action(keymap, Action::SelectEntry);
|
let select_entry_keys = keys_for_action(keymap, &Action::SelectEntry);
|
||||||
let select_entry_spans = build_spans_for_key_groups(
|
let select_entry_row = Row::new(build_cells_for_key_groups(
|
||||||
"Select entry",
|
"Select entry",
|
||||||
vec![select_entry_keys],
|
vec![select_entry_keys],
|
||||||
);
|
));
|
||||||
|
|
||||||
ns_line.extend(select_entry_spans);
|
|
||||||
ns_line.push_span(Span::styled(SEPARATOR, Style::default()));
|
|
||||||
|
|
||||||
// Switch channels
|
// Switch channels
|
||||||
let switch_channels_keys =
|
let switch_channels_keys =
|
||||||
keys_for_action(keymap, Action::ToggleChannelSelection);
|
keys_for_action(keymap, &Action::ToggleChannelSelection);
|
||||||
let switch_channels_spans = build_spans_for_key_groups(
|
let switch_channels_row = Row::new(build_cells_for_key_groups(
|
||||||
"Switch channels",
|
"Switch channels",
|
||||||
vec![switch_channels_keys],
|
vec![switch_channels_keys],
|
||||||
);
|
));
|
||||||
|
|
||||||
ns_line.extend(switch_channels_spans);
|
|
||||||
|
|
||||||
lines.push(ns_line);
|
|
||||||
|
|
||||||
// MISC line (quit, help, etc.)
|
|
||||||
// let mut misc_line = Line::default();
|
|
||||||
|
|
||||||
// Quit
|
// Quit
|
||||||
// let quit_keys = keys_for_action(keymap, Action::Quit);
|
let quit_keys = keys_for_action(keymap, &Action::Quit);
|
||||||
// let quit_spans = build_spans_for_key_groups("Quit", vec![quit_keys]);
|
let quit_row =
|
||||||
|
Row::new(build_cells_for_key_groups("Quit", vec![quit_keys]));
|
||||||
|
|
||||||
// misc_line.extend(quit_spans);
|
Ok(Table::new(
|
||||||
|
vec![results_row, select_entry_row, switch_channels_row, quit_row],
|
||||||
// lines.push(misc_line);
|
vec![Constraint::Fill(1), Constraint::Fill(2)],
|
||||||
|
))
|
||||||
Ok(Paragraph::new(lines))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the keymap for the current mode.
|
/// Get the keymap for the current mode.
|
||||||
@ -189,21 +166,23 @@ impl Television {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(spans.len(), 5);
|
/// assert_eq!(spans.len(), 5);
|
||||||
/// ```
|
/// ```
|
||||||
fn build_spans_for_key_groups(
|
fn build_cells_for_key_groups(
|
||||||
group_name: &str,
|
group_name: &str,
|
||||||
key_groups: Vec<Vec<String>>,
|
key_groups: Vec<Vec<String>>,
|
||||||
) -> Vec<Span> {
|
) -> Vec<Cell> {
|
||||||
if key_groups.is_empty() || key_groups.iter().all(|keys| keys.is_empty()) {
|
if key_groups.is_empty() || key_groups.iter().all(std::vec::Vec::is_empty)
|
||||||
return vec![];
|
{
|
||||||
|
return vec![group_name.into(), "No keybindings".into()];
|
||||||
}
|
}
|
||||||
let non_empty_groups = key_groups.iter().filter(|keys| !keys.is_empty());
|
let non_empty_groups = key_groups.iter().filter(|keys| !keys.is_empty());
|
||||||
let mut spans = vec![
|
let mut cells = vec![Cell::from(Span::styled(
|
||||||
Span::styled(
|
group_name.to_owned() + ": ",
|
||||||
group_name.to_owned() + ": ",
|
Style::default().fg(ACTION_COLOR),
|
||||||
Style::default().fg(ACTION_COLOR),
|
))];
|
||||||
),
|
|
||||||
Span::styled("[", Style::default().fg(KEY_COLOR)),
|
let mut spans = Vec::new();
|
||||||
];
|
//spans.push(Span::styled("[", Style::default().fg(KEY_COLOR)));
|
||||||
|
|
||||||
let key_group_spans: Vec<Span> = non_empty_groups
|
let key_group_spans: Vec<Span> = non_empty_groups
|
||||||
.map(|keys| {
|
.map(|keys| {
|
||||||
let key_group = keys.join(", ");
|
let key_group = keys.join(", ");
|
||||||
@ -213,12 +192,14 @@ fn build_spans_for_key_groups(
|
|||||||
key_group_spans.iter().enumerate().for_each(|(i, span)| {
|
key_group_spans.iter().enumerate().for_each(|(i, span)| {
|
||||||
spans.push(span.clone());
|
spans.push(span.clone());
|
||||||
if i < key_group_spans.len() - 1 {
|
if i < key_group_spans.len() - 1 {
|
||||||
spans.push(Span::styled(" | ", Style::default().fg(KEY_COLOR)));
|
spans.push(Span::styled(" / ", Style::default().fg(KEY_COLOR)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
spans.push(Span::styled("]", Style::default().fg(KEY_COLOR)));
|
//spans.push(Span::styled("]", Style::default().fg(KEY_COLOR)));
|
||||||
spans
|
cells.push(Cell::from(Line::from(spans)));
|
||||||
|
|
||||||
|
cells
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the keys for a given action.
|
/// Get the keys for a given action.
|
||||||
@ -246,11 +227,11 @@ fn build_spans_for_key_groups(
|
|||||||
/// ```
|
/// ```
|
||||||
fn keys_for_action(
|
fn keys_for_action(
|
||||||
keymap: &HashMap<Key, Action>,
|
keymap: &HashMap<Key, Action>,
|
||||||
action: Action,
|
action: &Action,
|
||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
keymap
|
keymap
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_key, act)| **act == action)
|
.filter(|(_key, act)| *act == action)
|
||||||
.map(|(key, _act)| format!("{key}"))
|
.map(|(key, _act)| format!("{key}"))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ impl Default for Dimensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub help_bar: Rect,
|
pub help_bar_left: Rect,
|
||||||
|
pub help_bar_middle: Rect,
|
||||||
|
pub help_bar_right: Rect,
|
||||||
pub results: Rect,
|
pub results: Rect,
|
||||||
pub input: Rect,
|
pub input: Rect,
|
||||||
pub preview_title: Option<Rect>,
|
pub preview_title: Option<Rect>,
|
||||||
@ -28,14 +30,18 @@ pub struct Layout {
|
|||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
help_bar: Rect,
|
help_bar_left: Rect,
|
||||||
|
help_bar_middle: Rect,
|
||||||
|
help_bar_right: Rect,
|
||||||
results: Rect,
|
results: Rect,
|
||||||
input: Rect,
|
input: Rect,
|
||||||
preview_title: Option<Rect>,
|
preview_title: Option<Rect>,
|
||||||
preview_window: Option<Rect>,
|
preview_window: Option<Rect>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
help_bar,
|
help_bar_left,
|
||||||
|
help_bar_middle,
|
||||||
|
help_bar_right,
|
||||||
results,
|
results,
|
||||||
input,
|
input,
|
||||||
preview_title,
|
preview_title,
|
||||||
@ -52,9 +58,19 @@ impl Layout {
|
|||||||
// split the main block into two vertical chunks (help bar + rest)
|
// split the main block into two vertical chunks (help bar + rest)
|
||||||
let hz_chunks = layout::Layout::default()
|
let hz_chunks = layout::Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Fill(1), Constraint::Length(5)])
|
.constraints([Constraint::Length(9), Constraint::Fill(1)])
|
||||||
.split(main_block);
|
.split(main_block);
|
||||||
|
|
||||||
|
// split the help bar into three horizontal chunks (left + center + right)
|
||||||
|
let help_bar_chunks = layout::Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(24),
|
||||||
|
])
|
||||||
|
.split(hz_chunks[0]);
|
||||||
|
|
||||||
if with_preview {
|
if with_preview {
|
||||||
// split the main block into two vertical chunks
|
// split the main block into two vertical chunks
|
||||||
let vt_chunks = layout::Layout::default()
|
let vt_chunks = layout::Layout::default()
|
||||||
@ -63,7 +79,7 @@ impl Layout {
|
|||||||
Constraint::Percentage(50),
|
Constraint::Percentage(50),
|
||||||
Constraint::Percentage(50),
|
Constraint::Percentage(50),
|
||||||
])
|
])
|
||||||
.split(hz_chunks[0]);
|
.split(hz_chunks[1]);
|
||||||
|
|
||||||
// left block: results + input field
|
// left block: results + input field
|
||||||
let left_chunks = layout::Layout::default()
|
let left_chunks = layout::Layout::default()
|
||||||
@ -78,7 +94,9 @@ impl Layout {
|
|||||||
.split(vt_chunks[1]);
|
.split(vt_chunks[1]);
|
||||||
|
|
||||||
Self::new(
|
Self::new(
|
||||||
hz_chunks[1],
|
help_bar_chunks[0],
|
||||||
|
help_bar_chunks[1],
|
||||||
|
help_bar_chunks[2],
|
||||||
left_chunks[0],
|
left_chunks[0],
|
||||||
left_chunks[1],
|
left_chunks[1],
|
||||||
Some(right_chunks[0]),
|
Some(right_chunks[0]),
|
||||||
@ -89,9 +107,17 @@ impl Layout {
|
|||||||
let chunks = layout::Layout::default()
|
let chunks = layout::Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(10), Constraint::Length(3)])
|
.constraints([Constraint::Min(10), Constraint::Length(3)])
|
||||||
.split(hz_chunks[0]);
|
.split(hz_chunks[1]);
|
||||||
|
|
||||||
Self::new(hz_chunks[1], chunks[0], chunks[1], None, None)
|
Self::new(
|
||||||
|
help_bar_chunks[0],
|
||||||
|
help_bar_chunks[1],
|
||||||
|
help_bar_chunks[2],
|
||||||
|
chunks[0],
|
||||||
|
chunks[1],
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
crates/television/ui/logo.rs
Normal file
26
crates/television/ui/logo.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use ratatui::widgets::Paragraph;
|
||||||
|
|
||||||
|
//const LOGO: &str = r" _______________
|
||||||
|
// __ __ _ _ |,----------. |\
|
||||||
|
// / /____ / /__ _ __(_)__ (_)__ ___ || |=| |
|
||||||
|
// / __/ -_) / -_) |/ / (_-</ / _ \/ _ \ || | | |
|
||||||
|
// \__/\__/_/\__/|___/_/___/_/\___/_//_/ || |o| |
|
||||||
|
// |`-----------' |/
|
||||||
|
// `--------------'";
|
||||||
|
|
||||||
|
const LOGO: &str = r" _______________
|
||||||
|
|,----------. |\
|
||||||
|
|| |=| |
|
||||||
|
|| | | |
|
||||||
|
|| |o| |
|
||||||
|
|`-----------' |/
|
||||||
|
`--------------'";
|
||||||
|
|
||||||
|
pub fn build_logo_paragraph<'a>() -> Paragraph<'a> {
|
||||||
|
let lines = LOGO
|
||||||
|
.lines()
|
||||||
|
.map(std::convert::Into::into)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let logo_paragraph = Paragraph::new(lines);
|
||||||
|
logo_paragraph
|
||||||
|
}
|
113
crates/television/ui/metadata.rs
Normal file
113
crates/television/ui/metadata.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
use ratatui::{
|
||||||
|
layout::Constraint,
|
||||||
|
style::{Color, Style},
|
||||||
|
text::Span,
|
||||||
|
widgets::{Cell, Row, Table},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::television::Television;
|
||||||
|
|
||||||
|
// television 0.1.6
|
||||||
|
// target triple: aarch64-apple-darwin
|
||||||
|
// build: 1.82.0 (2024-10-24)
|
||||||
|
// current_channel: git_repos
|
||||||
|
// current_mode: channel
|
||||||
|
|
||||||
|
impl Television {
|
||||||
|
pub fn build_metadata_table<'a>(&self) -> Table<'a> {
|
||||||
|
let version_row = Row::new(vec![
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
"version: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let target_triple_row = Row::new(vec![
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
"target triple: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
env!("VERGEN_CARGO_TARGET_TRIPLE"),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let build_row = Row::new(vec![
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
"build: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
env!("VERGEN_RUSTC_SEMVER"),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
" (",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
env!("VERGEN_BUILD_DATE"),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
")",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let current_dir_row = Row::new(vec![
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
"current directory: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
std::env::current_dir()
|
||||||
|
.expect("Could not get current directory")
|
||||||
|
.display()
|
||||||
|
.to_string(),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let current_channel_row = Row::new(vec![
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
"current channel: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
self.current_channel().to_string(),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let current_mode_row = Row::new(vec![
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
"current mode: ",
|
||||||
|
Style::default().fg(Color::DarkGray),
|
||||||
|
)),
|
||||||
|
Cell::from(Span::styled(
|
||||||
|
self.mode.to_string(),
|
||||||
|
Style::default().fg(Color::LightYellow),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let widths = vec![Constraint::Fill(1), Constraint::Fill(2)];
|
||||||
|
|
||||||
|
Table::new(
|
||||||
|
vec![
|
||||||
|
version_row,
|
||||||
|
target_triple_row,
|
||||||
|
build_row,
|
||||||
|
current_dir_row,
|
||||||
|
current_channel_row,
|
||||||
|
current_mode_row,
|
||||||
|
],
|
||||||
|
widths,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -224,7 +224,7 @@ fn impl_unit_channel(ast: &syn::DeriveInput) -> TokenStream {
|
|||||||
|
|
||||||
// Generate a unit enum from the given enum
|
// Generate a unit enum from the given enum
|
||||||
let unit_enum = quote! {
|
let unit_enum = quote! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
|
||||||
pub enum UnitChannel {
|
pub enum UnitChannel {
|
||||||
#(
|
#(
|
||||||
#variant_names,
|
#variant_names,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user