git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit09444aa2bebeca52b04a607903338fe7e7072fc3
parent2786a1eec4
authorLucas Galante <[email protected]>
date2026-06-22 21:33
Implement ImagePreviewData and update PreviewState for image preview support

 src/widget/display/mod.rs     |  2 +-
 src/widget/display/preview.rs | 73 ++++++++++++++++++++++++++++++++++++++++++-
 src/widget/mod.rs             |  2 +-
 3 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/src/widget/display/mod.rs b/src/widget/display/mod.rs
index 8f344d8..c0d9df4 100644
--- a/src/widget/display/mod.rs
+++ b/src/widget/display/mod.rs
@@ -40,5 +40,5 @@ pub use self::layout_preview::{PreviewLayoutMode, LayoutPreview};
 pub use self::font_preview::FontPreview;
 pub use self::info_box::InfoBox;
 pub use self::status_dot::{DotStatus, StatusDot};
-pub use self::preview::PreviewState;
+pub use self::preview::{PreviewState, ImagePreviewData};
 pub use self::text_sizer::{measure_text_width, measure_text};
diff --git a/src/widget/display/preview.rs b/src/widget/display/preview.rs
index 45aa738..7ea879b 100644
--- a/src/widget/display/preview.rs
+++ b/src/widget/display/preview.rs
@@ -4,6 +4,13 @@ use crate::context::UiContext;
 use crate::layout::{RenderTarget, SectionContext};
 use crate::color;
 
+#[derive(Debug, Clone, Default)]
+pub struct ImagePreviewData {
+    pub width: u32,
+    pub height: u32,
+    pub pixels: Vec<[u8; 4]>,
+}
+
 pub struct PreviewState {
     pub base: Widget,
     pub visible: bool,
@@ -17,6 +24,7 @@ pub struct PreviewState {
     pub file_type: String,
     pub target: String, // for symlinks
     pub content_preview: Option<String>,
+    pub image_preview: Option<ImagePreviewData>,
     pub scroll_line: usize,
 }
 
@@ -35,6 +43,7 @@ impl Default for PreviewState {
             file_type: String::new(),
             target: String::new(),
             content_preview: None,
+            image_preview: None,
             scroll_line: 0,
         }
     }
@@ -55,6 +64,7 @@ impl std::fmt::Debug for PreviewState {
             .field("file_type", &self.file_type)
             .field("target", &self.target)
             .field("content_preview", &self.content_preview)
+            .field("image_preview", &self.image_preview)
             .field("scroll_line", &self.scroll_line)
             .finish()
     }
@@ -75,6 +85,7 @@ impl Clone for PreviewState {
             file_type: self.file_type.clone(),
             target: self.target.clone(),
             content_preview: self.content_preview.clone(),
+            image_preview: self.image_preview.clone(),
             scroll_line: self.scroll_line,
         }
     }
@@ -245,7 +256,67 @@ impl PreviewState {
         let bg_color = color::scrollinglist_bg_color();
         canvas.rect(bg_color, cx + 12.0, cy + 32.0, cw - 24.0, half_h - 40.0);
 
-        if let Some(content) = &self.content_preview {
+        if let Some(image_data) = &self.image_preview {
+            let box_w = cw - 24.0;
+            let box_h = half_h - 40.0;
+            let img_w = image_data.width as f32;
+            let img_h = image_data.height as f32;
+            
+            let scale_x = box_w / img_w;
+            let scale_y = box_h / img_h;
+            let scale = scale_x.min(scale_y).min(4.0).max(1.0);
+            
+            let draw_w = img_w * scale;
+            let draw_h = img_h * scale;
+            
+            let start_x = cx + 12.0 + (box_w - draw_w) * 0.5;
+            let start_y = cy + 32.0 + (box_h - draw_h) * 0.5;
+            
+            for row in 0..image_data.height {
+                let mut col = 0;
+                while col < image_data.width {
+                    let idx = (row * image_data.width + col) as usize;
+                    if idx >= image_data.pixels.len() {
+                        break;
+                    }
+                    let pixel = image_data.pixels[idx];
+                    let r = pixel[0];
+                    let g = pixel[1];
+                    let b = pixel[2];
+                    let a = pixel[3];
+                    
+                    let mut run_len = 1;
+                    while col + run_len < image_data.width {
+                        let next_idx = (row * image_data.width + col + run_len) as usize;
+                        if next_idx >= image_data.pixels.len() {
+                            break;
+                        }
+                        if image_data.pixels[next_idx] == pixel {
+                            run_len += 1;
+                        } else {
+                            break;
+                        }
+                    }
+                    
+                    let alpha = a as f32 / 255.0;
+                    if alpha > 0.0 {
+                        let rf = r as f32 / 255.0;
+                        let gf = g as f32 / 255.0;
+                        let bf = b as f32 / 255.0;
+                        
+                        canvas.rect(
+                            [rf, gf, bf, alpha],
+                            start_x + col as f32 * scale,
+                            start_y + row as f32 * scale,
+                            run_len as f32 * scale,
+                            scale,
+                        );
+                    }
+                    
+                    col += run_len;
+                }
+            }
+        } else if let Some(content) = &self.content_preview {
             let mut text_y = cy + 44.0;
             for line in content.lines().skip(self.scroll_line) {
                 if text_y + 14.0 > cy + half_h - 16.0 {
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 226f9cb..02482ab 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -578,7 +578,7 @@ pub use self::display::{
     TextLabel, Label, SectionHeader, StyledLabel, TextItem, Svg, UsageBar,
     LayoutPreview, FontPreview, InfoBox, StatusDot, InteractiveListItem,
     GraphNode, Graph, Float3, ProgressBar, StatusBar, Splitter, Node, Separator,
-    DotStatus, PreviewLayoutMode, Sidebar, Panel, PreviewState, serialize_widgets
+    DotStatus, PreviewLayoutMode, Sidebar, Panel, PreviewState, ImagePreviewData, serialize_widgets
 };
 
 pub trait PageSelector {