@@ -5,15 +5,65 @@ use gartk_core::{Color, Modifiers, Point, Rect}; |
| 5 | use gartk_render::{Renderer, TextAlign, TextStyle}; | 5 | use gartk_render::{Renderer, TextAlign, TextStyle}; |
| 6 | use std::collections::HashSet; | 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 | /// Padding around cells. | 8 | /// Padding around cells. |
| 15 | pub const CELL_PADDING: u32 = 8; | 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 | /// Grid view for displaying file entries as icons. | 67 | /// Grid view for displaying file entries as icons. |
| 18 | pub struct GridView { | 68 | pub struct GridView { |
| 19 | /// Entries to display. | 69 | /// Entries to display. |
@@ -38,12 +88,15 @@ pub struct GridView { |
| 38 | drag_start: Option<Point>, | 88 | drag_start: Option<Point>, |
| 39 | /// Rubber band current position. | 89 | /// Rubber band current position. |
| 40 | drag_current: Option<Point>, | 90 | drag_current: Option<Point>, |
| | 91 | + /// Icon size setting. |
| | 92 | + icon_size: IconSize, |
| 41 | } | 93 | } |
| 42 | | 94 | |
| 43 | impl GridView { | 95 | impl GridView { |
| 44 | /// Create a new grid view. | 96 | /// Create a new grid view. |
| 45 | pub fn new(bounds: Rect) -> Self { | 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 | Self { | 100 | Self { |
| 48 | entries: Vec::new(), | 101 | entries: Vec::new(), |
| 49 | focused: 0, | 102 | focused: 0, |
@@ -56,12 +109,35 @@ impl GridView { |
| 56 | hovered: None, | 109 | hovered: None, |
| 57 | drag_start: None, | 110 | drag_start: None, |
| 58 | drag_current: None, | 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 | /// Calculate number of columns that fit in the given width. | 122 | /// Calculate number of columns that fit in the given width. |
| 63 | - fn calculate_columns(width: u32) -> usize { | 123 | + fn calculate_columns(&self, width: u32) -> usize { |
| 64 | - ((width - CELL_PADDING) / (CELL_SIZE + CELL_PADDING)).max(1) as 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 | /// Set the entries to display. | 143 | /// Set the entries to display. |
@@ -104,7 +180,8 @@ impl GridView { |
| 104 | | 180 | |
| 105 | /// Get the number of visible rows that fit in the view. | 181 | /// Get the number of visible rows that fit in the view. |
| 106 | fn visible_rows(&self) -> usize { | 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 | /// Get current sort settings (grid view doesn't track these, uses external). | 187 | /// Get current sort settings (grid view doesn't track these, uses external). |
@@ -226,7 +303,7 @@ impl GridView { |
| 226 | /// Update bounds. | 303 | /// Update bounds. |
| 227 | pub fn set_bounds(&mut self, bounds: Rect) { | 304 | pub fn set_bounds(&mut self, bounds: Rect) { |
| 228 | self.bounds = bounds; | 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 | /// Get cell bounds for an index. | 309 | /// Get cell bounds for an index. |
@@ -234,11 +311,12 @@ impl GridView { |
| 234 | let visible_index = index.saturating_sub(self.scroll_offset * self.columns); | 311 | let visible_index = index.saturating_sub(self.scroll_offset * self.columns); |
| 235 | let col = visible_index % self.columns; | 312 | let col = visible_index % self.columns; |
| 236 | let row = visible_index / self.columns; | 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); | 316 | + 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); | 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 | /// Handle mouse move for hover effects and rubber band drag. | 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 | let icon_style = TextStyle::new() | 535 | let icon_style = TextStyle::new() |
| 452 | .font_family(&theme.font_family) | 536 | .font_family(&theme.font_family) |
| 453 | - .font_size(32.0) | 537 | + .font_size(icon_font_size) |
| 454 | .color(icon_color); | 538 | .color(icon_color); |
| 455 | | 539 | |
| 456 | // Center icon horizontally in cell | 540 | // Center icon horizontally in cell |
| 457 | let icon_center_x = cell.x + cell.width as i32 / 2; | 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 | renderer.text_centered(icon, Point::new(icon_center_x, icon_center_y), &icon_style)?; | 543 | renderer.text_centered(icon, Point::new(icon_center_x, icon_center_y), &icon_style)?; |
| 460 | | 544 | |
| 461 | // File name (truncated) | 545 | // File name (truncated) |
@@ -467,9 +551,10 @@ impl GridView { |
| 467 | theme.item_foreground | 551 | theme.item_foreground |
| 468 | }; | 552 | }; |
| 469 | | 553 | |
| | 554 | + let name_font_size = self.icon_size.font_size(); |
| 470 | let name_style = TextStyle::new() | 555 | let name_style = TextStyle::new() |
| 471 | .font_family(&theme.font_family) | 556 | .font_family(&theme.font_family) |
| 472 | - .font_size(theme.font_size - 1.0) | 557 | + .font_size(name_font_size) |
| 473 | .color(name_color); | 558 | .color(name_color); |
| 474 | | 559 | |
| 475 | // Use Pango CENTER alignment for proper text centering (like Dolphin/Nautilus) | 560 | // Use Pango CENTER alignment for proper text centering (like Dolphin/Nautilus) |
@@ -479,11 +564,12 @@ impl GridView { |
| 479 | .max_width((cell.width - 8) as i32); | 564 | .max_width((cell.width - 8) as i32); |
| 480 | | 565 | |
| 481 | // Rectangle for the text area below the icon | 566 | // Rectangle for the text area below the icon |
| | 567 | + let icon_size = self.icon_size.icon_size(); |
| 482 | let text_rect = Rect::new( | 568 | let text_rect = Rect::new( |
| 483 | cell.x + 4, | 569 | cell.x + 4, |
| 484 | - cell.y + ICON_SIZE as i32 + 8, | 570 | + cell.y + icon_size as i32 + 8, |
| 485 | cell.width - 8, | 571 | cell.width - 8, |
| 486 | - cell.height - ICON_SIZE - 12, | 572 | + cell.height - icon_size - 12, |
| 487 | ); | 573 | ); |
| 488 | // Add "@" suffix for symlinks | 574 | // Add "@" suffix for symlinks |
| 489 | let display_name = if entry.is_symlink { | 575 | let display_name = if entry.is_symlink { |