diff --git a/plate-tool-eframe/src/app.rs b/plate-tool-eframe/src/app.rs index d660ebf..aab89f1 100644 --- a/plate-tool-eframe/src/app.rs +++ b/plate-tool-eframe/src/app.rs @@ -6,6 +6,7 @@ use eframe::egui::{self}; use plate_tool_lib::plate::PlateFormat; use crate::main_state::{construct_fake_mainstate, MainState}; +use crate::modals::{self, ModalState}; use crate::plate::{add_plate, PlateUiState}; use crate::transfer_menu::{self, transfer_menu, CurrentTransferState, TransferMenuState}; use crate::tree::tree; @@ -32,9 +33,9 @@ pub struct PlateToolEframe { main_window_state: MainWindowState, current_transfer_state: CurrentTransferState, #[serde(skip)] - transfer_menu_state: TransferMenuState, + modal_state: ModalState, #[serde(skip)] - transfer_region_cache: plate_tool_lib::transfer_region_cache::TransferRegionCache, + transfer_menu_state: TransferMenuState, main_state: MainState, } @@ -45,9 +46,8 @@ impl Default for PlateToolEframe { 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(), - transfer_region_cache: plate_tool_lib::transfer_region_cache::TransferRegionCache::new( - ), main_state: construct_fake_mainstate(), } } @@ -60,7 +60,7 @@ impl PlateToolEframe { return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); } else { let pte: PlateToolEframe = Default::default(); - pte.transfer_region_cache + pte.main_state.transfer_region_cache .generate_cache(&pte.main_state.transfers); pte @@ -79,7 +79,9 @@ impl eframe::App for PlateToolEframe { egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { egui::menu::bar(ui, |ui| { ui.menu_button("File", |ui| { - if ui.button("New").clicked() {} + if ui.button("New Plate").clicked() { + crate::modals::open_new_plate_modal(ctx, &mut self.modal_state); + } ui.menu_button("Export", |ui| { if ui.button("Export as CSV").clicked() {} if ui.button("Export as JSON").clicked() {} @@ -88,6 +90,7 @@ impl eframe::App for PlateToolEframe { if ui.button("Import from JSON").clicked() {} if ui.button("Import transfer from CSV").clicked() {} }); + if ui.button("Reset All").clicked() {} }); ui.menu_button("Options", |ui| { ui.menu_button("Styles", |ui| { @@ -118,7 +121,6 @@ impl eframe::App for PlateToolEframe { &self.current_transfer_state, &mut self.transfer_menu_state, &mut self.main_state, - &self.transfer_region_cache, ); }); }); @@ -144,27 +146,27 @@ impl eframe::App for PlateToolEframe { x.y /= 2.0; x }; - if self.main_state.get_current_source().is_some() { + if let Some(source_pi) = self.main_state.get_current_source_plateinstance() { add_plate( half_height, - PlateFormat::W96, - &self.main_state.transfers, + source_pi.plate.plate_format, + self.main_state.get_current_transfers(), plate_tool_lib::plate::PlateType::Source, &ordered_ids, - &self.transfer_region_cache, + &self.main_state.transfer_region_cache, Some(&self.current_transfer_state), ui, self.source_plate_state.lock().unwrap().deref_mut(), ); } - if self.main_state.get_current_destination().is_some() { + if let Some(destination_pi) = self.main_state.get_current_destination_plateinstance() { add_plate( half_height, - PlateFormat::W96, - &self.main_state.transfers, + destination_pi.plate.plate_format, + self.main_state.get_current_transfers(), plate_tool_lib::plate::PlateType::Destination, &ordered_ids, - &self.transfer_region_cache, + &self.main_state.transfer_region_cache, Some(&self.current_transfer_state), ui, self.destination_plate_state.lock().unwrap().deref_mut(), @@ -172,5 +174,22 @@ impl eframe::App for PlateToolEframe { } }); }); + + // Modal processing + modals::show_modal_if_open(ctx, &mut self.modal_state); + if let modals::ModalState::NewPlateComplete(modals::NewPlateModalComplete(Some(pi))) = + &self.modal_state + { + 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; + } } } diff --git a/plate-tool-eframe/src/lib.rs b/plate-tool-eframe/src/lib.rs index 4340019..4a97986 100644 --- a/plate-tool-eframe/src/lib.rs +++ b/plate-tool-eframe/src/lib.rs @@ -3,4 +3,5 @@ mod plate; mod tree; mod transfer_menu; mod main_state; +mod modals; pub use app::PlateToolEframe; diff --git a/plate-tool-eframe/src/main_state.rs b/plate-tool-eframe/src/main_state.rs index f09f921..9fa48fb 100644 --- a/plate-tool-eframe/src/main_state.rs +++ b/plate-tool-eframe/src/main_state.rs @@ -1,5 +1,6 @@ -use plate_tool_lib::plate_instances; use plate_tool_lib::plate::PlateFormat; +use plate_tool_lib::plate_instances; +use plate_tool_lib::transfer_region_cache::TransferRegionCache; #[derive(Default, Debug, serde::Serialize, serde::Deserialize)] pub struct MainState { @@ -14,37 +15,86 @@ pub struct MainState { current_source: Option, current_destination: Option, current_transfer: Option, + #[serde(skip)] + pub transfer_region_cache: plate_tool_lib::transfer_region_cache::TransferRegionCache, } impl MainState { - pub fn get_current_source(&self) -> Option { + pub fn get_current_source_uuid(&self) -> Option { self.current_source } - pub fn get_current_destination(&self) -> Option { + pub fn get_current_source_plateinstance( + &self, + ) -> Option<&plate_tool_lib::plate_instances::PlateInstance> { + if let Some(id) = self.current_source { + self.source_plates.iter().find(|x| x.get_uuid() == id) + } else { + None + } + } + + pub fn get_current_destination_uuid(&self) -> Option { self.current_destination } - pub fn get_current_transfer(&self) -> Option { + pub fn get_current_destination_plateinstance( + &self, + ) -> Option<&plate_tool_lib::plate_instances::PlateInstance> { + if let Some(id) = self.current_destination { + self.destination_plates.iter().find(|x| x.get_uuid() == id) + } else { + None + } + } + + pub fn get_current_transfer_uuid(&self) -> Option { self.current_transfer } + pub fn get_current_transfer_info(&self) -> Option<&plate_tool_lib::transfer::Transfer> { + if let Some(id) = self.current_transfer { + self.transfers.iter().find(|x| x.get_uuid() == id) + } else { + None + } + } + pub fn get_current_transfers(&self) -> Option> { + let source_uuid = self.get_current_source_uuid(); + let destination_uuid = self.get_current_destination_uuid(); + if let (Some(source_uuid), Some(destination_uuid)) = (source_uuid, destination_uuid) { + Some(self.transfers + .iter() + .filter(|tr| tr.source_id == source_uuid && tr.dest_id == destination_uuid) + .collect()) + } else { + None + } + } pub fn set_current_source(&mut self, id: plate_tool_lib::uuid::Uuid) -> bool { - if self.source_plates.iter().map(|x| x.get_uuid()).find(|x| *x == id).is_some() { + if self.check_source_exists(id) { self.current_source = Some(id); + self.set_no_current_transfer(); true } else { false } } pub fn set_current_destination(&mut self, id: plate_tool_lib::uuid::Uuid) -> bool { - if self.destination_plates.iter().map(|x| x.get_uuid()).find(|x| *x == id).is_some() { + if self.check_destination_exists(id) { self.current_destination = Some(id); + self.set_no_current_transfer(); true } else { false } } pub fn set_current_transfer(&mut self, id: plate_tool_lib::uuid::Uuid) -> bool { - if self.transfers.iter().map(|x| x.get_uuid()).find(|x| *x == id).is_some() { - self.current_transfer = Some(id); + if let Some(tr) = self.transfers.iter().find(|x| x.get_uuid() == id) { + let source_exists = self.check_source_exists(tr.source_id); + let destination_exists = self.check_destination_exists(tr.dest_id); + if source_exists && destination_exists { + self.current_transfer = Some(id); + self.current_source = Some(tr.source_id); + self.current_destination = Some(tr.dest_id); + } true } else { false @@ -59,6 +109,21 @@ impl MainState { pub fn set_no_current_transfer(&mut self) { self.current_transfer = None; } + + fn check_source_exists(&self, id: plate_tool_lib::uuid::Uuid) -> bool { + self.source_plates + .iter() + .map(|x| x.get_uuid()) + .find(|x| *x == id) + .is_some() + } + fn check_destination_exists(&self, id: plate_tool_lib::uuid::Uuid) -> bool { + self.destination_plates + .iter() + .map(|x| x.get_uuid()) + .find(|x| *x == id) + .is_some() + } } pub fn construct_fake_mainstate() -> MainState { @@ -110,7 +175,6 @@ pub fn construct_fake_mainstate() -> MainState { "Shrimp".to_owned(), ); - MainState { source_plates: vec![src_plate], destination_plates: vec![dest_plate], @@ -118,6 +182,6 @@ pub fn construct_fake_mainstate() -> MainState { current_source: None, current_destination: None, current_transfer: None, + transfer_region_cache: TransferRegionCache::default(), } } - diff --git a/plate-tool-eframe/src/modals.rs b/plate-tool-eframe/src/modals.rs new file mode 100644 index 0000000..ac7904d --- /dev/null +++ b/plate-tool-eframe/src/modals.rs @@ -0,0 +1,107 @@ +use eframe::egui; + +#[non_exhaustive] +#[derive(Debug)] +pub enum ModalState { + NewPlate(NewPlateModalState), + NewPlateComplete(NewPlateModalComplete), + None, +} +impl Default for ModalState { + fn default() -> Self { + Self::None + } +} + +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct NewPlateModalState { + pub name: String, + pub plate_type: plate_tool_lib::plate::PlateType, + pub plate_format: plate_tool_lib::plate::PlateFormat, +} + +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct NewPlateModalComplete(pub Option); + +pub fn show_modal_if_open(ctx: &egui::Context, modal_state: &mut ModalState) { + match modal_state { + ModalState::NewPlate(_) => show_new_plate_modal(ctx, modal_state), + _ => (), + } +} +fn show_new_plate_modal(ctx: &egui::Context, modal_state: &mut ModalState) { + let state: &mut NewPlateModalState = { + if let ModalState::NewPlate(x) = modal_state { + x + } else { + *modal_state = ModalState::NewPlate(NewPlateModalState::default()); + if let ModalState::NewPlate(y) = modal_state { + y + } else { + unreachable!() + } + } + }; + + let mut output: Option = None; + egui::Window::new("New Plate") + .order(egui::Order::Foreground) + .collapsible(false) + .resizable(false) + .show(ctx, |ui| { + ui.vertical(|ui| { + ui.add(egui::TextEdit::singleline(&mut state.name).hint_text("Plate Name")); + egui::ComboBox::from_label("Plate Type") + .selected_text(format!("{:?}", state.plate_type)) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut state.plate_type, + plate_tool_lib::plate::PlateType::Source, + "Source", + ); + ui.selectable_value( + &mut state.plate_type, + plate_tool_lib::plate::PlateType::Destination, + "Destination", + ); + }); + egui::ComboBox::from_label("Plate Format") + .selected_text(format!("{:?}", state.plate_format)) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut state.plate_format, + plate_tool_lib::plate::PlateFormat::W96, + "96W", + ); + ui.selectable_value( + &mut state.plate_format, + plate_tool_lib::plate::PlateFormat::W384, + "384W", + ); + ui.selectable_value( + &mut state.plate_format, + plate_tool_lib::plate::PlateFormat::W1536, + "1536W", + ); + }); + if ui.button("Add").clicked() { + output = Some(plate_tool_lib::plate_instances::PlateInstance::new(state.plate_type, state.plate_format, state.name.clone())); + } + }); + }); + if output.is_some() { + *modal_state = ModalState::NewPlateComplete(NewPlateModalComplete(output)); + } +} + +pub fn open_new_plate_modal( + ctx: &egui::Context, + modal_state: &mut ModalState, +) { + // Do not close another modal + if matches!(modal_state, ModalState::None) { + *modal_state = ModalState::NewPlate(NewPlateModalState::default()); + } +} diff --git a/plate-tool-eframe/src/plate.rs b/plate-tool-eframe/src/plate.rs index b2d40ad..2691395 100644 --- a/plate-tool-eframe/src/plate.rs +++ b/plate-tool-eframe/src/plate.rs @@ -125,7 +125,7 @@ fn get_well_from_pos( fn calculate_shading_for_wells( rows: u8, columns: u8, - transfers: &Vec, + transfers: Option>, plate_type: plate_tool_lib::plate::PlateType, ordered_ids: &[Uuid], cache: &plate_tool_lib::transfer_region_cache::TransferRegionCache, @@ -133,6 +133,7 @@ fn calculate_shading_for_wells( let box_size: usize = rows as usize * columns as usize; let mut well_infos: Box<[Option]> = vec![None; box_size].into_boxed_slice(); + if let Some(transfers) = transfers { for transfer in transfers { let cache_result = match plate_type { plate_tool_lib::plate::PlateType::Source => cache.get_or_calculate_source(transfer), @@ -160,6 +161,7 @@ fn calculate_shading_for_wells( } } } + } well_infos } @@ -175,7 +177,7 @@ fn add_plate_sub( size: egui::Vec2, rows: u8, columns: u8, - transfers: &Vec, + transfers: Option>, plate_type: plate_tool_lib::plate::PlateType, ordered_ids: &[Uuid], cache: &plate_tool_lib::transfer_region_cache::TransferRegionCache, @@ -393,7 +395,7 @@ fn add_plate_sub( pub fn add_plate( size: egui::Vec2, pf: PlateFormat, - transfers: &Vec, + transfers: Option>, plate_type: plate_tool_lib::plate::PlateType, ordered_ids: &[Uuid], cache: &plate_tool_lib::transfer_region_cache::TransferRegionCache, diff --git a/plate-tool-eframe/src/transfer_menu.rs b/plate-tool-eframe/src/transfer_menu.rs index 5797cd5..ad1b4ef 100644 --- a/plate-tool-eframe/src/transfer_menu.rs +++ b/plate-tool-eframe/src/transfer_menu.rs @@ -107,7 +107,7 @@ impl CurrentTransferStateInterior { } pub fn try_new_from_main_state_transfer(ms: &MainState) -> Option { - let transfer_id = ms.get_current_transfer(); + let transfer_id = ms.get_current_transfer_uuid(); let transfer = ms .transfers .iter() @@ -141,8 +141,8 @@ impl CurrentTransferStateInterior { } pub fn convert_to_transfer(&self, ms: &MainState) -> Option { - let source_plate_uuid = ms.get_current_source()?; - let destination_plate_uuid = ms.get_current_destination()?; + let source_plate_uuid = ms.get_current_source_uuid()?; + let destination_plate_uuid = ms.get_current_destination_uuid()?; let source_plate_instance = ms.source_plates.iter().find(|x| x.get_uuid() == source_plate_uuid)?; let destination_plate_instance = ms.destination_plates.iter().find(|x| x.get_uuid() == destination_plate_uuid)?; let transfer = Some(plate_tool_lib::transfer::Transfer::new( @@ -186,7 +186,6 @@ pub fn transfer_menu( state: &CurrentTransferState, ui_state: &mut TransferMenuState, main_state: &mut MainState, - transfer_region_cache: &plate_tool_lib::transfer_region_cache::TransferRegionCache, ) { // Can we reduce the length of this lock pls let mut state = state.lock().unwrap(); @@ -283,12 +282,12 @@ pub fn transfer_menu( ui.horizontal(|ui| { if ui.button("Save").clicked() { - if let Some(transfer_uuid) = main_state.get_current_transfer() { + if let Some(transfer_uuid) = main_state.get_current_transfer_uuid() { log::info!("should change transfer"); if let Some(mut transfer) = main_state.transfers.iter_mut().find(|x| x.id == transfer_uuid) { transfer.transfer_region = state.generate_transfer_region(); transfer.name = state.transfer_name.clone(); - transfer_region_cache.invalidate(&transfer); + main_state.transfer_region_cache.invalidate(&transfer); } } else { let new_transfer = state.convert_to_transfer(main_state); @@ -297,7 +296,7 @@ pub fn transfer_menu( main_state.transfers.push(new_transfer); let new_transfer = main_state.transfers.last() .expect("Cannot be empty, just added a transfer"); - transfer_region_cache.add_overwrite(new_transfer); + main_state.transfer_region_cache.add_overwrite(new_transfer); } } } diff --git a/plate-tool-eframe/src/tree.rs b/plate-tool-eframe/src/tree.rs index 62a8d2a..f82560d 100644 --- a/plate-tool-eframe/src/tree.rs +++ b/plate-tool-eframe/src/tree.rs @@ -15,7 +15,7 @@ pub fn tree(ui: &mut egui::Ui, ms: &mut MainState, cts: &CurrentTransferState) { for (name, uuid) in ms.source_plates.iter().map(|x| (&x.name, x.get_uuid())) { let f = { let mut f = egui::Frame::none(); - if ms.get_current_source().is_some_and(|x| x == uuid) { + if ms.get_current_source_uuid().is_some_and(|x| x == uuid) { f = f.fill(*SELECT_COLOR); } f @@ -28,12 +28,15 @@ pub fn tree(ui: &mut egui::Ui, ms: &mut MainState, cts: &CurrentTransferState) { ); if r.clicked() { new_uuid = Some(uuid); - log::info!("{:?}", uuid); } }); } if let Some(uuid) = new_uuid { ms.set_current_source(uuid); + ms.set_no_current_transfer(); + if let Some(mut cts) = cts.lock().ok() { + *cts = CurrentTransferStateInterior::default(); + } } }); @@ -48,7 +51,7 @@ pub fn tree(ui: &mut egui::Ui, ms: &mut MainState, cts: &CurrentTransferState) { { let f = { let mut f = egui::Frame::none(); - if ms.get_current_destination().is_some_and(|x| x == uuid) { + if ms.get_current_destination_uuid().is_some_and(|x| x == uuid) { f = f.fill(*SELECT_COLOR); } f @@ -61,12 +64,15 @@ pub fn tree(ui: &mut egui::Ui, ms: &mut MainState, cts: &CurrentTransferState) { ); if r.clicked() { new_uuid = Some(uuid); - log::info!("{:?}", uuid); } }); } if let Some(uuid) = new_uuid { ms.set_current_destination(uuid); + ms.set_no_current_transfer(); + if let Some(mut cts) = cts.lock().ok() { + *cts = CurrentTransferStateInterior::default(); + } } }); // Add all transfers @@ -76,7 +82,7 @@ pub fn tree(ui: &mut egui::Ui, ms: &mut MainState, cts: &CurrentTransferState) { for (name, uuid) in ms.transfers.iter().map(|x| (&x.name, x.get_uuid())) { let f = { let mut f = egui::Frame::none(); - if ms.get_current_transfer().is_some_and(|x| x == uuid) { + if ms.get_current_transfer_uuid().is_some_and(|x| x == uuid) { f = f.fill(*SELECT_COLOR); } f