summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLzzzzzt <101313294+Lzzzzzt@users.noreply.github.com>2023-08-12 20:50:20 +0800
committerGitHub <noreply@github.com>2023-08-12 08:50:20 -0400
commit0eec61eb82d10b8b9f76c8b5c9e2a0fae0514a1e (patch)
tree7195f1d89bc046597feb76b7d8d875b116b61071
parentfc2fdf3ca79cbfba18350dd5faaf548d7a98f96f (diff)
feat: tab bar can show current dir name (#390)
* add user & group on footer * linemode have more options * linemode have more options * linemode have more options * make tarbar title not hardcoded * topbar width with modified tab width * change the ellipsis
-rw-r--r--src/commands/mod.rs1
-rw-r--r--src/commands/tab_bar_mode.rs9
-rw-r--r--src/config/general/tab_raw.rs14
-rw-r--r--src/config/option/tab_option.rs63
-rw-r--r--src/context/app_context.rs2
-rw-r--r--src/context/tab_context.rs37
-rw-r--r--src/key_command/command.rs5
-rw-r--r--src/key_command/constants.rs1
-rw-r--r--src/key_command/impl_appcommand.rs1
-rw-r--r--src/key_command/impl_appexecute.rs6
-rw-r--r--src/key_command/impl_comment.rs11
-rw-r--r--src/key_command/impl_from_str.rs8
-rw-r--r--src/tab/tab_struct.rs9
-rw-r--r--src/ui/views/tui_folder_view.rs10
-rw-r--r--src/ui/widgets/tui_tab.rs36
-rw-r--r--src/ui/widgets/tui_topbar.rs34
-rw-r--r--src/util/format.rs24
17 files changed, 217 insertions, 54 deletions
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 5489a1c..0f821bd 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -30,6 +30,7 @@ pub mod show_tasks;
pub mod sort;
pub mod sub_process;
pub mod subdir_fzf;
+pub mod tab_bar_mode;
pub mod tab_ops;
pub mod touch_file;
pub mod uimodes;
diff --git a/src/commands/tab_bar_mode.rs b/src/commands/tab_bar_mode.rs
new file mode 100644
index 0000000..839ab49
--- /dev/null
+++ b/src/commands/tab_bar_mode.rs
@@ -0,0 +1,9 @@
+use crate::{config::option::TabBarDisplayMode, context::AppContext, error::JoshutoError};
+
+pub fn set_tab_bar_display_mode(
+ context: &mut AppContext,
+ mode: &TabBarDisplayMode,
+) -> Result<(), JoshutoError> {
+ context.tab_context_mut().display.mode = *mode;
+ Ok(())
+}
diff --git a/src/config/general/tab_raw.rs b/src/config/general/tab_raw.rs
index a6c4240..47914f1 100644
--- a/src/config/general/tab_raw.rs
+++ b/src/config/general/tab_raw.rs
@@ -2,23 +2,33 @@ use std::convert::From;
use serde_derive::Deserialize;
-use crate::config::option::TabOption;
+use crate::config::option::{TabBarDisplayMode, TabOption};
use crate::tab::TabHomePage;
fn default_home_page() -> String {
"home".to_string()
}
+const fn default_max_len() -> usize {
+ 16
+}
+
#[derive(Clone, Debug, Deserialize)]
pub struct TabOptionRaw {
#[serde(default = "default_home_page")]
pub home_page: String,
+ #[serde(default)]
+ pub display_mode: TabBarDisplayMode,
+ #[serde(default = "default_max_len")]
+ pub max_len: usize,
}
impl std::default::Default for TabOptionRaw {
fn default() -> Self {
Self {
home_page: default_home_page(),
+ display_mode: TabBarDisplayMode::default(),
+ max_len: 16,
}
}
}
@@ -27,6 +37,6 @@ impl From<TabOptionRaw> for TabOption {
fn from(raw: TabOptionRaw) -> Self {
let home_page = TabHomePage::from_str(raw.home_page.as_str()).unwrap_or(TabHomePage::Home);
- Self::new(home_page)
+ Self::new(home_page, raw.display_mode, raw.max_len)
}
}
diff --git a/src/config/option/tab_option.rs b/src/config/option/tab_option.rs
index b729254..efeca59 100644
--- a/src/config/option/tab_option.rs
+++ b/src/config/option/tab_option.rs
@@ -1,13 +1,27 @@
-use crate::tab::TabHomePage;
+use std::str::FromStr;
+
+use serde_derive::Deserialize;
+
+use crate::{
+ error::{JoshutoError, JoshutoErrorKind},
+ tab::TabHomePage,
+};
#[derive(Clone, Debug)]
pub struct TabOption {
pub _home_page: TabHomePage,
+ pub display: TabBarDisplayOption,
}
impl TabOption {
- pub fn new(_home_page: TabHomePage) -> Self {
- Self { _home_page }
+ pub fn new(_home_page: TabHomePage, display_mode: TabBarDisplayMode, max_len: usize) -> Self {
+ Self {
+ _home_page,
+ display: TabBarDisplayOption {
+ mode: display_mode,
+ max_len,
+ },
+ }
}
pub fn home_page(&self) -> TabHomePage {
self._home_page
@@ -18,6 +32,49 @@ impl std::default::Default for TabOption {
fn default() -> Self {
Self {
_home_page: TabHomePage::Home,
+ display: TabBarDisplayOption::default(),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct TabBarDisplayOption {
+ pub mode: TabBarDisplayMode,
+ pub max_len: usize,
+}
+
+impl Default for TabBarDisplayOption {
+ fn default() -> Self {
+ Self {
+ mode: Default::default(),
+ max_len: 16,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, Deserialize, Default)]
+pub enum TabBarDisplayMode {
+ #[serde(rename = "num")]
+ Number,
+ #[default]
+ #[serde(rename = "dir")]
+ Directory,
+ #[serde(rename = "all")]
+ All,
+}
+
+impl FromStr for TabBarDisplayMode {
+ type Err = JoshutoError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "num" => Ok(Self::Number),
+ "dir" => Ok(Self::Directory),
+ "all" => Ok(Self::All),
+ s => Err(JoshutoError::new(
+ JoshutoErrorKind::UnrecognizedArgument,
+ format!("tab_bar_mode: `{}` unknown argument.", s),
+ )),
}
}
}
diff --git a/src/context/app_context.rs b/src/context/app_context.rs
index 7971562..b283783 100644
--- a/src/context/app_context.rs
+++ b/src/context/app_context.rs
@@ -71,7 +71,7 @@ impl AppContext {
quit: QuitAction::DoNot,
events,
args,
- tab_context: TabContext::new(),
+ tab_context: TabContext::new(config.tab_options_ref().display),
local_state: None,
search_context: None,
message_queue: MessageQueue::new(),
diff --git a/src/context/tab_context.rs b/src/context/tab_context.rs
index 1393b06..ae05f55 100644
--- a/src/context/tab_context.rs
+++ b/src/context/tab_context.rs
@@ -3,18 +3,23 @@ use std::collections::HashMap;
use uuid::Uuid;
+use crate::config::option::{TabBarDisplayMode, TabBarDisplayOption};
use crate::tab::JoshutoTab;
#[derive(Default)]
pub struct TabContext {
pub index: usize,
pub tab_order: Vec<Uuid>,
+ pub display: TabBarDisplayOption,
tabs: HashMap<Uuid, JoshutoTab>,
}
impl TabContext {
- pub fn new() -> Self {
- Self::default()
+ pub fn new(display: TabBarDisplayOption) -> Self {
+ Self {
+ display,
+ ..Default::default()
+ }
}
pub fn len(&self) -> usize {
self.tab_order.len()
@@ -56,4 +61,32 @@ impl TabContext {
pub fn iter_mut(&mut self) -> IterMut<Uuid, JoshutoTab> {
self.tabs.iter_mut()
}
+
+ pub fn tab_title_width(&self) -> usize {
+ self.tabs
+ .values()
+ .map(|tab| {
+ let title_len = tab.tab_title().len();
+ (title_len > self.display.max_len)
+ .then(|| self.display.max_len)
+ .unwrap_or(title_len)
+ })
+ .sum()
+ }
+
+ pub fn tab_area_width(&self) -> usize {
+ let width_without_divider = match self.display.mode {
+ TabBarDisplayMode::Number => (1..=self.len()).map(|n| n.to_string().len() + 2).sum(), // each number has a horizontal padding(1 char width)
+ TabBarDisplayMode::Directory => self.tab_title_width(),
+ TabBarDisplayMode::All => {
+ // [number][: ](width = 2)[title]
+ self.tab_title_width()
+ + (1..=self.len())
+ .map(|n| n.to_string().len() + 2)
+ .sum::<usize>()
+ }
+ };
+
+ width_without_divider + 3 * (self.len() - 1)
+ }
}
diff --git a/src/key_command/command.rs b/src/key_command/command.rs
index c372fe4..b3bfc7e 100644
--- a/src/key_command/command.rs
+++ b/src/key_command/command.rs
@@ -1,7 +1,9 @@
use std::path;
use crate::commands::quit::QuitAction;
-use crate::config::option::{LineMode, LineNumberStyle, NewTabMode, SelectOption, SortType};
+use crate::config::option::{
+ LineMode, LineNumberStyle, NewTabMode, SelectOption, SortType, TabBarDisplayMode,
+};
use crate::io::FileOperationOptions;
#[derive(Clone, Debug)]
@@ -132,6 +134,7 @@ pub enum Command {
pattern: String,
},
+ SetTabBarDisplayMode(TabBarDisplayMode),
NewTab {
mode: NewTabMode,
},
diff --git a/src/key_command/constants.rs b/src/key_command/constants.rs
index 28b11ea..f834883 100644
--- a/src/key_command/constants.rs
+++ b/src/key_command/constants.rs
@@ -67,6 +67,7 @@ cmd_constants![
(CMD_SUBPROCESS_FOREGROUND, "shell"),
(CMD_SUBPROCESS_BACKGROUND, "spawn"),
(CMD_SHOW_TASKS, "show_tasks"),
+ (CMD_SET_TAB_BAR_MODE, "tab_bar_mode"),
(CMD_TAB_SWITCH, "tab_switch"),
(CMD_TAB_SWITCH_INDEX, "tab_switch_index"),
(CMD_TOGGLE_HIDDEN, "toggle_hidden"),
diff --git a/src/key_command/impl_appcommand.rs b/src/key_command/impl_appcommand.rs
index 72fccf9..3215f76 100644
--- a/src/key_command/impl_appcommand.rs
+++ b/src/key_command/impl_appcommand.rs
@@ -82,6 +82,7 @@ impl AppCommand for Command {
Self::SwitchLineNums(_) => CMD_SWITCH_LINE_NUMBERS,
Self::SetLineMode(_) => CMD_SET_LINEMODE,
+ Self::SetTabBarDisplayMode(_) => CMD_SET_TAB_BAR_MODE,
Self::TabSwitch { .. } => CMD_TAB_SWITCH,
Self::TabSwitchIndex { .. } => CMD_TAB_SWITCH_INDEX,
Self::ToggleHiddenFiles => CMD_TOGGLE_HIDDEN,
diff --git a/src/key_command/impl_appexecute.rs b/src/key_command/impl_appexecute.rs
index c369dcc..7616fb3 100644
--- a/src/key_command/impl_appexecute.rs
+++ b/src/key_command/impl_appexecute.rs
@@ -128,9 +128,11 @@ impl AppExecute for Command {
Self::ToggleHiddenFiles => show_hidden::toggle_hidden(context),
+ Self::SetTabBarDisplayMode(mode) => {
+ tab_bar_mode::set_tab_bar_display_mode(context, mode)
+ }
Self::TabSwitch { offset } => {
- tab_ops::tab_switch(context, *offset)?;
- Ok(())
+ tab_ops::tab_switch(context, *offset).map_err(|e| e.into())
}
Self::TabSwitchIndex { index } => tab_ops::tab_switch_index(context, *index),
Self::Help => show_help::help_loop(context, backend, keymap_t),
diff --git a/src/key_command/impl_comment.rs b/src/key_command/impl_comment.rs
index a7f373f..4635cdf 100644
--- a/src/key_command/impl_comment.rs
+++ b/src/key_command/impl_comment.rs
@@ -1,4 +1,4 @@
-use crate::config::option::{LineMode, SortType};
+use crate::config::option::{LineMode, SortType, TabBarDisplayMode};
use crate::io::FileOperationOptions;
use super::{Command, CommandComment};
@@ -117,6 +117,15 @@ impl CommandComment for Command {
Self::Filter { .. } => "Filter directory list",
+ Self::SetTabBarDisplayMode(mode) => match mode {
+ TabBarDisplayMode::Number => "TabBar only display with number ( 1 | 2 | 3 )",
+ TabBarDisplayMode::Directory => {
+ "TabBar only display with directory ( dir1 | dir2 | dir3 )"
+ }
+ TabBarDisplayMode::All => {
+ "TabBar display with numbar and directory ( 1: dir1 | 2: dir2 )"
+ }
+ },
Self::TabSwitch { .. } => "Switch to the next tab",
Self::TabSwitchIndex { .. } => "Switch to a given tab",
Self::Help => "Open this help page",
diff --git a/src/key_command/impl_from_str.rs b/src/key_command/impl_from_str.rs
index d684bf9..be0ce01 100644
--- a/src/key_command/impl_from_str.rs
+++ b/src/key_command/impl_from_str.rs
@@ -1,7 +1,9 @@
use std::path;
use crate::commands::quit::QuitAction;
-use crate::config::option::{LineMode, LineNumberStyle, NewTabMode, SelectOption, SortType};
+use crate::config::option::{
+ LineMode, LineNumberStyle, NewTabMode, SelectOption, SortType, TabBarDisplayMode,
+};
use crate::error::{JoshutoError, JoshutoErrorKind};
use crate::io::FileOperationOptions;
use crate::util::unix;
@@ -353,6 +355,10 @@ impl std::str::FromStr for Command {
}
} else if command == CMD_SET_LINEMODE {
Ok(Self::SetLineMode(LineMode::from_string(arg)?))
+ } else if command == CMD_SET_TAB_BAR_MODE {
+ Ok(Self::SetTabBarDisplayMode(TabBarDisplayMode::from_str(
+ arg,
+ )?))
} else if command == CMD_TAB_SWITCH {
match arg.parse::<i32>() {
Ok(s) => Ok(Self::TabSwitch { offset: s }),
diff --git a/src/tab/tab_struct.rs b/src/tab/tab_struct.rs
index 1afb642..7db65b4 100644
--- a/src/tab/tab_struct.rs
+++ b/src/tab/tab_struct.rs
@@ -1,4 +1,6 @@
+use std::borrow::Cow;
use std::collections::HashMap;
+use std::ffi::OsStr;
use std::path;
use crate::config::option::{DisplayOption, TabDisplayOption};
@@ -108,4 +110,11 @@ impl JoshutoTab {
self.history.get_mut(child_path.as_path())
}
+
+ pub fn tab_title(&self) -> Cow<'_, str> {
+ self.cwd()
+ .file_name()
+ .unwrap_or_else(|| OsStr::new("/"))
+ .to_string_lossy()
+ }
}
diff --git a/src/ui/views/tui_folder_view.rs b/src/ui/views/tui_folder_view.rs
index 2a6204b..c9ffeba 100644
--- a/src/ui/views/tui_folder_view.rs
+++ b/src/ui/views/tui_folder_view.rs
@@ -110,13 +110,13 @@ impl<'a> TuiFolderView<'a> {
}
}
- pub fn tab_area(&self, area: &Rect, num_tabs: usize) -> Rect {
+ pub fn tab_area(&self, area: &Rect, tabs_width: usize) -> Rect {
// render tabs
- let tab_width = (num_tabs * 8) as u16;
- let tab_width = if tab_width > area.width {
+ let tabs_width = tabs_width as u16;
+ let tab_width = if tabs_width > area.width {
area.width
} else {
- tab_width
+ tabs_width
};
let topbar_x = area.width.saturating_sub(tab_width);
@@ -239,7 +239,7 @@ impl<'a> Widget for TuiFolderView<'a> {
TuiTopBar::new(self.context, curr_tab_cwd).render(topbar_area, buf);
// render tabs
- let tab_area = self.tab_area(&area, self.context.tab_context_ref().len());
+ let tab_area = self.tab_area(&area, self.context.tab_context_ref().tab_area_width());
TuiTabBar::new(self.context.tab_context_ref()).render(tab_area, buf);
}
}
diff --git a/src/ui/widgets/tui_tab.rs b/src/ui/widgets/tui_tab.rs
index f724b86..d82dcbe 100644
--- a/src/ui/widgets/tui_tab.rs
+++ b/src/ui/widgets/tui_tab.rs
@@ -1,11 +1,11 @@
-use std::ffi::OsStr;
-
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Paragraph, Widget, Wrap};
+use crate::config::option::TabBarDisplayMode;
use crate::context::TabContext;
+use crate::util::format::format_tab_bar_title_string;
use crate::THEME_T;
pub struct TuiTabBar<'a> {
@@ -34,23 +34,27 @@ impl<'a> Widget for TuiTabBar<'a> {
regular_style
};
if let Some(curr_tab) = self.context.tab_ref(tab_id) {
- let preview_text: String = curr_tab
- .cwd()
- .file_name()
- .unwrap_or_else(|| OsStr::new("/"))
- .to_string_lossy()
- .chars()
- .take(4)
- .collect();
-
- spans_vec.push(Span::styled(
- format!("{}: {}", i + 1, preview_text),
- curr_style,
- ));
- spans_vec.push(Span::styled(" ", regular_style));
+ let preview_text = match self.context.display.mode {
+ TabBarDisplayMode::Number => format!(" {} ", i + 1),
+ TabBarDisplayMode::Directory => format_tab_bar_title_string(
+ self.context.display.max_len,
+ None,
+ curr_tab.tab_title(),
+ ),
+ TabBarDisplayMode::All => format_tab_bar_title_string(
+ self.context.display.max_len,
+ Some(i),
+ curr_tab.tab_title(),
+ ),
+ };
+
+ spans_vec.push(Span::styled(preview_text, curr_style));
+ spans_vec.push(Span::styled(" | ", regular_style));
}
}
+ spans_vec.pop();
+
Paragraph::new(Line::from(spans_vec))
.wrap(Wrap { trim: true })
.render(area, buf);
diff --git a/src/ui/widgets/tui_topbar.rs b/src/ui/widgets/tui_topbar.rs
index 700f318..10ed131 100644
--- a/src/ui/widgets/tui_topbar.rs
+++ b/src/ui/widgets/tui_topbar.rs
@@ -32,8 +32,7 @@ impl<'a> Widget for TuiTopBar<'a> {
let mut ellipses = None;
let mut curr_path_str = self.path.to_string_lossy().into_owned();
- let num_tabs = self.context.tab_context_ref().len();
- let tab_width = num_tabs * 8;
+ let tab_width = self.context.tab_context_ref().tab_area_width();
let name_width = USERNAME.as_str().len() + HOSTNAME.as_str().len() + 2;
if tab_width + name_width > area.width as usize {
@@ -79,24 +78,19 @@ impl<'a> Widget for TuiTopBar<'a> {
.add_modifier(Modifier::BOLD)
};
- let text = match ellipses {
- Some(s) => Line::from(vec![
- Span::styled(USERNAME.as_str(), username_style),
- Span::styled("@", username_style),
- Span::styled(HOSTNAME.as_str(), username_style),
- Span::styled(" ", username_style),
- s,
- Span::styled(curr_path_str, path_style),
- ]),
- None => Line::from(vec![
- Span::styled(USERNAME.as_str(), username_style),
- Span::styled("@", username_style),
- Span::styled(HOSTNAME.as_str(), username_style),
- Span::styled(" ", username_style),
- Span::styled(curr_path_str, path_style),
- ]),
- };
+ let mut text = vec![
+ Span::styled(USERNAME.as_str(), username_style),
+ Span::styled("@", username_style),
+ Span::styled(HOSTNAME.as_str(), username_style),
+ Span::styled(" ", username_style),
+ ];
+
+ if let Some(s) = ellipses {
+ text.push(s);
+ }
+
+ text.extend([Span::styled(curr_path_str, path_style)]);
- Paragraph::new(text).render(area, buf);
+ Paragraph::new(Line::from(text)).render(area, buf);
}
}
diff --git a/src/util/format.rs b/src/util/format.rs
index 3079840..3a9bc54 100644
--- a/src/util/format.rs
+++ b/src/util/format.rs
@@ -26,3 +26,27 @@ pub fn mtime_to_string(mtime: time::SystemTime) -> String {
let datetime: chrono::DateTime<chrono::offset::Local> = mtime.into();
datetime.format(MTIME_FORMATTING).to_string()
}
+
+pub fn format_tab_bar_title_string(
+ max_len: usize,
+ number: Option<usize>,
+ title: impl Into<String>,
+) -> String {
+ let title: String = title.into();
+
+ if let Some(number) = number {
+ if title.len() > max_len {
+ format!(
+ "{}: {}…",
+ number + 1,
+ title.chars().take(max_len - 1).collect::<String>()
+ )
+ } else {
+ format!("{}: {}", number + 1, title)
+ }
+ } else if title.len() > max_len {
+ format!("{}…", title.chars().take(max_len - 1).collect::<String>())
+ } else {
+ title.to_string()
+ }
+}