From dd0fcbc6718582ca77d3187289ca7bd92b443dc8 Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 9 Jul 2026 13:42:43 +0200 Subject: [PATCH] fix: selection range desync and arrow pointer in alternate screen --- src/main.rs | 56 ++++++++++++++++++++++++++++++++++--------------- src/renderer.rs | 17 +++++++++++++-- src/terminal.rs | 2 +- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 74855a6..2b74acd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2115,8 +2115,7 @@ impl App { for (pane_id, _) in &geometries { if let Some(pane) = tab.panes.get_mut(pane_id) { if pane.terminal.using_alternate_screen { - pane.selection = None; - pane.is_selecting = false; + // Keep selection if it's being modified by the user } let is_active = *pane_id == active_pane_id; let dim_factor = pane.calculate_dim_factor( @@ -3230,19 +3229,35 @@ impl ApplicationHandler for App { None }; - // Set cursor icon to IBeam when hovering over a pane - if let Some(window) = &self.window { - let icon = if pane_geom.is_some() { - CursorIcon::Text - } else { - CursorIcon::Default - }; - window.set_cursor(icon); - } + // Set cursor icon to IBeam when hovering over a pane, unless it's an alternate screen + if let Some(window) = &self.window { + let icon = if let Some(geom) = &pane_geom { + let is_alt = if let Some(tab) = self.active_tab() { + tab.panes + .get(&geom.pane_id) + .map(|p| p.terminal.using_alternate_screen) + .unwrap_or(false) + } else { + false + }; + if is_alt { + CursorIcon::Default + } else { + CursorIcon::Text + } + } else { + CursorIcon::Default + }; + window.set_cursor(icon); + } // Switch focus if dragging on a different pane - if self.mouse_down_pos.is_some() { - if let Some(geom) = &pane_geom { + if self.mouse_down_pos.is_some() { + if let Some(renderer) = &mut self.renderer { + renderer.force_full_redraw(); + } + + if let Some(geom) = &pane_geom { if let Some(tab) = self.active_tab_mut() { if tab.active_pane != geom.pane_id { if tab.focus_pane_by_id(geom.pane_id) { @@ -3263,8 +3278,9 @@ impl ApplicationHandler for App { }) .map(|p| p.is_selecting) .unwrap_or(false); - let is_selecting = - active_is_selecting || mouse_pane_is_selecting; + let is_selecting = + (active_is_selecting || mouse_pane_is_selecting) + || self.mouse_down_pos.is_some(); let mouse_tracking = pane_geom .as_ref() @@ -3521,6 +3537,14 @@ impl ApplicationHandler for App { } WindowEvent::MouseInput { state, button, .. } => { + if button == MouseButton::Left { + self.mouse_down_pos = if state == ElementState::Pressed { + Some(self.cursor_position) + } else { + None + }; + } + let button_code = match button { MouseButton::Left => 0, MouseButton::Middle => 1, @@ -3612,7 +3636,6 @@ impl ApplicationHandler for App { } else if button == MouseButton::Left { match state { ElementState::Pressed => { - self.mouse_down_pos = Some(self.cursor_position); if let Some(tab) = self.active_tab_mut() { if let Some(pane) = tab.active_pane_mut() { pane.selection = None; @@ -3624,7 +3647,6 @@ impl ApplicationHandler for App { } } ElementState::Released => { - self.mouse_down_pos = None; let was_selecting = self .active_pane() .map(|p| p.is_selecting) diff --git a/src/renderer.rs b/src/renderer.rs index ea0c337..f9edabf 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -20,7 +20,7 @@ use crate::graphics::ImageStorage; use crate::image_renderer::ImageRenderer; use crate::pane_resources::PaneGpuResources; use crate::pipeline::PipelineBuilder; -use crate::terminal::{Color, ColorPalette, CursorShape, Direction, Terminal}; +use crate::terminal::{Cell, Color, ColorPalette, CursorShape, Direction, Terminal}; use ab_glyph::{Font, FontRef, GlyphId, ScaleFont}; use rustc_hash::FxHashMap; use rustybuzz::UnicodeBuffer; @@ -5541,7 +5541,20 @@ impl Renderer { for row in *sr..=*er { if row < 256 && row < rows as usize { let mut max_col = -1i32; - let row_cells = &terminal.grid[row as usize]; + let row_cells: &[Cell] = if row < terminal.scroll_offset { + terminal.scrollback.get( + terminal.scrollback.len() + .saturating_sub(terminal.scroll_offset) + + row as usize, + ).map(|c| c.as_slice()).unwrap_or(&[]) + } else { + let grid_row = (row as usize).saturating_sub(terminal.scroll_offset); + if grid_row < terminal.line_map.len() { + &terminal.grid[terminal.line_map[grid_row]] + } else { + &[] + } + }; for col in 0..row_cells.len() { let cell = &row_cells[col]; if cell.character != ' ' || cell.wide_continuation { diff --git a/src/terminal.rs b/src/terminal.rs index f4bf852..86d33aa 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -418,7 +418,7 @@ pub struct Terminal { pub grid: Vec>, /// Maps visual row index to actual grid row index. /// This allows O(1) scrolling by rotating indices instead of moving cells. - line_map: Vec, + pub line_map: Vec, /// Number of columns. pub cols: usize, /// Number of rows.