aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/input.rs
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2021-04-13 03:24:42 +0000
committerGitHub <noreply@github.com>2021-04-13 03:24:42 +0000
commit96fc9ecc9a62c8a766da745d05fbe60e6c2e1efe (patch)
tree16e237d750d6c650713b58df98453dc4df16d33a /alacritty/src/input.rs
parent40bcdb11335cc49d4d42694b953be746cb383cb9 (diff)
downloadr-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.rs186
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 {