summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Zhao <jeff.no.zhao@gmail.com>2022-05-26 20:46:53 -0400
committerJeff Zhao <jeff.no.zhao@gmail.com>2022-05-26 20:46:53 -0400
commit9b48413d2f016479f1c06d4d789ae71bdd1ed0e2 (patch)
tree9f09928660367f6dc01b5a9209d52ee5b8eb5c69
parent34170bbec4a67dfc3c85ccf890ea0189fe2728ae (diff)
add vsplit view
-rw-r--r--config/joshuto.toml3
-rw-r--r--src/config/general/display_raw.rs16
-rw-r--r--src/config/option/display_option.rs12
-rw-r--r--src/ui/views/mod.rs2
-rw-r--r--src/ui/views/tui_folder_view.rs6
-rw-r--r--src/ui/views/tui_view.rs13
-rw-r--r--src/ui/views/tui_vsplit_view.rs195
-rw-r--r--src/ui/widgets/tui_dirlist.rs9
-rw-r--r--src/ui/widgets/tui_dirlist_detailed.rs12
9 files changed, 258 insertions, 10 deletions
diff --git a/config/joshuto.toml b/config/joshuto.toml
index 00d37ff..fad71a9 100644
--- a/config/joshuto.toml
+++ b/config/joshuto.toml
@@ -4,6 +4,9 @@ use_trash = true
watch_files = true
[display]
+# default, vsplit
+mode = "default"
+
automatically_count_files = false
collapse_preview = true
# ratios for parent view (optional), current view and preview
diff --git a/src/config/general/display_raw.rs b/src/config/general/display_raw.rs
index 0d44a97..0ea7a78 100644
--- a/src/config/general/display_raw.rs
+++ b/src/config/general/display_raw.rs
@@ -3,7 +3,7 @@ use std::convert::From;
use serde_derive::Deserialize;
use tui::layout::Constraint;
-use crate::config::option::{DisplayOption, LineNumberStyle};
+use crate::config::option::{DisplayMode, DisplayOption, LineNumberStyle};
use super::sort_raw::SortOptionRaw;
@@ -11,6 +11,10 @@ pub const fn default_column_ratio() -> (usize, usize, usize) {
(1, 3, 4)
}
+fn default_mode() -> String {
+ "default".to_string()
+}
+
const fn default_true() -> bool {
true
}
@@ -21,6 +25,9 @@ const fn default_scroll_offset() -> usize {
#[derive(Clone, Debug, Deserialize)]
pub struct DisplayOptionRaw {
+ #[serde(default = "default_mode")]
+ pub mode: String,
+
#[serde(default)]
pub automatically_count_files: bool,
@@ -55,6 +62,7 @@ pub struct DisplayOptionRaw {
impl std::default::Default for DisplayOptionRaw {
fn default() -> Self {
Self {
+ mode: default_mode(),
automatically_count_files: false,
collapse_preview: true,
column_ratio: None,
@@ -71,6 +79,11 @@ impl std::default::Default for DisplayOptionRaw {
impl From<DisplayOptionRaw> for DisplayOption {
fn from(raw: DisplayOptionRaw) -> Self {
+ let mode = match raw.mode.as_str() {
+ "vsplit" => DisplayMode::VSplit,
+ _ => DisplayMode::Default,
+ };
+
let column_ratio = match raw.column_ratio {
Some(s) if s.len() == 3 => (s[0], s[1], s[2]),
Some(s) if s.len() == 2 => (0, s[0], s[1]),
@@ -97,6 +110,7 @@ impl From<DisplayOptionRaw> for DisplayOption {
};
Self {
+ _mode: mode,
_automatically_count_files: raw.automatically_count_files,
_collapse_preview: raw.collapse_preview,
_scroll_offset: raw.scroll_offset,
diff --git a/src/config/option/display_option.rs b/src/config/option/display_option.rs
index 1749ece..c8c4d01 100644
--- a/src/config/option/display_option.rs
+++ b/src/config/option/display_option.rs
@@ -4,12 +4,19 @@ use tui::layout::Constraint;
use crate::config::option::SortOption;
+#[derive(Clone, Copy, Debug)]
+pub enum DisplayMode {
+ Default,
+ VSplit,
+}
+
pub const fn default_column_ratio() -> (usize, usize, usize) {
(1, 3, 4)
}
#[derive(Clone, Debug)]
pub struct DisplayOption {
+ pub _mode: DisplayMode,
pub _automatically_count_files: bool,
pub _collapse_preview: bool,
pub _scroll_offset: usize,
@@ -32,6 +39,10 @@ pub enum LineNumberStyle {
}
impl DisplayOption {
+ pub fn mode(&self) -> DisplayMode {
+ self._mode
+ }
+
pub fn automatically_count_files(&self) -> bool {
self._automatically_count_files
}
@@ -106,6 +117,7 @@ impl std::default::Default for DisplayOption {
];
Self {
+ _mode: DisplayMode::Default,
_automatically_count_files: false,
_collapse_preview: true,
column_ratio,
diff --git a/src/ui/views/mod.rs b/src/ui/views/mod.rs
index 6af230c..645694b 100644
--- a/src/ui/views/mod.rs
+++ b/src/ui/views/mod.rs
@@ -2,10 +2,12 @@ mod tui_command_menu;
mod tui_folder_view;
mod tui_textfield;
mod tui_view;
+mod tui_vsplit_view;
mod tui_worker_view;
pub use self::tui_command_menu::TuiCommandMenu;
pub use self::tui_folder_view::*;
pub use self::tui_textfield::TuiTextField;
pub use self::tui_view::TuiView;
+pub use self::tui_vsplit_view::*;
pub use self::tui_worker_view::TuiWorkerView;
diff --git a/src/ui/views/tui_folder_view.rs b/src/ui/views/tui_folder_view.rs
index 966a48e..986612f 100644
--- a/src/ui/views/tui_folder_view.rs
+++ b/src/ui/views/tui_folder_view.rs
@@ -106,14 +106,14 @@ impl<'a> Widget for TuiFolderView<'a> {
Constraint::Ratio(0, _) => (),
_ => {
if let Some(list) = curr_tab.parent_list_ref().as_ref() {
- TuiDirList::new(list).render(layout_rect[0], buf);
+ TuiDirList::new(list, true).render(layout_rect[0], buf);
}
}
}
// render current view
if let Some(list) = curr_list.as_ref() {
- TuiDirListDetailed::new(list, display_options).render(layout_rect[1], buf);
+ TuiDirListDetailed::new(list, display_options, true).render(layout_rect[1], buf);
let rect = Rect {
x: 0,
y: area.height - 1,
@@ -141,7 +141,7 @@ impl<'a> Widget for TuiFolderView<'a> {
}
if let Some(list) = child_list.as_ref() {
- TuiDirList::new(list).render(layout_rect[2], buf);
+ TuiDirList::new(list, true).render(layout_rect[2], buf);
} else if curr_entry.is_some() {
let preview_area = calculate_preview(self.context, layout_rect[2]);
if let Some(preview_area) = preview_area {
diff --git a/src/ui/views/tui_view.rs b/src/ui/views/tui_view.rs
index 2178475..d50c1a6 100644
--- a/src/ui/views/tui_view.rs
+++ b/src/ui/views/tui_view.rs
@@ -3,6 +3,8 @@ use tui::layout::Rect;
use tui::widgets::Widget;
use super::TuiFolderView;
+use super::TuiVSplitView;
+use crate::config::option::DisplayMode;
use crate::context::AppContext;
pub struct TuiView<'a> {
@@ -21,6 +23,15 @@ impl<'a> TuiView<'a> {
impl<'a> Widget for TuiView<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- TuiFolderView::new(self.context).render(area, buf);
+ let config = self.context.config_ref();
+ let display_options = config.display_options_ref();
+ match display_options.mode() {
+ DisplayMode::Default => {
+ TuiFolderView::new(self.context).render(area, buf);
+ }
+ DisplayMode::VSplit => {
+ TuiVSplitView::new(self.context).render(area, buf);
+ }
+ }
}
}
diff --git a/src/ui/views/tui_vsplit_view.rs b/src/ui/views/tui_vsplit_view.rs
new file mode 100644
index 0000000..e6b0a7d
--- /dev/null
+++ b/src/ui/views/tui_vsplit_view.rs
@@ -0,0 +1,195 @@
+use tui::buffer::Buffer;
+use tui::layout::{Constraint, Direction, Layout, Rect};
+use tui::style::{Color, Style};
+use tui::text::Span;
+use tui::widgets::{Block, Borders, Paragraph, Widget, Wrap};
+
+use crate::context::AppContext;
+use crate::ui::widgets::{TuiDirListDetailed, TuiFooter, TuiTabBar, TuiTopBar};
+
+const TAB_VIEW_WIDTH: u16 = 15;
+
+pub struct TuiVSplitView<'a> {
+ pub context: &'a AppContext,
+ pub show_bottom_status: bool,
+}
+
+impl<'a> TuiVSplitView<'a> {
+ pub fn new(context: &'a AppContext) -> Self {
+ Self {
+ context,
+ show_bottom_status: true,
+ }
+ }
+}
+
+impl<'a> Widget for TuiVSplitView<'a> {
+ fn render(self, area: Rect, buf: &mut Buffer) {
+ let tab_context = self.context.tab_context_ref();
+ let tab_index = tab_context.index;
+
+ let config = self.context.config_ref();
+ let display_options = config.display_options_ref();
+ let constraints = &[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)];
+
+ let layout_rect = if display_options.show_borders() {
+ let area = Rect {
+ y: area.top() + 1,
+ height: area.height - 2,
+ ..area
+ };
+
+ let layout = calculate_layout_with_borders(area, constraints);
+
+ let block = Block::default().borders(Borders::ALL);
+ let inner = block.inner(area);
+ block.render(area, buf);
+
+ let layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .constraints(constraints.as_ref())
+ .split(inner);
+
+ let block = Block::default().borders(Borders::RIGHT);
+ block.render(layout_rect[0], buf);
+
+ let block = Block::default().borders(Borders::LEFT);
+ block.render(layout_rect[1], buf);
+
+ layout
+ } else {
+ let area = Rect {
+ y: area.top() + 1,
+ height: area.height - 2,
+ ..area
+ };
+ calculate_layout(area, constraints)
+ };
+
+ if let Some(curr_tab) = tab_context.tab_ref(tab_index) {
+ let curr_list = curr_tab.curr_list_ref();
+
+ let layout_rect = if tab_index % 2 == 0 {
+ layout_rect[0]
+ } else {
+ layout_rect[1]
+ };
+
+ // render current view
+ if let Some(list) = curr_list.as_ref() {
+ TuiDirListDetailed::new(list, display_options, true).render(layout_rect, buf);
+ let rect = Rect {
+ x: 0,
+ y: area.height - 1,
+ width: area.width,
+ height: 1,
+ };
+
+ if self.show_bottom_status {
+ /* draw the bottom status bar */
+ if let Some(msg) = self.context.worker_context_ref().get_msg() {
+ let message_style = Style::default().fg(Color::Yellow);
+ let text = Span::styled(msg, message_style);
+ Paragraph::new(text)
+ .wrap(Wrap { trim: true })
+ .render(rect, buf);
+ } else if let Some(msg) = self.context.message_queue_ref().current_message() {
+ let text = Span::styled(msg.content.as_str(), msg.style);
+ Paragraph::new(text)
+ .wrap(Wrap { trim: true })
+ .render(rect, buf);
+ } else {
+ TuiFooter::new(list).render(rect, buf);
+ }
+ }
+ }
+
+ let topbar_width = area.width;
+ let rect = Rect {
+ x: 0,
+ y: 0,
+ width: topbar_width,
+ height: 1,
+ };
+ TuiTopBar::new(self.context, curr_tab.cwd()).render(rect, buf);
+
+ // render tabs
+ if self.context.tab_context_ref().len() > 1 {
+ let topbar_width = area.width.saturating_sub(TAB_VIEW_WIDTH);
+
+ let rect = Rect {
+ x: topbar_width,
+ y: 0,
+ width: TAB_VIEW_WIDTH,
+ height: 1,
+ };
+ let name = if let Some(ostr) = curr_tab.cwd().file_name() {
+ ostr.to_str().unwrap_or("")
+ } else {
+ ""
+ };
+ TuiTabBar::new(
+ name,
+ self.context.tab_context_ref().index,
+ self.context.tab_context_ref().len(),
+ )
+ .render(rect, buf);
+ }
+ }
+
+ let other_tab_index = if tab_index % 2 == 0 {
+ tab_index + 1
+ } else {
+ tab_index - 1
+ };
+
+ if let Some(curr_tab) = tab_context.tab_ref(other_tab_index) {
+ let curr_list = curr_tab.curr_list_ref();
+
+ let layout_rect = if other_tab_index % 2 == 0 {
+ layout_rect[0]
+ } else {
+ layout_rect[1]
+ };
+
+ if let Some(list) = curr_list.as_ref() {
+ TuiDirListDetailed::new(list, display_options, false).render(layout_rect, buf);
+ }
+ }
+ }
+}
+
+fn calculate_layout(area: Rect, constraints: &[Constraint; 2]) -> Vec<Rect> {
+ let mut layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .constraints(constraints.as_ref())
+ .split(area);
+
+ layout_rect[0] = Rect {
+ width: layout_rect[0].width - 1,
+ ..layout_rect[0]
+ };
+ layout_rect[1] = Rect {
+ width: layout_rect[1].width - 1,
+ ..layout_rect[1]
+ };
+ layout_rect
+}
+
+fn calculate_layout_with_borders(area: Rect, constraints: &[Constraint; 2]) -> Vec<Rect> {
+ let block = Block::default().borders(Borders::ALL);
+ let inner = block.inner(area);
+
+ let layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .constraints(constraints.as_ref())
+ .split(inner);
+
+ let block = Block::default().borders(Borders::RIGHT);
+ let inner1 = block.inner(layout_rect[0]);
+
+ let block = Block::default().borders(Borders::LEFT);
+ let inner2 = block.inner(layout_rect[1]);
+
+ vec![inner1, inner2]
+}
diff --git a/src/ui/widgets/tui_dirlist.rs b/src/ui/widgets/tui_dirlist.rs
index cffd8e3..54b5727 100644
--- a/src/ui/widgets/tui_dirlist.rs
+++ b/src/ui/widgets/tui_dirlist.rs
@@ -10,11 +10,12 @@ use crate::util::style;
pub struct TuiDirList<'a> {
dirlist: &'a JoshutoDirList,
+ pub focused: bool,
}
impl<'a> TuiDirList<'a> {
- pub fn new(dirlist: &'a JoshutoDirList) -> Self {
- Self { dirlist }
+ pub fn new(dirlist: &'a JoshutoDirList, focused: bool) -> Self {
+ Self { dirlist, focused }
}
}
@@ -47,7 +48,9 @@ impl<'a> Widget for TuiDirList<'a> {
.for_each(|(i, entry)| {
let ix = skip_dist + i;
- let style = if ix == curr_index {
+ let style = if !self.focused {
+ style::entry_style(entry)
+ } else if ix == curr_index {
style::entry_style(entry).add_modifier(Modifier::REVERSED)
} else {
style::entry_style(entry)
diff --git a/src/ui/widgets/tui_dirlist_detailed.rs b/src/ui/widgets/tui_dirlist_detailed.rs
index a98f466..85d5c7f 100644
--- a/src/ui/widgets/tui_dirlist_detailed.rs
+++ b/src/ui/widgets/tui_dirlist_detailed.rs
@@ -19,12 +19,18 @@ const ELLIPSIS: &str = "…";
pub struct TuiDirListDetailed<'a> {
dirlist: &'a JoshutoDirList,
display_options: &'a DisplayOption,
+ pub focused: bool,
}
impl<'a> TuiDirListDetailed<'a> {
- pub fn new(dirlist: &'a JoshutoDirList, display_options: &'a DisplayOption) -> Self {
+ pub fn new(
+ dirlist: &'a JoshutoDirList,
+ display_options: &'a DisplayOption,
+ focused: bool,
+ ) -> Self {
Self {
dirlist,
display_options,
+ focused,
}
}
}
@@ -67,7 +73,9 @@ impl<'a> Widget for TuiDirListDetailed<'a> {
.for_each(|(i, entry)| {
let ix = skip_dist + i;
- let style = if ix == curr_index {
+ let style = if !self.focused {
+ style::entry_style(entry)
+ } else if ix == curr_index {
style::entry_style(entry).add_modifier(Modifier::REVERSED)
} else {
style::entry_style(entry)