@@ -7,6 +7,7 @@ use gartk_core::{InputEvent, Key, Point, Rect, Theme}; |
| 7 | use gartk_render::{Renderer, Surface}; | 7 | use gartk_render::{Renderer, Surface}; |
| 8 | use gartk_x11::{Connection, EventLoop, EventLoopConfig, Window, WindowConfig}; | 8 | use gartk_x11::{Connection, EventLoop, EventLoopConfig, Window, WindowConfig}; |
| 9 | use std::path::PathBuf; | 9 | use std::path::PathBuf; |
| | 10 | +use std::time::Instant; |
| 10 | use x11rb::protocol::xproto::{ConnectionExt, ImageFormat}; | 11 | use x11rb::protocol::xproto::{ConnectionExt, ImageFormat}; |
| 11 | | 12 | |
| 12 | /// Height of the breadcrumb bar. | 13 | /// Height of the breadcrumb bar. |
@@ -50,6 +51,10 @@ pub struct App { |
| 50 | should_quit: bool, | 51 | should_quit: bool, |
| 51 | /// Pane divider resize in progress (split pane pointer path). | 52 | /// Pane divider resize in progress (split pane pointer path). |
| 52 | pane_resize_path: Option<Vec<bool>>, | 53 | pane_resize_path: Option<Vec<bool>>, |
| | 54 | + /// Last click time for double-click detection. |
| | 55 | + last_click_time: Option<Instant>, |
| | 56 | + /// Last click position for double-click detection. |
| | 57 | + last_click_pos: Option<Point>, |
| 53 | } | 58 | } |
| 54 | | 59 | |
| 55 | impl App { | 60 | impl App { |
@@ -175,6 +180,8 @@ impl App { |
| 175 | help_modal, | 180 | help_modal, |
| 176 | should_quit: false, | 181 | should_quit: false, |
| 177 | pane_resize_path: None, | 182 | pane_resize_path: None, |
| | 183 | + last_click_time: None, |
| | 184 | + last_click_pos: None, |
| 178 | }; | 185 | }; |
| 179 | | 186 | |
| 180 | app.update_status_bar(); | 187 | app.update_status_bar(); |
@@ -329,9 +336,32 @@ impl App { |
| 329 | } | 336 | } |
| 330 | } | 337 | } |
| 331 | | 338 | |
| 332 | - if let Some(pane) = self.focused_pane_mut() { | 339 | + // Check for double-click to enter directory |
| 333 | - if let Some(tab) = pane.active_tab_mut() { | 340 | + let now = Instant::now(); |
| 334 | - tab.on_click(pos, modifiers); | 341 | + let is_double_click = if let (Some(last_time), Some(last_pos)) = (self.last_click_time, self.last_click_pos) { |
| | 342 | + let elapsed = now.duration_since(last_time); |
| | 343 | + let distance = ((pos.x - last_pos.x).pow(2) + (pos.y - last_pos.y).pow(2)) as f64; |
| | 344 | + elapsed.as_millis() < 400 && distance.sqrt() < 5.0 |
| | 345 | + } else { |
| | 346 | + false |
| | 347 | + }; |
| | 348 | + |
| | 349 | + // Update click tracking |
| | 350 | + self.last_click_time = Some(now); |
| | 351 | + self.last_click_pos = Some(pos); |
| | 352 | + |
| | 353 | + if is_double_click { |
| | 354 | + // Double-click: enter the selected item |
| | 355 | + self.enter_selected(); |
| | 356 | + // Clear click tracking to prevent triple-click |
| | 357 | + self.last_click_time = None; |
| | 358 | + self.last_click_pos = None; |
| | 359 | + } else { |
| | 360 | + // Single click: handle selection |
| | 361 | + if let Some(pane) = self.focused_pane_mut() { |
| | 362 | + if let Some(tab) = pane.active_tab_mut() { |
| | 363 | + tab.on_click(pos, modifiers); |
| | 364 | + } |
| 335 | } | 365 | } |
| 336 | } | 366 | } |
| 337 | } | 367 | } |
@@ -506,11 +536,15 @@ impl App { |
| 506 | return; | 536 | return; |
| 507 | } | 537 | } |
| 508 | Key::Char('d') | Key::Char('D') => { | 538 | Key::Char('d') | Key::Char('D') => { |
| 509 | - if let Some(pane) = self.focused_pane() { | 539 | + // Bookmark the selected item (must be a directory) |
| 510 | - if let Some(tab) = pane.active_tab() { | 540 | + let bookmark_path = self.focused_pane() |
| 511 | - let current = tab.current_path().clone(); | 541 | + .and_then(|pane| pane.active_tab()) |
| 512 | - self.sidebar.toggle_bookmark(¤t); | 542 | + .and_then(|tab| tab.selected_entry()) |
| 513 | - } | 543 | + .filter(|e| e.is_dir()) |
| | 544 | + .map(|e| e.path.clone()); |
| | 545 | + |
| | 546 | + if let Some(path) = bookmark_path { |
| | 547 | + self.sidebar.toggle_bookmark(&path); |
| 514 | } | 548 | } |
| 515 | return; | 549 | return; |
| 516 | } | 550 | } |
@@ -1060,6 +1094,9 @@ impl App { |
| 1060 | // Draw status bar | 1094 | // Draw status bar |
| 1061 | self.status_bar.render(&self.renderer)?; | 1095 | self.status_bar.render(&self.renderer)?; |
| 1062 | | 1096 | |
| | 1097 | + // Draw toolbar tooltip overlay (on top of other UI) |
| | 1098 | + self.toolbar.render_tooltip_overlay(&self.renderer)?; |
| | 1099 | + |
| 1063 | // Draw help modal overlay (on top of everything) | 1100 | // Draw help modal overlay (on top of everything) |
| 1064 | self.help_modal.render(&self.renderer)?; | 1101 | self.help_modal.render(&self.renderer)?; |
| 1065 | | 1102 | |