Skip to main content

par_term/pane/
mouse.rs

1use crate::selection::Selection;
2use crate::url_detection;
3use std::time::Instant;
4
5/// State related to mouse interaction, selection, and URL detection
6pub struct MouseState {
7    pub(crate) selection: Option<Selection>, // Current text selection
8    pub(crate) is_selecting: bool,           // Whether currently dragging to select
9
10    pub(crate) position: (f64, f64), // Current mouse position in pixels
11    pub(crate) button_pressed: bool, // Whether any mouse button is currently pressed (for motion tracking)
12    pub(crate) last_click_time: Option<Instant>, // Time of last mouse click
13    pub(crate) click_count: u32, // Number of sequential clicks (1 = single, 2 = double, 3 = triple)
14    pub(crate) click_position: Option<(usize, usize)>, // Position of last click in cell coordinates
15    pub(crate) click_pixel_position: Option<(f64, f64)>, // Position of last click in pixels (for drag threshold)
16    /// Pixel position where the mouse button was pressed AND the event was consumed by mouse
17    /// tracking (i.e. forwarded to the PTY app).  Used to apply a drag dead-zone to
18    /// button-pressed motion events (button=32) so that trackpad tap jitter does not cause
19    /// tmux to interpret a pane-focus click as a drag-selection, which would wipe the clipboard.
20    /// Cleared on button release or when the press was not consumed by mouse tracking.
21    pub(crate) tracking_press_position: Option<(f64, f64)>,
22    pub(crate) detected_urls: Vec<url_detection::DetectedUrl>, // URLs detected in visible terminal area
23    pub(crate) url_detect_scroll_offset: usize, // scroll_offset used when detected_urls were computed
24    pub(crate) hovered_url: Option<String>,     // URL currently under mouse cursor
25    pub(crate) hovered_url_bounds: Option<(usize, usize, usize)>, // (row, start_col, end_col) of hovered URL
26
27    // Divider drag state
28    pub(crate) dragging_divider: Option<usize>, // Index of divider being dragged
29    pub(crate) divider_hover: bool,             // Whether hovering over a divider
30    pub(crate) hovered_divider_index: Option<usize>, // Index of the hovered divider
31}
32
33impl Default for MouseState {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl MouseState {
40    pub(crate) fn new() -> Self {
41        Self {
42            selection: None,
43            is_selecting: false,
44            position: (0.0, 0.0),
45            button_pressed: false,
46            last_click_time: None,
47            click_count: 0,
48            click_position: None,
49            click_pixel_position: None,
50            tracking_press_position: None,
51            detected_urls: Vec::new(),
52            url_detect_scroll_offset: 0,
53            hovered_url: None,
54            hovered_url_bounds: None,
55            dragging_divider: None,
56            divider_hover: false,
57            hovered_divider_index: None,
58        }
59    }
60
61    // Test accessors for integration tests
62    pub fn test_new() -> Self {
63        Self::new()
64    }
65    pub fn test_set_button_pressed(&mut self, v: bool) {
66        self.button_pressed = v;
67    }
68    pub fn test_button_pressed(&self) -> bool {
69        self.button_pressed
70    }
71    pub fn test_set_is_selecting(&mut self, v: bool) {
72        self.is_selecting = v;
73    }
74    pub fn test_is_selecting(&self) -> bool {
75        self.is_selecting
76    }
77    pub fn test_set_selection(&mut self, sel: Option<Selection>) {
78        self.selection = sel;
79    }
80    pub fn test_selection(&self) -> Option<&Selection> {
81        self.selection.as_ref()
82    }
83    pub fn test_set_click_pixel_position(&mut self, pos: Option<(f64, f64)>) {
84        self.click_pixel_position = pos;
85    }
86    pub fn test_set_click_position(&mut self, pos: Option<(usize, usize)>) {
87        self.click_position = pos;
88    }
89    pub fn test_set_click_count(&mut self, count: u32) {
90        self.click_count = count;
91    }
92    pub fn test_set_dragging_divider(&mut self, idx: Option<usize>) {
93        self.dragging_divider = idx;
94    }
95    pub fn test_dragging_divider(&self) -> Option<usize> {
96        self.dragging_divider
97    }
98}