diff --git a/plate-tool-eframe/src/app.rs b/plate-tool-eframe/src/app.rs index 6f8d8f8..97bc3ae 100644 --- a/plate-tool-eframe/src/app.rs +++ b/plate-tool-eframe/src/app.rs @@ -6,10 +6,28 @@ use eframe::egui::{self}; use plate_tool_lib::plate::PlateFormat; use crate::plate::{add_grid, PlateUiState}; +use crate::transfer_menu::{self, transfer_menu, TransferMenuState}; +#[non_exhaustive] +struct MainWindowState { + show_side_panel: bool, +} + +impl Default for MainWindowState { + fn default() -> Self { + Self { + show_side_panel: true, + } + } +} + +#[non_exhaustive] pub struct PlateToolEframe { format: PlateFormat, source_plate_state: Mutex, + destination_plate_state: Mutex, + main_window_state: MainWindowState, + transfer_menu_state: TransferMenuState, } impl Default for PlateToolEframe { @@ -17,6 +35,9 @@ impl Default for PlateToolEframe { Self { format: PlateFormat::W96, source_plate_state: Mutex::new(PlateUiState::default()), + destination_plate_state: Mutex::new(PlateUiState::default()), + main_window_state: MainWindowState::default(), + transfer_menu_state: TransferMenuState::default(), } } } @@ -39,16 +60,72 @@ impl eframe::App for PlateToolEframe { fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) { egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { - ui.label("shrimp"); + egui::menu::bar(ui, |ui| { + ui.menu_button("File", |ui| { + if ui.button("New").clicked() { + } + 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() { + } + }); + }); + ui.menu_button("Options", |ui| { + ui.menu_button("Styles", |ui| { + if ui.button("Toggle transfer hashes").clicked() { + } + if ui.button("Toggle volume heatmap").clicked() { + } + if ui.button("Toggle current coordinates view").clicked() { + } + }); + 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| { + transfer_menu(ui, &mut self.transfer_menu_state); + }); + }); + } + egui::CentralPanel::default().show(ctx, |ui| { - ui.heading("lobster"); - add_grid( - PlateFormat::W1536, - ui, - self.source_plate_state.lock().unwrap().deref_mut(), - ); + ui.vertical(|ui| { + let available_size = ui.available_size(); + let half_height = { + let mut x = available_size; + x.y /= 2.0; + x + }; + add_grid( + half_height, + PlateFormat::W1536, + ui, + self.source_plate_state.lock().unwrap().deref_mut(), + ); + add_grid( + half_height, + PlateFormat::W1536, + ui, + self.destination_plate_state.lock().unwrap().deref_mut(), + ); + }); }); } } diff --git a/plate-tool-eframe/src/lib.rs b/plate-tool-eframe/src/lib.rs index 98f8dfe..a6adf38 100644 --- a/plate-tool-eframe/src/lib.rs +++ b/plate-tool-eframe/src/lib.rs @@ -1,3 +1,5 @@ mod app; mod plate; +mod tree; +mod transfer_menu; pub use app::PlateToolEframe; diff --git a/plate-tool-eframe/src/plate.rs b/plate-tool-eframe/src/plate.rs index 602493e..d0fea69 100644 --- a/plate-tool-eframe/src/plate.rs +++ b/plate-tool-eframe/src/plate.rs @@ -1,7 +1,9 @@ use eframe::egui::{self, pos2, Color32, Rounding}; use plate_tool_lib::plate::PlateFormat; -#[derive(Clone, Copy, Debug)] +const PALETTE: plate_tool_lib::util::ColorPalette = plate_tool_lib::util::Palettes::RAINBOW; + +#[derive(Clone, Debug)] pub struct PlateUiState { pub drag_start_position: Option, } @@ -14,7 +16,15 @@ impl Default for PlateUiState { } } -pub fn add_grid(pf: PlateFormat, ui: &mut egui::Ui, state: &mut PlateUiState) { +#[derive(Clone, Copy, Debug)] +struct WellInfo { + volume: f32, + color: [f32; 3], +} + +pub fn add_grid(size: egui::Vec2, pf: PlateFormat, + transfers: &Vec, + ui: &mut egui::Ui, state: &mut PlateUiState) { fn calculate_grid_params( ul_origin: (f32, f32), tw: f32, @@ -66,10 +76,26 @@ pub fn add_grid(pf: PlateFormat, ui: &mut egui::Ui, state: &mut PlateUiState) { }) } - fn add_grid_sub(rows: u8, columns: u8, ui: &mut egui::Ui, state: &mut PlateUiState) { - let available_size = ui.available_size(); - let (response, painter) = - ui.allocate_painter(available_size, egui::Sense::click_and_drag()); + fn calculate_shading_for_wells( + rows: u8, + columns: u8, + transfers: &Vec, + ) -> Box<[Option]> { + let box_size: usize = rows as usize * columns as usize; + let mut well_infos: Box<[Option]> = vec![None; box_size].into_boxed_slice(); + + well_infos + } + + fn add_grid_sub( + size: egui::Vec2, + rows: u8, + columns: u8, + transfers: &Vec, + ui: &mut egui::Ui, + state: &mut PlateUiState, + ) { + let (response, painter) = ui.allocate_painter(size, egui::Sense::click_and_drag()); let rect = response.rect; @@ -135,12 +161,38 @@ pub fn add_grid(pf: PlateFormat, ui: &mut egui::Ui, state: &mut PlateUiState) { painter.circle_stroke(center, radius - 2.0, stroke); } } + + // Draw row/column labels + for c_row in 0..rows { + painter.text( + egui::pos2( + start_x - 10.0, + start_y + radius + 2.0 * radius * c_row as f32, + ), + egui::Align2::CENTER_CENTER, + (c_row + 1).to_string(), + egui::FontId::monospace(f32::min(radius, 14.0)), + egui::Color32::from_gray(128), + ); + } + for c_column in 0..columns { + painter.text( + egui::pos2( + start_x + radius + 2.0 * radius * c_column as f32, + start_y - 12.0, + ), + egui::Align2::CENTER_CENTER, + plate_tool_lib::util::num_to_letters(c_column + 1).unwrap(), + egui::FontId::monospace(f32::min(radius, 14.0)), + egui::Color32::from_gray(128), + ); + } } match pf { - PlateFormat::W96 => add_grid_sub(8, 12, ui, state), - PlateFormat::W384 => add_grid_sub(16, 24, ui, state), - PlateFormat::W1536 => add_grid_sub(32, 48, ui, state), + PlateFormat::W96 => add_grid_sub(size, 8, 12, transfers, ui, state), + PlateFormat::W384 => add_grid_sub(size, 16, 24, transfers, ui, state), + PlateFormat::W1536 => add_grid_sub(size, 32, 48, transfers, ui, state), _ => unimplemented!(), } } diff --git a/plate-tool-eframe/src/transfer_menu.rs b/plate-tool-eframe/src/transfer_menu.rs new file mode 100644 index 0000000..9fd3f2a --- /dev/null +++ b/plate-tool-eframe/src/transfer_menu.rs @@ -0,0 +1,67 @@ +use eframe::egui::{self, pos2, Color32, Rounding}; + +pub struct TransferMenuState { + pub transfer_name: String, + pub source_region: String, + pub destination_region: String, + pub source_row_interleave: f32, + pub source_column_interleave: f32, +} + +impl Default for TransferMenuState { + fn default() -> Self { + Self { + transfer_name: String::default(), + source_region: String::default(), + destination_region: String::default(), + source_row_interleave: 1.0, + source_column_interleave: 1.0, + } + } +} + +pub fn transfer_menu(ui: &mut egui::Ui, state: &mut TransferMenuState) { + ui.horizontal(|ui| { + ui.add(egui::Label::new("Name")); + ui.add( + egui::TextEdit::singleline(&mut state.transfer_name) + .hint_text("Transfer Name") + .horizontal_align(egui::Align::Center), + ); + }); + + ui.horizontal(|ui| { + ui.add(egui::Label::new("Source Region")); + ui.add( + egui::TextEdit::singleline(&mut state.source_region) + .hint_text("Source Region") + .char_limit(9) + .horizontal_align(egui::Align::Center), + ); + }); + ui.end_row(); + + ui.horizontal(|ui| { + ui.add(egui::Label::new("Destination Region")); + ui.add( + egui::TextEdit::singleline(&mut state.destination_region) + .hint_text("Destination Region") + .char_limit(9) + .horizontal_align(egui::Align::Center), + ); + }); + + ui.vertical(|ui| { + ui.label("Source Interleave"); + ui.horizontal(|ui| { + ui.label("Row: "); + ui.add(egui::DragValue::new(&mut state.source_row_interleave) + .fixed_decimals(0) + .range(0..=30)); + ui.label("Col: "); + ui.add(egui::DragValue::new(&mut state.source_column_interleave) + .fixed_decimals(0) + .range(0..=30)); + }); + }); +} diff --git a/plate-tool-eframe/src/tree.rs b/plate-tool-eframe/src/tree.rs new file mode 100644 index 0000000..e69de29