diff options
Diffstat (limited to 'src/ui/ribbon.rs')
| -rw-r--r-- | src/ui/ribbon.rs | 1056 |
1 files changed, 1056 insertions, 0 deletions
diff --git a/src/ui/ribbon.rs b/src/ui/ribbon.rs new file mode 100644 index 0000000..0da355f --- /dev/null +++ b/src/ui/ribbon.rs @@ -0,0 +1,1056 @@ +use crate::core::components::filter_builder::FilterBuilder; +use eframe::egui; +use egui_phosphor::variants::regular as icons; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +pub struct RibbonUI { + pub active_tab: String, + pub search_texts: HashMap<String, String>, + pub checkboxes: HashMap<String, bool>, + pub number_fields: HashMap<String, u32>, + pub filter_builder: FilterBuilder, +} + +impl Default for RibbonUI { + fn default() -> Self { + let mut number_fields = HashMap::new(); + number_fields.insert("inventory_limit".to_string(), 100); + number_fields.insert("templates_limit".to_string(), 200); + + Self { + active_tab: "Dashboard".to_string(), + search_texts: HashMap::new(), + checkboxes: HashMap::new(), + number_fields, + filter_builder: FilterBuilder::new(), + } + } +} + +impl RibbonUI { + pub fn preferred_height(&self) -> f32 { + 135.0 + } + + pub fn get_active_view(&self) -> Option<String> { + Some(self.active_tab.clone()) + } + + pub fn show(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) -> Option<String> { + // Clear one-shot trigger flags from previous frame so clicks only fire once + // NOTE: inventory_filter_changed and templates_filter_changed are NOT cleared here - they're cleared by their views after processing + for key in [ + "item_lookup_trigger", + "inventory_limit_refresh_trigger", + // Inventory Actions + "inventory_action_add", + "inventory_action_delete", + "inventory_action_edit_easy", + "inventory_action_edit_adv", + "inventory_action_print_label", + // Inventory Quick Actions + "inventory_quick_inventarize_room", + "inventory_quick_add_multiple_from_template", + // Templates + "templates_limit_changed", + "templates_action_new", + "templates_action_edit", + "templates_action_delete", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.vertical(|ui| { + // Tab headers row + ui.horizontal(|ui| { + let tabs = vec![ + "Dashboard", + "Inventory", + "Categories", + "Zones", + "Borrowing", + "Audits", + "Suppliers", + "Issues", + "Printers", + "Label Templates", + "Item Templates", + ]; + for tab in &tabs { + if ui.selectable_label(self.active_tab == *tab, *tab).clicked() { + self.active_tab = tab.to_string(); + // Update filter columns based on the active tab + match *tab { + "Zones" => self.filter_builder.set_columns_for_context("zones"), + "Inventory" => self.filter_builder.set_columns_for_context("assets"), + _ => {} + } + } + } + }); + + ui.separator(); + + // Content area with fixed height I dont even know what this here fucking does tbh + let ribbon_height = 90.0; + ui.allocate_ui_with_layout( + egui::vec2(ui.available_width(), ribbon_height), + egui::Layout::left_to_right(egui::Align::Min), + |ui| { + egui::ScrollArea::horizontal().show(ui, |ui| { + ui.horizontal(|ui| match self.active_tab.as_str() { + "Dashboard" => self.show_dashboard_tab(ui, ribbon_height), + "Inventory" => self.show_inventory_tab(ui, ribbon_height), + "Categories" => self.show_categories_tab(ui, ribbon_height), + "Zones" => self.show_zones_tab(ui, ribbon_height), + "Borrowing" => self.show_borrowing_tab(ui, ribbon_height), + "Audits" => self.show_audits_tab(ui, ribbon_height), + "Item Templates" => self.show_templates_tab(ui, ribbon_height), + "Suppliers" => self.show_suppliers_tab(ui, ribbon_height), + "Issues" => self.show_issues_tab(ui, ribbon_height), + "Printers" => self.show_printers_tab(ui, ribbon_height), + "Label Templates" => self.show_label_templates_tab(ui, ribbon_height), + _ => {} + }); + }); + }, + ); + }); + None + } + + /// Render a 2-row grid of actions with consistent column widths and set checkbox triggers by key. + fn render_actions_grid_with_keys( + &mut self, + ui: &mut egui::Ui, + grid_id: &str, + items: &[(String, &str)], + ) { + let rows: usize = 2; + if items.is_empty() { + return; + } + let cols: usize = (items.len() + rows - 1) / rows; + + let pad_x = ui.style().spacing.button_padding.x; + let row_height: f32 = 24.0; + let mut col_widths = vec![0.0f32; cols]; + for col in 0..cols { + for row in 0..rows { + let idx = col * rows + row; + if idx < items.len() { + let text = &items[idx].0; + let text_width = 8.0 * text.len() as f32; + col_widths[col] = col_widths[col].max(text_width + pad_x * 2.0); + } + } + } + + egui::Grid::new(grid_id) + .num_columns(cols) + .spacing(egui::vec2(8.0, 8.0)) + .show(ui, |ui| { + for row in 0..rows { + for col in 0..cols { + let idx = col * rows + row; + if idx < items.len() { + let (label, key) = &items[idx]; + let w = col_widths[col]; + if ui + .add_sized([w, row_height], egui::Button::new(label.clone())) + .clicked() + { + self.checkboxes.insert(key.to_string(), true); + } + } else { + ui.allocate_space(egui::vec2(col_widths[col], row_height)); + } + } + ui.end_row(); + } + }); + } + + fn show_dashboard_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("View"); + egui::ScrollArea::vertical() + .id_salt("dashboard_view_scroll") + .show(ui, |ui| if ui.button("Refresh").clicked() {}); + }); + }); + // Stats section removed for now + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Widgets"); + egui::ScrollArea::vertical() + .id_salt("dashboard_widgets_scroll") + .show(ui, |ui| { + ui.checkbox( + self.checkboxes + .entry("show_pie".to_string()) + .or_insert(true), + "Show Pie Chart", + ); + ui.checkbox( + self.checkboxes + .entry("show_timeline".to_string()) + .or_insert(true), + "Show Timeline", + ); + }); + }); + }); + } + + fn show_inventory_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + // 2x2 grid: Add, Edit, Remove, Print Label (Edit supports Alt for Advanced) + let labels = [ + &format!("{} {}", icons::PLUS, "Add Item"), + &format!("{} {}", icons::PENCIL, "Edit"), + &format!("{} {}", icons::TRASH, "Remove"), + &format!("{} {}", icons::PRINTER, "Print Label"), + ]; // 4 labels -> 2x2 grid + let rows: usize = 2; + let cols: usize = 2; + let row_height: f32 = 24.0; + let pad_x = ui.style().spacing.button_padding.x; + // Compute column widths based on text + let mut col_widths = vec![0.0f32; cols]; + for col in 0..cols { + for row in 0..rows { + let idx = col * rows + row; + let text = labels[idx]; + let text_width = 8.0 * text.len() as f32; + col_widths[col] = col_widths[col].max(text_width + pad_x * 2.0); + } + } + egui::Grid::new("inventory_actions_grid") + .num_columns(cols) + .spacing(egui::vec2(8.0, 8.0)) + .show(ui, |ui| { + for row in 0..rows { + for col in 0..cols { + let idx = col * rows + row; + let w = col_widths[col]; + let button = egui::Button::new(labels[idx]); + let clicked = ui.add_sized([w, row_height], button).clicked(); + if clicked { + // If user holds Alt while clicking Edit, trigger Advanced Edit instead of Easy + let alt_held = ui.input(|i| i.modifiers.alt); + match idx { + 0 => { + self.checkboxes + .insert("inventory_action_add".to_string(), true); + } + 1 => { + if alt_held { + self.checkboxes.insert( + "inventory_action_edit_adv".to_string(), + true, + ); + } else { + self.checkboxes.insert( + "inventory_action_edit_easy".to_string(), + true, + ); + } + } + 2 => { + self.checkboxes.insert( + "inventory_action_delete".to_string(), + true, + ); + } + 3 => { + self.checkboxes.insert( + "inventory_action_print_label".to_string(), + true, + ); + } + _ => {} + } + } + } + ui.end_row(); + } + }); + }); + }); + // Import/Export hidden for now (planned-only) + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.set_width(200.0); // Reduced width for compact design + ui.vertical(|ui| { + ui.label("Filter"); + egui::ScrollArea::vertical() + .id_salt("inventory_filter_scroll") + .show(ui, |ui| { + // Compact FilterBuilder with popup + let filter_changed = self.filter_builder.show_compact(ui); + if filter_changed { + // Set trigger for filter application + self.checkboxes + .insert("inventory_filter_changed".to_string(), true); + } + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("View"); + egui::ScrollArea::vertical() + .id_salt("inventory_view_scroll") + .show(ui, |ui| { + let old_show_retired = + self.checkboxes.get("show_retired").copied().unwrap_or(true); + let mut show_retired = old_show_retired; + ui.checkbox(&mut show_retired, "Show Retired"); + if show_retired != old_show_retired { + self.checkboxes + .insert("show_retired".to_string(), show_retired); + self.checkboxes + .insert("inventory_filter_changed".to_string(), true); + } + if ui.button("❓ Help").clicked() { + self.checkboxes + .insert("inventory_show_help".to_string(), true); + } + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Quick Actions"); + egui::ScrollArea::vertical() + .id_salt("inventory_quick_actions_scroll") + .show(ui, |ui| { + // Ensure both controls share the same visual width while stacking vertically + let pad_x = ui.style().spacing.button_padding.x; + let w1 = 8.0 * "Inventarize Room".len() as f32 + pad_x * 2.0; + let w2 = 8.0 * "Add from...".len() as f32 + pad_x * 2.0; + let w = w1.max(w2).max(130.0); + + if ui + .add_sized([w, 24.0], egui::Button::new("Inventarize Room")) + .clicked() + { + self.checkboxes + .insert("inventory_quick_inventarize_room".to_string(), true); + } + + // Use a fixed-width ComboBox as a dropdown to ensure equal width + egui::ComboBox::from_id_salt("inventory_add_from_combo") + .width(w) + .selected_text("Add from...") + .show_ui(ui, |ui| { + if ui.selectable_label(false, "Add from Template").clicked() { + self.checkboxes.insert( + "inventory_add_from_template_single".to_string(), + true, + ); + } + if ui + .selectable_label(false, "Add Multiple from Template") + .clicked() + { + self.checkboxes.insert( + "inventory_add_from_template_multiple".to_string(), + true, + ); + } + if ui + .selectable_label(false, "Add using Another Item") + .clicked() + { + self.checkboxes + .insert("inventory_add_from_item_single".to_string(), true); + } + if ui + .selectable_label(false, "Add Multiple from Another Item") + .clicked() + { + self.checkboxes.insert( + "inventory_add_from_item_multiple".to_string(), + true, + ); + } + }); + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Query"); + egui::ScrollArea::vertical() + .id_salt("inventory_query_scroll") + .show(ui, |ui| { + ui.horizontal(|ui| { + ui.label("Limit:"); + let limit = self + .number_fields + .entry("inventory_limit".to_string()) + .or_insert(100); + let mut limit_str = limit.to_string(); + let response = ui.add_sized( + [60.0, 20.0], + egui::TextEdit::singleline(&mut limit_str), + ); + + // Trigger refresh on Enter or when value changes + let enter_pressed = response.lost_focus() + && ui.input(|i| i.key_pressed(egui::Key::Enter)); + if response.changed() || enter_pressed { + if let Ok(val) = limit_str.parse::<u32>() { + *limit = val; + if enter_pressed { + // Set trigger for inventory refresh + let trigger = self + .checkboxes + .entry("inventory_limit_refresh_trigger".to_string()) + .or_insert(false); + *trigger = true; + } + } + } + }); + + let no_limit_changed = ui + .checkbox( + self.checkboxes + .entry("inventory_no_limit".to_string()) + .or_insert(false), + "Maximum", + ) + .changed(); + if no_limit_changed { + // Trigger refresh when no limit checkbox changes + let trigger = self + .checkboxes + .entry("inventory_limit_refresh_trigger".to_string()) + .or_insert(false); + *trigger = true; + } + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Lookup"); + egui::ScrollArea::vertical() + .id_salt("inventory_lookup_scroll") + .show(ui, |ui| { + ui.label("Item Lookup:"); + ui.horizontal(|ui| { + let lookup_text = self + .search_texts + .entry("item_lookup".to_string()) + .or_insert_with(String::new); + let response = ui.add_sized( + [120.0, 30.0], + egui::TextEdit::singleline(lookup_text).hint_text("Tag or ID"), + ); + + // Trigger search on Enter key + let enter_pressed = response.lost_focus() + && ui.input(|i| i.key_pressed(egui::Key::Enter)); + + if ui.button("Search").clicked() || enter_pressed { + // Set trigger flag for inventory view to detect + let trigger = self + .checkboxes + .entry("item_lookup_trigger".to_string()) + .or_insert(false); + *trigger = true; + } + }); + }); + }); + }); + } + + fn show_categories_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + // Clear one-shot action triggers from previous frame + for key in [ + "categories_refresh", + "categories_add", + "categories_edit", + "categories_delete", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + // Match Inventory layout: [Add, Edit, Delete, Refresh] (Refresh where Inventory has Print) + let items = vec![ + ( + format!("{} {}", icons::PLUS, "Add Category"), + "categories_add", + ), + ( + format!("{} {}", icons::PENCIL, "Edit Category"), + "categories_edit", + ), + ( + format!("{} {}", icons::TRASH, "Delete Category"), + "categories_delete", + ), + ( + format!("{} {}", icons::ARROWS_CLOCKWISE, "Refresh"), + "categories_refresh", + ), + ]; + self.render_actions_grid_with_keys(ui, "categories_actions_grid", &items); + }); + }); + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Search"); + egui::ScrollArea::vertical() + .id_salt("categories_filter_scroll") + .show(ui, |ui| { + let search_text = self + .search_texts + .entry("categories_search".to_string()) + .or_insert_with(String::new); + ui.text_edit_singleline(search_text); + }); + }); + }); + } + + fn show_zones_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + // Clear one-shot action triggers from previous frame + for key in [ + "zones_action_add", + "zones_action_edit", + "zones_action_remove", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ( + format!("{} {}", icons::PLUS, "Add Zone"), + "zones_action_add", + ), + ( + format!("{} {}", icons::PENCIL, "Edit Zone"), + "zones_action_edit", + ), + ]; + self.render_actions_grid_with_keys(ui, "zones_actions_grid", &items); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.set_width(200.0); // Reduced width for compact design + ui.vertical(|ui| { + ui.label("Filter"); + egui::ScrollArea::vertical() + .id_salt("zones_filter_scroll") + .show(ui, |ui| { + // Compact FilterBuilder with popup (same as Inventory) + let filter_changed = self.filter_builder.show_compact(ui); + if filter_changed { + // Set trigger for filter application + self.checkboxes + .insert("zones_filter_changed".to_string(), true); + } + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("View"); + egui::ScrollArea::vertical() + .id_salt("zones_view_scroll") + .show(ui, |ui| { + // Use new key `zones_show_empty` but mirror to legacy key for compatibility + let show_empty = self + .checkboxes + .get("zones_show_empty") + .copied() + .unwrap_or(true); + let mut local = show_empty; + if ui.checkbox(&mut local, "Show Empty").changed() { + self.checkboxes + .insert("zones_show_empty".to_string(), local); + self.checkboxes + .insert("show_empty_zones".to_string(), local); + } + ui.checkbox( + self.checkboxes + .entry("zones_show_items".to_string()) + .or_insert(true), + "Show Items in Zones", + ); + }); + }); + }); + } + + fn show_borrowing_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + // Clear one-shot action triggers from previous frame + for key in [ + "borrowing_action_checkout", + "borrowing_action_return", + "borrowing_action_register", + "borrowing_action_refresh", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ( + format!("{} {}", icons::ARROW_LEFT, "Check Out"), + "borrowing_action_checkout", + ), + ( + format!("{} {}", icons::ARROW_RIGHT, "Return"), + "borrowing_action_return", + ), + ( + format!("{} {}", icons::PENCIL, "Register"), + "borrowing_action_register", + ), + ( + format!("{} {}", icons::ARROWS_CLOCKWISE, "Refresh"), + "borrowing_action_refresh", + ), + ]; + self.render_actions_grid_with_keys(ui, "borrowing_actions_grid", &items); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.set_width(200.0); + ui.vertical(|ui| { + ui.label("Filter"); + egui::ScrollArea::vertical() + .id_salt("borrowing_filter_scroll") + .show(ui, |ui| { + // Compact FilterBuilder with popup + let filter_changed = self.filter_builder.show_compact(ui); + if filter_changed { + // Set trigger for filter application + self.checkboxes + .insert("borrowing_filter_changed".to_string(), true); + } + }); + }); + }); + } + + fn show_audits_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ("New Audit".to_string(), "audits_action_new"), + ("View Audit".to_string(), "audits_action_view"), + ("Export Report".to_string(), "audits_action_export"), + ]; + self.render_actions_grid_with_keys(ui, "audits_actions_grid", &items); + }); + }); + } + + fn show_templates_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + // Clear one-shot action triggers from previous frame + for key in [ + "templates_action_new", + "templates_action_edit", + "templates_action_delete", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ( + format!("{} {}", icons::PLUS, "Add Template"), + "templates_action_new", + ), + ( + format!("{} {}", icons::PENCIL, "Edit Template"), + "templates_action_edit", + ), + ]; + self.render_actions_grid_with_keys(ui, "templates_actions_grid", &items); + }); + }); + // Import/Export removed for now + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.set_width(200.0); // Reduced width for compact design + ui.vertical(|ui| { + ui.label("Filter"); + egui::ScrollArea::vertical() + .id_salt("templates_filter_scroll") + .show(ui, |ui| { + // Compact FilterBuilder with popup + let filter_changed = self.filter_builder.show_compact(ui); + if filter_changed { + // Set trigger for filter application + self.checkboxes + .insert("templates_filter_changed".to_string(), true); + } + }); + }); + }); + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Query"); + egui::ScrollArea::vertical() + .id_salt("templates_query_scroll") + .show(ui, |ui| { + ui.horizontal(|ui| { + ui.label("Limit:"); + let limit = self + .number_fields + .entry("templates_limit".to_string()) + .or_insert(200); + let mut limit_str = limit.to_string(); + let response = ui.add_sized( + [60.0, 20.0], + egui::TextEdit::singleline(&mut limit_str), + ); + + // Trigger refresh on Enter or when value changes + let enter_pressed = response.lost_focus() + && ui.input(|i| i.key_pressed(egui::Key::Enter)); + if response.changed() || enter_pressed { + if let Ok(val) = limit_str.parse::<u32>() { + *limit = val; + self.checkboxes + .insert("templates_limit_changed".to_string(), true); + } + } + }); + }); + }); + }); + } + + fn show_issues_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ("Report Issue".to_string(), "issues_action_report"), + ("View Issue".to_string(), "issues_action_view"), + ("Resolve Issue".to_string(), "issues_action_resolve"), + ]; + self.render_actions_grid_with_keys(ui, "issues_actions_grid", &items); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Filter"); + egui::ScrollArea::vertical() + .id_salt("issues_filter_scroll") + .show(ui, |ui| { + let search_text = self + .search_texts + .entry("issue_search".to_string()) + .or_insert_with(String::new); + ui.text_edit_singleline(search_text); + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("View"); + egui::ScrollArea::vertical() + .id_salt("issues_view_scroll") + .show(ui, |ui| { + ui.checkbox( + self.checkboxes + .entry("show_resolved".to_string()) + .or_insert(false), + "Show Resolved", + ); + ui.checkbox( + self.checkboxes + .entry("show_high_priority".to_string()) + .or_insert(true), + "High Priority Only", + ); + }); + }); + }); + } + + fn show_suppliers_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + for key in [ + "suppliers_action_new", + "suppliers_action_edit", + "suppliers_action_delete", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ( + format!("{} {}", icons::PLUS, "New Supplier"), + "suppliers_action_new", + ), + ( + format!("{} {}", icons::PENCIL, "Edit Supplier"), + "suppliers_action_edit", + ), + ]; + self.render_actions_grid_with_keys(ui, "suppliers_actions_grid", &items); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Search"); + egui::ScrollArea::vertical() + .id_salt("suppliers_filter_scroll") + .show(ui, |ui| { + let search_text = self + .search_texts + .entry("supplier_search".to_string()) + .or_insert_with(String::new); + ui.text_edit_singleline(search_text); + }); + }); + }); + } + + pub fn show_printers_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + // Clear one-shot action triggers from previous frame + for key in [ + "printers_action_add", + "printers_action_refresh", + "printers_view_print_history", + "printers_default_changed", + ] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ( + format!("{} {}", icons::PLUS, "Add Printer"), + "printers_action_add", + ), + ( + format!("{} {}", icons::ARROWS_CLOCKWISE, "Refresh"), + "printers_action_refresh", + ), + ]; + self.render_actions_grid_with_keys(ui, "printers_actions_grid", &items); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Settings"); + egui::ScrollArea::vertical() + .id_salt("printers_settings_scroll") + .show(ui, |ui| { + ui.label("Default Printer:"); + + // Get printer data injected by PrintersView + let printers_json = self + .search_texts + .get("_printers_list") + .cloned() + .unwrap_or_default(); + let current_default_str = self + .search_texts + .get("_printers_current_default") + .cloned() + .unwrap_or_default(); + let current_default = if current_default_str.is_empty() { + None + } else { + current_default_str.parse::<i64>().ok() + }; + + if printers_json.is_empty() { + ui.label("(Loading...)"); + } else { + // Parse printer list + let printers: Vec<serde_json::Value> = + serde_json::from_str(&printers_json).unwrap_or_default(); + + let current_name = if let Some(id) = current_default { + printers + .iter() + .find(|p| p.get("id").and_then(|v| v.as_i64()) == Some(id)) + .and_then(|p| p.get("printer_name")) + .and_then(|v| v.as_str()) + .unwrap_or("Unknown") + .to_string() + } else { + "None".to_string() + }; + + egui::ComboBox::from_id_salt("ribbon_default_printer_selector") + .selected_text(¤t_name) + .show_ui(ui, |ui| { + // None option + if ui + .selectable_label(current_default.is_none(), "None") + .clicked() + { + self.search_texts.insert( + "printers_default_id".to_string(), + "".to_string(), + ); + self.checkboxes + .insert("printers_default_changed".to_string(), true); + } + + // Printer options + for printer in &printers { + if let (Some(id), Some(name)) = ( + printer.get("id").and_then(|v| v.as_i64()), + printer.get("printer_name").and_then(|v| v.as_str()), + ) { + let is_selected = current_default == Some(id); + if ui.selectable_label(is_selected, name).clicked() { + self.search_texts.insert( + "printers_default_id".to_string(), + id.to_string(), + ); + self.checkboxes.insert( + "printers_default_changed".to_string(), + true, + ); + } + } + } + }); + } + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("View"); + egui::ScrollArea::vertical() + .id_salt("printers_view_scroll") + .show(ui, |ui| { + if ui + .button(format!( + "{} {}", + icons::ARROWS_CLOCKWISE, + "View Print History" + )) + .clicked() + { + self.checkboxes + .insert("printers_view_print_history".to_string(), true); + } + }); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Search"); + egui::ScrollArea::vertical() + .id_salt("printers_search_scroll") + .show(ui, |ui| { + let search_text = self + .search_texts + .entry("printers_search".to_string()) + .or_insert_with(String::new); + ui.text_edit_singleline(search_text); + }); + }); + }); + } + + fn show_label_templates_tab(&mut self, ui: &mut egui::Ui, ribbon_height: f32) { + // Clear one-shot action triggers from previous frame + for key in ["labels_action_add", "labels_action_refresh"] { + self.checkboxes.insert(key.to_string(), false); + } + + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Actions"); + let items = vec![ + ( + format!("{} {}", icons::PLUS, "Add Template"), + "labels_action_add", + ), + ( + format!("{} {}", icons::ARROWS_CLOCKWISE, "Refresh"), + "labels_action_refresh", + ), + ]; + self.render_actions_grid_with_keys(ui, "labels_actions_grid", &items); + }); + }); + ui.group(|ui| { + ui.set_height(ribbon_height); + ui.vertical(|ui| { + ui.label("Search"); + egui::ScrollArea::vertical() + .id_salt("labels_search_scroll") + .show(ui, |ui| { + let search_text = self + .search_texts + .entry("labels_search".to_string()) + .or_insert_with(String::new); + ui.text_edit_singleline(search_text); + }); + }); + }); + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct RibbonConfig {} |
