mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-07 03:55:23 +00:00
feat(layout): allow reversing the layout and placing input bar on top (#76)
This commit is contained in:
parent
9998b9d9f8
commit
220671106e
@ -25,6 +25,8 @@ use_nerd_font_icons = false
|
|||||||
ui_scale = 100
|
ui_scale = 100
|
||||||
# Whether to show the top help bar in the UI
|
# Whether to show the top help bar in the UI
|
||||||
show_help_bar = true
|
show_help_bar = true
|
||||||
|
# Where to place the input bar in the UI (top or bottom)
|
||||||
|
input_bar_position = "bottom"
|
||||||
|
|
||||||
# Previewers settings
|
# Previewers settings
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
@ -59,8 +59,6 @@ impl Keymap {
|
|||||||
|
|
||||||
/// The main application struct that holds the state of the application.
|
/// The main application struct that holds the state of the application.
|
||||||
pub struct App {
|
pub struct App {
|
||||||
/// The configuration of the application.
|
|
||||||
config: Config,
|
|
||||||
keymap: Keymap,
|
keymap: Keymap,
|
||||||
// maybe move these two into config instead of passing them
|
// maybe move these two into config instead of passing them
|
||||||
// via the cli?
|
// via the cli?
|
||||||
@ -123,13 +121,12 @@ impl App {
|
|||||||
channel: TelevisionChannel,
|
channel: TelevisionChannel,
|
||||||
tick_rate: f64,
|
tick_rate: f64,
|
||||||
frame_rate: f64,
|
frame_rate: f64,
|
||||||
passthrough_keybindings: Vec<String>,
|
passthrough_keybindings: &[String],
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||||
let (render_tx, _) = mpsc::unbounded_channel();
|
let (render_tx, _) = mpsc::unbounded_channel();
|
||||||
let (_, event_rx) = mpsc::unbounded_channel();
|
let (_, event_rx) = mpsc::unbounded_channel();
|
||||||
let (event_abort_tx, _) = mpsc::unbounded_channel();
|
let (event_abort_tx, _) = mpsc::unbounded_channel();
|
||||||
let television = Arc::new(Mutex::new(Television::new(channel)));
|
|
||||||
let config = Config::new()?;
|
let config = Config::new()?;
|
||||||
let keymap = Keymap::from(&config.keybindings).with_mode_mappings(
|
let keymap = Keymap::from(&config.keybindings).with_mode_mappings(
|
||||||
Mode::Channel,
|
Mode::Channel,
|
||||||
@ -142,9 +139,10 @@ impl App {
|
|||||||
.collect(),
|
.collect(),
|
||||||
)?;
|
)?;
|
||||||
debug!("{:?}", keymap);
|
debug!("{:?}", keymap);
|
||||||
|
let television =
|
||||||
|
Arc::new(Mutex::new(Television::new(channel, config.clone())));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
config,
|
|
||||||
keymap,
|
keymap,
|
||||||
tick_rate,
|
tick_rate,
|
||||||
frame_rate,
|
frame_rate,
|
||||||
@ -184,14 +182,12 @@ impl App {
|
|||||||
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
||||||
self.render_tx = render_tx.clone();
|
self.render_tx = render_tx.clone();
|
||||||
let action_tx_r = self.action_tx.clone();
|
let action_tx_r = self.action_tx.clone();
|
||||||
let config_r = self.config.clone();
|
|
||||||
let television_r = self.television.clone();
|
let television_r = self.television.clone();
|
||||||
let frame_rate = self.frame_rate;
|
let frame_rate = self.frame_rate;
|
||||||
let rendering_task = tokio::spawn(async move {
|
let rendering_task = tokio::spawn(async move {
|
||||||
render(
|
render(
|
||||||
render_rx,
|
render_rx,
|
||||||
action_tx_r,
|
action_tx_r,
|
||||||
config_r,
|
|
||||||
television_r,
|
television_r,
|
||||||
frame_rate,
|
frame_rate,
|
||||||
is_output_tty,
|
is_output_tty,
|
||||||
|
@ -2,6 +2,8 @@ use config::ValueKind;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::ui::layout::InputPosition;
|
||||||
|
|
||||||
const DEFAULT_UI_SCALE: u16 = 90;
|
const DEFAULT_UI_SCALE: u16 = 90;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
@ -9,6 +11,7 @@ pub struct UiConfig {
|
|||||||
pub use_nerd_font_icons: bool,
|
pub use_nerd_font_icons: bool,
|
||||||
pub ui_scale: u16,
|
pub ui_scale: u16,
|
||||||
pub show_help_bar: bool,
|
pub show_help_bar: bool,
|
||||||
|
pub input_bar_position: InputPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UiConfig {
|
impl Default for UiConfig {
|
||||||
@ -17,6 +20,7 @@ impl Default for UiConfig {
|
|||||||
use_nerd_font_icons: false,
|
use_nerd_font_icons: false,
|
||||||
ui_scale: DEFAULT_UI_SCALE,
|
ui_scale: DEFAULT_UI_SCALE,
|
||||||
show_help_bar: true,
|
show_help_bar: true,
|
||||||
|
input_bar_position: InputPosition::Bottom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,6 +40,10 @@ impl From<UiConfig> for ValueKind {
|
|||||||
String::from("show_help_bar"),
|
String::from("show_help_bar"),
|
||||||
ValueKind::Boolean(val.show_help_bar).into(),
|
ValueKind::Boolean(val.show_help_bar).into(),
|
||||||
);
|
);
|
||||||
|
m.insert(
|
||||||
|
String::from("input_position"),
|
||||||
|
ValueKind::String(val.input_bar_position.to_string()).into(),
|
||||||
|
);
|
||||||
ValueKind::Table(m)
|
ValueKind::Table(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ async fn main() -> Result<()> {
|
|||||||
},
|
},
|
||||||
args.tick_rate,
|
args.tick_rate,
|
||||||
args.frame_rate,
|
args.frame_rate,
|
||||||
args.passthrough_keybindings,
|
&args.passthrough_keybindings,
|
||||||
) {
|
) {
|
||||||
Ok(mut app) => {
|
Ok(mut app) => {
|
||||||
stdout().flush()?;
|
stdout().flush()?;
|
||||||
|
@ -12,7 +12,7 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::television::Television;
|
use crate::television::Television;
|
||||||
use crate::{action::Action, config::Config, tui::Tui};
|
use crate::{action::Action, tui::Tui};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RenderingTask {
|
pub enum RenderingTask {
|
||||||
@ -42,7 +42,6 @@ impl IoStream {
|
|||||||
pub async fn render(
|
pub async fn render(
|
||||||
mut render_rx: mpsc::UnboundedReceiver<RenderingTask>,
|
mut render_rx: mpsc::UnboundedReceiver<RenderingTask>,
|
||||||
action_tx: mpsc::UnboundedSender<Action>,
|
action_tx: mpsc::UnboundedSender<Action>,
|
||||||
config: Config,
|
|
||||||
television: Arc<Mutex<Television>>,
|
television: Arc<Mutex<Television>>,
|
||||||
frame_rate: f64,
|
frame_rate: f64,
|
||||||
is_output_tty: bool,
|
is_output_tty: bool,
|
||||||
@ -59,15 +58,11 @@ pub async fn render(
|
|||||||
debug!("Entering tui");
|
debug!("Entering tui");
|
||||||
tui.enter()?;
|
tui.enter()?;
|
||||||
|
|
||||||
debug!("Registering action handler and config handler");
|
debug!("Registering action handler");
|
||||||
television
|
television
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.register_action_handler(action_tx.clone())?;
|
.register_action_handler(action_tx.clone())?;
|
||||||
television
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.register_config_handler(config.clone())?;
|
|
||||||
|
|
||||||
// Rendering loop
|
// Rendering loop
|
||||||
loop {
|
loop {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::app::Keymap;
|
use crate::app::Keymap;
|
||||||
use crate::picker::Picker;
|
use crate::picker::Picker;
|
||||||
use crate::ui::input::actions::InputActionHandler;
|
use crate::ui::input::actions::InputActionHandler;
|
||||||
use crate::ui::layout::{Dimensions, Layout};
|
use crate::ui::layout::{Dimensions, InputPosition, Layout};
|
||||||
use crate::ui::spinner::Spinner;
|
use crate::ui::spinner::Spinner;
|
||||||
use crate::ui::spinner::SpinnerState;
|
use crate::ui::spinner::SpinnerState;
|
||||||
use crate::{action::Action, config::Config};
|
use crate::{action::Action, config::Config};
|
||||||
@ -14,7 +14,6 @@ use television_channels::channels::{
|
|||||||
remote_control::RemoteControl, OnAir, TelevisionChannel, UnitChannel,
|
remote_control::RemoteControl, OnAir, TelevisionChannel, UnitChannel,
|
||||||
};
|
};
|
||||||
use television_channels::entry::{Entry, ENTRY_PLACEHOLDER};
|
use television_channels::entry::{Entry, ENTRY_PLACEHOLDER};
|
||||||
use television_previewers::previewers;
|
|
||||||
use television_previewers::previewers::Previewer;
|
use television_previewers::previewers::Previewer;
|
||||||
use television_utils::strings::EMPTY_STRING;
|
use television_utils::strings::EMPTY_STRING;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
@ -54,23 +53,30 @@ pub struct Television {
|
|||||||
|
|
||||||
impl Television {
|
impl Television {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(mut channel: TelevisionChannel) -> Self {
|
pub fn new(mut channel: TelevisionChannel, config: Config) -> Self {
|
||||||
|
let results_picker = match config.ui.input_bar_position {
|
||||||
|
InputPosition::Bottom => Picker::default().inverted(),
|
||||||
|
InputPosition::Top => Picker::default(),
|
||||||
|
};
|
||||||
|
let previewer = Previewer::new(Some(config.previewers.clone().into()));
|
||||||
|
let keymap = Keymap::from(&config.keybindings);
|
||||||
|
|
||||||
channel.find(EMPTY_STRING);
|
channel.find(EMPTY_STRING);
|
||||||
let spinner = Spinner::default();
|
let spinner = Spinner::default();
|
||||||
Self {
|
Self {
|
||||||
action_tx: None,
|
action_tx: None,
|
||||||
config: Config::default(),
|
config,
|
||||||
keymap: Keymap::default(),
|
keymap,
|
||||||
channel,
|
channel,
|
||||||
remote_control: TelevisionChannel::RemoteControl(
|
remote_control: TelevisionChannel::RemoteControl(
|
||||||
RemoteControl::default(),
|
RemoteControl::default(),
|
||||||
),
|
),
|
||||||
mode: Mode::Channel,
|
mode: Mode::Channel,
|
||||||
current_pattern: EMPTY_STRING.to_string(),
|
current_pattern: EMPTY_STRING.to_string(),
|
||||||
results_picker: Picker::default().inverted(),
|
results_picker,
|
||||||
rc_picker: Picker::default(),
|
rc_picker: Picker::default(),
|
||||||
results_area_height: 0,
|
results_area_height: 0,
|
||||||
previewer: Previewer::default(),
|
previewer,
|
||||||
preview_scroll: None,
|
preview_scroll: None,
|
||||||
preview_pane_height: 0,
|
preview_pane_height: 0,
|
||||||
current_preview_total_lines: 0,
|
current_preview_total_lines: 0,
|
||||||
@ -220,24 +226,6 @@ impl Television {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a configuration handler that provides configuration settings if necessary.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `config` - Configuration settings.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// * `Result<()>` - An Ok result or an error.
|
|
||||||
pub fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
|
||||||
self.config = config;
|
|
||||||
self.keymap = Keymap::from(&self.config.keybindings);
|
|
||||||
let previewer_config =
|
|
||||||
std::convert::Into::<previewers::PreviewerConfig>::into(
|
|
||||||
self.config.previewers.clone(),
|
|
||||||
);
|
|
||||||
self.previewer.set_config(previewer_config);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the state of the component based on a received action.
|
/// Update the state of the component based on a received action.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -388,33 +376,34 @@ impl Television {
|
|||||||
area,
|
area,
|
||||||
!matches!(self.mode, Mode::Channel),
|
!matches!(self.mode, Mode::Channel),
|
||||||
self.config.ui.show_help_bar,
|
self.config.ui.show_help_bar,
|
||||||
|
self.config.ui.input_bar_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
// help bar (metadata, keymaps, logo)
|
// help bar (metadata, keymaps, logo)
|
||||||
self.draw_help_bar(f, &layout)?;
|
self.draw_help_bar(f, &layout.help_bar)?;
|
||||||
|
|
||||||
self.results_area_height = u32::from(layout.results.height - 2); // 2 for the borders
|
self.results_area_height = u32::from(layout.results.height - 2); // 2 for the borders
|
||||||
self.preview_pane_height = layout.preview_window.height;
|
self.preview_pane_height = layout.preview_window.height;
|
||||||
|
|
||||||
// top left block: results
|
// results list
|
||||||
self.draw_results_list(f, &layout)?;
|
self.draw_results_list(f, layout.results)?;
|
||||||
|
|
||||||
// bottom left block: input
|
// input box
|
||||||
self.draw_input_box(f, &layout)?;
|
self.draw_input_box(f, layout.input)?;
|
||||||
|
|
||||||
let selected_entry = self
|
let selected_entry = self
|
||||||
.get_selected_entry(Some(Mode::Channel))
|
.get_selected_entry(Some(Mode::Channel))
|
||||||
.unwrap_or(ENTRY_PLACEHOLDER);
|
.unwrap_or(ENTRY_PLACEHOLDER);
|
||||||
let preview = self.previewer.preview(&selected_entry);
|
let preview = self.previewer.preview(&selected_entry);
|
||||||
|
|
||||||
// top right block: preview title
|
// preview title
|
||||||
self.current_preview_total_lines = preview.total_lines();
|
self.current_preview_total_lines = preview.total_lines();
|
||||||
self.draw_preview_title_block(f, &layout, &preview)?;
|
self.draw_preview_title_block(f, layout.preview_title, &preview)?;
|
||||||
|
|
||||||
// bottom right block: preview content
|
// preview content
|
||||||
self.draw_preview_content_block(
|
self.draw_preview_content_block(
|
||||||
f,
|
f,
|
||||||
&layout,
|
layout.preview_window,
|
||||||
selected_entry
|
selected_entry
|
||||||
.line_number
|
.line_number
|
||||||
.map(|l| u16::try_from(l).unwrap_or(0)),
|
.map(|l| u16::try_from(l).unwrap_or(0)),
|
||||||
@ -423,7 +412,7 @@ impl Television {
|
|||||||
|
|
||||||
// remote control
|
// remote control
|
||||||
if matches!(self.mode, Mode::RemoteControl | Mode::SendToChannel) {
|
if matches!(self.mode, Mode::RemoteControl | Mode::SendToChannel) {
|
||||||
self.draw_remote_control(f, &layout.remote_control.unwrap())?;
|
self.draw_remote_control(f, layout.remote_control.unwrap())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::television::Television;
|
use crate::television::Television;
|
||||||
use crate::ui::layout::Layout;
|
|
||||||
use crate::ui::logo::build_logo_paragraph;
|
use crate::ui::logo::build_logo_paragraph;
|
||||||
use crate::ui::mode::mode_color;
|
use crate::ui::mode::mode_color;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
@ -7,6 +6,8 @@ use ratatui::prelude::{Color, Style};
|
|||||||
use ratatui::widgets::{Block, BorderType, Borders, Padding};
|
use ratatui::widgets::{Block, BorderType, Borders, Padding};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
|
use super::layout::HelpBarLayout;
|
||||||
|
|
||||||
pub fn draw_logo_block(f: &mut Frame, area: Rect, color: Color) {
|
pub fn draw_logo_block(f: &mut Frame, area: Rect, color: Color) {
|
||||||
let logo_block = Block::default()
|
let logo_block = Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
@ -24,9 +25,9 @@ impl Television {
|
|||||||
pub(crate) fn draw_help_bar(
|
pub(crate) fn draw_help_bar(
|
||||||
&self,
|
&self,
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
layout: &Layout,
|
layout: &Option<HelpBarLayout>,
|
||||||
) -> color_eyre::Result<()> {
|
) -> color_eyre::Result<()> {
|
||||||
if let Some(help_bar) = layout.help_bar {
|
if let Some(help_bar) = layout {
|
||||||
self.draw_metadata_block(f, help_bar.left);
|
self.draw_metadata_block(f, help_bar.left);
|
||||||
self.draw_keymaps_block(f, help_bar.middle)?;
|
self.draw_keymaps_block(f, help_bar.middle)?;
|
||||||
draw_logo_block(f, help_bar.right, mode_color(self.mode));
|
draw_logo_block(f, help_bar.right, mode_color(self.mode));
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::television::Television;
|
use crate::television::Television;
|
||||||
use crate::ui::layout::Layout;
|
|
||||||
use crate::ui::BORDER_COLOR;
|
use crate::ui::BORDER_COLOR;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use ratatui::layout::{
|
use ratatui::layout::{
|
||||||
Alignment, Constraint, Direction, Layout as RatatuiLayout,
|
Alignment, Constraint, Direction, Layout as RatatuiLayout, Rect,
|
||||||
};
|
};
|
||||||
use ratatui::prelude::{Span, Style};
|
use ratatui::prelude::{Span, Style};
|
||||||
use ratatui::style::Stylize;
|
use ratatui::style::Stylize;
|
||||||
@ -416,7 +415,7 @@ impl Television {
|
|||||||
pub(crate) fn draw_input_box(
|
pub(crate) fn draw_input_box(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
layout: &Layout,
|
rect: Rect,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let input_block = Block::default()
|
let input_block = Block::default()
|
||||||
.title_top(Line::from(" Pattern ").alignment(Alignment::Center))
|
.title_top(Line::from(" Pattern ").alignment(Alignment::Center))
|
||||||
@ -425,12 +424,12 @@ impl Television {
|
|||||||
.border_style(Style::default().fg(BORDER_COLOR))
|
.border_style(Style::default().fg(BORDER_COLOR))
|
||||||
.style(Style::default());
|
.style(Style::default());
|
||||||
|
|
||||||
let input_block_inner = input_block.inner(layout.input);
|
let input_block_inner = input_block.inner(rect);
|
||||||
if input_block_inner.area() == 0 {
|
if input_block_inner.area() == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
f.render_widget(input_block, layout.input);
|
f.render_widget(input_block, rect);
|
||||||
|
|
||||||
// split input block into 4 parts: prompt symbol, input, result count, spinner
|
// split input block into 4 parts: prompt symbol, input, result count, spinner
|
||||||
let total_count = self.channel.total_count();
|
let total_count = self.channel.total_count();
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use ratatui::layout;
|
use ratatui::layout;
|
||||||
use ratatui::layout::{Constraint, Direction, Rect};
|
use ratatui::layout::{Constraint, Direction, Rect};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
pub struct Dimensions {
|
pub struct Dimensions {
|
||||||
pub x: u16,
|
pub x: u16,
|
||||||
@ -41,6 +44,24 @@ impl HelpBarLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, Default)]
|
||||||
|
pub enum InputPosition {
|
||||||
|
#[serde(rename = "top")]
|
||||||
|
Top,
|
||||||
|
#[serde(rename = "bottom")]
|
||||||
|
#[default]
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InputPosition {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
InputPosition::Top => write!(f, "top"),
|
||||||
|
InputPosition::Bottom => write!(f, "bottom"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
pub help_bar: Option<HelpBarLayout>,
|
pub help_bar: Option<HelpBarLayout>,
|
||||||
pub results: Rect,
|
pub results: Rect,
|
||||||
@ -75,6 +96,7 @@ impl Layout {
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
with_remote: bool,
|
with_remote: bool,
|
||||||
with_help_bar: bool,
|
with_help_bar: bool,
|
||||||
|
input_position: InputPosition,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let main_block = centered_rect(dimensions.x, dimensions.y, area);
|
let main_block = centered_rect(dimensions.x, dimensions.y, area);
|
||||||
// split the main block into two vertical chunks (help bar + rest)
|
// split the main block into two vertical chunks (help bar + rest)
|
||||||
@ -127,23 +149,47 @@ impl Layout {
|
|||||||
.split(main_rect);
|
.split(main_rect);
|
||||||
|
|
||||||
// left block: results + input field
|
// left block: results + input field
|
||||||
|
let results_constraints =
|
||||||
|
vec![Constraint::Min(3), Constraint::Length(3)];
|
||||||
|
|
||||||
let left_chunks = layout::Layout::default()
|
let left_chunks = layout::Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(3), Constraint::Length(3)])
|
.constraints(match input_position {
|
||||||
|
InputPosition::Top => {
|
||||||
|
results_constraints.into_iter().rev().collect()
|
||||||
|
}
|
||||||
|
InputPosition::Bottom => results_constraints,
|
||||||
|
})
|
||||||
.split(vt_chunks[0]);
|
.split(vt_chunks[0]);
|
||||||
|
let (input, results) = match input_position {
|
||||||
|
InputPosition::Bottom => (left_chunks[1], left_chunks[0]),
|
||||||
|
InputPosition::Top => (left_chunks[0], left_chunks[1]),
|
||||||
|
};
|
||||||
|
|
||||||
// right block: preview title + preview
|
// right block: preview title + preview
|
||||||
|
let preview_constraints =
|
||||||
|
vec![Constraint::Length(3), Constraint::Min(3)];
|
||||||
|
|
||||||
let right_chunks = layout::Layout::default()
|
let right_chunks = layout::Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Length(3), Constraint::Min(3)])
|
.constraints(match input_position {
|
||||||
|
InputPosition::Top => {
|
||||||
|
preview_constraints.into_iter().rev().collect()
|
||||||
|
}
|
||||||
|
InputPosition::Bottom => preview_constraints,
|
||||||
|
})
|
||||||
.split(vt_chunks[1]);
|
.split(vt_chunks[1]);
|
||||||
|
let (preview_title, preview_window) = match input_position {
|
||||||
|
InputPosition::Bottom => (right_chunks[0], right_chunks[1]),
|
||||||
|
InputPosition::Top => (right_chunks[1], right_chunks[0]),
|
||||||
|
};
|
||||||
|
|
||||||
Self::new(
|
Self::new(
|
||||||
help_bar_layout,
|
help_bar_layout,
|
||||||
left_chunks[0],
|
results,
|
||||||
left_chunks[1],
|
input,
|
||||||
right_chunks[0],
|
preview_title,
|
||||||
right_chunks[1],
|
preview_window,
|
||||||
if with_remote {
|
if with_remote {
|
||||||
Some(vt_chunks[2])
|
Some(vt_chunks[2])
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::television::Television;
|
use crate::television::Television;
|
||||||
use crate::ui::layout::Layout;
|
|
||||||
use crate::ui::BORDER_COLOR;
|
use crate::ui::BORDER_COLOR;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use ratatui::layout::{Alignment, Rect};
|
use ratatui::layout::{Alignment, Rect};
|
||||||
@ -26,7 +25,7 @@ impl Television {
|
|||||||
pub(crate) fn draw_preview_title_block(
|
pub(crate) fn draw_preview_title_block(
|
||||||
&self,
|
&self,
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
layout: &Layout,
|
rect: Rect,
|
||||||
preview: &Arc<Preview>,
|
preview: &Arc<Preview>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut preview_title_spans = Vec::new();
|
let mut preview_title_spans = Vec::new();
|
||||||
@ -44,7 +43,7 @@ impl Television {
|
|||||||
preview_title_spans.push(Span::styled(
|
preview_title_spans.push(Span::styled(
|
||||||
shrink_with_ellipsis(
|
shrink_with_ellipsis(
|
||||||
&preview.title,
|
&preview.title,
|
||||||
layout.preview_window.width.saturating_sub(4) as usize,
|
rect.width.saturating_sub(4) as usize,
|
||||||
),
|
),
|
||||||
Style::default().fg(DEFAULT_PREVIEW_TITLE_FG).bold(),
|
Style::default().fg(DEFAULT_PREVIEW_TITLE_FG).bold(),
|
||||||
));
|
));
|
||||||
@ -57,14 +56,14 @@ impl Television {
|
|||||||
.border_style(Style::default().fg(BORDER_COLOR)),
|
.border_style(Style::default().fg(BORDER_COLOR)),
|
||||||
)
|
)
|
||||||
.alignment(Alignment::Left);
|
.alignment(Alignment::Left);
|
||||||
f.render_widget(preview_title, layout.preview_title);
|
f.render_widget(preview_title, rect);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn draw_preview_content_block(
|
pub(crate) fn draw_preview_content_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
layout: &Layout,
|
rect: Rect,
|
||||||
target_line: Option<u16>,
|
target_line: Option<u16>,
|
||||||
preview: &Arc<Preview>,
|
preview: &Arc<Preview>,
|
||||||
) {
|
) {
|
||||||
@ -83,8 +82,8 @@ impl Television {
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 1,
|
left: 1,
|
||||||
});
|
});
|
||||||
let inner = preview_outer_block.inner(layout.preview_window);
|
let inner = preview_outer_block.inner(rect);
|
||||||
f.render_widget(preview_outer_block, layout.preview_window);
|
f.render_widget(preview_outer_block, rect);
|
||||||
|
|
||||||
//if let PreviewContent::Image(img) = &preview.content {
|
//if let PreviewContent::Image(img) = &preview.content {
|
||||||
// let image_component = StatefulImage::new(None);
|
// let image_component = StatefulImage::new(None);
|
||||||
|
@ -18,7 +18,7 @@ impl Television {
|
|||||||
pub fn draw_remote_control(
|
pub fn draw_remote_control(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
area: &Rect,
|
rect: Rect,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let layout = Layout::default()
|
let layout = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
@ -30,7 +30,7 @@ impl Television {
|
|||||||
]
|
]
|
||||||
.as_ref(),
|
.as_ref(),
|
||||||
)
|
)
|
||||||
.split(*area);
|
.split(rect);
|
||||||
self.draw_rc_channels(f, &layout[0])?;
|
self.draw_rc_channels(f, &layout[0])?;
|
||||||
self.draw_rc_input(f, &layout[1])?;
|
self.draw_rc_input(f, &layout[1])?;
|
||||||
draw_rc_logo(f, layout[2], mode_color(self.mode));
|
draw_rc_logo(f, layout[2], mode_color(self.mode));
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::television::Television;
|
use crate::television::Television;
|
||||||
use crate::ui::layout::Layout;
|
use crate::ui::layout::InputPosition;
|
||||||
use crate::ui::BORDER_COLOR;
|
use crate::ui::BORDER_COLOR;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use ratatui::layout::Alignment;
|
use ratatui::layout::{Alignment, Rect};
|
||||||
use ratatui::prelude::{Color, Line, Span, Style};
|
use ratatui::prelude::{Color, Line, Span, Style};
|
||||||
use ratatui::widgets::{
|
use ratatui::widgets::{
|
||||||
Block, BorderType, Borders, List, ListDirection, Padding,
|
Block, BorderType, Borders, List, ListDirection, Padding,
|
||||||
@ -164,7 +164,7 @@ impl Television {
|
|||||||
pub(crate) fn draw_results_list(
|
pub(crate) fn draw_results_list(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
layout: &Layout,
|
rect: Rect,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let results_block = Block::default()
|
let results_block = Block::default()
|
||||||
.title_top(Line::from(" Results ").alignment(Alignment::Center))
|
.title_top(Line::from(" Results ").alignment(Alignment::Center))
|
||||||
@ -181,21 +181,24 @@ impl Television {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let entries = self.channel.results(
|
let entries = self.channel.results(
|
||||||
layout.results.height.saturating_sub(2).into(),
|
rect.height.saturating_sub(2).into(),
|
||||||
u32::try_from(self.results_picker.offset())?,
|
u32::try_from(self.results_picker.offset())?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let results_list = build_results_list(
|
let results_list = build_results_list(
|
||||||
results_block,
|
results_block,
|
||||||
&entries,
|
&entries,
|
||||||
ListDirection::BottomToTop,
|
match self.config.ui.input_bar_position {
|
||||||
|
InputPosition::Bottom => ListDirection::BottomToTop,
|
||||||
|
InputPosition::Top => ListDirection::TopToBottom,
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
self.config.ui.use_nerd_font_icons,
|
self.config.ui.use_nerd_font_icons,
|
||||||
);
|
);
|
||||||
|
|
||||||
f.render_stateful_widget(
|
f.render_stateful_widget(
|
||||||
results_list,
|
results_list,
|
||||||
layout.results,
|
rect,
|
||||||
&mut self.results_picker.relative_state,
|
&mut self.results_picker.relative_state,
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user