From 1a8cd172e520e493bacc9c6a2ae6f80de086eaa3 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 18 Mar 2020 02:35:08 +0000 Subject: Add modal keyboard motion mode This implements a basic mode for navigating inside of Alacritty's history with keyboard bindings. They're bound by default to vi's motion shortcuts but are fully customizable. Since this relies on key bindings only single key bindings are currently supported (so no `ge`, or repetition). Other than navigating the history and moving the viewport, this mode should enable making use of all available selection modes to copy content to the clipboard and launch URLs below the cursor. This also changes the rendering of the block cursor at the side of selections, since previously it could be inverted to be completely invisible. Since that would have caused some troubles with this keyboard selection mode, the block cursor now is no longer inverted when it is at the edges of a selection. Fixes #262. --- alacritty/src/event.rs | 88 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 25 deletions(-) (limited to 'alacritty/src/event.rs') diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index e635283b..9757893d 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -27,10 +27,10 @@ use alacritty_terminal::event::{Event, EventListener, Notify}; use alacritty_terminal::grid::Scroll; use alacritty_terminal::index::{Column, Line, Point, Side}; use alacritty_terminal::message_bar::{Message, MessageBuffer}; -use alacritty_terminal::selection::Selection; +use alacritty_terminal::selection::{Selection, SelectionType}; use alacritty_terminal::sync::FairMutex; use alacritty_terminal::term::cell::Cell; -use alacritty_terminal::term::{SizeInfo, Term}; +use alacritty_terminal::term::{SizeInfo, Term, TermMode}; #[cfg(not(windows))] use alacritty_terminal::tty; use alacritty_terminal::util::{limit, start_daemon}; @@ -40,6 +40,7 @@ use crate::config; use crate::config::Config; use crate::display::Display; use crate::input::{self, ActionContext as _, FONT_SIZE_STEP}; +use crate::url::{Url, Urls}; use crate::window::Window; #[derive(Default, Clone, Debug, PartialEq)] @@ -68,6 +69,7 @@ pub struct ActionContext<'a, N, T> { pub display_update_pending: &'a mut DisplayUpdate, pub config: &'a mut Config, pub event_loop: &'a EventLoopWindowTarget, + pub urls: &'a Urls, font_size: &'a mut Size, } @@ -83,7 +85,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionCon fn scroll(&mut self, scroll: Scroll) { self.terminal.scroll_display(scroll); - if let ElementState::Pressed = self.mouse().left_button_state { + // Update selection + if self.terminal.mode().contains(TermMode::VI) + && self.terminal.selection().as_ref().map(|s| s.is_empty()) != Some(true) + { + self.update_selection(self.terminal.vi_mode_cursor.point, Side::Right); + } else if ElementState::Pressed == self.mouse().left_button_state { let (x, y) = (self.mouse().x, self.mouse().y); let size_info = self.size_info(); let point = size_info.pixels_to_coords(x, y); @@ -113,35 +120,35 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionCon let point = self.terminal.visible_to_buffer(point); // Update selection if one exists - if let Some(ref mut selection) = self.terminal.selection_mut() { + let vi_mode = self.terminal.mode().contains(TermMode::VI); + if let Some(selection) = self.terminal.selection_mut() { selection.update(point, side); - } - - self.terminal.dirty = true; - } - fn simple_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::simple(point, side)); - self.terminal.dirty = true; - } + if vi_mode { + selection.include_all(); + } - fn block_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::block(point, side)); - self.terminal.dirty = true; + self.terminal.dirty = true; + } } - fn semantic_selection(&mut self, point: Point) { + fn start_selection(&mut self, ty: SelectionType, point: Point, side: Side) { let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::semantic(point)); + *self.terminal.selection_mut() = Some(Selection::new(ty, point, side)); self.terminal.dirty = true; } - fn line_selection(&mut self, point: Point) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::lines(point)); - self.terminal.dirty = true; + fn toggle_selection(&mut self, ty: SelectionType, point: Point, side: Side) { + match self.terminal.selection_mut() { + Some(selection) if selection.ty == ty && !selection.is_empty() => { + self.clear_selection(); + }, + Some(selection) if !selection.is_empty() => { + selection.ty = ty; + self.terminal.dirty = true; + }, + _ => self.start_selection(ty, point, side), + } } fn mouse_coords(&self) -> Option { @@ -155,6 +162,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionCon } } + #[inline] + fn mouse_mode(&self) -> bool { + self.terminal.mode().intersects(TermMode::MOUSE_MODE) + && !self.terminal.mode().contains(TermMode::VI) + } + #[inline] fn mouse_mut(&mut self) -> &mut Mouse { self.mouse @@ -254,8 +267,32 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionCon fn event_loop(&self) -> &EventLoopWindowTarget { self.event_loop } + + fn urls(&self) -> &Urls { + self.urls + } + + /// Spawn URL launcher when clicking on URLs. + fn launch_url(&self, url: Url) { + if self.mouse.block_url_launcher { + return; + } + + if let Some(ref launcher) = self.config.ui_config.mouse.url.launcher { + let mut args = launcher.args().to_vec(); + let start = self.terminal.visible_to_buffer(url.start()); + let end = self.terminal.visible_to_buffer(url.end()); + args.push(self.terminal.bounds_to_string(start, end)); + + match start_daemon(launcher.program(), &args) { + Ok(_) => debug!("Launched {} with args {:?}", launcher.program(), args), + Err(_) => warn!("Unable to launch {} with args {:?}", launcher.program(), args), + } + } + } } +#[derive(Debug)] pub enum ClickState { None, Click, @@ -264,6 +301,7 @@ pub enum ClickState { } /// State of the mouse +#[derive(Debug)] pub struct Mouse { pub x: usize, pub y: usize, @@ -412,10 +450,10 @@ impl Processor { window: &mut self.display.window, font_size: &mut self.font_size, config: &mut self.config, + urls: &self.display.urls, event_loop, }; - let mut processor = - input::Processor::new(context, &self.display.urls, &self.display.highlighted_url); + let mut processor = input::Processor::new(context, &self.display.highlighted_url); for event in event_queue.drain(..) { Processor::handle_event(event, &mut processor); -- cgit