Improve config inheritance for layout UI

With this commit, users will be able to define the common configuration
as super config and inherit from them in each layout.
pull/129/head v0.7.0
Arijit Basu 3 years ago committed by Arijit Basu
parent 0270fecec9
commit 38812e733b

2
Cargo.lock generated

@ -1630,7 +1630,7 @@ dependencies = [
[[package]]
name = "xplr"
version = "0.6.0"
version = "0.7.0"
dependencies = [
"anyhow",
"chrono",

@ -1,6 +1,6 @@
[package]
name = "xplr"
version = "0.6.0" # Update config.yml, config.rs and default.nix
version = "0.7.0" # Update config.yml, config.rs and default.nix
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
description = "A hackable, minimal, fast TUI file explorer"

@ -196,7 +196,7 @@ pub struct UiElement {
}
impl UiElement {
fn extend(mut self, other: Self) -> Self {
pub fn extend(mut self, other: Self) -> Self {
self.format = other.format.or(self.format);
self.style = self.style.extend(other.style);
self
@ -439,6 +439,9 @@ pub struct SortAndFilterUi {
#[serde(default)]
separator: UiElement,
#[serde(default)]
default_identifier: UiElement,
#[serde(default)]
sort_direction_identifiers: SortDirectionIdentifiersUi,
@ -452,6 +455,7 @@ pub struct SortAndFilterUi {
impl SortAndFilterUi {
pub fn extend(mut self, other: Self) -> Self {
self.separator = self.separator.extend(other.separator);
self.default_identifier = self.default_identifier.extend(other.default_identifier);
self.sort_direction_identifiers = self
.sort_direction_identifiers
.extend(other.sort_direction_identifiers);
@ -479,6 +483,75 @@ impl SortAndFilterUi {
pub fn filter_identifiers(&self) -> &HashMap<NodeFilter, UiElement> {
&self.filter_identifiers
}
/// Get a reference to the sort and filter ui's default identifier.
pub fn default_identifier(&self) -> &UiElement {
&self.default_identifier
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PanelUi {
#[serde(default)]
default: PanelUiConfig,
#[serde(default)]
table: PanelUiConfig,
#[serde(default)]
sort_and_filter: PanelUiConfig,
#[serde(default)]
selection: PanelUiConfig,
#[serde(default)]
input_and_logs: PanelUiConfig,
#[serde(default)]
help_menu: PanelUiConfig,
}
impl PanelUi {
fn extend(mut self, other: Self) -> Self {
self.default = self.default.extend(other.default);
self.table = self.table.extend(other.table);
self.sort_and_filter = self.sort_and_filter.extend(other.sort_and_filter);
self.selection = self.selection.extend(other.selection);
self.input_and_logs = self.input_and_logs.extend(other.input_and_logs);
self.help_menu = self.help_menu.extend(other.help_menu);
self
}
/// Get a reference to the panel ui's default.
pub fn default(&self) -> &PanelUiConfig {
&self.default
}
/// Get a reference to the panel ui's table.
pub fn table(&self) -> &PanelUiConfig {
&self.table
}
/// Get a reference to the panel ui's sort and filter.
pub fn sort_and_filter(&self) -> &PanelUiConfig {
&self.sort_and_filter
}
/// Get a reference to the panel ui's selection.
pub fn selection(&self) -> &PanelUiConfig {
&self.selection
}
/// Get a reference to the panel ui's input and log.
pub fn input_and_logs(&self) -> &PanelUiConfig {
&self.input_and_logs
}
/// Get a reference to the panel ui's help menu.
pub fn help_menu(&self) -> &PanelUiConfig {
&self.help_menu
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@ -514,6 +587,9 @@ pub struct GeneralConfig {
#[serde(default)]
sort_and_filter_ui: SortAndFilterUi,
#[serde(default)]
panel_ui: PanelUi,
#[serde(default)]
initial_sorting: Option<IndexSet<NodeSorterApplicable>>,
@ -536,6 +612,7 @@ impl GeneralConfig {
self.focus_ui = self.focus_ui.extend(other.focus_ui);
self.selection_ui = self.selection_ui.extend(other.selection_ui);
self.sort_and_filter_ui = self.sort_and_filter_ui.extend(other.sort_and_filter_ui);
self.panel_ui = self.panel_ui.extend(other.panel_ui);
self.initial_sorting = other.initial_sorting.or(self.initial_sorting);
self.initial_layout = other.initial_layout.or(self.initial_layout);
self.initial_mode = other.initial_mode.or(self.initial_mode);
@ -606,6 +683,11 @@ impl GeneralConfig {
pub fn initial_layout(&self) -> &Option<String> {
&self.initial_layout
}
/// Get a reference to the general config's panel ui.
pub fn panel_ui(&self) -> &PanelUi {
&self.panel_ui
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@ -1072,7 +1154,7 @@ impl LayoutOptions {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BlockConfig {
pub struct PanelUiConfig {
#[serde(default)]
title: UiElement,
@ -1083,7 +1165,13 @@ pub struct BlockConfig {
style: Style,
}
impl BlockConfig {
impl PanelUiConfig {
pub fn extend(mut self, other: Self) -> Self {
self.title = self.title.extend(other.title);
self.borders = other.borders.or(self.borders);
self.style = self.style.extend(other.style);
self
}
/// Get a reference to the block config's borders.
pub fn borders(&self) -> &Option<IndexSet<Border>> {
&self.borders
@ -1103,12 +1191,12 @@ impl BlockConfig {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum Layout {
Nothing(BlockConfig),
Table(BlockConfig),
InputAndLogs(BlockConfig),
Selection(BlockConfig),
HelpMenu(BlockConfig),
SortAndFilter(BlockConfig),
Nothing(Option<PanelUiConfig>),
Table(Option<PanelUiConfig>),
InputAndLogs(Option<PanelUiConfig>),
Selection(Option<PanelUiConfig>),
HelpMenu(Option<PanelUiConfig>),
SortAndFilter(Option<PanelUiConfig>),
Horizontal {
config: LayoutOptions,
splits: Vec<Layout>,
@ -1129,6 +1217,38 @@ impl Layout {
pub fn extend(self, other: Self) -> Self {
match (self, other) {
(s, Self::Nothing(_)) => s,
(Self::Table(s), Self::Table(o)) => Self::Table(o.or(s)),
(Self::InputAndLogs(s), Self::InputAndLogs(o)) => Self::InputAndLogs(o.or(s)),
(Self::Selection(s), Self::Selection(o)) => Self::Selection(o.or(s)),
(Self::HelpMenu(s), Self::HelpMenu(o)) => Self::HelpMenu(o.or(s)),
(Self::SortAndFilter(s), Self::SortAndFilter(o)) => Self::SortAndFilter(o.or(s)),
(
Self::Horizontal {
config: sconfig,
splits: _,
},
Self::Horizontal {
config: oconfig,
splits: osplits,
},
) => Self::Horizontal {
config: sconfig.extend(oconfig),
splits: osplits,
},
(
Self::Vertical {
config: sconfig,
splits: _,
},
Self::Vertical {
config: oconfig,
splits: osplits,
},
) => Self::Vertical {
config: sconfig.extend(oconfig),
splits: osplits,
},
(_, other) => other,
}
}
@ -1274,7 +1394,7 @@ impl Config {
pub fn is_compatible(&self) -> Result<bool> {
let result = match self.parsed_version()? {
(0, 6, 0) => true,
(0, 7, 0) => true,
(_, _, _) => false,
};

@ -1,4 +1,4 @@
version: v0.6.0
version: v0.7.0
layouts:
custom: {}
builtin:
@ -19,22 +19,17 @@ layouts:
- Min: 1
- Length: 3
splits:
- SortAndFilter:
borders: [Top, Right, Bottom, Left]
- Table:
borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- SortAndFilter: ~
- Table: ~
- InputAndLogs: ~
- Vertical:
config:
constraints:
- Percentage: 50
- Percentage: 50
splits:
- Selection:
borders: [Top, Right, Bottom, Left]
- HelpMenu:
borders: [Top, Right, Bottom, Left]
- Selection: ~
- HelpMenu: ~
no_help:
Horizontal:
config:
@ -49,14 +44,10 @@ layouts:
- Min: 1
- Length: 3
splits:
- SortAndFilter:
borders: [Top, Right, Bottom, Left]
- Table:
borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- Selection:
borders: [Top, Right, Bottom, Left]
- SortAndFilter: ~
- Table: ~
- InputAndLogs: ~
- Selection: ~
no_selection:
Horizontal:
@ -72,14 +63,10 @@ layouts:
- Min: 1
- Length: 3
splits:
- SortAndFilter:
borders: [Top, Right, Bottom, Left]
- Table:
borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- HelpMenu:
borders: [Top, Right, Bottom, Left]
- SortAndFilter: ~
- Table: ~
- InputAndLogs: ~
- HelpMenu: ~
no_help_no_selection:
Vertical:
@ -89,12 +76,9 @@ layouts:
- Min: 1
- Length: 3
splits:
- SortAndFilter:
borders: [Top, Right, Bottom, Left]
- Table:
borders: [Top, Right, Bottom, Left]
- InputAndLogs:
borders: [Top, Right, Bottom, Left]
- SortAndFilter: ~
- Table: ~
- InputAndLogs: ~
general:
show_hidden: false
@ -161,27 +145,38 @@ general:
default_ui:
prefix: ' '
suffix: ''
focus_ui:
prefix: ▸[
suffix: ']'
style:
fg: Blue
add_modifiers: [Bold]
selection_ui:
prefix: ' {'
suffix: '}'
style:
fg: LightGreen
add_modifiers: [Bold]
panel_ui:
default:
borders: [Top, Right, Bottom, Left]
sort_and_filter_ui:
separator:
format: " "
style:
add_modifiers: [Dim]
default_identifier:
style:
add_modifiers: [Bold]
sort_direction_identifiers:
forward:
format: "↓"
reverse:
format: "↑"
sorter_identifiers:
ByRelativePath:
format: "rel"

@ -1,8 +1,8 @@
use crate::app;
use crate::app::HelpMenuLine;
use crate::app::{Node, ResolvedNode};
use crate::config::BlockConfig;
use crate::config::Layout;
use crate::config::PanelUiConfig;
use handlebars::Handlebars;
use indexmap::IndexSet;
use lazy_static::lazy_static;
@ -233,7 +233,7 @@ impl NodeUiMetadata {
}
}
fn block<'a>(config: BlockConfig, default_title: String) -> Block<'a> {
fn block<'a>(config: PanelUiConfig, default_title: String) -> Block<'a> {
Block::default()
.borders(TuiBorders::from_bits_truncate(
config
@ -253,13 +253,21 @@ fn block<'a>(config: BlockConfig, default_title: String) -> Block<'a> {
}
fn draw_table<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
screen_size: Rect,
layout_size: Rect,
app: &app::App,
hb: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config
.default()
.clone()
.extend(panel_config.table().clone());
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
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();
@ -430,13 +438,21 @@ fn draw_table<B: Backend>(
}
fn draw_selection<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config
.default()
.clone()
.extend(panel_config.selection().clone());
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
let selection: Vec<ListItem> = app
.selection()
.iter()
@ -457,13 +473,21 @@ fn draw_selection<B: Backend>(
}
fn draw_help_menu<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config
.default()
.clone()
.extend(panel_config.help_menu().clone());
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
let help_menu_rows = app
.mode()
.help_menu()
@ -505,13 +529,21 @@ fn draw_help_menu<B: Backend>(
}
fn draw_input_buffer<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config
.default()
.clone()
.extend(panel_config.input_and_logs().clone());
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
let input_buf = Paragraph::new(Spans::from(vec![
Span::styled(
app.config()
@ -538,41 +570,31 @@ fn draw_input_buffer<B: Backend>(
}
fn draw_sort_n_filter<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config
.default()
.clone()
.extend(panel_config.sort_and_filter().clone());
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
let ui = app.config().general().sort_and_filter_ui().clone();
let filter_by = app.explorer_config().filters();
let sort_by = app.explorer_config().sorters();
let forward = Span::styled(
ui.sort_direction_identifiers()
.forward()
.format()
.to_owned()
.unwrap_or_default(),
ui.sort_direction_identifiers()
.forward()
.style()
.clone()
.into(),
);
let reverse = Span::styled(
ui.sort_direction_identifiers()
.reverse()
.format()
.to_owned()
.unwrap_or_default(),
ui.sort_direction_identifiers()
.reverse()
.style()
.clone()
.into(),
);
let defaultui = ui.default_identifier();
let forwardui = defaultui
.clone()
.extend(ui.sort_direction_identifiers().forward().clone());
let reverseui = defaultui
.clone()
.extend(ui.sort_direction_identifiers().reverse().clone());
let mut spans = filter_by
.iter()
@ -580,35 +602,36 @@ fn draw_sort_n_filter<B: Backend>(
ui.filter_identifiers()
.get(&f.filter())
.map(|u| {
let ui = defaultui.clone().extend(u.clone());
(
Span::styled(
u.format().to_owned().unwrap_or_default(),
u.style().clone().into(),
ui.format().to_owned().unwrap_or_default(),
ui.style().clone().into(),
),
Span::raw(f.input().clone()),
Span::styled(f.input().clone(), ui.style().clone().into()),
)
})
.unwrap_or_else(|| (Span::raw("f"), Span::raw("")))
})
.chain(sort_by.iter().map(|s| {
let direction = if s.reverse() {
reverse.clone()
} else {
forward.clone()
};
let direction = if s.reverse() { &reverseui } else { &forwardui };
ui.sorter_identifiers()
.get(&s.sorter())
.map(|u| {
let ui = defaultui.clone().extend(u.clone());
(
Span::styled(
u.format().to_owned().unwrap_or_default(),
u.style().clone().into(),
ui.format().to_owned().unwrap_or_default(),
ui.style().clone().into(),
),
Span::styled(
direction.format().to_owned().unwrap_or_default(),
direction.style().clone().into(),
),
direction.clone(),
)
})
.unwrap_or_else(|| (Span::raw("s"), direction.clone()))
.unwrap_or_else(|| (Span::raw("s"), Span::raw("")))
}))
.zip(std::iter::repeat(Span::styled(
ui.separator().format().to_owned().unwrap_or_default(),
@ -628,13 +651,21 @@ fn draw_sort_n_filter<B: Backend>(
}
fn draw_logs<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
app: &app::App,
_: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config
.default()
.clone()
.extend(panel_config.input_and_logs().clone());
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
let logs_config = app.config().general().logs().clone();
let logs = app
.logs()
@ -680,13 +711,18 @@ fn draw_logs<B: Backend>(
}
pub fn draw_nothing<B: Backend>(
config: BlockConfig,
config: Option<PanelUiConfig>,
f: &mut Frame<B>,
_screen_size: Rect,
layout_size: Rect,
_app: &app::App,
app: &app::App,
_hb: &Handlebars,
) {
let panel_config = app.config().general().panel_ui();
let default_panel_config = panel_config.default().clone();
let config = config
.map(|c| default_panel_config.clone().extend(c))
.unwrap_or(default_panel_config);
let nothing = Paragraph::new("").block(block(config, "".into()));
f.render_widget(nothing, layout_size);
}

Loading…
Cancel
Save