From a0fffdc01b04aa560ff249e6686a841f1349a88f Mon Sep 17 00:00:00 2001 From: Jeff Zhao Date: Tue, 19 Jan 2021 13:10:36 -0500 Subject: add mouse clicking support - add optional features --- Cargo.toml | 7 +- src/fs/entry.rs | 5 ++ src/run.rs | 9 +-- src/ui/views/tui_worker_view.rs | 4 +- src/ui/widgets/tui_menu.rs | 6 +- src/ui/widgets/tui_prompt.rs | 4 +- src/ui/widgets/tui_textfield.rs | 4 +- src/util/input.rs | 148 ++++++++++++++++++++++++++++++++++++++++ src/util/input_process.rs | 104 ---------------------------- src/util/key_mapping.rs | 46 ------------- src/util/mod.rs | 5 +- src/util/to_string.rs | 47 +++++++++++++ 12 files changed, 222 insertions(+), 167 deletions(-) create mode 100644 src/util/input.rs delete mode 100644 src/util/input_process.rs create mode 100644 src/util/to_string.rs diff --git a/Cargo.toml b/Cargo.toml index d4f87d1..6da0236 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,7 @@ unicode-width = "^0" users = "^0" whoami = "^0" xdg = "^2" -phf = { version = "^0", features = ["macros"] } -# notify = { version = "5.0.0-pre.2" } +phf = { version = "^0", features = ["macros"], optional = true } # fs_extra = "*" # lazy_static = "*" @@ -49,5 +48,7 @@ phf = { version = "^0", features = ["macros"] } # path = "lib/wordexp-rs" [features] +devicons = [ "phf" ] file_mimetype = [] -default = [] +mouse = [] +default = [ "phf", "mouse" ] diff --git a/src/fs/entry.rs b/src/fs/entry.rs index 5362c76..d2a694b 100644 --- a/src/fs/entry.rs +++ b/src/fs/entry.rs @@ -3,6 +3,8 @@ use std::{fs, path}; use tui::style::Style; use crate::fs::{FileType, JoshutoMetadata}; + +#[cfg(feature = "devicons")] use crate::util::devicons::*; use crate::util::unix; @@ -28,6 +30,7 @@ impl JoshutoDirEntry { .to_string_lossy() .to_string(); + #[cfg(feature = "devicons")] let label = if show_icons { let icon = match metadata.file_type() { FileType::Directory => DIR_NODE_EXACT_MATCHES @@ -56,6 +59,8 @@ impl JoshutoDirEntry { } else { name.clone() }; + #[cfg(not(feature = "devicons"))] + let label = name.clone(); Ok(Self { name, diff --git a/src/run.rs b/src/run.rs index d65b4f7..36bdfd1 100644 --- a/src/run.rs +++ b/src/run.rs @@ -8,8 +8,9 @@ use crate::ui; use crate::ui::views::TuiView; use crate::ui::widgets::TuiCommandMenu; use crate::util::event::JoshutoEvent; -use crate::util::input_process; +use crate::util::input; use crate::util::load_child::LoadChild; +use crate::util::to_string::ToString; pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io::Result<()> { let mut backend: ui::TuiBackend = ui::TuiBackend::new()?; @@ -38,7 +39,7 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: }; match event { JoshutoEvent::Termion(Event::Mouse(event)) => { - input_process::process_mouse(event, &mut context, &mut backend); + input::process_mouse(event, &mut context, &mut backend); } JoshutoEvent::Termion(key) => { if !context.message_queue_ref().is_empty() { @@ -46,7 +47,7 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: } match keymap_t.as_ref().get(&key) { None => { - context.push_msg(format!("Unmapped input: {:?}", key)); + context.push_msg(format!("Unmapped input: {}", key.to_string())); } Some(CommandKeybind::SimpleKeybind(command)) => { if let Err(e) = command.execute(&mut context, &mut backend) { @@ -68,7 +69,7 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: } context.flush_event(); } - event => input_process::process_noninteractive(event, &mut context), + event => input::process_noninteractive(event, &mut context), } } diff --git a/src/ui/views/tui_worker_view.rs b/src/ui/views/tui_worker_view.rs index 58eaef2..09b43b9 100644 --- a/src/ui/views/tui_worker_view.rs +++ b/src/ui/views/tui_worker_view.rs @@ -6,7 +6,7 @@ use crate::context::JoshutoContext; use crate::ui::widgets::TuiWorker; use crate::ui::TuiBackend; use crate::util::event::JoshutoEvent; -use crate::util::input_process; +use crate::util::input; pub struct TuiWorkerView {} @@ -42,7 +42,7 @@ impl TuiWorkerView { } context.flush_event(); } - event => input_process::process_noninteractive(event, context), + event => input::process_noninteractive(event, context), }; } } diff --git a/src/ui/widgets/tui_menu.rs b/src/ui/widgets/tui_menu.rs index 38eed64..0f2570d 100644 --- a/src/ui/widgets/tui_menu.rs +++ b/src/ui/widgets/tui_menu.rs @@ -12,8 +12,8 @@ use crate::context::JoshutoContext; use crate::ui::views::TuiView; use crate::ui::TuiBackend; use crate::util::event::JoshutoEvent; -use crate::util::input_process; -use crate::util::key_mapping::ToString; +use crate::util::input; +use crate::util::to_string::ToString; const BORDER_HEIGHT: usize = 1; const BOTTOM_MARGIN: usize = 1; @@ -95,7 +95,7 @@ impl TuiCommandMenu { } context.flush_event(); } - event => input_process::process_noninteractive(event, context), + event => input::process_noninteractive(event, context), } } } diff --git a/src/ui/widgets/tui_prompt.rs b/src/ui/widgets/tui_prompt.rs index c4d0c1c..581149c 100644 --- a/src/ui/widgets/tui_prompt.rs +++ b/src/ui/widgets/tui_prompt.rs @@ -8,7 +8,7 @@ use crate::context::JoshutoContext; use crate::ui::views::TuiView; use crate::ui::TuiBackend; use crate::util::event::JoshutoEvent; -use crate::util::input_process; +use crate::util::input; pub struct TuiPrompt<'a> { prompt: &'a str, @@ -62,7 +62,7 @@ impl<'a> TuiPrompt<'a> { JoshutoEvent::Termion(_) => { context.flush_event(); } - event => input_process::process_noninteractive(event, context), + event => input::process_noninteractive(event, context), }; } } diff --git a/src/ui/widgets/tui_textfield.rs b/src/ui/widgets/tui_textfield.rs index d6e9371..0aed6df 100644 --- a/src/ui/widgets/tui_textfield.rs +++ b/src/ui/widgets/tui_textfield.rs @@ -12,7 +12,7 @@ use crate::ui::views::TuiView; use crate::ui::widgets::TuiMenu; use crate::ui::TuiBackend; use crate::util::event::JoshutoEvent; -use crate::util::input_process; +use crate::util::input; struct CompletionTracker { pub index: usize, @@ -240,7 +240,7 @@ impl<'a> TuiTextField<'a> { JoshutoEvent::Termion(_) => { context.flush_event(); } - event => input_process::process_noninteractive(event, context), + event => input::process_noninteractive(event, context), }; } } diff --git a/src/util/input.rs b/src/util/input.rs new file mode 100644 index 0000000..af9e326 --- /dev/null +++ b/src/util/input.rs @@ -0,0 +1,148 @@ +use signal_hook::consts::signal; +use termion::event::{MouseButton, MouseEvent}; +use tui::layout::{Constraint, Direction, Layout}; + +use crate::commands::{cursor_move, parent_cursor_move, JoshutoRunnable, KeyCommand}; +use crate::context::JoshutoContext; +use crate::history::DirectoryHistory; +use crate::io::{FileOp, IOWorkerProgress}; +use crate::ui; +use crate::util::event::JoshutoEvent; +use crate::util::format; + +pub fn process_mouse( + event: MouseEvent, + context: &mut JoshutoContext, + backend: &mut ui::TuiBackend, +) { + let f_size = backend.terminal.as_ref().unwrap().size().unwrap(); + + let constraints: &[Constraint; 3] = &context.config_ref().default_layout; + let layout_rect = Layout::default() + .direction(Direction::Horizontal) + .vertical_margin(1) + .constraints(constraints.as_ref()) + .split(f_size); + + match event { + MouseEvent::Press(MouseButton::WheelUp, x, _) => { + if x < layout_rect[1].x { + let command = KeyCommand::ParentCursorMoveUp(1); + if let Err(e) = command.execute(context, backend) { + context.push_msg(e.to_string()); + } + } else if x < layout_rect[2].x { + let command = KeyCommand::CursorMoveUp(1); + if let Err(e) = command.execute(context, backend) { + context.push_msg(e.to_string()); + } + } else { + // TODO: scroll in child list + let command = KeyCommand::CursorMoveUp(1); + if let Err(e) = command.execute(context, backend) { + context.push_msg(e.to_string()); + } + } + } + MouseEvent::Press(MouseButton::WheelDown, x, _) => { + if x < layout_rect[1].x { + let command = KeyCommand::ParentCursorMoveDown(1); + if let Err(e) = command.execute(context, backend) { + context.push_msg(e.to_string()); + } + } else if x < layout_rect[2].x { + let command = KeyCommand::CursorMoveDown(1); + if let Err(e) = command.execute(context, backend) { + context.push_msg(e.to_string()); + } + } else { + // TODO: scroll in child list + let command = KeyCommand::CursorMoveDown(1); + if let Err(e) = command.execute(context, backend) { + context.push_msg(e.to_string()); + } + } + } + MouseEvent::Press(MouseButton::Left, x, y) + if y > layout_rect[1].y && y <= layout_rect[1].y + layout_rect[1].height => + { + if x < layout_rect[1].x { + if let Some(dirlist) = context.tab_context_ref().curr_tab_ref().curr_list_ref() { + if let Some(curr_index) = dirlist.index { + let skip_dist = curr_index / layout_rect[1].height as usize + * layout_rect[1].height as usize; + + let new_index = skip_dist + (y - layout_rect[1].y - 1) as usize; + if let Err(e) = parent_cursor_move::parent_cursor_move(new_index, context) { + context.push_msg(e.to_string()); + } + } + } + } else if x < layout_rect[2].x { + if let Some(dirlist) = context.tab_context_ref().curr_tab_ref().curr_list_ref() { + if let Some(curr_index) = dirlist.index { + let skip_dist = curr_index / layout_rect[1].height as usize + * layout_rect[1].height as usize; + + let new_index = skip_dist + (y - layout_rect[1].y - 1) as usize; + if let Err(e) = cursor_move::cursor_move(new_index, context) { + context.push_msg(e.to_string()); + } + } + } + } else { + } + } + MouseEvent::Press(MouseButton::Left, x, y) + if y > layout_rect[1].y && y <= layout_rect[1].y + layout_rect[1].height => {} + _ => {} + } + context.flush_event(); +} + +pub fn process_noninteractive(event: JoshutoEvent, context: &mut JoshutoContext) { + match event { + JoshutoEvent::IOWorkerProgress(res) => process_worker_progress(context, res), + JoshutoEvent::IOWorkerResult(res) => process_finished_worker(context, res), + JoshutoEvent::Signal(signal::SIGWINCH) => {} + _ => {} + } +} + +pub fn process_worker_progress(context: &mut JoshutoContext, res: IOWorkerProgress) { + context.set_worker_progress(res); + context.update_worker_msg(); +} + +pub fn process_finished_worker( + context: &mut JoshutoContext, + res: std::io::Result, +) { + let observer = context.remove_job().unwrap(); + let options = context.config_ref().sort_option.clone(); + for tab in context.tab_context_mut().iter_mut() { + let _ = tab.history_mut().reload(observer.dest_path(), &options); + let _ = tab.history_mut().reload(observer.src_path(), &options); + } + observer.join(); + match res { + Ok(progress) => { + let op = match progress.kind() { + FileOp::Copy => "copied", + FileOp::Cut => "moved", + }; + let size_str = format::file_size_to_string(progress.processed()); + let msg = format!( + "successfully {} {} items ({})", + op, + progress.len(), + size_str + ); + context.push_msg(msg); + } + Err(e) => { + let msg = format!("{}", e); + context.push_msg(msg); + } + } +} diff --git a/src/util/input_process.rs b/src/util/input_process.rs deleted file mode 100644 index 942703b..0000000 --- a/src/util/input_process.rs +++ /dev/null @@ -1,104 +0,0 @@ -use signal_hook::consts::signal; -use termion::event::{MouseButton, MouseEvent}; -use tui::layout::{Constraint, Direction, Layout}; - -use crate::commands::{JoshutoRunnable, KeyCommand}; -use crate::context::JoshutoContext; -use crate::history::DirectoryHistory; -use crate::io::{FileOp, IOWorkerProgress}; -use crate::ui; -use crate::util::event::JoshutoEvent; -use crate::util::format; - -pub fn process_mouse( - event: MouseEvent, - context: &mut JoshutoContext, - backend: &mut ui::TuiBackend, -) { - let f_size = backend.terminal.as_ref().unwrap().size().unwrap(); - - let constraints: &[Constraint; 3] = &context.config_ref().default_layout; - let layout_rect = Layout::default() - .direction(Direction::Horizontal) - .vertical_margin(1) - .constraints(constraints.as_ref()) - .split(f_size); - - let command = match event { - MouseEvent::Press(MouseButton::WheelUp, x, _) => { - if x < layout_rect[1].x { - Some(KeyCommand::ParentCursorMoveUp(1)) - } else if x < layout_rect[2].x { - Some(KeyCommand::CursorMoveUp(1)) - } else { - // TODO: scroll in child list - Some(KeyCommand::CursorMoveUp(1)) - } - } - MouseEvent::Press(MouseButton::WheelDown, x, _) => { - if x < layout_rect[1].x { - Some(KeyCommand::ParentCursorMoveDown(1)) - } else if x < layout_rect[2].x { - Some(KeyCommand::CursorMoveDown(1)) - } else { - // TODO: scroll in child list - Some(KeyCommand::CursorMoveDown(1)) - } - } - _ => None, - }; - - if let Some(command) = command { - if let Err(e) = command.execute(context, backend) { - context.push_msg(e.to_string()); - } - } - context.flush_event(); -} - -pub fn process_noninteractive(event: JoshutoEvent, context: &mut JoshutoContext) { - match event { - JoshutoEvent::IOWorkerProgress(res) => process_worker_progress(context, res), - JoshutoEvent::IOWorkerResult(res) => process_finished_worker(context, res), - JoshutoEvent::Signal(signal::SIGWINCH) => {} - _ => {} - } -} - -pub fn process_worker_progress(context: &mut JoshutoContext, res: IOWorkerProgress) { - context.set_worker_progress(res); - context.update_worker_msg(); -} - -pub fn process_finished_worker( - context: &mut JoshutoContext, - res: std::io::Result, -) { - let observer = context.remove_job().unwrap(); - let options = context.config_ref().sort_option.clone(); - for tab in context.tab_context_mut().iter_mut() { - let _ = tab.history_mut().reload(observer.dest_path(), &options); - let _ = tab.history_mut().reload(observer.src_path(), &options); - } - observer.join(); - match res { - Ok(progress) => { - let op = match progress.kind() { - FileOp::Copy => "copied", - FileOp::Cut => "moved", - }; - let size_str = format::file_size_to_string(progress.processed()); - let msg = format!( - "successfully {} {} items ({})", - op, - progress.len(), - size_str - ); - context.push_msg(msg); - } - Err(e) => { - let msg = format!("{}", e); - context.push_msg(msg); - } - } -} diff --git a/src/util/key_mapping.rs b/src/util/key_mapping.rs index 0ac7ee7..de1e0dd 100644 --- a/src/util/key_mapping.rs +++ b/src/util/key_mapping.rs @@ -1,51 +1,5 @@ use termion::event::{Event, Key, MouseButton, MouseEvent}; -pub trait ToString { - fn to_string(&self) -> String; -} - -impl ToString for Key { - fn to_string(&self) -> String { - match *self { - Key::Char(c) => format!("{}", c), - Key::Ctrl(c) => format!("ctrl+{}", c), - Key::Left => "arrow_left".to_string(), - Key::Right => "arrow_right".to_string(), - Key::Up => "arrow_up".to_string(), - Key::Down => "arrow_down".to_string(), - Key::Backspace => "backspace".to_string(), - Key::Home => "home".to_string(), - Key::End => "end".to_string(), - Key::PageUp => "page_up".to_string(), - Key::PageDown => "page_down".to_string(), - Key::BackTab => "backtab".to_string(), - Key::Insert => "insert".to_string(), - Key::Delete => "delete".to_string(), - Key::Esc => "escape".to_string(), - Key::F(i) => format!("f{}", i), - k => format!("{:?}", k), - } - } -} - -impl ToString for MouseEvent { - fn to_string(&self) -> String { - match *self { - k => format!("{:?}", k), - } - } -} - -impl ToString for Event { - fn to_string(&self) -> String { - match self { - Event::Key(key) => key.to_string(), - Event::Mouse(mouse) => mouse.to_string(), - Event::Unsupported(v) => format!("{:?}", v), - } - } -} - pub fn str_to_event(s: &str) -> Option { if let Some(k) = str_to_key(s) { Some(Event::Key(k)) diff --git a/src/util/mod.rs b/src/util/mod.rs index bca6387..a85e78d 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,8 +1,11 @@ +#[cfg(feature = "devicons")] pub mod devicons; + pub mod event; pub mod format; -pub mod input_process; +pub mod input; pub mod key_mapping; pub mod load_child; pub mod sort; +pub mod to_string; pub mod unix; diff --git a/src/util/to_string.rs b/src/util/to_string.rs new file mode 100644 index 0000000..22d1f02 --- /dev/null +++ b/src/util/to_string.rs @@ -0,0 +1,47 @@ +use termion::event::{Event, Key, MouseButton, MouseEvent}; + +pub trait ToString { + fn to_string(&self) -> String; +} + +impl ToString for Key { + fn to_string(&self) -> String { + match *self { + Key::Char(c) => format!("{}", c), + Key::Ctrl(c) => format!("ctrl+{}", c), + Key::Left => "arrow_left".to_string(), + Key::Right => "arrow_right".to_string(), + Key::Up => "arrow_up".to_string(), + Key::Down => "arrow_down".to_string(), + Key::Backspace => "backspace".to_string(), + Key::Home => "home".to_string(), + Key::End => "end".to_string(), + Key::PageUp => "page_up".to_string(), + Key::PageDown => "page_down".to_string(), + Key::BackTab => "backtab".to_string(), + Key::Insert => "insert".to_string(), + Key::Delete => "delete".to_string(), + Key::Esc => "escape".to_string(), + Key::F(i) => format!("f{}", i), + k => format!("{:?}", k), + } + } +} + +impl ToString for MouseEvent { + fn to_string(&self) -> String { + match *self { + k => format!("{:?}", k), + } + } +} + +impl ToString for Event { + fn to_string(&self) -> String { + match self { + Event::Key(key) => key.to_string(), + Event::Mouse(mouse) => mouse.to_string(), + Event::Unsupported(v) => format!("{:?}", v), + } + } +} -- cgit v1.2.3