summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-16 22:01:27 -0500
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-16 22:01:27 -0500
commit686033bfcf6d34bfb180e0b0f64ae4493340bf38 (patch)
treef9318e77aaa6dd317d084676faf4eeb0d99d09d0
parent88da91c0f97dcb40e7e4cab54941af92a46d2e1a (diff)
move topbar functionality into widget
- add ability to show file sizes in the main column
-rw-r--r--src/ui/tui_backend.rs62
-rw-r--r--src/ui/widgets/mod.rs4
-rw-r--r--src/ui/widgets/tui_dirlist.rs181
-rw-r--r--src/ui/widgets/tui_topbar.rs41
-rw-r--r--src/util/load_child.rs1
-rw-r--r--src/util/menu.rs5
6 files changed, 203 insertions, 91 deletions
diff --git a/src/ui/tui_backend.rs b/src/ui/tui_backend.rs
index 2a0c53e..583f501 100644
--- a/src/ui/tui_backend.rs
+++ b/src/ui/tui_backend.rs
@@ -1,22 +1,16 @@
-use std::io::{self, Write};
+use std::io::Write;
-use termion::clear;
-use termion::cursor::Goto;
-use termion::event::Key;
-use termion::input::TermRead;
+use tui::buffer::Buffer;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::screen::AlternateScreen;
use tui::backend::TermionBackend;
use tui::layout::{Constraint, Direction, Layout, Rect};
-use tui::style::{Color, Modifier, Style};
-use tui::widgets::{Block, Borders, List, Paragraph, SelectableList, Text, Widget};
+use tui::widgets::Widget;
use unicode_width::UnicodeWidthStr;
-use super::widgets::TuiDirList;
+use super::widgets::{TuiDirList, TuiDirListDetailed, TuiTopBar};
use crate::context::JoshutoContext;
-use crate::{HOSTNAME, USERNAME};
-
pub struct TuiBackend {
pub terminal: tui::Terminal<TermionBackend<AlternateScreen<RawTerminal<std::io::Stdout>>>>,
}
@@ -46,6 +40,18 @@ impl TuiBackend {
self.terminal.draw(|mut frame| {
let f_size = frame.size();
+ {
+ let top_rect = Rect {
+ x: 0,
+ y: 0,
+ width: f_size.width,
+ height: 1,
+ };
+
+ TuiTopBar::new(curr_tab.curr_path.as_path())
+ .render(&mut frame, top_rect);
+ }
+
let constraints = match child_list {
Some(_) => [
Constraint::Ratio(1, 6),
@@ -64,40 +70,12 @@ impl TuiBackend {
.constraints(constraints.as_ref())
.split(f_size);
- {
- let username_style = Style::default()
- .fg(Color::LightGreen)
- .modifier(Modifier::BOLD);
-
- let path_style = Style::default()
- .fg(Color::LightBlue)
- .modifier(Modifier::BOLD);
-
- let curr_path_str = curr_tab.curr_path.to_string_lossy();
-
- let text = [
- Text::styled(format!("{}@{} ", *USERNAME, *HOSTNAME), username_style),
- Text::styled(curr_path_str, path_style),
- ];
-
- let top_rect = Rect {
- x: 0,
- y: 0,
- width: f_size.width,
- height: 1,
- };
-
- Paragraph::new(text.iter())
- .wrap(true)
- .render(&mut frame, top_rect);
- }
-
if let Some(curr_list) = parent_list.as_ref() {
TuiDirList::new(&curr_list).render(&mut frame, layout_rect[0]);
};
if let Some(curr_list) = curr_list.as_ref() {
- TuiDirList::new(&curr_list).render(&mut frame, layout_rect[1]);
+ TuiDirListDetailed::new(&curr_list).render(&mut frame, layout_rect[1]);
};
if let Some(curr_list) = child_list.as_ref() {
@@ -106,3 +84,9 @@ impl TuiBackend {
});
}
}
+
+impl Widget for TuiBackend {
+ fn draw(&mut self, area: Rect, buf: &mut Buffer) {
+
+ }
+}
diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs
index 9ace2b7..e619450 100644
--- a/src/ui/widgets/mod.rs
+++ b/src/ui/widgets/mod.rs
@@ -1,3 +1,5 @@
+pub mod tui_topbar;
pub mod tui_dirlist;
-pub use self::tui_dirlist::TuiDirList;
+pub use self::tui_dirlist::{TuiDirList, TuiDirListDetailed};
+pub use self::tui_topbar::TuiTopBar;
diff --git a/src/ui/widgets/tui_dirlist.rs b/src/ui/widgets/tui_dirlist.rs
index cda2da9..73425c4 100644
--- a/src/ui/widgets/tui_dirlist.rs
+++ b/src/ui/widgets/tui_dirlist.rs
@@ -1,88 +1,109 @@
-use std::io::{self, Write};
-
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::{Color, Modifier, Style};
use tui::widgets::Widget;
+use unicode_width::UnicodeWidthStr;
+use unicode_width::UnicodeWidthChar;
use crate::fs::JoshutoDirList;
-/*
-pub struct TermionWindow<'a> {
- rect: Rect,
+
+pub struct TuiDirList<'a> {
dirlist: &'a JoshutoDirList,
}
-impl<'a> TermionWindow<'a> {
- pub fn new(rect: &Rect, dirlist: &'a JoshutoDirList) -> Self {
- Self {
- rect: rect.clone(),
- dirlist
- }
- }
-
- pub fn update_rect(&mut self, rect: &Rect) {
- self.rect = rect.clone();
+impl<'a> TuiDirList<'a> {
+ pub fn new(dirlist: &'a JoshutoDirList) -> Self {
+ Self { dirlist }
}
+}
- pub fn clear<W: Write>(&self, terminal: &mut W) {
- for i in 1..self.rect.height + 1 {
- write!(terminal, "{}{}", Goto(self.rect.width, i as u16), clear::BeforeCursor);
+impl<'a> Widget for TuiDirList<'a> {
+ fn draw(&mut self, area: Rect, buf: &mut Buffer) {
+ if area.width < 1 || area.height < 1 {
+ return;
}
- }
- pub fn draw<W: Write>(&self, terminal: &mut W) {
- if self.rect.height < 4 {
+ if area.width < 4 {
return;
}
+
+ let x = area.left();
+ let y = area.top();
+
let dir_len = self.dirlist.contents.len();
if dir_len == 0 {
- write!(terminal, "{}EMPTY", Goto(self.rect.x, self.rect.y));
+ let style = Style::default().bg(Color::Red).fg(Color::White);
+ buf.set_stringn(x, y, "empty", area.width as usize, style);
return;
}
let curr_index = self.dirlist.index.unwrap();
+ let skip_dist = curr_index / area.height as usize * area.height as usize;
+
+ let screen_index = if skip_dist > 0 {
+ curr_index % skip_dist
+ } else {
+ curr_index
+ };
- let height = self.rect.height as usize;
+ let area_width = area.width as usize - 1;
+ for (i, entry) in self
+ .dirlist
+ .contents
+ .iter()
+ .skip(skip_dist)
+ .enumerate()
+ .take(area.height as usize)
+ {
+ let name = entry.file_name();
+ let mut style = entry.get_style();
- for (i, entry) in self.dirlist.contents.iter().enumerate() {
- let goto_i = i + 1;
- if goto_i > height {
- break;
+ if i == screen_index {
+ style = style.modifier(Modifier::REVERSED);
}
- let fg = entry.get_fg_color();
- let bg = entry.get_bg_color();
- let file_ansi_text = entry.as_ansi_text();
-
- if i == curr_index {
- write!(terminal, "{}{}{}{}{}",
- Goto(self.rect.x, goto_i as u16),
- style::Invert,
- bg.bg_str(),
- fg.fg_str(),
- file_ansi_text);
+ let name_width = name.width();
+ if name_width < area_width {
+ buf.set_stringn(x, y + i as u16,
+ name,
+ area_width, style);
} else {
- write!(terminal, "{}{}{}{}{}",
- Goto(self.rect.x, goto_i as u16),
- style::Reset,
- bg.bg_str(),
- fg.fg_str(),
- file_ansi_text);
+ match name.rfind('.') {
+ None => {
+ buf.set_stringn(x, y + i as u16,
+ name,
+ area_width, style);
+ }
+ Some(p_ind) => {
+ let ext_width = name[p_ind..].width();
+ let file_name_width = area_width - ext_width - 1;
+
+ buf.set_stringn(x, y + i as u16,
+ &name[..p_ind],
+ file_name_width, style);
+ buf.set_string(x + file_name_width as u16, y + i as u16,
+ "…", style);
+ buf.set_string(x + file_name_width as u16 + 1, y + i as u16,
+ &name[p_ind..], style);
+ }
+ }
}
}
}
}
-*/
-pub struct TuiDirList<'a> {
+
+const FILE_SIZE_WIDTH: usize = 8;
+
+pub struct TuiDirListDetailed<'a> {
dirlist: &'a JoshutoDirList,
}
-impl<'a> TuiDirList<'a> {
+impl<'a> TuiDirListDetailed<'a> {
pub fn new(dirlist: &'a JoshutoDirList) -> Self {
Self { dirlist }
}
}
-impl<'a> Widget for TuiDirList<'a> {
+impl<'a> Widget for TuiDirListDetailed<'a> {
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
if area.width < 1 || area.height < 1 {
return;
@@ -97,7 +118,9 @@ impl<'a> Widget for TuiDirList<'a> {
let dir_len = self.dirlist.contents.len();
if dir_len == 0 {
- let style = Style::default().bg(Color::Red).fg(Color::White);
+ let style = Style::default()
+ .bg(Color::Red)
+ .fg(Color::White);
buf.set_stringn(x, y, "empty", area.width as usize, style);
return;
}
@@ -110,6 +133,8 @@ impl<'a> Widget for TuiDirList<'a> {
} else {
curr_index
};
+
+ let area_width = area.width as usize;
for (i, entry) in self
.dirlist
.contents
@@ -124,7 +149,63 @@ impl<'a> Widget for TuiDirList<'a> {
if i == screen_index {
style = style.modifier(Modifier::REVERSED);
}
- buf.set_stringn(x, y + i as u16, name, area.width as usize, style);
+ let file_type = entry.metadata.file_type;
+
+ let name_width = name.width();
+ if file_type.is_dir() {
+ buf.set_stringn(x, y + i as u16,
+ name,
+ area_width, style);
+ continue;
+ }
+
+ if name_width < area_width - FILE_SIZE_WIDTH {
+ buf.set_stringn(x, y + i as u16,
+ name,
+ area_width - FILE_SIZE_WIDTH, style);
+ } else {
+ match name.rfind('.') {
+ None => {
+ buf.set_stringn(x, y + i as u16,
+ name,
+ area_width - FILE_SIZE_WIDTH, style);
+ }
+ Some(p_ind) => {
+ let ext_width = name[p_ind..].width();
+ let file_name_width = area_width - FILE_SIZE_WIDTH - ext_width - 1;
+
+ buf.set_stringn(x, y + i as u16,
+ &name[..p_ind],
+ file_name_width, style);
+ buf.set_string(x + file_name_width as u16, y + i as u16,
+ "…", style);
+ buf.set_string(x + file_name_width as u16 + 1, y + i as u16,
+ &name[p_ind..], style);
+ }
+ }
+ }
+ let file_size_string = file_size_to_string(entry.metadata.len as f64);
+ buf.set_string(x + (area_width - FILE_SIZE_WIDTH) as u16, y + i as u16,
+ file_size_string, style);
}
}
}
+
+fn file_size_to_string(mut file_size: f64) -> String {
+ const FILE_UNITS: [&str; 6] = ["B", "K", "M", "G", "T", "E"];
+ const CONV_RATE: f64 = 1024.0;
+
+ let mut index = 0;
+ while file_size > CONV_RATE {
+ file_size /= CONV_RATE;
+ index += 1;
+ }
+
+ if file_size >= 100.0 {
+ format!("{:>4.0} {}", file_size, FILE_UNITS[index])
+ } else if file_size >= 10.0 {
+ format!("{:>4.1} {}", file_size, FILE_UNITS[index])
+ } else {
+ format!("{:>4.2} {}", file_size, FILE_UNITS[index])
+ }
+}
diff --git a/src/ui/widgets/tui_topbar.rs b/src/ui/widgets/tui_topbar.rs
new file mode 100644
index 0000000..6ff46b7
--- /dev/null
+++ b/src/ui/widgets/tui_topbar.rs
@@ -0,0 +1,41 @@
+use std::path::Path;
+
+use tui::buffer::Buffer;
+use tui::layout::Rect;
+use tui::style::{Color, Modifier, Style};
+use tui::widgets::{Paragraph, Text, Widget};
+
+use crate::{HOSTNAME, USERNAME};
+
+pub struct TuiTopBar<'a> {
+ path: &'a Path,
+}
+
+impl<'a> TuiTopBar<'a> {
+ pub fn new(path: &'a Path) -> Self {
+ Self { path }
+ }
+}
+
+impl<'a> Widget for TuiTopBar<'a> {
+ fn draw(&mut self, area: Rect, buf: &mut Buffer) {
+ let username_style = Style::default()
+ .fg(Color::LightGreen)
+ .modifier(Modifier::BOLD);
+
+ let path_style = Style::default()
+ .fg(Color::LightBlue)
+ .modifier(Modifier::BOLD);
+
+ let curr_path_str = self.path.to_string_lossy();
+
+ let text = [
+ Text::styled(format!("{}@{} ", *USERNAME, *HOSTNAME), username_style),
+ Text::styled(curr_path_str, path_style),
+ ];
+
+ Paragraph::new(text.iter())
+ .wrap(true)
+ .draw(area, buf);
+ }
+}
diff --git a/src/util/load_child.rs b/src/util/load_child.rs
index 386cd63..78872b8 100644
--- a/src/util/load_child.rs
+++ b/src/util/load_child.rs
@@ -2,7 +2,6 @@ use std::path::PathBuf;
use crate::context::JoshutoContext;
use crate::history::DirectoryHistory;
-use crate::ui::TuiBackend;
pub struct LoadChild {}
diff --git a/src/util/menu.rs b/src/util/menu.rs
index 6a9b5b2..d60c9a1 100644
--- a/src/util/menu.rs
+++ b/src/util/menu.rs
@@ -13,6 +13,7 @@ use tui::widgets::{Block, Borders, List, Paragraph, Text, Widget};
use tui::Terminal;
use unicode_width::UnicodeWidthStr;
+use crate::context::JoshutoContext;
use crate::ui::TuiBackend;
use crate::util::event::{Event, Events};
@@ -26,6 +27,10 @@ impl<'a> OptionMenu<'a> {
Self { backend, events }
}
+ pub fn render(&mut self, context: &JoshutoContext) {
+
+ }
+
pub fn get_option(&mut self, options: &[&str]) -> Option<Key> {
let events = self.events;