Add more UI styling options

With this commit, the following can be done:

- Define layout constraints based on screen size and relative panel
  size.
- Define borders.
- Define panel style.
- Define panel title and title style.
This commit is contained in:
Arijit Basu 2021-05-08 08:54:20 +05:30 committed by Arijit Basu
parent cfa82cf99f
commit 59b55ee192
3 changed files with 311 additions and 145 deletions

View File

@ -11,6 +11,8 @@ use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use tui::layout::Constraint as TuiConstraint; use tui::layout::Constraint as TuiConstraint;
use tui::layout::Rect;
use tui::widgets::Borders as TuiBorders;
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
@ -254,24 +256,50 @@ pub enum Constraint {
Percentage(u16), Percentage(u16),
Ratio(u32, u32), Ratio(u32, u32),
Length(u16), Length(u16),
LengthLessThanScreenHeight(u16),
LengthLessThanScreenWidth(u16),
LengthLessThanLayoutHeight(u16),
LengthLessThanLayoutWidth(u16),
Max(u16), Max(u16),
MaxLessThanScreenHeight(u16),
MaxLessThanScreenWidth(u16),
MaxLessThanLayoutHeight(u16),
MaxthLessThanLayoutWidth(u16),
Min(u16), Min(u16),
MinLessThanScreenHeight(u16),
MinLessThanScreenWidth(u16),
MinLessThanLayoutHeight(u16),
MinLessThanLayoutWidth(u16),
} }
impl Default for Constraint { impl Constraint {
fn default() -> Self { pub fn to_tui(self, screen_size: Rect, layout_size: Rect) -> TuiConstraint {
Self::Min(1)
}
}
impl Into<TuiConstraint> for Constraint {
fn into(self) -> TuiConstraint {
match self { match self {
Self::Length(n) => TuiConstraint::Length(n),
Self::Percentage(n) => TuiConstraint::Percentage(n), Self::Percentage(n) => TuiConstraint::Percentage(n),
Self::Ratio(x, y) => TuiConstraint::Ratio(x, y), Self::Ratio(x, y) => TuiConstraint::Ratio(x, y),
Self::Length(n) => TuiConstraint::Length(n),
Self::LengthLessThanScreenHeight(n) => {
TuiConstraint::Length(screen_size.height.max(n) - n)
}
Self::LengthLessThanScreenWidth(n) => {
TuiConstraint::Length(screen_size.width.max(n) - n)
}
Self::LengthLessThanLayoutHeight(n) => {
TuiConstraint::Length(layout_size.height.max(n) - n)
}
Self::LengthLessThanLayoutWidth(n) => {
TuiConstraint::Length(layout_size.width.max(n) - n)
}
Self::Max(n) => TuiConstraint::Max(n), Self::Max(n) => TuiConstraint::Max(n),
Self::MaxLessThanScreenHeight(n) => TuiConstraint::Max(screen_size.height.max(n) - n),
Self::MaxLessThanScreenWidth(n) => TuiConstraint::Max(screen_size.width.max(n) - n),
Self::MaxLessThanLayoutHeight(n) => TuiConstraint::Max(layout_size.height.max(n) - n),
Self::MaxthLessThanLayoutWidth(n) => TuiConstraint::Max(layout_size.width.max(n) - n),
Self::Min(n) => TuiConstraint::Min(n), Self::Min(n) => TuiConstraint::Min(n),
Self::MinLessThanScreenHeight(n) => TuiConstraint::Min(screen_size.height.max(n) - n),
Self::MinLessThanScreenWidth(n) => TuiConstraint::Min(screen_size.width.max(n) - n),
Self::MinLessThanLayoutHeight(n) => TuiConstraint::Min(layout_size.height.max(n) - n),
Self::MinLessThanLayoutWidth(n) => TuiConstraint::Min(layout_size.width.max(n) - n),
} }
} }
} }
@ -999,6 +1027,26 @@ impl ModesConfig {
} }
} }
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum Border {
Top,
Right,
Bottom,
Left,
}
impl Border {
pub fn bits(self) -> u32 {
match self {
Self::Top => TuiBorders::TOP.bits(),
Self::Right => TuiBorders::RIGHT.bits(),
Self::Bottom => TuiBorders::BOTTOM.bits(),
Self::Left => TuiBorders::LEFT.bits(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct LayoutOptions { pub struct LayoutOptions {
@ -1009,7 +1057,7 @@ pub struct LayoutOptions {
#[serde(default)] #[serde(default)]
vertical_margin: Option<u16>, vertical_margin: Option<u16>,
#[serde(default)] #[serde(default)]
constraints: Vec<Constraint>, constraints: Option<Vec<Constraint>>,
} }
impl LayoutOptions { impl LayoutOptions {
@ -1017,12 +1065,12 @@ impl LayoutOptions {
self.margin = other.margin.or(self.margin); self.margin = other.margin.or(self.margin);
self.horizontal_margin = other.horizontal_margin.or(self.horizontal_margin); self.horizontal_margin = other.horizontal_margin.or(self.horizontal_margin);
self.vertical_margin = other.vertical_margin.or(self.vertical_margin); self.vertical_margin = other.vertical_margin.or(self.vertical_margin);
self.constraints = other.constraints; self.constraints = other.constraints.or(self.constraints);
self self
} }
/// Get a reference to the layout options's constraints. /// Get a reference to the layout options's constraints.
pub fn constraints(&self) -> &Vec<Constraint> { pub fn constraints(&self) -> &Option<Vec<Constraint>> {
&self.constraints &self.constraints
} }
@ -1042,15 +1090,45 @@ impl LayoutOptions {
} }
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BlockConfig {
#[serde(default)]
title: UiElement,
#[serde(default)]
borders: Option<IndexSet<Border>>,
#[serde(default)]
style: Style,
}
impl BlockConfig {
/// Get a reference to the block config's borders.
pub fn borders(&self) -> &Option<IndexSet<Border>> {
&self.borders
}
/// Get a reference to the block config's title.
pub fn title(&self) -> &UiElement {
&self.title
}
/// Get a reference to the block config's style.
pub fn style(&self) -> Style {
self.style
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub enum Layout { pub enum Layout {
Nothing, Nothing(BlockConfig),
Table, Table(BlockConfig),
InputAndLogs, InputAndLogs(BlockConfig),
Selection, Selection(BlockConfig),
HelpMenu, HelpMenu(BlockConfig),
SortAndFilter, SortAndFilter(BlockConfig),
Horizontal { Horizontal {
config: LayoutOptions, config: LayoutOptions,
splits: Vec<Layout>, splits: Vec<Layout>,
@ -1063,41 +1141,14 @@ pub enum Layout {
impl Default for Layout { impl Default for Layout {
fn default() -> Self { fn default() -> Self {
Self::Nothing Self::Nothing(Default::default())
} }
} }
impl Layout { impl Layout {
pub fn extend(self, other: Self) -> Self { pub fn extend(self, other: Self) -> Self {
match (self, other) { match (self, other) {
( (s, Self::Nothing(_)) => s,
Self::Horizontal {
config: sc,
splits: _,
},
Self::Horizontal {
config: oc,
splits: os,
},
) => Self::Horizontal {
config: sc.extend(oc),
splits: os,
},
(
Self::Vertical {
config: sc,
splits: _,
},
Self::Vertical {
config: oc,
splits: os,
},
) => Self::Vertical {
config: sc.extend(oc),
splits: os,
},
(s, Self::Nothing) => s,
(_, other) => other, (_, other) => other,
} }
} }

View File

@ -19,17 +19,25 @@ layouts:
- Min: 1 - Min: 1
- Length: 3 - Length: 3
splits: splits:
- SortAndFilter - SortAndFilter:
- Table borders: [Top, Right, Bottom, Left]
- InputAndLogs - Table:
title:
style:
fg: Red
borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- Vertical: - Vertical:
config: config:
constraints: constraints:
- Percentage: 50 - Percentage: 50
- Percentage: 50 - Percentage: 50
splits: splits:
- Selection - Selection:
- HelpMenu borders: [Top, Right, Bottom, Left]
- HelpMenu:
borders: [Top, Right, Bottom, Left]
no_help: no_help:
Horizontal: Horizontal:
config: config:
@ -44,10 +52,14 @@ layouts:
- Min: 1 - Min: 1
- Length: 3 - Length: 3
splits: splits:
- SortAndFilter - SortAndFilter:
- Table borders: [Top, Right, Bottom, Left]
- InputAndLogs - Table:
- Selection borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- Selection:
borders: [Top, Right, Bottom, Left]
no_selection: no_selection:
Horizontal: Horizontal:
@ -63,10 +75,14 @@ layouts:
- Min: 1 - Min: 1
- Length: 3 - Length: 3
splits: splits:
- SortAndFilter - SortAndFilter:
- Table borders: [Top, Right, Bottom, Left]
- InputAndLogs - Table:
- HelpMenu borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- HelpMenu:
borders: [Top, Right, Bottom, Left]
no_help_no_selection: no_help_no_selection:
Vertical: Vertical:
@ -76,9 +92,12 @@ layouts:
- Min: 1 - Min: 1
- Length: 3 - Length: 3
splits: splits:
- SortAndFilter - SortAndFilter:
- Table borders: [Top, Right, Bottom, Left]
- InputAndLogs - Table:
borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
general: general:
show_hidden: false show_hidden: false

256
src/ui.rs
View File

@ -1,6 +1,7 @@
use crate::app; use crate::app;
use crate::app::HelpMenuLine; use crate::app::HelpMenuLine;
use crate::app::{Node, ResolvedNode}; use crate::app::{Node, ResolvedNode};
use crate::config::BlockConfig;
use crate::config::Layout; use crate::config::Layout;
use handlebars::Handlebars; use handlebars::Handlebars;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -13,7 +14,7 @@ use tui::layout::Rect;
use tui::layout::{Constraint as TuiConstraint, Direction, Layout as TuiLayout}; use tui::layout::{Constraint as TuiConstraint, Direction, Layout as TuiLayout};
use tui::style::{Color, Modifier, Style as TuiStyle}; use tui::style::{Color, Modifier, Style as TuiStyle};
use tui::text::{Span, Spans}; use tui::text::{Span, Spans};
use tui::widgets::{Block, Borders, Cell, List, ListItem, Paragraph, Row, Table}; use tui::widgets::{Block, Borders as TuiBorders, Cell, List, ListItem, Paragraph, Row, Table};
use tui::Frame; use tui::Frame;
lazy_static! { lazy_static! {
@ -168,10 +169,36 @@ impl NodeUiMetadata {
} }
} }
fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Handlebars) { fn block<'a>(config: BlockConfig, default_title: String) -> Block<'a> {
let config = app.config().to_owned(); Block::default()
let header_height = config.general().table().header().height().unwrap_or(1); .borders(TuiBorders::from_bits_truncate(
let height: usize = (rect.height.max(header_height + 2) - (header_height + 2)).into(); config
.borders()
.clone()
.unwrap_or_default()
.iter()
.map(|b| b.bits())
.reduce(|a, b| (a ^ b))
.unwrap_or_else(|| TuiBorders::NONE.bits()),
))
.title(Span::styled(
config.title().format().clone().unwrap_or(default_title),
config.title().style().into(),
))
.style(config.style().into())
}
fn draw_table<B: Backend>(
config: BlockConfig,
f: &mut Frame<B>,
screen_size: Rect,
layout_size: Rect,
app: &app::App,
hb: &Handlebars,
) {
let app_config = app.config().to_owned();
let header_height = app_config.general().table().header().height().unwrap_or(1);
let height: usize = (layout_size.height.max(header_height + 2) - (header_height + 2)).into();
let rows = app let rows = app
.directory_buffer() .directory_buffer()
@ -190,7 +217,7 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
let is_first = index == 0; let is_first = index == 0;
let is_last = index == dir.total().max(1) - 1; let is_last = index == dir.total().max(1) - 1;
let tree = config let tree = app_config
.general() .general()
.table() .table()
.tree() .tree()
@ -206,19 +233,24 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
}) })
.unwrap_or_default(); .unwrap_or_default();
let node_type = config let node_type = app_config
.node_types() .node_types()
.special() .special()
.get(node.relative_path()) .get(node.relative_path())
.or_else(|| config.node_types().extension().get(node.extension())) .or_else(|| app_config.node_types().extension().get(node.extension()))
.or_else(|| config.node_types().mime_essence().get(node.mime_essence())) .or_else(|| {
app_config
.node_types()
.mime_essence()
.get(node.mime_essence())
})
.unwrap_or_else(|| { .unwrap_or_else(|| {
if node.is_symlink() { if node.is_symlink() {
&config.node_types().symlink() &app_config.node_types().symlink()
} else if node.is_dir() { } else if node.is_dir() {
&config.node_types().directory() &app_config.node_types().directory()
} else { } else {
&config.node_types().file() &app_config.node_types().file()
} }
}); });
@ -230,7 +262,7 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
}; };
let (mut prefix, mut suffix, mut style) = { let (mut prefix, mut suffix, mut style) = {
let ui = config.general().default_ui().clone(); let ui = app_config.general().default_ui().clone();
( (
ui.prefix().clone(), ui.prefix().clone(),
ui.suffix().clone(), ui.suffix().clone(),
@ -239,14 +271,14 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
}; };
if is_selected { if is_selected {
let ui = config.general().selection_ui().clone(); let ui = app_config.general().selection_ui().clone();
prefix = ui.prefix().clone().or(prefix); prefix = ui.prefix().clone().or(prefix);
suffix = ui.suffix().clone().or(suffix); suffix = ui.suffix().clone().or(suffix);
style = style.extend(ui.style()); style = style.extend(ui.style());
}; };
if is_focused { if is_focused {
let ui = config.general().focus_ui().clone(); let ui = app_config.general().focus_ui().clone();
prefix = ui.prefix().clone().or(prefix); prefix = ui.prefix().clone().or(prefix);
suffix = ui.suffix().clone().or(suffix); suffix = ui.suffix().clone().or(suffix);
style = style.extend(ui.style()); style = style.extend(ui.style());
@ -281,30 +313,41 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
}) })
.unwrap_or_default(); .unwrap_or_default();
let table_constraints: Vec<TuiConstraint> = config let table_constraints: Vec<TuiConstraint> = app_config
.general() .general()
.table() .table()
.col_widths() .col_widths()
.clone() .clone()
.unwrap_or_default() .unwrap_or_default()
.into_iter() .into_iter()
.map(|c| c.into()) .map(|c| c.to_tui(screen_size, layout_size))
.collect(); .collect();
let table = Table::new(rows) let table = Table::new(rows)
.widths(&table_constraints) .widths(&table_constraints)
.style(config.general().table().style().into()) .style(app_config.general().table().style().into())
.highlight_style(config.general().focus_ui().style().into()) .highlight_style(app_config.general().focus_ui().style().into())
.column_spacing(config.general().table().col_spacing().unwrap_or_default()) .column_spacing(
.block(Block::default().borders(Borders::ALL).title(format!( app_config
.general()
.table()
.col_spacing()
.unwrap_or_default(),
)
.block(block(
config,
format!(
" {} ({}) ", " {} ({}) ",
app.pwd(), app.pwd(),
app.directory_buffer().map(|d| d.total()).unwrap_or_default() app.directory_buffer()
))); .map(|d| d.total())
.unwrap_or_default()
),
));
let table = table.clone().header( let table = table.clone().header(
Row::new( Row::new(
config app_config
.general() .general()
.table() .table()
.header() .header()
@ -316,18 +359,25 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
.collect::<Vec<Cell>>(), .collect::<Vec<Cell>>(),
) )
.height(header_height) .height(header_height)
.style(config.general().table().header().style().into()), .style(app_config.general().table().header().style().into()),
); );
f.render_widget(table, rect); f.render_widget(table, layout_size);
} }
fn draw_selection<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) { fn draw_selection<B: Backend>(
config: BlockConfig,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let selection: Vec<ListItem> = app let selection: Vec<ListItem> = app
.selection() .selection()
.iter() .iter()
.rev() .rev()
.take((rect.height.max(2) - 2).into()) .take((layout_size.height.max(2) - 2).into())
.rev() .rev()
.map(|n| n.absolute_path().clone()) .map(|n| n.absolute_path().clone())
.map(ListItem::new) .map(ListItem::new)
@ -336,16 +386,20 @@ fn draw_selection<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &
let selection_count = selection.len(); let selection_count = selection.len();
// Selected items // Selected items
let selection_list = List::new(selection).block( let selection_list =
Block::default() List::new(selection).block(block(config, format!(" Selection ({}) ", selection_count)));
.borders(Borders::ALL)
.title(format!(" Selection ({}) ", selection_count)),
);
f.render_widget(selection_list, rect); f.render_widget(selection_list, layout_size);
} }
fn draw_help_menu<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) { fn draw_help_menu<B: Backend>(
config: BlockConfig,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let help_menu_rows = app let help_menu_rows = app
.mode() .mode()
.help_menu() .help_menu()
@ -374,20 +428,26 @@ fn draw_help_menu<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &
}; };
let help_menu = Table::new(help_menu_rows) let help_menu = Table::new(help_menu_rows)
.block(Block::default().borders(Borders::ALL).title(format!( .block(block(
" Help [{}{}] ", config,
&app.mode().name(), format!(" Help [{}{}] ", &app.mode().name(), read_only_indicator),
read_only_indicator ))
)))
.widths(&[ .widths(&[
TuiConstraint::Percentage(20), TuiConstraint::Percentage(20),
TuiConstraint::Percentage(20), TuiConstraint::Percentage(20),
TuiConstraint::Percentage(60), TuiConstraint::Percentage(60),
]); ]);
f.render_widget(help_menu, rect); f.render_widget(help_menu, layout_size);
} }
fn draw_input_buffer<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) { fn draw_input_buffer<B: Backend>(
config: BlockConfig,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let input_buf = Paragraph::new(Spans::from(vec![ let input_buf = Paragraph::new(Spans::from(vec![
Span::styled( Span::styled(
app.config() app.config()
@ -409,11 +469,18 @@ fn draw_input_buffer<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _
app.config().general().cursor().style().into(), app.config().general().cursor().style().into(),
), ),
])) ]))
.block(Block::default().borders(Borders::ALL).title(" Input ")); .block(block(config, " Input ".into()));
f.render_widget(input_buf, rect); f.render_widget(input_buf, layout_size);
} }
fn draw_sort_n_filter<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) { fn draw_sort_n_filter<B: Backend>(
config: BlockConfig,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let ui = app.config().general().sort_and_filter_ui().clone(); let ui = app.config().general().sort_and_filter_ui().clone();
let filter_by = app.explorer_config().filters(); let filter_by = app.explorer_config().filters();
let sort_by = app.explorer_config().sorters(); let sort_by = app.explorer_config().sorters();
@ -474,15 +541,23 @@ fn draw_sort_n_filter<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App,
.collect::<Vec<Span>>(); .collect::<Vec<Span>>();
spans.pop(); spans.pop();
let p = Paragraph::new(Spans::from(spans)).block(Block::default().borders(Borders::ALL).title( let p = Paragraph::new(Spans::from(spans)).block(block(
config,
format!(" Sort & filter ({}) ", filter_by.len() + sort_by.len()), format!(" Sort & filter ({}) ", filter_by.len() + sort_by.len()),
)); ));
f.render_widget(p, rect); f.render_widget(p, layout_size);
} }
fn draw_logs<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handlebars) { fn draw_logs<B: Backend>(
let config = app.config().general().logs().clone(); config: BlockConfig,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let logs_config = app.config().general().logs().clone();
let logs = app let logs = app
.logs() .logs()
.iter() .iter()
@ -495,55 +570,70 @@ fn draw_logs<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _: &Handl
app::LogLevel::Info => ListItem::new(format!( app::LogLevel::Info => ListItem::new(format!(
"{} | {} | {}", "{} | {} | {}",
&time, &time,
&config.info().format().to_owned().unwrap_or_default(), &logs_config.info().format().to_owned().unwrap_or_default(),
l.message() l.message()
)) ))
.style(config.info().style().into()), .style(logs_config.info().style().into()),
app::LogLevel::Success => ListItem::new(format!( app::LogLevel::Success => ListItem::new(format!(
"{} | {} | {}", "{} | {} | {}",
&time, &time,
&config.success().format().to_owned().unwrap_or_default(), &logs_config
.success()
.format()
.to_owned()
.unwrap_or_default(),
l.message() l.message()
)) ))
.style(config.success().style().into()), .style(logs_config.success().style().into()),
app::LogLevel::Error => ListItem::new(format!( app::LogLevel::Error => ListItem::new(format!(
"{} | {} | {}", "{} | {} | {}",
&time, &time,
&config.error().format().to_owned().unwrap_or_default(), &logs_config.error().format().to_owned().unwrap_or_default(),
l.message() l.message()
)) ))
.style(config.error().style().into()), .style(logs_config.error().style().into()),
} }
}) })
.collect::<Vec<ListItem>>(); .collect::<Vec<ListItem>>();
let logs_list = List::new(logs).block( let logs_list = List::new(logs).block(block(config, format!(" Logs ({}) ", app.logs().len())));
Block::default()
.borders(Borders::ALL)
.title(format!(" Logs ({}) ", app.logs().len())),
);
f.render_widget(logs_list, rect); f.render_widget(logs_list, layout_size);
}
pub fn draw_nothing<B: Backend>(
config: BlockConfig,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
_app: &app::App,
_hb: &Handlebars,
) {
let nothing = Paragraph::new("").block(block(config, "".into()));
f.render_widget(nothing, layout_size);
} }
pub fn draw_layout<B: Backend>( pub fn draw_layout<B: Backend>(
layout: Layout, layout: Layout,
f: &mut Frame<B>, f: &mut Frame<B>,
rect: Rect, screen_size: Rect,
layout_size: Rect,
app: &app::App, app: &app::App,
hb: &Handlebars, hb: &Handlebars,
) { ) {
match layout { match layout {
Layout::Nothing => {} Layout::Nothing(config) => draw_nothing(config, f, screen_size, layout_size, app, hb),
Layout::Table => draw_table(f, rect, app, hb), Layout::Table(config) => draw_table(config, f, screen_size, layout_size, app, hb),
Layout::SortAndFilter => draw_sort_n_filter(f, rect, app, hb), Layout::SortAndFilter(config) => {
Layout::HelpMenu => draw_help_menu(f, rect, app, hb), draw_sort_n_filter(config, f, screen_size, layout_size, app, hb)
Layout::Selection => draw_selection(f, rect, app, hb), }
Layout::InputAndLogs => { Layout::HelpMenu(config) => draw_help_menu(config, f, screen_size, layout_size, app, hb),
Layout::Selection(config) => draw_selection(config, f, screen_size, layout_size, app, hb),
Layout::InputAndLogs(config) => {
if app.input_buffer().is_some() { if app.input_buffer().is_some() {
draw_input_buffer(f, rect, app, hb); draw_input_buffer(config, f, screen_size, layout_size, app, hb);
} else { } else {
draw_logs(f, rect, app, hb); draw_logs(config, f, screen_size, layout_size, app, hb);
}; };
} }
Layout::Horizontal { config, splits } => { Layout::Horizontal { config, splits } => {
@ -552,8 +642,10 @@ pub fn draw_layout<B: Backend>(
.constraints( .constraints(
config config
.constraints() .constraints()
.clone()
.unwrap_or_default()
.iter() .iter()
.map(|c| (*c).into()) .map(|c| c.to_tui(screen_size, layout_size))
.collect::<Vec<TuiConstraint>>(), .collect::<Vec<TuiConstraint>>(),
) )
.horizontal_margin( .horizontal_margin(
@ -568,11 +660,12 @@ pub fn draw_layout<B: Backend>(
.or_else(|| config.margin()) .or_else(|| config.margin())
.unwrap_or_default(), .unwrap_or_default(),
) )
.split(rect); .split(layout_size);
splits splits
.into_iter() .into_iter()
.enumerate() .zip(chunks.into_iter())
.for_each(|(i, s)| draw_layout(s, f, chunks[i], app, hb)); .for_each(|(split, chunk)| draw_layout(split, f, screen_size, chunk, app, hb));
} }
Layout::Vertical { config, splits } => { Layout::Vertical { config, splits } => {
@ -581,8 +674,10 @@ pub fn draw_layout<B: Backend>(
.constraints( .constraints(
config config
.constraints() .constraints()
.clone()
.unwrap_or_default()
.iter() .iter()
.map(|c| (*c).into()) .map(|c| c.to_tui(screen_size, layout_size))
.collect::<Vec<TuiConstraint>>(), .collect::<Vec<TuiConstraint>>(),
) )
.horizontal_margin( .horizontal_margin(
@ -597,20 +692,21 @@ pub fn draw_layout<B: Backend>(
.or_else(|| config.margin()) .or_else(|| config.margin())
.unwrap_or_default(), .unwrap_or_default(),
) )
.split(rect); .split(layout_size);
splits splits
.into_iter() .into_iter()
.enumerate() .zip(chunks.into_iter())
.for_each(|(i, s)| draw_layout(s, f, chunks[i], app, hb)); .for_each(|(split, chunk)| draw_layout(split, f, screen_size, chunk, app, hb));
} }
} }
} }
pub fn draw<B: Backend>(f: &mut Frame<B>, app: &app::App, hb: &Handlebars) { pub fn draw<B: Backend>(f: &mut Frame<B>, app: &app::App, hb: &Handlebars) {
let rect = f.size(); let screen_size = f.size();
let layout = app.layout().clone(); let layout = app.layout().clone();
draw_layout(layout, f, rect, app, hb); draw_layout(layout, f, screen_size, screen_size, app, hb);
} }
#[cfg(test)] #[cfg(test)]