aboutsummaryrefslogtreecommitdiff
path: root/src/core/components/interactions.rs
diff options
context:
space:
mode:
authorUMTS at Teleco <crt@teleco.ch>2025-12-13 02:51:15 +0100
committerUMTS at Teleco <crt@teleco.ch>2025-12-13 02:51:15 +0100
commit8323fdd73272a2882781aba3c499ba0be3dff2a6 (patch)
treeffbf86473933e69cfaeef30d5c6ea7e5b494856c /src/core/components/interactions.rs
committing to insanityHEADmaster
Diffstat (limited to 'src/core/components/interactions.rs')
-rw-r--r--src/core/components/interactions.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/core/components/interactions.rs b/src/core/components/interactions.rs
new file mode 100644
index 0000000..ab9e5ac
--- /dev/null
+++ b/src/core/components/interactions.rs
@@ -0,0 +1,225 @@
+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<String>) -> Self {
+ Self {
+ label: label.into(),
+ hint: String::new(),
+ value: String::new(),
+ multiline: false,
+ }
+ }
+
+ pub fn hint(mut self, hint: impl Into<String>) -> 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<String>,
+ pub item_id: Option<String>,
+ pub show: bool,
+ pub is_dangerous: bool,
+ pub confirm_text: String,
+ pub cancel_text: String,
+ pub input_fields: Vec<ConfirmInputField>,
+}
+
+impl ConfirmDialog {
+ pub fn new(title: impl Into<String>, message: impl Into<String>) -> 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<String>, id: impl Into<String>) -> 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<String>) -> Self {
+ self.confirm_text = text.into();
+ self
+ }
+
+ #[allow(dead_code)]
+ pub fn cancel_text(mut self, text: impl Into<String>) -> 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<String>, id: impl Into<String>) {
+ 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<String, String> {
+ 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<bool> {
+ 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?")
+ }
+}