From 8360d0d9b4bd99ce1be7bfa56af6ac952b0bfcfd Mon Sep 17 00:00:00 2001 From: Stanislav Pastushenko Date: Tue, 9 Dec 2025 18:28:31 +0100 Subject: [PATCH] tabs scroll --- src/theme.rs | 13 ------- src/ui/tabs_panel.rs | 88 +++++++++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index 552713a..95da7c1 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -6,7 +6,6 @@ pub struct ColorPalette { pub bg_primary: egui::Color32, // Main background pub bg_secondary: egui::Color32, // Secondary panels pub bg_tertiary: egui::Color32, // Elevated elements - pub bg_hover: egui::Color32, // Hover states // Accent colors pub accent_primary: egui::Color32, // Primary purple accent @@ -20,14 +19,8 @@ pub struct ColorPalette { // UI element colors pub selection: egui::Color32, - pub highlight: egui::Color32, pub line_number: egui::Color32, pub border: egui::Color32, - - // Status colors - pub success: egui::Color32, - pub warning: egui::Color32, - pub error: egui::Color32, } impl ColorPalette { @@ -37,7 +30,6 @@ impl ColorPalette { bg_primary: egui::Color32::from_rgb(24, 20, 32), // #18141F bg_secondary: egui::Color32::from_rgb(32, 26, 42), // #201A2A bg_tertiary: egui::Color32::from_rgb(42, 35, 54), // #2A2336 - bg_hover: egui::Color32::from_rgba_premultiplied(90, 75, 115, 40), // Purple accents - modern and vibrant accent_primary: egui::Color32::from_rgb(138, 98, 208), // #8A62D0 @@ -51,14 +43,9 @@ impl ColorPalette { // UI elements selection: egui::Color32::from_rgb(108, 68, 178), // #6C44B2 - highlight: egui::Color32::from_rgba_premultiplied(138, 98, 208, 60), line_number: egui::Color32::from_rgb(110, 100, 130), // #6E6482 border: egui::Color32::from_rgb(60, 50, 75), // #3C324B - // Status colors - success: egui::Color32::from_rgb(100, 200, 130), // #64C882 - warning: egui::Color32::from_rgb(255, 180, 80), // #FFB450 - error: egui::Color32::from_rgb(240, 100, 120), // #F06478 } } } diff --git a/src/ui/tabs_panel.rs b/src/ui/tabs_panel.rs index 64d61fd..b12634e 100644 --- a/src/ui/tabs_panel.rs +++ b/src/ui/tabs_panel.rs @@ -13,29 +13,75 @@ pub fn render_tabs_panel( } egui::TopBottomPanel::top("tabs_panel").show(ctx, |ui| { - ui.horizontal(|ui| { - for (idx, tab) in tabs.iter().enumerate() { - let is_active = idx == *active_tab_index; - let button_text = if is_active { - egui::RichText::new(format!("📄 {}", tab.filename())).strong() - } else { - egui::RichText::new(format!("📄 {}", tab.filename())) - }; + // Get or initialize scroll offset from persistent storage + let scroll_id = egui::Id::new("tabs_scroll_offset"); + let mut scroll_offset: f32 = ui.ctx().data_mut(|d| d.get_persisted(scroll_id).unwrap_or(0.0)); - if ui.selectable_label(is_active, button_text).clicked() { - *active_tab_index = idx; + // Use horizontal ScrollArea for tabs with hidden scrollbar + let mut scroll_area = egui::ScrollArea::horizontal() + .auto_shrink([false; 2]) + .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden); + + // Apply the stored scroll offset + scroll_area = scroll_area.horizontal_scroll_offset(scroll_offset); + + let scroll_output = scroll_area.show(ui, |ui| { + ui.horizontal(|ui| { + for (idx, tab) in tabs.iter().enumerate() { + let is_active = idx == *active_tab_index; + let button_text = if is_active { + egui::RichText::new(format!("📄 {}", tab.filename())).strong() + } else { + egui::RichText::new(format!("📄 {}", tab.filename())) + }; + + if ui.selectable_label(is_active, button_text).clicked() { + *active_tab_index = idx; + } + + if ui.small_button("✖").clicked() { + *on_close_tab = Some(idx); + } + + ui.separator(); } - - if ui.small_button("✖").clicked() { - *on_close_tab = Some(idx); - } - - ui.separator(); - } - - if let Some(tab) = tabs.get(*active_tab_index) { - ui.label(format!("({} lines)", tab.line_index.total_lines)); - } + }) }); + + // Check if content overflows (tabs exceed screen width) + let content_width = scroll_output.content_size.x; + let viewport_width = scroll_output.inner_rect.width(); + let content_overflows = content_width > viewport_width; + + // Handle mouse wheel scrolling when hovering over tabs and content overflows + let mut should_update_offset = false; + if content_overflows && ui.rect_contains_pointer(scroll_output.inner_rect) { + // Get raw scroll delta outside of any closures + let raw_scroll_y = ui.input(|i| i.raw_scroll_delta.y); + + // Check for raw mouse wheel events + if raw_scroll_y != 0.0 { + // Use vertical scroll (mouse wheel) for horizontal scrolling + let scroll_amount = -raw_scroll_y * 2.0; + scroll_offset = (scroll_offset + scroll_amount).max(0.0); + + // Clamp to valid range + let max_offset = (content_width - viewport_width).max(0.0); + scroll_offset = scroll_offset.min(max_offset); + + should_update_offset = true; + } + } else { + // Update offset from ScrollArea state (in case of other interactions) + scroll_offset = scroll_output.state.offset.x; + should_update_offset = true; + } + + // Store the offset outside of any input closures + if should_update_offset { + ui.ctx().data_mut(|d| { + d.insert_persisted(scroll_id, scroll_offset); + }); + } }); }