diff options
author | Christian Duerr <contact@christianduerr.com> | 2021-04-13 03:24:42 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-13 03:24:42 +0000 |
commit | 96fc9ecc9a62c8a766da745d05fbe60e6c2e1efe (patch) | |
tree | 16e237d750d6c650713b58df98453dc4df16d33a /alacritty/src/input.rs | |
parent | 40bcdb11335cc49d4d42694b953be746cb383cb9 (diff) | |
download | r-alacritty-96fc9ecc9a62c8a766da745d05fbe60e6c2e1efe.tar.gz r-alacritty-96fc9ecc9a62c8a766da745d05fbe60e6c2e1efe.tar.bz2 r-alacritty-96fc9ecc9a62c8a766da745d05fbe60e6c2e1efe.zip |
Add vi/mouse hint highlighting support
This patch removes the old url highlighting code and replaces it with a
new implementation making use of hints as sources for finding matches in
the terminal.
Diffstat (limited to 'alacritty/src/input.rs')
-rw-r--r-- | alacritty/src/input.rs | 186 |
1 files changed, 68 insertions, 118 deletions
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index 38a346d9..7dd47803 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -22,7 +22,7 @@ use glutin::window::CursorIcon; use alacritty_terminal::ansi::{ClearMode, Handler}; use alacritty_terminal::event::EventListener; use alacritty_terminal::grid::{Dimensions, Scroll}; -use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point, Side}; +use alacritty_terminal::index::{Boundary, Column, Direction, Point, Side}; use alacritty_terminal::selection::SelectionType; use alacritty_terminal::term::search::Match; use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode}; @@ -31,12 +31,12 @@ use alacritty_terminal::vi_mode::ViMotion; use crate::clipboard::Clipboard; use crate::config::{Action, BindingMode, Config, Key, SearchAction, ViAction}; use crate::daemon::start_daemon; -use crate::display::hint::HintState; +use crate::display::hint::HintMatch; use crate::display::window::Window; +use crate::display::{self, Display}; use crate::event::{ClickState, Event, Mouse, TYPING_SEARCH_DELAY}; use crate::message_bar::{self, Message}; use crate::scheduler::{Scheduler, TimerId}; -use crate::url::{Url, Urls}; /// Font size change interval. pub const FONT_SIZE_STEP: f32 = 0.5; @@ -75,8 +75,8 @@ pub trait ActionContext<T: EventListener> { fn suppress_chars(&mut self) -> &mut bool; fn modifiers(&mut self) -> &mut ModifiersState; fn scroll(&mut self, _scroll: Scroll) {} - fn window(&self) -> &Window; - fn window_mut(&mut self) -> &mut Window; + fn window(&mut self) -> &mut Window; + fn display(&mut self) -> &mut Display; fn terminal(&self) -> &Term<T>; fn terminal_mut(&mut self) -> &mut Term<T>; fn spawn_new_instance(&mut self) {} @@ -86,9 +86,6 @@ pub trait ActionContext<T: EventListener> { fn message(&self) -> Option<&Message>; fn config(&self) -> &Config; fn event_loop(&self) -> &EventLoopWindowTarget<Event>; - fn urls(&self) -> &Urls; - fn launch_url(&self, _url: Url) {} - fn highlighted_url(&self) -> Option<&Url>; fn mouse_mode(&self) -> bool; fn clipboard_mut(&mut self) -> &mut Clipboard; fn scheduler_mut(&mut self) -> &mut Scheduler; @@ -105,8 +102,8 @@ pub trait ActionContext<T: EventListener> { fn search_active(&self) -> bool; fn on_typing_start(&mut self) {} fn toggle_vi_mode(&mut self) {} - fn hint_state(&mut self) -> &mut HintState; fn hint_input(&mut self, _character: char) {} + fn trigger_hint(&mut self, _hint: &HintMatch) {} fn paste(&mut self, _text: &str) {} } @@ -142,7 +139,7 @@ impl<T: EventListener> Execute<T> for Action { }, Action::Command(program) => start_daemon(program.program(), program.args()), Action::Hint(hint) => { - ctx.hint_state().start(hint.clone()); + ctx.display().hint_state.start(hint.clone()); ctx.mark_dirty(); }, Action::ToggleViMode => ctx.toggle_vi_mode(), @@ -164,12 +161,12 @@ impl<T: EventListener> Execute<T> for Action { Self::toggle_selection(ctx, SelectionType::Semantic); }, Action::ViAction(ViAction::Open) => { - ctx.mouse_mut().block_url_launcher = false; - let vi_point = ctx.terminal().vi_mode_cursor.point; - let line = (vi_point.line + ctx.terminal().grid().display_offset()).0 as usize; - if let Some(url) = ctx.urls().find_at(Point::new(line, vi_point.column)) { - ctx.launch_url(url); + let hint = ctx.display().vi_highlighted_hint.take(); + if let Some(hint) = &hint { + ctx.mouse_mut().block_hint_launcher = false; + ctx.trigger_hint(hint); } + ctx.display().vi_highlighted_hint = hint; }, Action::ViAction(ViAction::SearchNext) => { let terminal = ctx.terminal(); @@ -250,9 +247,9 @@ impl<T: EventListener> Execute<T> for Action { let text = ctx.clipboard_mut().load(ClipboardType::Selection); ctx.paste(&text); }, - Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(), + Action::ToggleFullscreen => ctx.window().toggle_fullscreen(), #[cfg(target_os = "macos")] - Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(), + Action::ToggleSimpleFullscreen => ctx.window().toggle_simple_fullscreen(), #[cfg(target_os = "macos")] Action::Hide => ctx.event_loop().hide_application(), #[cfg(target_os = "macos")] @@ -327,25 +324,6 @@ impl<T: EventListener> Execute<T> for Action { } } -#[derive(Debug, Clone, PartialEq)] -pub enum MouseState { - Url(Url), - MessageBar, - MessageBarButton, - Mouse, - Text, -} - -impl From<MouseState> for CursorIcon { - fn from(mouse_state: MouseState) -> CursorIcon { - match mouse_state { - MouseState::Url(_) | MouseState::MessageBarButton => CursorIcon::Hand, - MouseState::Text => CursorIcon::Text, - _ => CursorIcon::Default, - } - } -} - impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { pub fn new(ctx: A) -> Self { Self { ctx, _phantom: Default::default() } @@ -375,11 +353,6 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { let cell_changed = point != self.ctx.mouse().point; - // Update mouse state and check for URL change. - let mouse_state = self.mouse_state(); - self.update_url_state(&mouse_state); - self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); - // If the mouse hasn't changed cells, do nothing. if !cell_changed && self.ctx.mouse().cell_side == cell_side @@ -392,13 +365,20 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { self.ctx.mouse_mut().cell_side = cell_side; self.ctx.mouse_mut().point = point; + // Update mouse state and check for URL change. + let mouse_state = self.cursor_state(); + self.ctx.window().set_mouse_cursor(mouse_state); + + // Prompt hint highlight update. + self.ctx.mouse_mut().hint_highlight_dirty = true; + // Don't launch URLs if mouse has moved. - self.ctx.mouse_mut().block_url_launcher = true; + self.ctx.mouse_mut().block_hint_launcher = true; if (lmb_pressed || rmb_pressed) && (self.ctx.modifiers().shift() || !self.ctx.mouse_mode()) { - let line = Line(point.line as i32) - self.ctx.terminal().grid().display_offset(); - let point = Point::new(line, point.column); + let display_offset = self.ctx.terminal().grid().display_offset(); + let point = display::viewport_to_point(display_offset, point); self.ctx.update_selection(point, cell_side); } else if cell_changed && point.line < self.ctx.terminal().screen_lines() @@ -562,10 +542,8 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { }; // Load mouse point, treating message bar and padding as the closest cell. - let point = self.ctx.mouse().point; let display_offset = self.ctx.terminal().grid().display_offset(); - let absolute_line = Line(point.line as i32) - display_offset; - let point = Point::new(absolute_line, point.column); + let point = display::viewport_to_point(display_offset, self.ctx.mouse().point); match button { MouseButton::Left => self.on_left_click(point), @@ -619,7 +597,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { match self.ctx.mouse().click_state { ClickState::Click => { // Don't launch URLs if this click cleared the selection. - self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty(); + self.ctx.mouse_mut().block_hint_launcher = !self.ctx.selection_is_empty(); self.ctx.clear_selection(); @@ -631,11 +609,11 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } }, ClickState::DoubleClick => { - self.ctx.mouse_mut().block_url_launcher = true; + self.ctx.mouse_mut().block_hint_launcher = true; self.ctx.start_selection(SelectionType::Semantic, point, side); }, ClickState::TripleClick => { - self.ctx.mouse_mut().block_url_launcher = true; + self.ctx.mouse_mut().block_hint_launcher = true; self.ctx.start_selection(SelectionType::Lines, point, side); }, ClickState::None => (), @@ -658,10 +636,15 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { }; self.mouse_report(code, ElementState::Released); return; - } else if let (MouseButton::Left, MouseState::Url(url)) = (button, self.mouse_state()) { - self.ctx.launch_url(url); } + // Trigger hints highlighted by the mouse. + let hint = self.ctx.display().highlighted_hint.take(); + if let Some(hint) = hint.as_ref().filter(|_| button == MouseButton::Left) { + self.ctx.trigger_hint(hint); + } + self.ctx.display().highlighted_hint = hint; + self.ctx.scheduler_mut().unschedule(TimerId::SelectionScrolling); } @@ -748,7 +731,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } // Skip normal mouse events if the message bar has been clicked. - if self.message_bar_mouse_state() == Some(MouseState::MessageBarButton) + if self.message_bar_cursor_state() == Some(CursorIcon::Hand) && state == ElementState::Pressed { let size = self.ctx.size_info(); @@ -773,7 +756,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { }, }; - self.ctx.window_mut().set_mouse_cursor(new_icon); + self.ctx.window().set_mouse_cursor(new_icon); } else { match state { ElementState::Pressed => { @@ -788,7 +771,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { /// Process key input. pub fn key_input(&mut self, input: KeyboardInput) { // All key bindings are disabled while a hint is being selected. - if self.ctx.hint_state().active() { + if self.ctx.display().hint_state.active() { *self.ctx.suppress_chars() = false; return; } @@ -813,17 +796,19 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { pub fn modifiers_input(&mut self, modifiers: ModifiersState) { *self.ctx.modifiers() = modifiers; + // Prompt hint highlight update. + self.ctx.mouse_mut().hint_highlight_dirty = true; + // Update mouse state and check for URL change. - let mouse_state = self.mouse_state(); - self.update_url_state(&mouse_state); - self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); + let mouse_state = self.cursor_state(); + self.ctx.window().set_mouse_cursor(mouse_state); } /// Reset mouse cursor based on modifier and terminal state. #[inline] pub fn reset_mouse_cursor(&mut self) { - let mouse_state = self.mouse_state(); - self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); + let mouse_state = self.cursor_state(); + self.ctx.window().set_mouse_cursor(mouse_state); } /// Process a received character. @@ -831,7 +816,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { let suppress_chars = *self.ctx.suppress_chars(); // Handle hint selection over anything else. - if self.ctx.hint_state().active() && !suppress_chars { + if self.ctx.display().hint_state.active() && !suppress_chars { self.ctx.hint_input(c); return; } @@ -925,8 +910,8 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } } - /// Check mouse state in relation to the message bar. - fn message_bar_mouse_state(&self) -> Option<MouseState> { + /// Check mouse icon state in relation to the message bar. + fn message_bar_cursor_state(&self) -> Option<CursorIcon> { // Since search is above the message bar, the button is offset by search's height. let search_height = if self.ctx.search_active() { 1 } else { 0 }; @@ -941,53 +926,30 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } else if mouse.y <= terminal_end + size.cell_height() as usize && mouse.point.column + message_bar::CLOSE_BUTTON_TEXT.len() >= size.columns() { - Some(MouseState::MessageBarButton) + Some(CursorIcon::Hand) } else { - Some(MouseState::MessageBar) - } - } - - /// Trigger redraw when URL highlight changed. - #[inline] - fn update_url_state(&mut self, mouse_state: &MouseState) { - let highlighted_url = self.ctx.highlighted_url(); - if let MouseState::Url(url) = mouse_state { - if Some(url) != highlighted_url { - self.ctx.mark_dirty(); - } - } else if highlighted_url.is_some() { - self.ctx.mark_dirty(); + Some(CursorIcon::Default) } } - /// Location of the mouse cursor. - fn mouse_state(&mut self) -> MouseState { - // Check message bar before URL to ignore URLs in the message bar. - if let Some(mouse_state) = self.message_bar_mouse_state() { - return mouse_state; - } - - let mouse_mode = self.ctx.mouse_mode(); - - // Check for URL at mouse cursor. - let mods = *self.ctx.modifiers(); - let highlighted_url = self.ctx.urls().highlighted( - self.ctx.config(), - self.ctx.mouse(), - mods, - mouse_mode, - !self.ctx.selection_is_empty(), - ); - - if let Some(url) = highlighted_url { - return MouseState::Url(url); - } + /// Icon state of the cursor. + fn cursor_state(&mut self) -> CursorIcon { + // Define function to check if mouse is on top of a hint. + let display_offset = self.ctx.terminal().grid().display_offset(); + let mouse_point = self.ctx.mouse().point; + let hint_highlighted = |hint: &HintMatch| { + let point = display::viewport_to_point(display_offset, mouse_point); + hint.bounds.contains(&point) + }; - // Check mouse mode if location is not special. - if !self.ctx.modifiers().shift() && mouse_mode { - MouseState::Mouse + if let Some(mouse_state) = self.message_bar_cursor_state() { + mouse_state + } else if self.ctx.display().highlighted_hint.as_ref().map_or(false, hint_highlighted) { + CursorIcon::Hand + } else if !self.ctx.modifiers().shift() && self.ctx.mouse_mode() { + CursorIcon::Default } else { - MouseState::Text + CursorIcon::Text } } @@ -1129,11 +1091,11 @@ mod tests { &mut self.modifiers } - fn window(&self) -> &Window { + fn window(&mut self) -> &mut Window { unimplemented!(); } - fn window_mut(&mut self) -> &mut Window { + fn display(&mut self) -> &mut Display { unimplemented!(); } @@ -1157,21 +1119,9 @@ mod tests { unimplemented!(); } - fn urls(&self) -> &Urls { - unimplemented!(); - } - - fn highlighted_url(&self) -> Option<&Url> { - unimplemented!(); - } - fn scheduler_mut(&mut self) -> &mut Scheduler { unimplemented!(); } - - fn hint_state(&mut self) -> &mut HintState { - unimplemented!(); - } } macro_rules! test_clickstate { |