352 lines
14 KiB
Rust
352 lines
14 KiB
Rust
use std::ops::DerefMut;
|
|
use std::sync::Mutex;
|
|
|
|
use serde_json;
|
|
|
|
use eframe::egui::{self};
|
|
use plate_tool_lib::csv::{
|
|
records_to_csv, records_to_echo_client_csv, transfer_to_records, TransferRecord,
|
|
};
|
|
|
|
use crate::file_handling::save_file;
|
|
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,
|
|
csv_export_type: CsvExportType,
|
|
}
|
|
|
|
impl Default for MainWindowState {
|
|
fn default() -> Self {
|
|
Self {
|
|
show_side_panel: true,
|
|
plate_display_options: PlateDisplayOptions::default(),
|
|
csv_export_type: CsvExportType::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[non_exhaustive]
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, std::default::Default)]
|
|
enum CsvExportType {
|
|
#[default]
|
|
Normal,
|
|
EchoClient,
|
|
}
|
|
|
|
impl std::fmt::Display for CsvExportType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::Normal => write!(f, "Normal"),
|
|
Self::EchoClient => write!(f, "Echo Client"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[non_exhaustive]
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
pub struct PlateToolEframe {
|
|
source_plate_state: Mutex<PlateUiState>,
|
|
destination_plate_state: Mutex<PlateUiState>,
|
|
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::MenuBar::new().ui(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() {
|
|
let records: Vec<TransferRecord> = self
|
|
.main_state
|
|
.transfers
|
|
.iter()
|
|
.flat_map(|transfer| {
|
|
let src_barcode = self
|
|
.main_state
|
|
.source_plates
|
|
.iter()
|
|
.find(|spi| spi.get_uuid() == transfer.source_id)?;
|
|
let dest_barcode = self
|
|
.main_state
|
|
.destination_plates
|
|
.iter()
|
|
.find(|dpi| dpi.get_uuid() == transfer.dest_id)?;
|
|
|
|
if self.main_window_state.csv_export_type
|
|
!= CsvExportType::EchoClient
|
|
|| self
|
|
.main_state
|
|
.get_current_source_uuid()
|
|
.is_some_and(|x| x != src_barcode.get_uuid())
|
|
|| self
|
|
.main_state
|
|
.get_current_destination_uuid()
|
|
.is_some_and(|x| x != dest_barcode.get_uuid())
|
|
{
|
|
return None;
|
|
}
|
|
|
|
Some(transfer_to_records(
|
|
transfer,
|
|
&src_barcode.name,
|
|
&dest_barcode.name,
|
|
))
|
|
})
|
|
.flatten()
|
|
.collect();
|
|
|
|
let data = match self.main_window_state.csv_export_type {
|
|
CsvExportType::Normal => records_to_csv(records),
|
|
CsvExportType::EchoClient => records_to_echo_client_csv(records),
|
|
};
|
|
if let Ok(data) = data {
|
|
let bytes: &[u8] = data.as_bytes();
|
|
save_file(bytes, Some("transfers.csv"));
|
|
}
|
|
}
|
|
if ui.button("Export as JSON").clicked() {
|
|
let data = serde_json::to_string_pretty(&self.main_state);
|
|
if let Ok(data) = data {
|
|
let bytes: &[u8] = data.as_bytes();
|
|
save_file(bytes, Some("plate_tool_state.json"));
|
|
}
|
|
}
|
|
});
|
|
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| {
|
|
ui.menu_button("CSV Export Type", |ui| {
|
|
ui.radio_value(
|
|
&mut self.main_window_state.csv_export_type,
|
|
CsvExportType::Normal,
|
|
format!("{}", CsvExportType::Normal),
|
|
);
|
|
ui.radio_value(
|
|
&mut self.main_window_state.csv_export_type,
|
|
CsvExportType::EchoClient,
|
|
format!("{}", CsvExportType::EchoClient),
|
|
);
|
|
});
|
|
});
|
|
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<plate_tool_lib::uuid::Uuid> = {
|
|
let mut ids: Vec<plate_tool_lib::uuid::Uuid> = 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;
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|