mirror of
https://github.com/sayanarijit/xplr
synced 2024-11-10 07:10:45 +00:00
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:
parent
cfa82cf99f
commit
59b55ee192
145
src/config.rs
145
src/config.rs
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
256
src/ui.rs
@ -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)]
|
||||||
|
Loading…
Reference in New Issue
Block a user