diff options
Diffstat (limited to 'src/input.rs')
-rw-r--r-- | src/input.rs | 233 |
1 files changed, 151 insertions, 82 deletions
diff --git a/src/input.rs b/src/input.rs index c77a9f49..26a12de6 100644 --- a/src/input.rs +++ b/src/input.rs @@ -29,10 +29,12 @@ use crate::config::{self, Key}; use crate::grid::Scroll; use crate::event::{ClickState, Mouse}; use crate::index::{Line, Column, Side, Point}; -use crate::term::SizeInfo; +use crate::term::{Term, SizeInfo, Search}; use crate::term::mode::TermMode; use crate::util::fmt::Red; use crate::util::start_daemon; +use crate::message_bar; +use crate::ansi::{Handler, ClearMode}; pub const FONT_SIZE_STEP: f32 = 0.5; @@ -54,7 +56,6 @@ pub struct Processor<'a, A: 'a> { pub trait ActionContext { fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _: B); - fn terminal_mode(&self) -> TermMode; fn size_info(&self) -> SizeInfo; fn copy_selection(&self, _: ClipboardBuffer); fn clear_selection(&mut self); @@ -69,13 +70,10 @@ pub trait ActionContext { fn received_count(&mut self) -> &mut usize; fn suppress_chars(&mut self) -> &mut bool; fn last_modifiers(&mut self) -> &mut ModifiersState; - fn change_font_size(&mut self, delta: f32); - fn reset_font_size(&mut self); fn scroll(&mut self, scroll: Scroll); - fn clear_history(&mut self); fn hide_window(&mut self); - fn url(&self, _: Point<usize>) -> Option<String>; - fn clear_log(&mut self); + fn terminal(&self) -> &Term; + fn terminal_mut(&mut self) -> &mut Term; fn spawn_new_instance(&mut self); } @@ -296,17 +294,16 @@ impl Action { ctx.hide_window(); }, Action::Quit => { - // FIXME should do a more graceful shutdown - ::std::process::exit(0); + ctx.terminal_mut().exit(); }, Action::IncreaseFontSize => { - ctx.change_font_size(FONT_SIZE_STEP); + ctx.terminal_mut().change_font_size(FONT_SIZE_STEP); }, Action::DecreaseFontSize => { - ctx.change_font_size(-FONT_SIZE_STEP); + ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP); } Action::ResetFontSize => { - ctx.reset_font_size(); + ctx.terminal_mut().reset_font_size(); }, Action::ScrollPageUp => { ctx.scroll(Scroll::PageUp); @@ -321,10 +318,10 @@ impl Action { ctx.scroll(Scroll::Bottom); }, Action::ClearHistory => { - ctx.clear_history(); + ctx.terminal_mut().clear_screen(ClearMode::Saved); }, Action::ClearLogNotice => { - ctx.clear_log(); + ctx.terminal_mut().message_buffer_mut().pop(); }, Action::SpawnNewInstance => { ctx.spawn_new_instance(); @@ -334,7 +331,7 @@ impl Action { } fn paste<A: ActionContext>(&self, ctx: &mut A, contents: &str) { - if ctx.terminal_mode().contains(TermMode::BRACKETED_PASTE) { + if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) { ctx.write_to_pty(&b"\x1b[200~"[..]); ctx.write_to_pty(contents.replace("\x1b","").into_bytes()); ctx.write_to_pty(&b"\x1b[201~"[..]); @@ -396,8 +393,13 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { self.ctx.mouse_mut().block_url_launcher = true; } + // Ignore motions over the message bar + if self.mouse_over_message_bar(point) { + return; + } + if self.ctx.mouse().left_button_state == ElementState::Pressed - && (modifiers.shift || !self.ctx.terminal_mode().intersects(report_mode)) + && (modifiers.shift || !self.ctx.terminal().mode().intersects(report_mode)) { self.ctx.update_selection( Point { @@ -406,7 +408,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { }, cell_side, ); - } else if self.ctx.terminal_mode().intersects(motion_mode) + } else if self.ctx.terminal().mode().intersects(motion_mode) // Only report motion when changing cells && (prev_line != self.ctx.mouse().line || prev_col != self.ctx.mouse().column) && size_info.contains_point(x, y) @@ -417,7 +419,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { self.mouse_report(33, ElementState::Pressed, modifiers); } else if self.ctx.mouse().right_button_state == ElementState::Pressed { self.mouse_report(34, ElementState::Pressed, modifiers); - } else if self.ctx.terminal_mode().contains(TermMode::MOUSE_MOTION) { + } else if self.ctx.terminal().mode().contains(TermMode::MOUSE_MOTION) { self.mouse_report(35, ElementState::Pressed, modifiers); } } @@ -430,7 +432,8 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { let cell_x = x.saturating_sub(size_info.padding_x as usize) % size_info.cell_width as usize; let half_cell_width = (size_info.cell_width / 2.0) as usize; - let additional_padding = (size_info.width - size_info.padding_x * 2.) % size_info.cell_width; + let additional_padding = + (size_info.width - size_info.padding_x * 2.) % size_info.cell_width; let end_of_grid = size_info.width - size_info.padding_x - additional_padding; if cell_x > half_cell_width @@ -485,7 +488,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } // Report mouse events - if self.ctx.terminal_mode().contains(TermMode::SGR_MOUSE) { + if self.ctx.terminal().mode().contains(TermMode::SGR_MOUSE) { self.sgr_mouse_report(button + mods, state); } else if let ElementState::Released = state { self.normal_mouse_report(3 + mods); @@ -494,19 +497,24 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } } - pub fn on_mouse_double_click(&mut self, button: MouseButton) { - if let (Some(point) , MouseButton::Left) = (self.ctx.mouse_coords(), button) { + pub fn on_mouse_double_click(&mut self, button: MouseButton, point: Point) { + if button == MouseButton::Left { self.ctx.semantic_selection(point); } } - pub fn on_mouse_triple_click(&mut self, button: MouseButton) { - if let (Some(point), MouseButton::Left) = (self.ctx.mouse_coords(), button) { + pub fn on_mouse_triple_click(&mut self, button: MouseButton, point: Point) { + if button == MouseButton::Left { self.ctx.line_selection(point); } } - pub fn on_mouse_press(&mut self, button: MouseButton, modifiers: ModifiersState) { + pub fn on_mouse_press( + &mut self, + button: MouseButton, + modifiers: ModifiersState, + point: Point, + ) { let now = Instant::now(); let elapsed = self.ctx.mouse().last_click_timestamp.elapsed(); self.ctx.mouse_mut().last_click_timestamp = now; @@ -518,14 +526,14 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { if !button_changed && elapsed < self.mouse_config.double_click.threshold => { self.ctx.mouse_mut().block_url_launcher = true; - self.on_mouse_double_click(button); + self.on_mouse_double_click(button, point); ClickState::DoubleClick }, ClickState::DoubleClick if !button_changed && elapsed < self.mouse_config.triple_click.threshold => { self.ctx.mouse_mut().block_url_launcher = true; - self.on_mouse_triple_click(button); + self.on_mouse_triple_click(button, point); ClickState::TripleClick }, _ => { @@ -535,20 +543,20 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { self.ctx.clear_selection(); // Start new empty selection - if let Some(point) = self.ctx.mouse_coords() { - let side = self.ctx.mouse().cell_side; - self.ctx.simple_selection(point, side); - } - - let report_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION; - if !modifiers.shift && self.ctx.terminal_mode().intersects(report_modes) { - match button { - MouseButton::Left => self.mouse_report(0, ElementState::Pressed, modifiers), - MouseButton::Middle => self.mouse_report(1, ElementState::Pressed, modifiers), - MouseButton::Right => self.mouse_report(2, ElementState::Pressed, modifiers), + let side = self.ctx.mouse().cell_side; + self.ctx.simple_selection(point, side); + + let report_modes = + TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION; + if !modifiers.shift && self.ctx.terminal().mode().intersects(report_modes) { + let code = match button { + MouseButton::Left => 0, + MouseButton::Middle => 1, + MouseButton::Right => 2, // Can't properly report more than three buttons. - MouseButton::Other(_) => (), + MouseButton::Other(_) => return, }; + self.mouse_report(code, ElementState::Pressed, modifiers); return; } @@ -557,38 +565,40 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { }; } - pub fn on_mouse_release(&mut self, button: MouseButton, modifiers: ModifiersState) { - let report_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION; - if !modifiers.shift && self.ctx.terminal_mode().intersects(report_modes) - { - match button { - MouseButton::Left => self.mouse_report(0, ElementState::Released, modifiers), - MouseButton::Middle => self.mouse_report(1, ElementState::Released, modifiers), - MouseButton::Right => self.mouse_report(2, ElementState::Released, modifiers), + pub fn on_mouse_release( + &mut self, + button: MouseButton, + modifiers: ModifiersState, + point: Point, + ) { + let report_modes = + TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION; + if !modifiers.shift && self.ctx.terminal().mode().intersects(report_modes) { + let code = match button { + MouseButton::Left => 0, + MouseButton::Middle => 1, + MouseButton::Right => 2, // Can't properly report more than three buttons. - MouseButton::Other(_) => (), + MouseButton::Other(_) => return, }; + self.mouse_report(code, ElementState::Released, modifiers); return; } else if button == MouseButton::Left { - self.launch_url(modifiers); + self.launch_url(modifiers, point); } - if self.save_to_clipboard { - self.ctx.copy_selection(ClipboardBuffer::Primary); - } - self.ctx.copy_selection(ClipboardBuffer::Selection); + self.copy_selection(); } // Spawn URL launcher when clicking on URLs - fn launch_url(&self, modifiers: ModifiersState) -> Option<()> { + fn launch_url(&self, modifiers: ModifiersState, point: Point) -> Option<()> { if !self.mouse_config.url.modifiers.relaxed_eq(modifiers) || self.ctx.mouse().block_url_launcher { return None; } - let point = self.ctx.mouse_coords()?; - let text = self.ctx.url(point.into())?; + let text = self.ctx.terminal().url_search(point.into())?; let launcher = self.mouse_config.url.launcher.as_ref()?; let mut args = launcher.args().to_vec(); @@ -602,7 +612,12 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { Some(()) } - pub fn on_mouse_wheel(&mut self, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState) { + pub fn on_mouse_wheel( + &mut self, + delta: MouseScrollDelta, + phase: TouchPhase, + modifiers: ModifiersState, + ) { match delta { MouseScrollDelta::LineDelta(_columns, lines) => { let new_scroll_px = lines * self.ctx.size_info().cell_height; @@ -634,7 +649,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { .faux_scrollback_lines .unwrap_or(self.scrolling_config.faux_multiplier as usize); - if self.ctx.terminal_mode().intersects(mouse_modes) { + if self.ctx.terminal().mode().intersects(mouse_modes) { self.ctx.mouse_mut().scroll_px += new_scroll_px; let code = if new_scroll_px > 0 { 64 } else { 65 }; @@ -643,7 +658,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { for _ in 0..lines { self.mouse_report(code, ElementState::Pressed, modifiers); } - } else if self.ctx.terminal_mode().contains(TermMode::ALT_SCREEN) + } else if self.ctx.terminal().mode().contains(TermMode::ALT_SCREEN) && faux_multiplier > 0 && !modifiers.shift { @@ -672,7 +687,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } pub fn on_focus_change(&mut self, is_focused: bool) { - if self.ctx.terminal_mode().contains(TermMode::FOCUS_IN_OUT) { + if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) { let chr = if is_focused { "I" } else { @@ -684,7 +699,12 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } } - pub fn mouse_input(&mut self, state: ElementState, button: MouseButton, modifiers: ModifiersState) { + pub fn mouse_input( + &mut self, + state: ElementState, + button: MouseButton, + modifiers: ModifiersState + ) { match button { MouseButton::Left => self.ctx.mouse_mut().left_button_state = state, MouseButton::Middle => self.ctx.mouse_mut().middle_button_state = state, @@ -692,12 +712,22 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { _ => (), } - match state { - ElementState::Pressed => { - self.process_mouse_bindings(modifiers, button); - self.on_mouse_press(button, modifiers); - }, - ElementState::Released => self.on_mouse_release(button, modifiers), + let point = match self.ctx.mouse_coords() { + Some(point) => point, + None => return, + }; + + // Skip normal mouse events if the message bar has been clicked + if self.mouse_over_message_bar(point) { + self.on_message_bar_click(state, point); + } else { + match state { + ElementState::Pressed => { + self.process_mouse_bindings(modifiers, button); + self.on_mouse_press(button, modifiers, point); + }, + ElementState::Released => self.on_mouse_release(button, modifiers, point), + } } self.ctx.mouse_mut().last_button = button; @@ -759,14 +789,19 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { for binding in self.key_bindings { let is_triggered = match binding.trigger { Key::Scancode(_) => binding.is_triggered_by( - self.ctx.terminal_mode(), + *self.ctx.terminal().mode(), input.modifiers, &Key::Scancode(input.scancode), false, ), _ => if let Some(key) = input.virtual_keycode { let key = Key::from_glutin_input(key); - binding.is_triggered_by(self.ctx.terminal_mode(), input.modifiers, &key, false) + binding.is_triggered_by( + *self.ctx.terminal().mode(), + input.modifiers, + &key, + false + ) } else { false }, @@ -791,9 +826,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { fn process_mouse_bindings(&mut self, mods: ModifiersState, button: MouseButton) -> bool { let mut has_binding = false; for binding in self.mouse_bindings { - if binding.is_triggered_by(self.ctx.terminal_mode(), mods, &button, true) { + if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) { // binding was triggered; run the action - let mouse_mode = !mods.shift && self.ctx.terminal_mode().intersects( + let mouse_mode = !mods.shift && self.ctx.terminal().mode().intersects( TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION @@ -805,6 +840,43 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { has_binding } + + /// Check if a point is within the message bar + fn mouse_over_message_bar(&mut self, point: Point) -> bool { + if let Some(message) = self.ctx.terminal_mut().message_buffer_mut().message() { + let size = self.ctx.size_info(); + point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) + } else { + false + } + } + + /// Handle clicks on the message bar. + fn on_message_bar_click(&mut self, button_state: ElementState, point: Point) { + match button_state { + ElementState::Released => self.copy_selection(), + ElementState::Pressed => { + let size = self.ctx.size_info(); + if let Some(message) = self.ctx.terminal_mut().message_buffer_mut().message() { + if point.col + message_bar::CLOSE_BUTTON_TEXT.len() >= size.cols() + && point.line == size.lines() - message.text(&size).len() + { + self.ctx.terminal_mut().message_buffer_mut().pop(); + } + } + + self.ctx.clear_selection(); + } + } + } + + /// Copy text selection. + fn copy_selection(&mut self) { + if self.save_to_clipboard { + self.ctx.copy_selection(ClipboardBuffer::Primary); + } + self.ctx.copy_selection(ClipboardBuffer::Selection); + } } #[cfg(test)] @@ -820,6 +892,7 @@ mod tests { use crate::index::{Point, Side}; use crate::selection::Selection; use crate::grid::Scroll; + use crate::message_bar::MessageBuffer; use super::{Action, Binding, Processor}; use copypasta::Buffer as ClipboardBuffer; @@ -851,15 +924,15 @@ mod tests { fn simple_selection(&mut self, _point: Point, _side: Side) {} fn copy_selection(&self, _buffer: ClipboardBuffer) {} fn clear_selection(&mut self) {} - fn change_font_size(&mut self, _delta: f32) {} - fn reset_font_size(&mut self) {} - fn clear_history(&mut self) {} - fn clear_log(&mut self) {} fn hide_window(&mut self) {} fn spawn_new_instance(&mut self) {} - fn terminal_mode(&self) -> TermMode { - *self.terminal.mode() + fn terminal(&self) -> &Term { + &self.terminal + } + + fn terminal_mut(&mut self) -> &mut Term { + &mut self.terminal } fn size_info(&self) -> SizeInfo { @@ -897,10 +970,6 @@ mod tests { self.mouse } - fn url(&self, _: Point<usize>) -> Option<String> { - None - } - fn received_count(&mut self) -> &mut usize { &mut self.received_count } @@ -936,7 +1005,7 @@ mod tests { dpr: 1.0, }; - let mut terminal = Term::new(&config, size); + let mut terminal = Term::new(&config, size, MessageBuffer::new()); let mut mouse = Mouse::default(); mouse.click_state = $initial_state; |