From 686033bfcf6d34bfb180e0b0f64ae4493340bf38 Mon Sep 17 00:00:00 2001 From: Jiayi Zhao Date: Sun, 16 Feb 2020 22:01:27 -0500 Subject: move topbar functionality into widget - add ability to show file sizes in the main column --- src/ui/tui_backend.rs | 62 ++++++--------- src/ui/widgets/mod.rs | 4 +- src/ui/widgets/tui_dirlist.rs | 181 ++++++++++++++++++++++++++++++------------ src/ui/widgets/tui_topbar.rs | 41 ++++++++++ src/util/load_child.rs | 1 - src/util/menu.rs | 5 ++ 6 files changed, 203 insertions(+), 91 deletions(-) create mode 100644 src/ui/widgets/tui_topbar.rs 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>>>, } @@ -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(&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(&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 { let events = self.events; -- cgit v1.2.3