use super::editor::{TextEditor, UnsavedAction}; use eframe::egui; impl TextEditor { pub fn has_unsaved_changes(&self) -> bool { self.tabs.iter().any(|tab| tab.is_modified) } pub fn get_unsaved_files(&self) -> Vec { self.tabs .iter() .filter(|tab| tab.is_modified) .map(|tab| tab.title.clone()) .collect() } pub fn request_quit(&mut self, ctx: &egui::Context) { if self.has_unsaved_changes() { self.pending_unsaved_action = Some(UnsavedAction::Quit); } else { self.clean_quit_requested = true; ctx.send_viewport_cmd(egui::ViewportCommand::Close); } } pub fn force_quit(&mut self, ctx: &egui::Context) { self.force_quit_confirmed = true; ctx.send_viewport_cmd(egui::ViewportCommand::Close); } pub(crate) fn show_unsaved_changes_dialog(&mut self, ctx: &egui::Context) { let mut close_action_now = None; let mut cancel_action = false; let (files_to_list, title, confirmation_text, button_text, action) = if let Some(action) = &self.pending_unsaved_action { match action { UnsavedAction::Quit => ( self.get_unsaved_files(), "Unsaved Changes".to_string(), "You have unsaved changes.".to_string(), "Quit Without Saving".to_string(), action.clone(), ), UnsavedAction::CloseTab(tab_index) => { let file_name = self .tabs .get(*tab_index) .map_or_else(|| "unknown file".to_string(), |tab| tab.title.clone()); ( vec![file_name], "Unsaved Changes".to_string(), "The file has unsaved changes.".to_string(), "Close Without Saving".to_string(), action.clone(), ) } } } else { return; // Should not happen if called correctly }; let visuals = &ctx.style().visuals; egui::Window::new(title) .collapsible(false) .resizable(false) .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO) .frame(egui::Frame { fill: visuals.window_fill, stroke: visuals.window_stroke, corner_radius: egui::CornerRadius::same(8), shadow: visuals.window_shadow, inner_margin: egui::Margin::same(16), outer_margin: egui::Margin::same(0), }) .show(ctx, |ui| { ui.vertical(|ui| { ui.label(egui::RichText::new(&confirmation_text).size(14.0)); ui.add_space(8.0); for file in &files_to_list { ui.label(egui::RichText::new(format!("• {}", file)).size(18.0).weak()); } ui.add_space(12.0); ui.horizontal(|ui| { let cancel_fill = ui.visuals().widgets.inactive.bg_fill; let cancel_stroke = ui.visuals().widgets.inactive.bg_stroke; let cancel_button = egui::Button::new("Cancel") .fill(cancel_fill) .stroke(cancel_stroke); if ui.add(cancel_button).clicked() { cancel_action = true; } ui.add_space(8.0); let destructive_color = ui.visuals().error_fg_color; let confirm_button = egui::Button::new(&button_text) .fill(destructive_color) .stroke(egui::Stroke::new(1.0, destructive_color)); if ui.add(confirm_button).clicked() { close_action_now = Some(action); } }); }); }); if cancel_action { self.pending_unsaved_action = None; } if let Some(action) = close_action_now { match action { UnsavedAction::Quit => self.force_quit(ctx), UnsavedAction::CloseTab(tab_index) => { self.close_tab(tab_index); } } self.pending_unsaved_action = None; } } }