use std::ops::DerefMut; use std::sync::Mutex; use eframe::egui::{self}; use crate::main_state::{construct_fake_mainstate, MainState}; use crate::modals::{self, ModalState}; use crate::plate::{add_plate, PlateDisplayOptions, PlateUiState}; use crate::transfer_menu::{transfer_menu, CurrentTransferState, TransferMenuState}; use crate::tree::tree; #[non_exhaustive] #[derive(Debug, serde::Serialize, serde::Deserialize)] struct MainWindowState { show_side_panel: bool, plate_display_options: PlateDisplayOptions, } impl Default for MainWindowState { fn default() -> Self { Self { show_side_panel: true, plate_display_options: PlateDisplayOptions::default(), } } } #[non_exhaustive] #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct PlateToolEframe { source_plate_state: Mutex, destination_plate_state: Mutex, main_window_state: MainWindowState, current_transfer_state: CurrentTransferState, #[serde(skip)] modal_state: ModalState, #[serde(skip)] transfer_menu_state: TransferMenuState, main_state: MainState, } impl Default for PlateToolEframe { fn default() -> Self { Self { source_plate_state: Mutex::new(PlateUiState::default()), destination_plate_state: Mutex::new(PlateUiState::default()), main_window_state: MainWindowState::default(), current_transfer_state: CurrentTransferState::default(), modal_state: ModalState::default(), transfer_menu_state: TransferMenuState::default(), main_state: construct_fake_mainstate(), } } } impl PlateToolEframe { pub fn new(cc: &eframe::CreationContext<'_>) -> Self { // Would load state here if let Some(storage) = cc.storage { let out: PlateToolEframe = eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); return out; } else { let pte: PlateToolEframe = Default::default(); pte.main_state .transfer_region_cache .generate_cache(&pte.main_state.transfers); pte } } } impl eframe::App for PlateToolEframe { // State storage fn save(&mut self, storage: &mut dyn eframe::Storage) { log::info!("Saving state"); eframe::set_value(storage, eframe::APP_KEY, self); } fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) { crate::styling::set_visuals(&ctx); egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { egui::menu::bar(ui, |ui| { ui.menu_button("File", |ui| { if ui.button("New Plate").clicked() { crate::modals::open_new_plate_modal(&mut self.modal_state); } ui.menu_button("Export", |ui| { if ui.button("Export as CSV").clicked() {} if ui.button("Export as JSON").clicked() {} }); ui.menu_button("Import", |ui| { if ui.button("Import from JSON").clicked() {} if ui.button("Import transfer from CSV").clicked() {} }); if ui.button("Reset All").clicked() { self.main_state = MainState::default(); self.current_transfer_state = CurrentTransferState::default(); } if ui.button("Dump State").clicked() { log::warn!("{:?}\n{:?}", self.main_state, self.current_transfer_state); } }); ui.menu_button("Options", |ui| { ui.menu_button("Styles", |ui| { ui.toggle_value( &mut self .main_window_state .plate_display_options .show_transfer_hashes, "Toggle transfer hashes", ); ui.toggle_value( &mut self .main_window_state .plate_display_options .show_volume_heatmap, "Toggle volume heatmap", ); ui.toggle_value( &mut self .main_window_state .plate_display_options .show_coordinates, "Toggle coordinate highlighting", ); }); ui.menu_button("Exports", |ui| { if ui.button("Change CSV export type").clicked() {} }); ui.menu_button("Windows", |ui| { ui.toggle_value( &mut self.main_window_state.show_side_panel, "Toggle side panel", ); }); }); }); }); if self.main_window_state.show_side_panel { egui::SidePanel::left("side_menus").show(ctx, |ui| { ui.vertical(|ui| { tree( ui, &mut self.main_state, &self.current_transfer_state, &mut self.modal_state, ); ui.separator(); transfer_menu( ui, &self.current_transfer_state, &mut self.transfer_menu_state, &mut self.main_state, ); }); }); } // This MUST happen after the menu calls as they can mutate main state let ordered_ids: Vec = { let mut ids: Vec = self .main_state .transfers .clone() .iter() .map(|x| x.id) .collect(); ids.sort_unstable(); ids }; egui::CentralPanel::default().show(ctx, |ui| { ui.vertical(|ui| { let available_size = ui.available_size(); let half_height = { let mut x = available_size; x.y /= 2.0; x }; if let Some(source_pi) = self.main_state.get_current_source_plateinstance() { add_plate( half_height, source_pi.plate.plate_format, self.main_state.get_current_source_transfers(), plate_tool_lib::plate::PlateType::Source, &ordered_ids, &self.main_state.transfer_region_cache, Some(&self.current_transfer_state), ui, self.source_plate_state.lock().unwrap().deref_mut(), self.main_window_state.plate_display_options, ); } if let Some(destination_pi) = self.main_state.get_current_destination_plateinstance() { add_plate( half_height, destination_pi.plate.plate_format, self.main_state.get_current_destination_transfers(), plate_tool_lib::plate::PlateType::Destination, &ordered_ids, &self.main_state.transfer_region_cache, Some(&self.current_transfer_state), ui, self.destination_plate_state.lock().unwrap().deref_mut(), self.main_window_state.plate_display_options, ); } }); }); // Modal processing modals::show_modal_if_open(ctx, &mut self.modal_state); match &self.modal_state { modals::ModalState::NewPlateComplete(modals::NewPlateModalComplete(Some(pi))) => { let plate_type = pi.plate.plate_type; match plate_type { plate_tool_lib::plate::PlateType::Source => { self.main_state.source_plates.push(pi.clone()); } plate_tool_lib::plate::PlateType::Destination => { self.main_state.destination_plates.push(pi.clone()); } } self.modal_state = modals::ModalState::None; } modals::ModalState::EditPlateComplete(modals::EditPlateModalStateComplete(Some(x))) => { let pi = { match x.plate_type { plate_tool_lib::plate::PlateType::Source => self .main_state .source_plates .iter_mut() .find(|y| y.get_uuid() == x.id), plate_tool_lib::plate::PlateType::Destination => self .main_state .destination_plates .iter_mut() .find(|y| y.get_uuid() == x.id), } }; if let Some(pi) = pi { pi.name = x.name.to_owned(); pi.change_format(&x.plate_format); } self.modal_state = modals::ModalState::None; } ModalState::NewPlateComplete(modals::NewPlateModalComplete(None)) => { self.modal_state = modals::ModalState::None; } ModalState::EditPlateComplete(modals::EditPlateModalStateComplete(None)) => { self.modal_state = modals::ModalState::None; } _ => (), } } }