Skip to content

Commit 6a10e28

Browse files
shanselmanCopilot
andcommitted
fix: mouse click selects correct package after list scrolls
Persist table scroll offset from ratatui's TableState after rendering and add it to the clicked row index in both left-click and right-click handlers. Previously, clicking a row in a scrolled list selected the wrong package because the scroll offset was not accounted for. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8a4665e commit 6a10e28

File tree

4 files changed

+9
-4
lines changed

4 files changed

+9
-4
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ pub struct App {
8282
pub show_help: bool,
8383
pub should_quit: bool,
8484
pub layout: LayoutRegions,
85+
/// Scroll offset of the package list table (set during rendering)
86+
pub table_scroll_offset: usize,
8587
/// Tick counter for animations (spinner, etc.)
8688
pub tick: usize,
8789
/// Incremented on each view refresh; stale results are discarded
@@ -114,6 +116,7 @@ impl App {
114116
show_help: false,
115117
should_quit: false,
116118
layout: LayoutRegions::default(),
119+
table_scroll_offset: 0,
117120
tick: 0,
118121
view_generation: 0,
119122
detail_generation: 0,

src/handler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ fn handle_mouse(
260260

261261
let content_y = app.layout.list_content_y;
262262
if row >= content_y {
263-
let clicked_idx = (row - content_y) as usize;
263+
let clicked_idx = (row - content_y) as usize + app.table_scroll_offset;
264264
if clicked_idx < app.filtered_packages.len() {
265265
app.selected = clicked_idx;
266266
load_detail_for_selected(app);
@@ -290,7 +290,7 @@ fn handle_mouse(
290290
if in_rect(col, row, app.layout.package_list) {
291291
let content_y = app.layout.list_content_y;
292292
if row >= content_y {
293-
let clicked_idx = (row - content_y) as usize;
293+
let clicked_idx = (row - content_y) as usize + app.table_scroll_offset;
294294
if clicked_idx < app.filtered_packages.len() {
295295
app.selected = clicked_idx;
296296
load_detail_for_selected(app);

src/ui.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ fn draw_main_content(f: &mut Frame, app: &mut App, area: Rect) {
130130
draw_detail_panel(f, app, chunks[1]);
131131
}
132132

133-
fn draw_package_list(f: &mut Frame, app: &App, area: Rect) {
133+
fn draw_package_list(f: &mut Frame, app: &mut App, area: Rect) {
134134
let (icon, title) = match app.mode {
135135
AppMode::Search => ("🔍", "Search Results"),
136136
AppMode::Installed => ("📦", "Installed"),
@@ -254,6 +254,8 @@ fn draw_package_list(f: &mut Frame, app: &App, area: Rect) {
254254
let mut state = TableState::default();
255255
state.select(Some(app.selected));
256256
f.render_stateful_widget(table, area, &mut state);
257+
// Capture scroll offset for mouse click hit-testing
258+
app.table_scroll_offset = state.offset();
257259

258260
// Scrollbar
259261
if app.filtered_packages.len() > (area.height as usize).saturating_sub(3) {

0 commit comments

Comments
 (0)