aboutsummaryrefslogtreecommitdiff
path: root/src/app/input
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-11 02:46:41 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-11 02:46:58 +0300
commit46174563489273199a17c982356c6e4674ef00d4 (patch)
treee87a16bdb34e45b66124732c3458eed1e64d91a0 /src/app/input
parent3b8aeee166d1594e1f8e41631a51748104d76834 (diff)
feat: add copy-on-select setting
Diffstat (limited to 'src/app/input')
-rw-r--r--src/app/input/mod.rs5
-rw-r--r--src/app/input/mouse.rs14
-rw-r--r--src/app/input/terminal.rs81
3 files changed, 89 insertions, 11 deletions
diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs
index bcd5da98..421b3738 100644
--- a/src/app/input/mod.rs
+++ b/src/app/input/mod.rs
@@ -408,6 +408,11 @@ impl App {
}
fn handle_pane_double_click(&mut self, mouse: MouseEvent) -> bool {
+ if !self.state.copy_on_select {
+ self.last_pane_click = None;
+ return false;
+ }
+
// A pane press stops being a double-click candidate once it becomes
// a drag or completes as a real text selection.
match mouse.kind {
diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs
index 495bcd53..911e27c0 100644
--- a/src/app/input/mouse.rs
+++ b/src/app/input/mouse.rs
@@ -640,12 +640,14 @@ impl AppState {
mouse.row - info.inner_rect.y,
mouse.column - info.inner_rect.x,
);
- self.selection = Some(Selection::anchor(
- info.id,
- row,
- col,
- self.pane_scroll_metrics(terminal_runtimes, info.id),
- ));
+ if self.copy_on_select {
+ self.selection = Some(Selection::anchor(
+ info.id,
+ row,
+ col,
+ self.pane_scroll_metrics(terminal_runtimes, info.id),
+ ));
+ }
if let Some(ws_idx) = self.active {
return Some(MouseAction::FocusPane {
ws_idx,
diff --git a/src/app/input/terminal.rs b/src/app/input/terminal.rs
index 0fc35bd3..dd1e811f 100644
--- a/src/app/input/terminal.rs
+++ b/src/app/input/terminal.rs
@@ -459,6 +459,34 @@ mod tests {
}
#[tokio::test]
+ async fn copy_on_select_disabled_ignores_drag_and_double_click_selection() {
+ let (mut app, info) = app_with_screen_bytes(b"alpha beta");
+ app.state.copy_on_select = false;
+ let row = info.inner_rect.y;
+ let start_col = info.inner_rect.x;
+ let end_col = info.inner_rect.x + 4;
+
+ app.handle_mouse(mouse(
+ MouseEventKind::Down(MouseButton::Left),
+ start_col,
+ row,
+ ));
+ app.handle_mouse(mouse(MouseEventKind::Drag(MouseButton::Left), end_col, row));
+ app.handle_mouse(mouse(MouseEventKind::Up(MouseButton::Left), end_col, row));
+
+ assert!(app.state.selection.is_none());
+ assert!(app.state.selection_autoscroll.is_none());
+ assert!(app.event_rx.try_recv().is_err());
+
+ double_click(&mut app, start_col, row);
+
+ assert!(app.state.selection.is_none());
+ assert!(app.last_pane_click.is_none());
+ assert!(app.selection_highlight_clear_deadline.is_none());
+ assert!(app.event_rx.try_recv().is_err());
+ }
+
+ #[tokio::test]
async fn double_click_uses_display_columns_for_wide_chars() {
let (mut app, info) = app_with_screen_bytes("echo 你好-world done".as_bytes());
let col = info.inner_rect.x + 8;
@@ -730,15 +758,58 @@ mod tests {
}
#[tokio::test]
- async fn double_click_is_forwarded_when_mouse_reporting_is_enabled() {
- let (mut app, info) = app_with_screen_bytes(b"\x1b[?1002halpha beta");
- let col = info.inner_rect.x + 8;
- let row = info.inner_rect.y;
- double_click(&mut app, col, row);
+ async fn copy_on_select_disabled_still_forwards_mouse_reporting_gestures() {
+ let mut app = app_for_mouse_test();
+ let mut ws = Workspace::test_new("test");
+ let pane_id = ws.tabs[0].root_pane;
+ let pane_infos = ws.tabs[0].layout.panes(Rect::new(26, 2, 80, 18));
+ let info = pane_infos[0].clone();
+ let (runtime, mut input_rx) =
+ crate::terminal::TerminalRuntime::test_with_channel_and_scrollback_bytes(
+ info.inner_rect.width,
+ info.inner_rect.height,
+ 0,
+ b"\x1b[?1002h\x1b[?1006h",
+ 4,
+ );
+ ws.insert_test_runtime(pane_id, runtime);
+ app.state.workspaces = vec![ws];
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.mode = Mode::Terminal;
+ app.state.view.pane_infos = pane_infos;
+ app.state.copy_on_select = false;
+
+ let col = info.inner_rect.x + 2;
+ let row = info.inner_rect.y + 3;
+ app.handle_mouse(mouse(MouseEventKind::Down(MouseButton::Left), col, row));
+ app.handle_mouse(mouse(
+ MouseEventKind::Drag(MouseButton::Left),
+ col + 1,
+ row + 1,
+ ));
+ app.handle_mouse(mouse(
+ MouseEventKind::Up(MouseButton::Left),
+ col + 1,
+ row + 1,
+ ));
assert!(app.event_rx.try_recv().is_err());
assert!(app.state.selection.is_none());
assert!(app.selection_highlight_clear_deadline.is_none());
+ assert_eq!(
+ input_rx.try_recv().expect("forwarded left mouse down"),
+ Bytes::from_static(b"\x1b[<0;3;4M")
+ );
+ assert_eq!(
+ input_rx.try_recv().expect("forwarded left mouse drag"),
+ Bytes::from_static(b"\x1b[<32;4;5M")
+ );
+ assert_eq!(
+ input_rx.try_recv().expect("forwarded left mouse up"),
+ Bytes::from_static(b"\x1b[<0;4;5m")
+ );
+ assert!(input_rx.try_recv().is_err());
}
#[tokio::test]