Skip to content

Commit e67ee7a

Browse files
committed
Draggable scrollbar
1 parent 846fd0c commit e67ee7a

File tree

2 files changed

+82
-3
lines changed

2 files changed

+82
-3
lines changed

src/views/table.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::{
2222
command::{result::CommandResult, Command, PromptKind},
2323
file_system::path_info::PathInfo,
2424
};
25+
use log::debug;
2526

2627
#[derive(Default)]
2728
pub(super) struct TableView {
@@ -39,6 +40,7 @@ pub(super) struct TableView {
3940
columns: Columns,
4041
double_click: DoubleClick,
4142
mapper: LineItemMap,
43+
is_scrollbar_dragging: bool,
4244
}
4345

4446
impl TableView {
@@ -308,4 +310,64 @@ impl TableView {
308310
self.columns.sort_by(column);
309311
self.sort()
310312
}
313+
314+
fn is_scrollbar_click(&self, x: u16, y: u16) -> bool {
315+
// Check if click is within the scrollbar area
316+
self.scrollbar_area.intersects(Rect::new(x, y, 1, 1))
317+
}
318+
319+
fn update_scrollbar_position(&mut self, y: u16) {
320+
let total_lines = self.mapper.total_number_of_lines();
321+
let visible_height = self.table_area.height as usize;
322+
323+
if total_lines <= visible_height {
324+
return;
325+
}
326+
327+
// Calculate the scroll position based on the click/drag position
328+
let scrollbar_height = self.scrollbar_area.height;
329+
let scrollbar_y = self.scrollbar_area.y;
330+
let relative_y = y.saturating_sub(scrollbar_y);
331+
332+
// Use a slightly reduced height to ensure we can reach the bottom when dragging
333+
let effective_height = scrollbar_height.saturating_sub(2);
334+
335+
// Calculate percentage of scrollbar that was clicked/dragged
336+
let percentage = if relative_y >= effective_height {
337+
1.0 // At bottom
338+
} else {
339+
relative_y as f32 / effective_height as f32
340+
};
341+
342+
// Map percentage to content position
343+
let selected_pos = (percentage * (total_lines - 1) as f32).round() as usize;
344+
345+
// Calculate offset (what appears at the top of the view)
346+
let max_offset = total_lines.saturating_sub(visible_height);
347+
let offset = if percentage >= 0.95 {
348+
// When near the bottom, show the last page
349+
max_offset
350+
} else if selected_pos + visible_height > total_lines {
351+
// When selection would be off-screen, adjust offset
352+
max_offset
353+
} else {
354+
// Normal case: selected position at the top
355+
selected_pos
356+
};
357+
358+
// Update scrollbar visual state
359+
self.scrollbar_state = self
360+
.scrollbar_state
361+
.content_length(total_lines)
362+
.position(selected_pos);
363+
364+
// Update the table scroll position
365+
*self.table_state.offset_mut() = offset;
366+
367+
// Update the selection to match
368+
if !self.directory_items_sorted.is_empty() {
369+
let max_index = self.directory_items_sorted.len() - 1;
370+
self.table_state.select(Some(selected_pos.min(max_index)));
371+
}
372+
}
311373
}

src/views/table/handler.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,40 @@ impl CommandHandler for TableView {
5757
}
5858

5959
fn handle_mouse(&mut self, event: &MouseEvent) -> CommandResult {
60+
let x = event.column.saturating_sub(self.table_area.x);
61+
let y = event.row.saturating_sub(self.table_area.y);
62+
6063
match event.kind {
6164
MouseEventKind::Down(MouseButton::Left) => {
62-
let x = event.column.saturating_sub(self.table_area.x);
63-
let y = event.row.saturating_sub(self.table_area.y);
65+
if self.is_scrollbar_click(x, y) {
66+
self.is_scrollbar_dragging = true;
67+
self.update_scrollbar_position(y);
68+
return CommandResult::none();
69+
}
6470
if y == 0 {
6571
self.click_header(x)
6672
} else {
6773
self.click_table(y)
6874
}
6975
}
76+
MouseEventKind::Up(MouseButton::Left) => {
77+
self.is_scrollbar_dragging = false;
78+
CommandResult::none()
79+
}
80+
MouseEventKind::Drag(MouseButton::Left) => {
81+
if self.is_scrollbar_dragging {
82+
self.update_scrollbar_position(y);
83+
return CommandResult::none();
84+
}
85+
CommandResult::none()
86+
}
7087
MouseEventKind::ScrollUp => self.previous(),
7188
MouseEventKind::ScrollDown => self.next(),
7289
_ => CommandResult::none(),
7390
}
7491
}
7592

7693
fn should_receive_mouse(&self, x: u16, y: u16) -> bool {
77-
self.table_area.intersects(Rect::new(x, y, 1, 1))
94+
self.table_area.intersects(Rect::new(x, y, 1, 1)) || self.is_scrollbar_click(x, y)
7895
}
7996
}

0 commit comments

Comments
 (0)