use crate::ntree::{NTree, NTreeNode}; use crate::solar_system::body::{BodyId, OrbitalBody}; use crate::solar_system::{SolarSystem, SystemId}; use crate::timeman::TimeMan; #[derive(Default, Clone)] pub struct BodiesWindowState { pub open: bool, selected_body: Option } #[derive(Default, Clone)] pub struct BodiesWindowAction { pub focus_body: Option } impl BodiesWindowState { pub fn paint( &mut self, ui: &mut egui::Ui, current_system: &SolarSystem) -> BodiesWindowAction { let mut action = BodiesWindowAction::default(); let mut bodies_window_open = self.open; egui::Window::new("Bodies") .open(&mut &mut bodies_window_open) .resizable(true) .show(ui.ctx(), |ui| { ui.horizontal(|ui| { self.paint_bodies_list(current_system, ui); self.paint_body_info_panel(&mut action, current_system, ui); }); }); self.open = bodies_window_open; action } fn paint_bodies_node_rec( &self, star_system: &SolarSystem, node: &NTreeNode, ui: &mut egui::Ui) -> Option { let body = star_system.body(*node.value()); let children = node.children(); let selected = self.selected_body.is_some_and(|v| { v == *node.value() }); let mut new_selected = None; if children.is_empty() { if ui.selectable_label(selected, body.name()).clicked() { new_selected = Some(*node.value()); } }else{ egui::collapsing_header::CollapsingState::load_with_default_open( ui.ctx(), ui.make_persistent_id(format!("bodies_window_body_{}", body.id())), true) .show_header(ui, |ui| { if ui.selectable_label(selected, body.name()).clicked() { new_selected = Some(*node.value()); } }) .body(|ui| { for child in children { let child_selected = self.paint_bodies_node_rec(star_system, child, ui); if child_selected.is_some() { new_selected = child_selected; } } }); }; new_selected } fn paint_bodies_list( &mut self, star_system: &SolarSystem, ui: &mut egui::Ui) -> Option { let resp = egui::ScrollArea::vertical() .auto_shrink(true) .min_scrolled_height(200.0) .show(ui, |ui| { ui.vertical(|ui| { let root = star_system.heirarchy(); let new_sel = self.paint_bodies_node_rec(star_system, root, ui); if new_sel.is_some() { self.selected_body = new_sel; } }).response }); Some(resp.inner) } fn paint_body_info_panel( &mut self, action: &mut BodiesWindowAction, star_system: &SolarSystem, ui: &mut egui::Ui) { let selected_body = match self.selected_body { Some(id) => { star_system.body(id) }, None => { return; } }; ui.separator(); ui.vertical(|ui| { egui::Frame::canvas(ui.style()) .show(ui, |ui| { ui.set_width(200.0); ui.vertical_centered(|ui| { ui.label( egui::RichText::new(selected_body.name()) .heading()); ui.separator(); }); ui.vertical(|ui| { ui.style_mut().interaction.selectable_labels = false; egui_extras::TableBuilder::new(ui) .column(egui_extras::Column::auto()) .column(egui_extras::Column::remainder()) .body(|mut body| { body.row(16.0, |mut row| { row.col(|col| { col.label("Mass"); }); row.col(|col| { col.label(format!("{:.4E}", selected_body.mass())); }); }); body.row(16.0, |mut row| { row.col(|col| { col.label("Radius"); }); row.col(|col| { col.label(format!("{:.4E}", selected_body.radius())); }); }); if let Some(orbit) = selected_body.get_orbit() { let parent = star_system.body(orbit.parent()); body.row(16.0, |mut row| { row.col(|col| { col.label("Orbiting"); }); row.col(|col| { col.label(parent.name()); }); }); body.row(16.0, |mut row| { row.col(|col| { col.label("Period"); }); row.col(|col| { col.label(format!("{}", TimeMan::format_duration(orbit.period(selected_body)) )); }); }); body.row(16.0, |mut row| { row.col(|col| { col.label("Semi-major Axis"); }); row.col(|col| { col.label(format!("{:.4E} km", orbit.sma())); }); }); } }); }); }); ui.horizontal(|ui| { let focus_resp = ui.button("Focus"); if focus_resp.clicked() { action.focus_body = Some(selected_body.id()); } }); }); } }