use eframe::egui; use std::collections::HashMap; /// Optional input field for confirmation dialogs #[allow(dead_code)] #[derive(Clone)] pub struct ConfirmInputField { pub label: String, pub hint: String, pub value: String, pub multiline: bool, } #[allow(dead_code)] impl ConfirmInputField { pub fn new(label: impl Into) -> Self { Self { label: label.into(), hint: String::new(), value: String::new(), multiline: false, } } pub fn hint(mut self, hint: impl Into) -> Self { self.hint = hint.into(); self } pub fn multiline(mut self, multiline: bool) -> Self { self.multiline = multiline; self } } /// A reusable confirmation dialog for destructive actions pub struct ConfirmDialog { pub title: String, pub message: String, pub item_name: Option, pub item_id: Option, pub show: bool, pub is_dangerous: bool, pub confirm_text: String, pub cancel_text: String, pub input_fields: Vec, } impl ConfirmDialog { pub fn new(title: impl Into, message: impl Into) -> Self { Self { title: title.into(), message: message.into(), item_name: None, item_id: None, show: false, is_dangerous: true, confirm_text: "Confirm".to_string(), cancel_text: "Cancel".to_string(), input_fields: Vec::new(), } } #[allow(dead_code)] pub fn with_item(mut self, name: impl Into, id: impl Into) -> Self { self.item_name = Some(name.into()); self.item_id = Some(id.into()); self } #[allow(dead_code)] pub fn dangerous(mut self, dangerous: bool) -> Self { self.is_dangerous = dangerous; self } #[allow(dead_code)] pub fn confirm_text(mut self, text: impl Into) -> Self { self.confirm_text = text.into(); self } #[allow(dead_code)] pub fn cancel_text(mut self, text: impl Into) -> Self { self.cancel_text = text.into(); self } #[allow(dead_code)] pub fn with_input_field(mut self, field: ConfirmInputField) -> Self { self.input_fields.push(field); self } pub fn open(&mut self, name: impl Into, id: impl Into) { self.item_name = Some(name.into()); self.item_id = Some(id.into()); self.show = true; } pub fn close(&mut self) { self.show = false; self.item_name = None; self.item_id = None; // Clear input field values for field in &mut self.input_fields { field.value.clear(); } } /// Get the values of input fields as a HashMap #[allow(dead_code)] pub fn get_input_values(&self) -> HashMap { self.input_fields .iter() .map(|field| (field.label.clone(), field.value.clone())) .collect() } /// Shows the dialog and returns Some(true) if confirmed, Some(false) if cancelled, None if still open pub fn show_dialog(&mut self, ctx: &egui::Context) -> Option { if !self.show { return None; } let mut result = None; let mut keep_open = true; let screen_rect = ctx.input(|i| { i.viewport().inner_rect.unwrap_or(egui::Rect::from_min_max( egui::pos2(0.0, 0.0), egui::pos2(1920.0, 1080.0), )) }); let mut default_pos = screen_rect.center() - egui::vec2(180.0, 120.0); default_pos.x = default_pos.x.max(0.0); default_pos.y = default_pos.y.max(0.0); egui::Window::new(&self.title) .collapsible(false) .resizable(false) .movable(true) .default_pos(default_pos) .open(&mut keep_open) .show(ctx, |ui| { ui.label(&self.message); if let (Some(name), Some(id)) = (&self.item_name, &self.item_id) { ui.add_space(8.0); ui.label(egui::RichText::new(format!("Name: {}", name)).strong()); ui.label(egui::RichText::new(format!("ID: {}", id)).strong()); } if self.is_dangerous { ui.add_space(12.0); ui.colored_label( egui::Color32::from_rgb(244, 67, 54), "⚠ This action cannot be undone!", ); } // Render input fields if any if !self.input_fields.is_empty() { ui.add_space(12.0); ui.separator(); ui.add_space(8.0); for field in &mut self.input_fields { ui.label(&field.label); if field.multiline { ui.add( egui::TextEdit::multiline(&mut field.value) .hint_text(&field.hint) .desired_rows(3), ); } else { ui.add( egui::TextEdit::singleline(&mut field.value).hint_text(&field.hint), ); } ui.add_space(4.0); } } ui.add_space(12.0); ui.horizontal(|ui| { if ui.button(&self.cancel_text).clicked() { result = Some(false); self.close(); } ui.add_space(8.0); let confirm_button = if self.is_dangerous { ui.add( egui::Button::new( egui::RichText::new(&self.confirm_text).color(egui::Color32::WHITE), ) .fill(egui::Color32::from_rgb(244, 67, 54)), ) } else { ui.button(&self.confirm_text) }; if confirm_button.clicked() { result = Some(true); self.close(); } }); }); if !keep_open { self.close(); result = Some(false); } result } } impl Default for ConfirmDialog { fn default() -> Self { Self::new("Confirm Action", "Are you sure?") } }