gardesk/garfield / 67f51e9

Browse files

app picker: fix layout spacing for title, items, and descriptions

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
67f51e9ca0b33e85c83d832cca5ac7bf733b0a7e
Parents
9ce5971
Tree
c3f682b

1 changed file

StatusFile+-
M garfield/src/ui/app_picker.rs 37 19
garfield/src/ui/app_picker.rsmodified
@@ -13,13 +13,19 @@ use std::collections::HashSet;
1313
 use std::path::PathBuf;
1414
 
1515
 /// Maximum number of visible items in the list.
16
-const MAX_VISIBLE_ITEMS: usize = 10;
16
+const MAX_VISIBLE_ITEMS: usize = 8;
1717
 
18
-/// Item height in pixels.
19
-const ITEM_HEIGHT: u32 = 36;
18
+/// Item height in pixels (name + description + padding).
19
+const ITEM_HEIGHT: u32 = 48;
2020
 
2121
 /// Input field height.
22
-const INPUT_HEIGHT: u32 = 40;
22
+const INPUT_HEIGHT: u32 = 36;
23
+
24
+/// Title area height.
25
+const TITLE_HEIGHT: u32 = 32;
26
+
27
+/// Gap between sections.
28
+const SECTION_GAP: u32 = 12;
2329
 
2430
 /// Padding inside dialog.
2531
 const DIALOG_PADDING: u32 = 16;
@@ -431,7 +437,7 @@ impl AppPickerDialog {
431437
         }
432438
 
433439
         // Check if click is in item list area
434
-        let list_y_start = dialog_rect.y + DIALOG_PADDING as i32 + INPUT_HEIGHT as i32 + 8;
440
+        let list_y_start = self.list_y_start();
435441
         let list_y_end = list_y_start + (MAX_VISIBLE_ITEMS as i32 * ITEM_HEIGHT as i32);
436442
 
437443
         if pos.y >= list_y_start && pos.y < list_y_end {
@@ -466,7 +472,7 @@ impl AppPickerDialog {
466472
         }
467473
 
468474
         let dialog_rect = self.dialog_rect();
469
-        let list_y_start = dialog_rect.y + DIALOG_PADDING as i32 + INPUT_HEIGHT as i32 + 8;
475
+        let list_y_start = self.list_y_start();
470476
         let list_y_end = list_y_start + (MAX_VISIBLE_ITEMS as i32 * ITEM_HEIGHT as i32);
471477
 
472478
         if pos.y >= list_y_start && pos.y < list_y_end
@@ -488,8 +494,10 @@ impl AppPickerDialog {
488494
 
489495
     /// Get the dialog rectangle (centered).
490496
     fn dialog_rect(&self) -> Rect {
491
-        let dialog_width = 500.min(self.bounds.width.saturating_sub(40));
492
-        let dialog_height = (DIALOG_PADDING * 2 + INPUT_HEIGHT + 8 + (MAX_VISIBLE_ITEMS as u32 * ITEM_HEIGHT) + 24)
497
+        let dialog_width = 520.min(self.bounds.width.saturating_sub(40));
498
+        // Title + gap + input + gap + items + gap + count
499
+        let dialog_height = (DIALOG_PADDING * 2 + TITLE_HEIGHT + SECTION_GAP + INPUT_HEIGHT + SECTION_GAP
500
+            + (MAX_VISIBLE_ITEMS as u32 * ITEM_HEIGHT) + SECTION_GAP + 20)
493501
             .min(self.bounds.height.saturating_sub(40));
494502
 
495503
         let x = self.bounds.x + (self.bounds.width as i32 - dialog_width as i32) / 2;
@@ -501,14 +509,22 @@ impl AppPickerDialog {
501509
     /// Get the input field rectangle.
502510
     fn input_rect(&self) -> Rect {
503511
         let dialog = self.dialog_rect();
512
+        // Position after title + gap
513
+        let y = dialog.y + DIALOG_PADDING as i32 + TITLE_HEIGHT as i32 + SECTION_GAP as i32;
504514
         Rect::new(
505515
             dialog.x + DIALOG_PADDING as i32,
506
-            dialog.y + DIALOG_PADDING as i32,
516
+            y,
507517
             dialog.width - DIALOG_PADDING * 2,
508518
             INPUT_HEIGHT,
509519
         )
510520
     }
511521
 
522
+    /// Get the Y position where the item list starts.
523
+    fn list_y_start(&self) -> i32 {
524
+        let input_rect = self.input_rect();
525
+        input_rect.y + INPUT_HEIGHT as i32 + SECTION_GAP as i32
526
+    }
527
+
512528
     /// Render the dialog.
513529
     pub fn render(&self, renderer: &Renderer) -> Result<()> {
514530
         if !self.visible {
@@ -528,13 +544,13 @@ impl AppPickerDialog {
528544
         // Title
529545
         let title_style = TextStyle::new()
530546
             .font_family(&theme.font_family)
531
-            .font_size(theme.font_size + 2.0)
547
+            .font_size(theme.font_size + 4.0)
532548
             .color(theme.foreground);
533549
 
534550
         renderer.text(
535551
             "Open With Application",
536552
             (dialog_rect.x + DIALOG_PADDING as i32) as f64,
537
-            (dialog_rect.y + 8) as f64,
553
+            (dialog_rect.y + DIALOG_PADDING as i32) as f64,
538554
             &title_style,
539555
         )?;
540556
 
@@ -590,17 +606,17 @@ impl AppPickerDialog {
590606
         )?;
591607
 
592608
         // Item list
593
-        let list_y_start = dialog_rect.y + DIALOG_PADDING as i32 + INPUT_HEIGHT as i32 + 8;
609
+        let list_y_start = self.list_y_start();
594610
         let list_width = dialog_rect.width - DIALOG_PADDING * 2;
595611
 
596612
         let name_style = TextStyle::new()
597613
             .font_family(&theme.font_family)
598
-            .font_size(theme.font_size)
614
+            .font_size(theme.font_size + 1.0)
599615
             .color(theme.foreground);
600616
 
601617
         let desc_style = TextStyle::new()
602618
             .font_family(&theme.font_family)
603
-            .font_size(theme.font_size - 2.0)
619
+            .font_size(theme.font_size - 1.0)
604620
             .color(theme.item_description);
605621
 
606622
         let visible_end = (self.scroll_offset + MAX_VISIBLE_ITEMS).min(self.filtered_apps.len());
@@ -627,28 +643,30 @@ impl AppPickerDialog {
627643
                 renderer.fill_rounded_rect(item_rect, 4.0, theme.item_hover_background)?;
628644
             }
629645
 
630
-            // App name
646
+            // App name (positioned near top of item)
647
+            let name_y = item_y + 10;
631648
             renderer.text(
632649
                 &app.name,
633650
                 (item_rect.x + 12) as f64,
634
-                (item_y + 6) as f64,
651
+                name_y as f64,
635652
                 &name_style,
636653
             )?;
637654
 
638
-            // App description (if any)
655
+            // App description (if any, positioned below name with gap)
639656
             if let Some(desc) = &app.description {
640657
                 // Truncate long descriptions
641
-                let max_desc_len = 60;
658
+                let max_desc_len = 70;
642659
                 let truncated = if desc.len() > max_desc_len {
643660
                     format!("{}...", &desc[..max_desc_len])
644661
                 } else {
645662
                     desc.clone()
646663
                 };
647664
 
665
+                let desc_y = name_y + (theme.font_size as i32) + 6;
648666
                 renderer.text(
649667
                     &truncated,
650668
                     (item_rect.x + 12) as f64,
651
-                    (item_y + 6 + theme.font_size as i32) as f64,
669
+                    desc_y as f64,
652670
                     &desc_style,
653671
                 )?;
654672
             }