@@ -5,15 +5,65 @@ use gartk_core::{Color, Modifiers, Point, Rect}; |
| 5 | 5 | use gartk_render::{Renderer, TextAlign, TextStyle}; |
| 6 | 6 | use std::collections::HashSet; |
| 7 | 7 | |
| 8 | | -/// Size of each grid cell. |
| 9 | | -pub const CELL_SIZE: u32 = 100; |
| 10 | | - |
| 11 | | -/// Icon size within each cell. |
| 12 | | -pub const ICON_SIZE: u32 = 48; |
| 13 | | - |
| 14 | 8 | /// Padding around cells. |
| 15 | 9 | pub const CELL_PADDING: u32 = 8; |
| 16 | 10 | |
| 11 | +/// Icon size setting for grid view. |
| 12 | +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] |
| 13 | +pub enum IconSize { |
| 14 | + Small, |
| 15 | + #[default] |
| 16 | + Medium, |
| 17 | + Large, |
| 18 | +} |
| 19 | + |
| 20 | +impl IconSize { |
| 21 | + /// Get the cell size for this icon size. |
| 22 | + pub fn cell_size(&self) -> u32 { |
| 23 | + match self { |
| 24 | + IconSize::Small => 70, |
| 25 | + IconSize::Medium => 100, |
| 26 | + IconSize::Large => 140, |
| 27 | + } |
| 28 | + } |
| 29 | + |
| 30 | + /// Get the icon size within the cell. |
| 31 | + pub fn icon_size(&self) -> u32 { |
| 32 | + match self { |
| 33 | + IconSize::Small => 32, |
| 34 | + IconSize::Medium => 48, |
| 35 | + IconSize::Large => 64, |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + /// Get the font size for labels. |
| 40 | + pub fn font_size(&self) -> f64 { |
| 41 | + match self { |
| 42 | + IconSize::Small => 10.0, |
| 43 | + IconSize::Medium => 12.0, |
| 44 | + IconSize::Large => 14.0, |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + /// Cycle to the next size. |
| 49 | + pub fn next(&self) -> Self { |
| 50 | + match self { |
| 51 | + IconSize::Small => IconSize::Medium, |
| 52 | + IconSize::Medium => IconSize::Large, |
| 53 | + IconSize::Large => IconSize::Small, |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + /// Get display name. |
| 58 | + pub fn name(&self) -> &'static str { |
| 59 | + match self { |
| 60 | + IconSize::Small => "Small", |
| 61 | + IconSize::Medium => "Medium", |
| 62 | + IconSize::Large => "Large", |
| 63 | + } |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 17 | 67 | /// Grid view for displaying file entries as icons. |
| 18 | 68 | pub struct GridView { |
| 19 | 69 | /// Entries to display. |
@@ -38,12 +88,15 @@ pub struct GridView { |
| 38 | 88 | drag_start: Option<Point>, |
| 39 | 89 | /// Rubber band current position. |
| 40 | 90 | drag_current: Option<Point>, |
| 91 | + /// Icon size setting. |
| 92 | + icon_size: IconSize, |
| 41 | 93 | } |
| 42 | 94 | |
| 43 | 95 | impl GridView { |
| 44 | 96 | /// Create a new grid view. |
| 45 | 97 | pub fn new(bounds: Rect) -> Self { |
| 46 | | - let columns = Self::calculate_columns(bounds.width); |
| 98 | + let icon_size = IconSize::default(); |
| 99 | + let columns = Self::calculate_columns_for_size(bounds.width, icon_size); |
| 47 | 100 | Self { |
| 48 | 101 | entries: Vec::new(), |
| 49 | 102 | focused: 0, |
@@ -56,12 +109,35 @@ impl GridView { |
| 56 | 109 | hovered: None, |
| 57 | 110 | drag_start: None, |
| 58 | 111 | drag_current: None, |
| 112 | + icon_size, |
| 59 | 113 | } |
| 60 | 114 | } |
| 61 | 115 | |
| 116 | + /// Calculate number of columns that fit in the given width for a specific icon size. |
| 117 | + fn calculate_columns_for_size(width: u32, icon_size: IconSize) -> usize { |
| 118 | + let cell_size = icon_size.cell_size(); |
| 119 | + ((width - CELL_PADDING) / (cell_size + CELL_PADDING)).max(1) as usize |
| 120 | + } |
| 121 | + |
| 62 | 122 | /// Calculate number of columns that fit in the given width. |
| 63 | | - fn calculate_columns(width: u32) -> usize { |
| 64 | | - ((width - CELL_PADDING) / (CELL_SIZE + CELL_PADDING)).max(1) as usize |
| 123 | + fn calculate_columns(&self, width: u32) -> usize { |
| 124 | + Self::calculate_columns_for_size(width, self.icon_size) |
| 125 | + } |
| 126 | + |
| 127 | + /// Get the current icon size. |
| 128 | + pub fn icon_size(&self) -> IconSize { |
| 129 | + self.icon_size |
| 130 | + } |
| 131 | + |
| 132 | + /// Set the icon size. |
| 133 | + pub fn set_icon_size(&mut self, size: IconSize) { |
| 134 | + self.icon_size = size; |
| 135 | + self.columns = self.calculate_columns(self.bounds.width); |
| 136 | + } |
| 137 | + |
| 138 | + /// Cycle to the next icon size. |
| 139 | + pub fn cycle_icon_size(&mut self) { |
| 140 | + self.set_icon_size(self.icon_size.next()); |
| 65 | 141 | } |
| 66 | 142 | |
| 67 | 143 | /// Set the entries to display. |
@@ -104,7 +180,8 @@ impl GridView { |
| 104 | 180 | |
| 105 | 181 | /// Get the number of visible rows that fit in the view. |
| 106 | 182 | fn visible_rows(&self) -> usize { |
| 107 | | - (self.bounds.height / (CELL_SIZE + CELL_PADDING)).max(1) as usize |
| 183 | + let cell_size = self.icon_size.cell_size(); |
| 184 | + (self.bounds.height / (cell_size + CELL_PADDING)).max(1) as usize |
| 108 | 185 | } |
| 109 | 186 | |
| 110 | 187 | /// Get current sort settings (grid view doesn't track these, uses external). |
@@ -226,7 +303,7 @@ impl GridView { |
| 226 | 303 | /// Update bounds. |
| 227 | 304 | pub fn set_bounds(&mut self, bounds: Rect) { |
| 228 | 305 | self.bounds = bounds; |
| 229 | | - self.columns = Self::calculate_columns(bounds.width); |
| 306 | + self.columns = self.calculate_columns(bounds.width); |
| 230 | 307 | } |
| 231 | 308 | |
| 232 | 309 | /// Get cell bounds for an index. |
@@ -234,11 +311,12 @@ impl GridView { |
| 234 | 311 | let visible_index = index.saturating_sub(self.scroll_offset * self.columns); |
| 235 | 312 | let col = visible_index % self.columns; |
| 236 | 313 | let row = visible_index / self.columns; |
| 314 | + let cell_size = self.icon_size.cell_size(); |
| 237 | 315 | |
| 238 | | - let x = self.bounds.x + CELL_PADDING as i32 + (col as i32 * (CELL_SIZE + CELL_PADDING) as i32); |
| 239 | | - let y = self.bounds.y + CELL_PADDING as i32 + (row as i32 * (CELL_SIZE + CELL_PADDING) as i32); |
| 316 | + let x = self.bounds.x + CELL_PADDING as i32 + (col as i32 * (cell_size + CELL_PADDING) as i32); |
| 317 | + let y = self.bounds.y + CELL_PADDING as i32 + (row as i32 * (cell_size + CELL_PADDING) as i32); |
| 240 | 318 | |
| 241 | | - Rect::new(x, y, CELL_SIZE, CELL_SIZE) |
| 319 | + Rect::new(x, y, cell_size, cell_size) |
| 242 | 320 | } |
| 243 | 321 | |
| 244 | 322 | /// Handle mouse move for hover effects and rubber band drag. |
@@ -448,14 +526,20 @@ impl GridView { |
| 448 | 526 | } |
| 449 | 527 | }; |
| 450 | 528 | |
| 529 | + // Scale icon font size based on icon size setting |
| 530 | + let icon_font_size = match self.icon_size { |
| 531 | + IconSize::Small => 24.0, |
| 532 | + IconSize::Medium => 32.0, |
| 533 | + IconSize::Large => 48.0, |
| 534 | + }; |
| 451 | 535 | let icon_style = TextStyle::new() |
| 452 | 536 | .font_family(&theme.font_family) |
| 453 | | - .font_size(32.0) |
| 537 | + .font_size(icon_font_size) |
| 454 | 538 | .color(icon_color); |
| 455 | 539 | |
| 456 | 540 | // Center icon horizontally in cell |
| 457 | 541 | let icon_center_x = cell.x + cell.width as i32 / 2; |
| 458 | | - let icon_center_y = cell.y + 10 + 16; // 10px top padding + half icon height |
| 542 | + let icon_center_y = cell.y + 10 + (icon_font_size / 2.0) as i32; |
| 459 | 543 | renderer.text_centered(icon, Point::new(icon_center_x, icon_center_y), &icon_style)?; |
| 460 | 544 | |
| 461 | 545 | // File name (truncated) |
@@ -467,9 +551,10 @@ impl GridView { |
| 467 | 551 | theme.item_foreground |
| 468 | 552 | }; |
| 469 | 553 | |
| 554 | + let name_font_size = self.icon_size.font_size(); |
| 470 | 555 | let name_style = TextStyle::new() |
| 471 | 556 | .font_family(&theme.font_family) |
| 472 | | - .font_size(theme.font_size - 1.0) |
| 557 | + .font_size(name_font_size) |
| 473 | 558 | .color(name_color); |
| 474 | 559 | |
| 475 | 560 | // Use Pango CENTER alignment for proper text centering (like Dolphin/Nautilus) |
@@ -479,11 +564,12 @@ impl GridView { |
| 479 | 564 | .max_width((cell.width - 8) as i32); |
| 480 | 565 | |
| 481 | 566 | // Rectangle for the text area below the icon |
| 567 | + let icon_size = self.icon_size.icon_size(); |
| 482 | 568 | let text_rect = Rect::new( |
| 483 | 569 | cell.x + 4, |
| 484 | | - cell.y + ICON_SIZE as i32 + 8, |
| 570 | + cell.y + icon_size as i32 + 8, |
| 485 | 571 | cell.width - 8, |
| 486 | | - cell.height - ICON_SIZE - 12, |
| 572 | + cell.height - icon_size - 12, |
| 487 | 573 | ); |
| 488 | 574 | // Add "@" suffix for symlinks |
| 489 | 575 | let display_name = if entry.is_symlink { |