From c4de8acec5af9c1d4d6f2561b8f8e19d25401e00 Mon Sep 17 00:00:00 2001 From: candle Date: Tue, 22 Jul 2025 22:26:48 -0400 Subject: [PATCH] can click and drag tab bar now, fix hashing, unification --- src/app/tab.rs | 30 +++----- src/ui/central_panel/editor.rs | 5 -- src/ui/menu_bar.rs | 10 +-- src/ui/tab_bar.rs | 136 +++++++++++++++++---------------- 4 files changed, 84 insertions(+), 97 deletions(-) diff --git a/src/app/tab.rs b/src/app/tab.rs index 2e34888..fcd0253 100644 --- a/src/app/tab.rs +++ b/src/app/tab.rs @@ -2,35 +2,32 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::path::PathBuf; -pub fn compute_content_hash(content: &str, hasher: &mut DefaultHasher) -> u64 { - content.hash(hasher); - hasher.finish() +pub fn compute_content_hash(content: &str) -> u64 { + let mut hasher = DefaultHasher::new(); + content.hash(&mut hasher); + let hash = hasher.finish(); + hash } #[derive(Clone)] pub struct Tab { pub content: String, pub original_content_hash: u64, - pub last_content_hash: u64, pub file_path: Option, pub is_modified: bool, pub title: String, - pub hasher: DefaultHasher, } impl Tab { pub fn new_empty(tab_number: usize) -> Self { let content = String::new(); - let mut hasher = DefaultHasher::new(); - let hash = compute_content_hash(&content, &mut hasher); + let hash = compute_content_hash(&content); Self { original_content_hash: hash, - last_content_hash: hash, content, file_path: None, is_modified: false, title: format!("new_{tab_number}"), - hasher, } } @@ -38,19 +35,16 @@ impl Tab { let title = file_path .file_name() .and_then(|n| n.to_str()) - .unwrap_or("Untitled") + .unwrap_or("UNKNOWN") .to_string(); - let mut hasher = DefaultHasher::new(); - let hash = compute_content_hash(&content, &mut hasher); + let hash = compute_content_hash(&content); Self { original_content_hash: hash, - last_content_hash: hash, content, file_path: Some(file_path), is_modified: false, title, - hasher, } } @@ -63,15 +57,13 @@ impl Tab { if self.title.starts_with("new_") { self.is_modified = !self.content.is_empty(); } else { - let current_hash = compute_content_hash(&self.content, &mut self.hasher); - self.is_modified = current_hash != self.last_content_hash; - self.last_content_hash = current_hash; + let current_hash = compute_content_hash(&self.content); + self.is_modified = current_hash != self.original_content_hash; } } pub fn mark_as_saved(&mut self) { - self.original_content_hash = compute_content_hash(&self.content, &mut self.hasher); - self.last_content_hash = self.original_content_hash; + self.original_content_hash = compute_content_hash(&self.content); self.is_modified = false; } } diff --git a/src/ui/central_panel/editor.rs b/src/ui/central_panel/editor.rs index 24441ca..a9302f6 100644 --- a/src/ui/central_panel/editor.rs +++ b/src/ui/central_panel/editor.rs @@ -248,11 +248,6 @@ pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::R app.previous_content = content.to_owned(); app.previous_cursor_char_index = current_cursor_pos; - - if let Some(active_tab) = app.get_active_tab_mut() { - active_tab.last_content_hash = - crate::app::tab::compute_content_hash(&active_tab.content, &mut active_tab.hasher); - } } if app.font_settings_changed || app.text_needs_processing { diff --git a/src/ui/menu_bar.rs b/src/ui/menu_bar.rs index 268840e..15dc49e 100644 --- a/src/ui/menu_bar.rs +++ b/src/ui/menu_bar.rs @@ -276,21 +276,15 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { if app.hide_tab_bar { let tab_title = if let Some(tab) = app.get_active_tab() { - tab.title.to_owned() + tab.get_display_title() } else { let empty_tab = crate::app::tab::Tab::new_empty(1); - empty_tab.title.to_owned() + empty_tab.get_display_title() }; let window_width = ctx.screen_rect().width(); let font_id = ui.style().text_styles[&egui::TextStyle::Body].to_owned(); - let tab_title = if app.get_active_tab().is_some_and(|tab| tab.is_modified) { - format!("{tab_title}*") - } else { - tab_title - }; - let text_galley = ui.fonts(|fonts| { fonts.layout_job(egui::text::LayoutJob::simple_singleline( tab_title, diff --git a/src/ui/tab_bar.rs b/src/ui/tab_bar.rs index d09b297..86f631b 100644 --- a/src/ui/tab_bar.rs +++ b/src/ui/tab_bar.rs @@ -3,86 +3,92 @@ use eframe::egui::{self, Frame}; pub(crate) fn tab_bar(app: &mut TextEditor, ctx: &egui::Context) { let frame = Frame::NONE.fill(ctx.style().visuals.panel_fill); - let response = egui::TopBottomPanel::top("tab_bar") + let tab_bar = egui::TopBottomPanel::top("tab_bar") .frame(frame) .show(ctx, |ui| { - ui.horizontal(|ui| { - let mut tab_to_close_unmodified = None; - let mut tab_to_close_modified = None; - let mut tab_to_switch = None; - let mut add_new_tab = false; + egui::ScrollArea::horizontal() + .auto_shrink([false, true]) + .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) + .scroll_source(egui::scroll_area::ScrollSource::DRAG) + .show(ui, |ui| { + ui.horizontal(|ui| { + let mut tab_to_close_unmodified = None; + let mut tab_to_close_modified = None; + let mut tab_to_switch = None; + let mut add_new_tab = false; - let tabs_len = app.tabs.len(); - let active_tab_index = app.active_tab_index; + let tabs_len = app.tabs.len(); + let active_tab_index = app.active_tab_index; - let tabs_info: Vec<(String, bool)> = app - .tabs - .iter() - .map(|tab| (tab.get_display_title(), tab.is_modified)) - .collect(); + let tabs_info: Vec<(String, bool)> = app + .tabs + .iter() + .map(|tab| (tab.get_display_title(), tab.is_modified)) + .collect(); - for (i, (title, is_modified)) in tabs_info.iter().enumerate() { - let is_active = i == active_tab_index; + for (i, (title, is_modified)) in tabs_info.iter().enumerate() { + let is_active = i == active_tab_index; - let mut label_text = if is_active { - egui::RichText::new(title).strong() - } else { - egui::RichText::new(title).color(ui.visuals().weak_text_color()) - }; + let mut label_text = if is_active { + egui::RichText::new(title).strong() + } else { + egui::RichText::new(title).color(ui.visuals().weak_text_color()) + }; - if *is_modified { - label_text = label_text.italics(); - } + if *is_modified { + label_text = label_text.italics(); + } - let tab_response = - ui.add(egui::Label::new(label_text).sense(egui::Sense::click())); - if tab_response.clicked() { - tab_to_switch = Some(i); - } + let tab_response = ui.add(egui::Label::new(label_text).selectable(false).sense(egui::Sense::click())); + if tab_response.clicked() { + tab_to_switch = Some(i); + } + + if tabs_len > 1 { + let visuals = ui.visuals(); + let close_button = egui::Button::new("×") + .small() + .fill(visuals.panel_fill) + .stroke(egui::Stroke::new(0.0, egui::Color32::from_rgb(0, 0, 0))); + let close_response = ui.add(close_button); + if close_response.clicked() { + if *is_modified { + tab_to_close_modified = Some(i); + } else { + tab_to_close_unmodified = Some(i); + } + } + } + + ui.separator(); + } - if tabs_len > 1 { let visuals = ui.visuals(); - let close_button = egui::Button::new("×") + let add_button = egui::Button::new("+") .small() .fill(visuals.panel_fill) .stroke(egui::Stroke::new(0.0, egui::Color32::from_rgb(0, 0, 0))); - let close_response = ui.add(close_button); - if close_response.clicked() { - if *is_modified { - tab_to_close_modified = Some(i); - } else { - tab_to_close_unmodified = Some(i); - } + if ui.add(add_button).clicked() { + add_new_tab = true; } - } - ui.separator(); - } - - let visuals = ui.visuals(); - let add_button = egui::Button::new("+") - .small() - .fill(visuals.panel_fill) - .stroke(egui::Stroke::new(0.0, egui::Color32::from_rgb(0, 0, 0))); - if ui.add(add_button).clicked() { - add_new_tab = true; - } - - if let Some(tab_index) = tab_to_switch { - app.switch_to_tab(tab_index); - } - if let Some(tab_index) = tab_to_close_unmodified { - app.close_tab(tab_index); - } - if let Some(tab_index) = tab_to_close_modified { - app.switch_to_tab(tab_index); - app.pending_unsaved_action = Some(UnsavedAction::CloseTab(tab_index)); - } - if add_new_tab { - app.add_new_tab(); - } - }); + if let Some(tab_index) = tab_to_switch { + app.switch_to_tab(tab_index); + } + if let Some(tab_index) = tab_to_close_unmodified { + app.close_tab(tab_index); + } + if let Some(tab_index) = tab_to_close_modified { + app.switch_to_tab(tab_index); + app.pending_unsaved_action = Some(UnsavedAction::CloseTab(tab_index)); + } + if add_new_tab { + app.add_new_tab(); + } + }); + }); }); - app.tab_bar_rect = Some(response.response.rect); + app.tab_bar_rect = Some(tab_bar.response.rect); + }