From c1adf64c1aaecd5a2b9d532d707ef35971f1aa18 Mon Sep 17 00:00:00 2001 From: Jon Santmyer Date: Thu, 7 May 2026 08:50:05 -0400 Subject: begin work on body info window --- src/ui/bodies_window.rs | 180 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ui/camera_info.rs | 3 +- src/ui/fleet_window.rs | 80 +++++++++++++++++++++ src/ui/topbar.rs | 151 +++++++++++++++++++++++++--------------- 4 files changed, 355 insertions(+), 59 deletions(-) create mode 100644 src/ui/bodies_window.rs create mode 100644 src/ui/fleet_window.rs (limited to 'src/ui') diff --git a/src/ui/bodies_window.rs b/src/ui/bodies_window.rs new file mode 100644 index 0000000..6300f5f --- /dev/null +++ b/src/ui/bodies_window.rs @@ -0,0 +1,180 @@ +use egui::Sense; + +use crate::eguictx::EguiCtx; +use crate::ntree::{NTree, NTreeNode}; +use crate::solar_system::body::{BodyId, OrbitalBody}; +use crate::solar_system::{SolarSystem, SystemId}; + + +#[derive(Default, Clone)] +pub struct BodiesWindowState +{ + last_system: Option, + system_heirarchy: NTree, + selected_body: Option +} + +#[derive(Default, Clone)] +pub struct BodiesWindowAction +{ + pub focus_body: Option +} + +impl BodiesWindowState +{ + fn build_system_heirarchy_rec( + bodies: &[OrbitalBody], + node: &mut NTreeNode) + { + for body in bodies { + let orbit = body.get_orbit(); + match orbit { + Some(orbit) => { + if orbit.parent() != *node.value() { + continue; + } + }, + None => { + continue; + } + } + let mut subnode = NTreeNode::new(body.id()); + BodiesWindowState::build_system_heirarchy_rec(bodies, &mut subnode); + node.insert_node(subnode); + } + } + + fn rebuild_system_heirarchy( + &mut self, + star_system: &SolarSystem) + { + let mut root_node = NTreeNode::::new(0); + + let bodies = star_system.bodies(); + BodiesWindowState::build_system_heirarchy_rec(bodies, &mut root_node); + self.system_heirarchy.set_root(root_node); + } + + pub fn render( + &mut self, + current_system: &SolarSystem, + eguictx: &EguiCtx) + -> BodiesWindowAction + { + match self.last_system { + Some(last_system) => { + if last_system != current_system.id() { + self.rebuild_system_heirarchy(current_system); + } + }, + None => { + self.rebuild_system_heirarchy(current_system); + } + } + self.last_system = Some(current_system.id()); + + let mut action = BodiesWindowAction::default(); + + egui::Window::new("Bodies") + .resizable(true) + .show(eguictx.context(), |ui| { + + ui.horizontal(|ui| { + self.paint_bodies_list(current_system, ui); + self.paint_body_info_panel(&mut action, current_system, ui); + }); + }); + 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 = self.system_heirarchy.root(); + let new_sel = self.paint_bodies_node_rec(star_system, root.as_ref().unwrap(), 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(); + 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.horizontal(|ui| { + let focus_resp = ui.button("Focus"); + + if focus_resp.clicked() { + action.focus_body = Some(selected_body.id()); + } + }); + }); + } +} diff --git a/src/ui/camera_info.rs b/src/ui/camera_info.rs index 8114f63..5332370 100644 --- a/src/ui/camera_info.rs +++ b/src/ui/camera_info.rs @@ -16,7 +16,7 @@ impl CameraWindowState { pub fn render( ui_state: &mut ui::State, - game_state: &RefCell, + game_state: &GameState, eguictx: &EguiCtx) { let topbar_state = &ui_state.topbar_sate; @@ -25,7 +25,6 @@ impl CameraWindowState } let camera_state = &mut ui_state.camera_info; - let game_state = game_state.borrow(); let current_system = &game_state.solar_systems()[topbar_state.current_system.unwrap()]; diff --git a/src/ui/fleet_window.rs b/src/ui/fleet_window.rs new file mode 100644 index 0000000..67a0dd9 --- /dev/null +++ b/src/ui/fleet_window.rs @@ -0,0 +1,80 @@ +use std::cell::RefCell; + +use crate::{GameState, eguictx::EguiCtx, solar_system::{self, SolarSystem, fleet::Fleet}, ui}; + + +#[derive(Default, Clone)] +pub struct FleetWindowState +{ + +} + +#[derive(Default, Clone)] +pub struct FleetWindowAction +{ + +} + +impl FleetWindowState +{ + pub fn render( + &mut self, + game_state: &GameState, + eguictx: &EguiCtx) + -> FleetWindowAction + { + let mut action = FleetWindowAction::default(); + + let star_systems = game_state.solar_systems(); + + egui::Window::new("Fleet Manager") + .show(eguictx.context(), |ui| { + + ui.horizontal(|ui| { + self.paint_systems_list(star_systems, ui); + ui.add(egui::Separator::default().vertical()); + }); + }); + action + } + + fn paint_systems_list( + &mut self, + star_systems: &[SolarSystem], + ui: &mut egui::Ui) + -> egui::InnerResponse<()> + { + ui.vertical(|ui| { + for system in star_systems { + let resp = self.paint_fleet_list(&system, ui); + if resp.header_response.secondary_clicked() { + resp.header_response.context_menu(|ui| { + + }); + } + } + }) + } + + fn paint_fleet_list( + &mut self, + star_system: &SolarSystem, + ui: &mut egui::Ui) + -> egui::CollapsingResponse<()> + { + let fleets = star_system.fleets(); + ui.collapsing(star_system.name(), |ui| { + for fleet in fleets { + self.paint_fleet_entry(fleet, ui); + } + }) + } + + fn paint_fleet_entry( + &mut self, + fleet: &Fleet, + ui: &mut egui::Ui) + { + ui.label(fleet.name()); + } +} diff --git a/src/ui/topbar.rs b/src/ui/topbar.rs index 296a84e..a5d92c5 100644 --- a/src/ui/topbar.rs +++ b/src/ui/topbar.rs @@ -7,84 +7,64 @@ pub struct TopBarState { pub current_system: Option, pub auto_tick: Option, - pub do_auto_tick: bool + pub do_auto_tick: bool, + + pub bodies_window_visible: bool, + pub fleet_window_visible: bool, +} + +#[derive(Default, Clone)] +pub struct TopBarAction +{ + pub advance_tick: Option, } impl TopBarState { pub fn render( - state: &mut TopBarState, - game_state: &RefCell, + &mut self, + game_state: &GameState, eguictx: &EguiCtx) + -> TopBarAction { - let mut game_state = game_state.borrow_mut(); + let mut action: TopBarAction = TopBarAction::default(); + + let solar_systems = game_state.solar_systems(); + let timeman = game_state.timeman(); egui::TopBottomPanel::top("topbar").show( eguictx.context(), |ui| { ui.horizontal(|ui| { - let solar_systems = game_state.solar_systems(); - let selected_system_label = match state.current_system { - Some(id) => solar_systems[id].name(), - None => "" - }; - - egui::ComboBox::from_label("Current System") - .selected_text(selected_system_label) - .show_ui(ui, |ui| { - - for (i, system) in solar_systems.iter().enumerate() { - ui.selectable_value( - &mut state.current_system, - Some(i), - system.name() - ); - } - }); - - ui.separator(); - - let button_seconds = [ - 1, - 5, - 30, - timeman::MINUTE, - timeman::MINUTE * 5, - timeman::MINUTE * 30, - timeman::HOUR, - timeman::DAY, - timeman::DAY * 5, - timeman::YEAR - ]; - let selected_button = state.auto_tick; - - let timeman = game_state.timeman_mut(); ui.vertical(|ui| { - ui.label("Manual"); - ui.checkbox(&mut state.do_auto_tick, "Auto"); - }); - - button_seconds.iter().for_each(|&seconds| { - ui.vertical(|ui| { - let auto_selected = match selected_button { - Some(o) => o == seconds, - None => false - }; - let label = TimeMan::format_duration(seconds); + let selected_system_label = match self.current_system { + Some(id) => solar_systems[id].name(), + None => "" + }; - if ui.button(label.clone()).clicked() { - timeman.advance(seconds); + egui::ComboBox::from_label("Current System") + .selected_text(selected_system_label) + .show_ui(ui, |ui| { + + for (i, system) in solar_systems.iter().enumerate() { + ui.selectable_value( + &mut self.current_system, + Some(i), + system.name() + ); } + }); - if ui.add(egui::Button::new(label.clone()).selected(auto_selected)).clicked() { - state.auto_tick = Some(seconds); - } + ui.horizontal(|ui| { + self.paint_empire_buttons(ui); }); }); + + self.paint_tick_buttons(&mut action, timeman, ui); + }); ui.vertical_centered_justified(|ui| { - let timeman = game_state.timeman_mut(); let time_str = TimeMan::format_duration(timeman.seconds()); ui.label( egui::RichText::new(time_str) @@ -92,5 +72,62 @@ impl TopBarState ); }); }); + action + } + + pub fn paint_empire_buttons( + &mut self, + ui: &mut egui::Ui) + { + if ui.add(egui::Button::new("Bodies").selected(self.bodies_window_visible)).clicked() { + self.bodies_window_visible = !self.bodies_window_visible; + } + if ui.add(egui::Button::new("Fleets").selected(self.fleet_window_visible)).clicked() { + self.fleet_window_visible = !self.fleet_window_visible; + } + } + + pub fn paint_tick_buttons( + &mut self, + action: &mut TopBarAction, + timeman: &TimeMan, + ui: &mut egui::Ui) + { + let button_seconds = [ + 1, + 5, + 30, + timeman::MINUTE, + timeman::MINUTE * 5, + timeman::MINUTE * 30, + timeman::HOUR, + timeman::DAY, + timeman::DAY * 5, + timeman::YEAR + ]; + let selected_button = self.auto_tick; + + ui.vertical(|ui| { + ui.label("Manual"); + ui.checkbox(&mut self.do_auto_tick, "Auto"); + }); + + button_seconds.iter().for_each(|&seconds| { + ui.vertical(|ui| { + let auto_selected = match selected_button { + Some(o) => o == seconds, + None => false + }; + let label = TimeMan::format_duration(seconds); + + if ui.button(label.clone()).clicked() { + action.advance_tick = Some(seconds); + } + + if ui.add(egui::Button::new(label.clone()).selected(auto_selected)).clicked() { + self.auto_tick = Some(seconds); + } + }); + }); } } -- cgit v1.2.3