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

commit4bafd7f5825e5189335ee54628b0e65f81c0682a
parentd7da4a1ade
authorLucas Galante <[email protected]>
date2026-06-20 22:44
cce-ui: Add PreviewState as a generic display state object

 src/widget/display/mod.rs     |  2 ++
 src/widget/display/preview.rs | 54 +++++++++++++++++++++++++++++++++++++++++++
 src/widget/mod.rs             |  2 +-
 3 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/src/widget/display/mod.rs b/src/widget/display/mod.rs
index cfdf886..d1bf2be 100644
--- a/src/widget/display/mod.rs
+++ b/src/widget/display/mod.rs
@@ -17,6 +17,7 @@ pub mod layout_preview;
 pub mod font_preview;
 pub mod info_box;
 pub mod status_dot;
+pub mod preview;
 
 pub use self::text_label::TextLabel;
 pub(crate) use self::text_label::make_widget_text_buffer;
@@ -38,3 +39,4 @@ 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;
diff --git a/src/widget/display/preview.rs b/src/widget/display/preview.rs
new file mode 100644
index 0000000..7ef1a56
--- /dev/null
+++ b/src/widget/display/preview.rs
@@ -0,0 +1,54 @@
+use std::path::PathBuf;
+
+#[derive(Debug, Clone, Default)]
+pub struct PreviewState {
+    pub path: Option<PathBuf>,
+    pub path_display: String,
+    pub name: String,
+    pub is_dir: bool,
+    pub size: String,
+    pub permissions: String,
+    pub modified: String,
+    pub file_type: String,
+    pub target: String, // for symlinks
+    pub content_preview: Option<String>,
+    pub scroll_line: usize,
+}
+
+impl PreviewState {
+    pub fn handle_mouse_wheel(&mut self, delta: &crate::widget::MouseScrollDelta, ch: f32) -> bool {
+        let content = match &self.content_preview {
+            Some(c) => c,
+            None => return false,
+        };
+        let total_lines = content.lines().count();
+        let half_h = ch * 0.5;
+        let mut max_visible_lines = 0;
+        let mut text_y = 44.0;
+        while text_y + 14.0 <= half_h - 16.0 {
+            max_visible_lines += 1;
+            text_y += 15.0;
+        }
+        if total_lines <= max_visible_lines {
+            if self.scroll_line != 0 {
+                self.scroll_line = 0;
+                return true;
+            }
+            return false;
+        }
+        let max_scroll = total_lines.saturating_sub(max_visible_lines);
+        let scroll_speed = 3.0;
+        let diff = match delta {
+            crate::widget::MouseScrollDelta::LineDelta(_, y) => {
+                -y * scroll_speed
+            }
+            crate::widget::MouseScrollDelta::PixelDelta(pos) => {
+                -pos.y as f32 / 15.0
+            }
+        };
+        let prev_scroll = self.scroll_line;
+        let new_scroll = (self.scroll_line as f32 + diff).round() as isize;
+        self.scroll_line = new_scroll.clamp(0, max_scroll as isize) as usize;
+        self.scroll_line != prev_scroll
+    }
+}
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 4fe49db..9cb83a3 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -558,7 +558,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, serialize_widgets
+    DotStatus, PreviewLayoutMode, Sidebar, Panel, PreviewState, serialize_widgets
 };
 
 pub trait PageSelector {