aboutsummaryrefslogtreecommitdiff
path: root/src/input.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/input.rs')
-rw-r--r--src/input.rs94
1 files changed, 71 insertions, 23 deletions
diff --git a/src/input.rs b/src/input.rs
index ed9aa7fc..49a9101e 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -63,6 +63,7 @@ pub trait ActionContext {
fn simple_selection(&mut self, point: Point, side: Side);
fn semantic_selection(&mut self, point: Point);
fn line_selection(&mut self, point: Point);
+ fn selection_is_empty(&self) -> bool;
fn mouse_mut(&mut self) -> &mut Mouse;
fn mouse(&self) -> &Mouse;
fn mouse_coords(&self) -> Option<Point>;
@@ -74,6 +75,7 @@ pub trait ActionContext {
fn scroll(&mut self, scroll: Scroll);
fn clear_history(&mut self);
fn hide_window(&mut self);
+ fn url(&self, _: Point<usize>) -> Option<String>;
}
/// Describes a state and action to take in that state
@@ -326,16 +328,25 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
let size_info = self.ctx.size_info();
let point = size_info.pixels_to_coords(x, y);
+ let cell_side = self.get_mouse_side();
+ let prev_side = mem::replace(&mut self.ctx.mouse_mut().cell_side, cell_side);
let prev_line = mem::replace(&mut self.ctx.mouse_mut().line, point.line);
let prev_col = mem::replace(&mut self.ctx.mouse_mut().column, point.col);
let motion_mode = TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG;
let report_mode = TermMode::MOUSE_REPORT_CLICK | motion_mode;
+ // Don't launch URLs if mouse has moved
+ if prev_line != self.ctx.mouse().line
+ || prev_col != self.ctx.mouse().column
+ || prev_side != cell_side
+ {
+ self.ctx.mouse_mut().block_url_launcher = true;
+ }
+
if self.ctx.mouse().left_button_state == ElementState::Pressed &&
( modifiers.shift || !self.ctx.terminal_mode().intersects(report_mode))
{
- let cell_side = self.get_mouse_side();
self.ctx.update_selection(Point {
line: point.line,
col: point.col
@@ -357,6 +368,26 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
}
}
+ fn get_mouse_side(&self) -> Side {
+ let size_info = self.ctx.size_info();
+ let x = self.ctx.mouse().x;
+
+ 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 end_of_grid = size_info.width - size_info.padding_x - additional_padding;
+
+ if cell_x > half_cell_width
+ // Edge case when mouse leaves the window
+ || x as f32 >= end_of_grid
+ {
+ Side::Right
+ } else {
+ Side::Left
+ }
+ }
+
pub fn normal_mouse_report(&mut self, button: u8) {
let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column);
@@ -427,19 +458,24 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
ClickState::Click if elapsed < self.mouse_config.double_click.threshold => {
+ self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_double_click();
ClickState::DoubleClick
},
ClickState::DoubleClick if elapsed < self.mouse_config.triple_click.threshold => {
+ self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_triple_click();
ClickState::TripleClick
},
_ => {
+ // Don't launch URLs if this click cleared the selection
+ self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
+
self.ctx.clear_selection();
// Start new empty selection
if let Some(point) = self.ctx.mouse_coords() {
- let side = self.get_mouse_side();
+ let side = self.ctx.mouse().cell_side;
self.ctx.simple_selection(point, side);
}
@@ -460,26 +496,6 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
};
}
- fn get_mouse_side(&self) -> Side {
- let size_info = self.ctx.size_info();
- let x = self.ctx.mouse().x;
-
- 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 end_of_grid = size_info.width - size_info.padding_x - additional_padding;
-
- if cell_x > half_cell_width
- // Edge case when mouse leaves the window
- || x as f32 >= end_of_grid
- {
- Side::Right
- } else {
- Side::Left
- }
- }
-
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)
@@ -492,6 +508,8 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
MouseButton::Other(_) => (),
};
return;
+ } else {
+ self.launch_url(modifiers);
}
if self.save_to_clipboard {
@@ -500,6 +518,27 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
self.ctx.copy_selection(ClipboardBuffer::Selection);
}
+ // Spawn URL launcher when clicking on URLs
+ fn launch_url(&self, modifiers: ModifiersState) -> Option<()> {
+ if modifiers != self.mouse_config.url.modifiers || self.ctx.mouse().block_url_launcher {
+ return None;
+ }
+
+ let point = self.ctx.mouse_coords()?;
+ let text = self.ctx.url(point.into())?;
+
+ let launcher = self.mouse_config.url.launcher.as_ref()?;
+ let mut args = launcher.args().to_vec();
+ args.push(text);
+
+ match Command::new(launcher.program()).args(&args).spawn() {
+ Ok(_) => debug!("Launched: {} {:?}", launcher.program(), args),
+ Err(_) => warn!("Unable to launch: {} {:?}", launcher.program(), args),
+ }
+
+ Some(())
+ }
+
pub fn on_mouse_wheel(&mut self, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState) {
match delta {
MouseScrollDelta::LineDelta(_columns, lines) => {
@@ -777,6 +816,10 @@ mod tests {
self.last_action = MultiClick::TripleClick;
}
+ fn selection_is_empty(&self) -> bool {
+ true
+ }
+
fn scroll(&mut self, scroll: Scroll) {
self.terminal.scroll_display(scroll);
}
@@ -795,6 +838,10 @@ mod tests {
self.mouse
}
+ fn url(&self, _: Point<usize>) -> Option<String> {
+ None
+ }
+
fn received_count(&mut self) -> &mut usize {
&mut self.received_count
}
@@ -863,11 +910,12 @@ mod tests {
threshold: Duration::from_millis(1000),
},
faux_scrollback_lines: None,
+ url: Default::default(),
},
scrolling_config: &config::Scrolling::default(),
key_bindings: &config.key_bindings()[..],
mouse_bindings: &config.mouse_bindings()[..],
- save_to_clipboard: config.selection().save_to_clipboard
+ save_to_clipboard: config.selection().save_to_clipboard,
};
if let Event::WindowEvent { event: WindowEvent::MouseInput { state, button, modifiers, .. }, .. } = $input {