From e4faa16c8a7f2a51303ad3fe9f3891d8f7eabb82 Mon Sep 17 00:00:00 2001 From: Jiayi Zhao Date: Wed, 30 Dec 2020 14:02:10 -0500 Subject: add resize notification support and preliminary mouse support - currently scroll is hardmapped to cursor_up and cursor_down --- src/config/keymap.rs | 124 +++++++++++++++++++--------------------- src/context/context.rs | 12 ++-- src/run.rs | 38 ++++++++---- src/ui/tui_backend.rs | 13 +++-- src/ui/views/tui_worker_view.rs | 19 +++--- src/ui/widgets/tui_menu.rs | 50 +++------------- src/ui/widgets/tui_prompt.rs | 18 +++--- src/ui/widgets/tui_textfield.rs | 18 +++--- src/util/event.rs | 50 ++++++++++------ src/util/input_process.rs | 55 ++++++++++++++++++ src/util/key_mapping.rs | 66 ++++++++++++++++++++- src/util/mod.rs | 2 +- src/util/worker.rs | 43 -------------- 13 files changed, 286 insertions(+), 222 deletions(-) create mode 100644 src/util/input_process.rs delete mode 100644 src/util/worker.rs (limited to 'src') diff --git a/src/config/keymap.rs b/src/config/keymap.rs index e21efff..80f0a6f 100644 --- a/src/config/keymap.rs +++ b/src/config/keymap.rs @@ -2,27 +2,27 @@ use std::collections::{hash_map::Entry, HashMap}; use serde_derive::Deserialize; -use termion::event::Key; +use termion::event::{Event, Key, MouseEvent}; use super::{parse_to_config_file, ConfigStructure, Flattenable}; use crate::commands::{CommandKeybind, KeyCommand}; use crate::io::IOWorkerOptions; -use crate::util::key_mapping::str_to_key; +use crate::util::key_mapping::str_to_event; use crate::KEYMAP_FILE; #[derive(Debug)] pub struct JoshutoCommandMapping { - map: HashMap, + map: HashMap, } -impl std::convert::AsRef> for JoshutoCommandMapping { - fn as_ref(&self) -> &HashMap { +impl std::convert::AsRef> for JoshutoCommandMapping { + fn as_ref(&self) -> &HashMap { &self.map } } -impl std::convert::AsMut> for JoshutoCommandMapping { - fn as_mut(&mut self) -> &mut HashMap { +impl std::convert::AsMut> for JoshutoCommandMapping { + fn as_mut(&mut self) -> &mut HashMap { &mut self.map } } @@ -38,154 +38,154 @@ impl JoshutoCommandMapping { let mut m = self; let cmd = KeyCommand::CursorMoveUp(1); - let keys = [Key::Up]; + let keys = [Event::Key(Key::Up)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CursorMoveDown(1); - let keys = [Key::Down]; + let keys = [Event::Key(Key::Down)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::ParentDirectory; - let keys = [Key::Left]; + let keys = [Event::Key(Key::Left)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::OpenFile; - let keys = [Key::Right]; + let keys = [Event::Key(Key::Right)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::OpenFile; - let keys = [Key::Char('\n')]; + let keys = [Event::Key(Key::Char('\n'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CursorMoveHome; - let keys = [Key::Home]; + let keys = [Event::Key(Key::Home)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CursorMoveEnd; - let keys = [Key::End]; + let keys = [Event::Key(Key::End)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CursorMovePageUp; - let keys = [Key::PageUp]; + let keys = [Event::Key(Key::PageUp)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CursorMovePageDown; - let keys = [Key::PageDown]; + let keys = [Event::Key(Key::PageDown)]; insert_keycommand(&mut m, cmd, &keys)?; // vim keys let cmd = KeyCommand::CursorMoveUp(1); - let keys = [Key::Char('k')]; + let keys = [Event::Key(Key::Char('k'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CursorMoveDown(1); - let keys = [Key::Char('j')]; + let keys = [Event::Key(Key::Char('j'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::ParentDirectory; - let keys = [Key::Char('h')]; + let keys = [Event::Key(Key::Char('h'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::OpenFile; - let keys = [Key::Char('l')]; + let keys = [Event::Key(Key::Char('l'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::NewTab; - let keys = [Key::Char('T')]; + let keys = [Event::Key(Key::Char('T'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::NewTab; - let keys = [Key::Ctrl('t')]; + let keys = [Event::Key(Key::Ctrl('t'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CloseTab; - let keys = [Key::Char('W')]; + let keys = [Event::Key(Key::Char('W'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CloseTab; - let keys = [Key::Ctrl('w')]; + let keys = [Event::Key(Key::Ctrl('w'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CloseTab; - let keys = [Key::Char('q')]; + let keys = [Event::Key(Key::Char('q'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::ForceQuit; - let keys = [Key::Char('Q')]; + let keys = [Event::Key(Key::Char('Q'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::ReloadDirList; - let keys = [Key::Char('R')]; + let keys = [Event::Key(Key::Char('R'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::ToggleHiddenFiles; - let keys = [Key::Char('z'), Key::Char('h')]; + let keys = [Event::Key(Key::Char('z')), Event::Key(Key::Char('h'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::TabSwitch(1); - let keys = [Key::Char('\t')]; + let keys = [Event::Key(Key::Char('\t'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::TabSwitch(-1); - let keys = [Key::BackTab]; + let keys = [Event::Key(Key::BackTab)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::OpenFileWith; - let keys = [Key::Char('r')]; + let keys = [Event::Key(Key::Char('r'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CutFiles; - let keys = [Key::Char('d'), Key::Char('d')]; + let keys = [Event::Key(Key::Char('d')), Event::Key(Key::Char('d'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CopyFiles; - let keys = [Key::Char('y'), Key::Char('y')]; + let keys = [Event::Key(Key::Char('y')), Event::Key(Key::Char('y'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::PasteFiles(IOWorkerOptions::default()); - let keys = [Key::Char('p'), Key::Char('p')]; + let keys = [Event::Key(Key::Char('p')), Event::Key(Key::Char('p'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::DeleteFiles; - let keys = [Key::Delete]; + let keys = [Event::Key(Key::Delete)]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::DeleteFiles; - let keys = [Key::Char('D'), Key::Char('d')]; + let keys = [Event::Key(Key::Char('D')), Event::Key(Key::Char('d'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::RenameFileAppend; - let keys = [Key::Char('a')]; + let keys = [Event::Key(Key::Char('a'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::RenameFilePrepend; - let keys = [Key::Char('A')]; + let keys = [Event::Key(Key::Char('A'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CommandLine("search ".to_string(), "".to_string()); - let keys = [Key::Char('/')]; + let keys = [Event::Key(Key::Char('/'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::SearchNext; - let keys = [Key::Char('n')]; + let keys = [Event::Key(Key::Char('n'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::SearchPrev; - let keys = [Key::Char('N')]; + let keys = [Event::Key(Key::Char('N'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::BulkRename; - let keys = [Key::Char('b'), Key::Char('b')]; + let keys = [Event::Key(Key::Char('b')), Event::Key(Key::Char('b'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::SetMode; - let keys = [Key::Char('=')]; + let keys = [Event::Key(Key::Char('='))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CommandLine("".to_string(), "".to_string()); - let keys = [Key::Char(';')]; + let keys = [Event::Key(Key::Char(';'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CommandLine("mkdir ".to_string(), "".to_string()); - let keys = [Key::Char('m'), Key::Char('k')]; + let keys = [Event::Key(Key::Char('m')), Event::Key(Key::Char('k'))]; insert_keycommand(&mut m, cmd, &keys)?; let cmd = KeyCommand::CommandLine("rename ".to_string(), "".to_string()); - let keys = [Key::Char('c'), Key::Char('w')]; + let keys = [Event::Key(Key::Char('c')), Event::Key(Key::Char('w'))]; insert_keycommand(&mut m, cmd, &keys)?; Ok(()) @@ -197,9 +197,7 @@ impl std::default::Default for JoshutoCommandMapping { let mut m = Self { map: HashMap::new(), }; - let _ = m.default_res(); - m } } @@ -229,18 +227,18 @@ impl Flattenable for JoshutoRawCommandMapping { for m in self.mapcommand { match KeyCommand::parse_command(m.command.as_str()) { Ok(command) => { - let keycodes: Vec = m + let events: Vec = m .keys .iter() - .filter_map(|s| str_to_key(s.as_str())) + .filter_map(|s| str_to_event(s.as_str())) .collect(); - if keycodes.len() != m.keys.len() { - eprintln!("Failed to parse keycodes: {:?}", m.keys); + if events.len() != m.keys.len() { + eprintln!("Failed to parse events: {:?}", m.keys); continue; } - let result = insert_keycommand(&mut keymaps, command, &keycodes); + let result = insert_keycommand(&mut keymaps, command, &events); match result { Ok(_) => {} Err(e) => eprintln!("{}", e), @@ -256,18 +254,16 @@ impl Flattenable for JoshutoRawCommandMapping { fn insert_keycommand( keymap: &mut JoshutoCommandMapping, keycommand: KeyCommand, - keycodes: &[Key], + events: &[Event], ) -> Result<(), String> { - let keycode_len = keycodes.len(); - - if keycode_len == 0 { + let num_events = events.len(); + if num_events == 0 { return Ok(()); } - let key = keycodes[0]; - - if keycode_len == 1 { - match keymap.as_mut().entry(key) { + let event = events[0].clone(); + if num_events == 1 { + match keymap.as_mut().entry(event) { Entry::Occupied(_) => { return Err(format!("Error: Keybindings ambiguous for {}", keycommand)) } @@ -276,16 +272,16 @@ fn insert_keycommand( return Ok(()); } - match keymap.as_mut().entry(key) { + match keymap.as_mut().entry(event) { Entry::Occupied(mut entry) => match entry.get_mut() { CommandKeybind::CompositeKeybind(ref mut m) => { - insert_keycommand(m, keycommand, &keycodes[1..]) + insert_keycommand(m, keycommand, &events[1..]) } _ => Err(format!("Error: Keybindings ambiguous for {}", keycommand)), }, Entry::Vacant(entry) => { let mut new_map = JoshutoCommandMapping::new(); - let result = insert_keycommand(&mut new_map, keycommand, &keycodes[1..]); + let result = insert_keycommand(&mut new_map, keycommand, &events[1..]); if result.is_ok() { let composite_command = CommandKeybind::CompositeKeybind(new_map); entry.insert(composite_command); diff --git a/src/context/context.rs b/src/context/context.rs index bb5a512..574b7af 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -6,7 +6,7 @@ use std::thread; use crate::config; use crate::context::{LocalStateContext, TabContext}; use crate::io::{IOWorkerObserver, IOWorkerProgress, IOWorkerThread}; -use crate::util::event::{Event, Events}; +use crate::util::event::{Events, JoshutoEvent}; pub struct JoshutoContext { pub exit: bool, @@ -61,10 +61,10 @@ impl JoshutoContext { } // event related - pub fn poll_event(&self) -> Result { + pub fn poll_event(&self) -> Result { self.events.next() } - pub fn get_event_tx(&self) -> mpsc::Sender { + pub fn get_event_tx(&self) -> mpsc::Sender { self.events.event_tx.clone() } pub fn flush_event(&self) { @@ -134,17 +134,17 @@ impl JoshutoContext { let worker_handle = thread::spawn(move || worker.start(wtx)); // relay worker info to event loop while let Ok(progress) = wrx.recv() { - let _ = tx.send(Event::IOWorkerProgress(progress)); + let _ = tx.send(JoshutoEvent::IOWorkerProgress(progress)); } let result = worker_handle.join(); match result { Ok(res) => { - let _ = tx.send(Event::IOWorkerResult(res)); + let _ = tx.send(JoshutoEvent::IOWorkerResult(res)); } Err(_) => { let err = std::io::Error::new(std::io::ErrorKind::Other, "Sending Error"); - let _ = tx.send(Event::IOWorkerResult(Err(err))); + let _ = tx.send(JoshutoEvent::IOWorkerResult(Err(err))); } } }); diff --git a/src/run.rs b/src/run.rs index 11ef0e2..19d2486 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,13 +1,15 @@ -use crate::commands::{CommandKeybind, JoshutoRunnable}; +use termion::event::{Event, MouseButton, MouseEvent}; + +use crate::commands::{CommandKeybind, JoshutoRunnable, KeyCommand}; use crate::config::{JoshutoCommandMapping, JoshutoConfig}; use crate::context::JoshutoContext; use crate::tab::JoshutoTab; use crate::ui; use crate::ui::views::TuiView; use crate::ui::widgets::TuiCommandMenu; -use crate::util::event::Event; +use crate::util::event::JoshutoEvent; +use crate::util::input_process; use crate::util::load_child::LoadChild; -use crate::util::worker; pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io::Result<()> { let mut backend: ui::TuiBackend = ui::TuiBackend::new()?; @@ -34,21 +36,34 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: Ok(event) => event, Err(_) => return Ok(()), // TODO }; - match event { - Event::IOWorkerProgress(res) => { - worker::process_worker_progress(&mut context, res); - } - Event::IOWorkerResult(res) => { - worker::process_finished_worker(&mut context, res); + JoshutoEvent::Termion(Event::Mouse(event)) => { + let command = match event { + MouseEvent::Press(MouseButton::WheelUp, _, _) => { + Some(KeyCommand::CursorMoveUp(1)) + } + MouseEvent::Press(MouseButton::WheelDown, _, _) => { + Some(KeyCommand::CursorMoveDown(1)) + } + e => None, + }; + match command { + Some(c) => { + if let Err(e) = c.execute(&mut context, &mut backend) { + context.push_msg(e.to_string()); + } + } + None => context.push_msg(format!("Unmapped input: {:?}", event)), + } + context.flush_event(); } - Event::Input(key) => { + JoshutoEvent::Termion(key) => { if !context.message_queue_ref().is_empty() { context.pop_msg(); } match keymap_t.as_ref().get(&key) { None => { - context.push_msg(format!("Unknown keycode: {:?}", key)); + context.push_msg(format!("Unmapped input: {:?}", key)); } Some(CommandKeybind::SimpleKeybind(command)) => { if let Err(e) = command.execute(&mut context, &mut backend) { @@ -70,6 +85,7 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: } context.flush_event(); } + event => input_process::process_noninteractive(event, &mut context), } } diff --git a/src/ui/tui_backend.rs b/src/ui/tui_backend.rs index 2294737..6a42a5f 100644 --- a/src/ui/tui_backend.rs +++ b/src/ui/tui_backend.rs @@ -1,20 +1,23 @@ use std::io::stdout; use std::io::Write; +use termion::input::MouseTerminal; use termion::raw::{IntoRawMode, RawTerminal}; use termion::screen::AlternateScreen; use tui::backend::TermionBackend; use tui::widgets::Widget; +pub type JoshutoTerminal = + tui::Terminal>>>>; + pub struct TuiBackend { - pub terminal: - Option>>>>, + pub terminal: Option, } impl TuiBackend { pub fn new() -> std::io::Result { let stdout = std::io::stdout().into_raw_mode()?; - let mut alt_screen = AlternateScreen::from(stdout); + let mut alt_screen = MouseTerminal::from(AlternateScreen::from(stdout)); // clears the screen of artifacts write!(alt_screen, "{}", termion::clear::All)?; @@ -36,9 +39,7 @@ impl TuiBackend { }); } - pub fn terminal_mut( - &mut self, - ) -> &mut tui::Terminal>>> { + pub fn terminal_mut(&mut self) -> &mut JoshutoTerminal { self.terminal.as_mut().unwrap() } diff --git a/src/ui/views/tui_worker_view.rs b/src/ui/views/tui_worker_view.rs index 536c45a..58eaef2 100644 --- a/src/ui/views/tui_worker_view.rs +++ b/src/ui/views/tui_worker_view.rs @@ -1,12 +1,12 @@ -use termion::event::Key; +use termion::event::{Event, Key}; use tui::layout::Rect; use crate::context::JoshutoContext; use crate::ui::widgets::TuiWorker; use crate::ui::TuiBackend; -use crate::util::event::Event; -use crate::util::worker; +use crate::util::event::JoshutoEvent; +use crate::util::input_process; pub struct TuiWorkerView {} @@ -33,21 +33,16 @@ impl TuiWorkerView { if let Ok(event) = context.poll_event() { match event { - Event::IOWorkerProgress(res) => { - worker::process_worker_progress(context, res); - } - Event::IOWorkerResult(res) => { - worker::process_finished_worker(context, res); - } - Event::Input(key) => { - match key { - Key::Esc => { + JoshutoEvent::Termion(event) => { + match event { + Event::Key(Key::Esc) => { break; } _ => {} } context.flush_event(); } + event => input_process::process_noninteractive(event, context), }; } } diff --git a/src/ui/widgets/tui_menu.rs b/src/ui/widgets/tui_menu.rs index d8fc148..38eed64 100644 --- a/src/ui/widgets/tui_menu.rs +++ b/src/ui/widgets/tui_menu.rs @@ -1,6 +1,6 @@ use std::iter::Iterator; -use termion::event::Key; +use termion::event::{Event, Key}; use tui::buffer::Buffer; use tui::layout::Rect; use tui::style::{Color, Style}; @@ -11,42 +11,15 @@ use crate::config::JoshutoCommandMapping; use crate::context::JoshutoContext; use crate::ui::views::TuiView; use crate::ui::TuiBackend; -use crate::util::event::Event; -use crate::util::worker; +use crate::util::event::JoshutoEvent; +use crate::util::input_process; +use crate::util::key_mapping::ToString; const BORDER_HEIGHT: usize = 1; const BOTTOM_MARGIN: usize = 1; pub struct TuiCommandMenu; -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 TuiCommandMenu { pub fn new() -> Self { Self {} @@ -107,16 +80,10 @@ impl TuiCommandMenu { if let Ok(event) = context.poll_event() { match event { - Event::IOWorkerProgress(res) => { - worker::process_worker_progress(context, res); - } - Event::IOWorkerResult(res) => { - worker::process_finished_worker(context, res); - } - Event::Input(key) => { - match key { - Key::Esc => return None, - key => match map.as_ref().get(&key) { + JoshutoEvent::Termion(event) => { + match event { + Event::Key(Key::Esc) => return None, + event => match map.as_ref().get(&event) { Some(CommandKeybind::SimpleKeybind(s)) => { return Some(s); } @@ -128,6 +95,7 @@ impl TuiCommandMenu { } context.flush_event(); } + event => input_process::process_noninteractive(event, context), } } } diff --git a/src/ui/widgets/tui_prompt.rs b/src/ui/widgets/tui_prompt.rs index f520de9..c4d0c1c 100644 --- a/src/ui/widgets/tui_prompt.rs +++ b/src/ui/widgets/tui_prompt.rs @@ -1,4 +1,4 @@ -use termion::event::Key; +use termion::event::{Event, Key}; use tui::layout::Rect; use tui::style::{Color, Style}; use tui::text::Span; @@ -7,8 +7,8 @@ use tui::widgets::{Clear, Paragraph, Wrap}; use crate::context::JoshutoContext; use crate::ui::views::TuiView; use crate::ui::TuiBackend; -use crate::util::event::Event; -use crate::util::worker; +use crate::util::event::JoshutoEvent; +use crate::util::input_process; pub struct TuiPrompt<'a> { prompt: &'a str, @@ -56,15 +56,13 @@ impl<'a> TuiPrompt<'a> { if let Ok(event) = context.poll_event() { match event { - Event::IOWorkerProgress(res) => { - worker::process_worker_progress(context, res); - } - Event::IOWorkerResult(res) => { - worker::process_finished_worker(context, res); - } - Event::Input(key) => { + JoshutoEvent::Termion(Event::Key(key)) => { return key; } + JoshutoEvent::Termion(_) => { + context.flush_event(); + } + event => input_process::process_noninteractive(event, context), }; } } diff --git a/src/ui/widgets/tui_textfield.rs b/src/ui/widgets/tui_textfield.rs index c70c079..d6e9371 100644 --- a/src/ui/widgets/tui_textfield.rs +++ b/src/ui/widgets/tui_textfield.rs @@ -1,7 +1,7 @@ use rustyline::completion::{Candidate, Completer, FilenameCompleter, Pair}; use rustyline::line_buffer; -use termion::event::Key; +use termion::event::{Event, Key}; use tui::layout::Rect; use tui::style::{Color, Modifier, Style}; use tui::text::{Span, Spans}; @@ -11,8 +11,8 @@ use crate::context::JoshutoContext; use crate::ui::views::TuiView; use crate::ui::widgets::TuiMenu; use crate::ui::TuiBackend; -use crate::util::event::Event; -use crate::util::worker; +use crate::util::event::JoshutoEvent; +use crate::util::input_process; struct CompletionTracker { pub index: usize, @@ -159,13 +159,7 @@ impl<'a> TuiTextField<'a> { if let Ok(event) = context.poll_event() { match event { - Event::IOWorkerProgress(res) => { - worker::process_worker_progress(context, res); - } - Event::IOWorkerResult(res) => { - worker::process_finished_worker(context, res); - } - Event::Input(key) => { + JoshutoEvent::Termion(Event::Key(key)) => { match key { Key::Backspace => { if line_buffer.backspace(1) { @@ -243,6 +237,10 @@ impl<'a> TuiTextField<'a> { } context.flush_event(); } + JoshutoEvent::Termion(_) => { + context.flush_event(); + } + event => input_process::process_noninteractive(event, context), }; } } diff --git a/src/util/event.rs b/src/util/event.rs index 9e7f120..4d79035 100644 --- a/src/util/event.rs +++ b/src/util/event.rs @@ -2,16 +2,21 @@ use std::io; use std::sync::mpsc; use std::thread; -use termion::event::Key; +use signal_hook::consts::signal; +use signal_hook::iterator::exfiltrator::SignalOnly; +use signal_hook::iterator::SignalsInfo; + +use termion::event::Event; use termion::input::TermRead; use crate::io::IOWorkerProgress; #[derive(Debug)] -pub enum Event { - Input(Key), +pub enum JoshutoEvent { + Termion(Event), IOWorkerProgress(IOWorkerProgress), IOWorkerResult(io::Result), + Signal(i32), // Filesystem(notify::Result), } @@ -27,10 +32,9 @@ impl Default for Config { /// A small event handler that wrap termion input and tick events. Each event /// type is handled in its own thread and returned to a common `Receiver` pub struct Events { - pub event_tx: mpsc::Sender, - event_rx: mpsc::Receiver, + pub event_tx: mpsc::Sender, + event_rx: mpsc::Receiver, pub input_tx: mpsc::SyncSender<()>, - fileio_handle: thread::JoinHandle<()>, } impl Events { @@ -42,15 +46,28 @@ impl Events { let (input_tx, input_rx) = mpsc::sync_channel(1); let (event_tx, event_rx) = mpsc::channel(); + // signal thread let event_tx2 = event_tx.clone(); + let _ = thread::spawn(move || { + let sigs = vec![signal::SIGWINCH]; + let mut signals = SignalsInfo::::new(&sigs).unwrap(); + for signal in &mut signals { + if let Err(e) = event_tx2.send(JoshutoEvent::Signal(signal)) { + eprintln!("Signal thread send err: {:#?}", e); + return; + } + } + }); - let fileio_handle = thread::spawn(move || { + // input thread + let event_tx2 = event_tx.clone(); + let _ = thread::spawn(move || { let stdin = io::stdin(); - let mut keys = stdin.keys(); - match keys.next() { - Some(key) => match key { - Ok(key) => { - if let Err(e) = event_tx2.send(Event::Input(key)) { + let mut events = stdin.events(); + match events.next() { + Some(event) => match event { + Ok(event) => { + if let Err(e) = event_tx2.send(JoshutoEvent::Termion(event)) { eprintln!("Input thread send err: {:#?}", e); return; } @@ -61,9 +78,9 @@ impl Events { } while input_rx.recv().is_ok() { - if let Some(key) = keys.next() { - if let Ok(key) = key { - if let Err(e) = event_tx2.send(Event::Input(key)) { + if let Some(event) = events.next() { + if let Ok(event) = event { + if let Err(e) = event_tx2.send(JoshutoEvent::Termion(event)) { eprintln!("Input thread send err: {:#?}", e); return; } @@ -76,11 +93,10 @@ impl Events { event_tx, event_rx, input_tx, - fileio_handle, } } - pub fn next(&self) -> Result { + pub fn next(&self) -> Result { let event = self.event_rx.recv()?; Ok(event) } diff --git a/src/util/input_process.rs b/src/util/input_process.rs new file mode 100644 index 0000000..362d87b --- /dev/null +++ b/src/util/input_process.rs @@ -0,0 +1,55 @@ +use signal_hook::consts::signal; + +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_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 e1049aa..d4c16f9 100644 --- a/src/util/key_mapping.rs +++ b/src/util/key_mapping.rs @@ -1,4 +1,60 @@ -use termion::event::Key; +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)) + } else if let Some(m) = str_to_mouse(s) { + Some(Event::Mouse(m)) + } else { + None + } +} pub fn str_to_key(s: &str) -> Option { if s.is_empty() { @@ -62,3 +118,11 @@ pub fn str_to_key(s: &str) -> Option { } None } + +pub fn str_to_mouse(s: &str) -> Option { + match s { + "scroll_up" => Some(MouseEvent::Press(MouseButton::WheelUp, 0, 0)), + "scroll_down" => Some(MouseEvent::Press(MouseButton::WheelDown, 0, 0)), + s => None, + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 8955c76..bca6387 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,8 +1,8 @@ pub mod devicons; pub mod event; pub mod format; +pub mod input_process; pub mod key_mapping; pub mod load_child; pub mod sort; pub mod unix; -pub mod worker; diff --git a/src/util/worker.rs b/src/util/worker.rs deleted file mode 100644 index f3631fd..0000000 --- a/src/util/worker.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::context::JoshutoContext; -use crate::history::DirectoryHistory; -use crate::io::{FileOp, IOWorkerProgress}; - -use super::format; - -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); - } - } -} -- cgit v1.2.3