summaryrefslogtreecommitdiffstats
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/bodies_window.rs174
-rw-r--r--src/ui/contact.rs100
-rw-r--r--src/ui/contacts_widget.rs146
-rw-r--r--src/ui/current_system_widget.rs47
-rw-r--r--src/ui/fleet_schedule.rs150
-rw-r--r--src/ui/fleet_window.rs314
-rw-r--r--src/ui/fleets_widget.rs246
-rw-r--r--src/ui/schedule_widget.rs6
-rw-r--r--src/ui/time_control_widget.rs89
-rw-r--r--src/ui/topbar.rs136
10 files changed, 634 insertions, 774 deletions
diff --git a/src/ui/bodies_window.rs b/src/ui/bodies_window.rs
deleted file mode 100644
index a93f11c..0000000
--- a/src/ui/bodies_window.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-use crate::ntree::NTreeNode;
-use crate::solar_system::body::BodyId;
-use crate::solar_system::SolarSystem;
-use crate::timeman::TimeMan;
-
-
-#[derive(Default, Clone)]
-pub struct BodiesWindowState
-{
- pub open: bool,
- selected_body: Option<BodyId>
-}
-
-#[derive(Default, Clone)]
-pub struct BodiesWindowAction
-{
- pub focus_body: Option<BodyId>
-}
-
-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<BodyId>,
- ui: &mut egui::Ui)
- -> Option<BodyId>
- {
- 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<egui::Response>
- {
- let resp = egui::ScrollArea::vertical()
- .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());
- }
- });
- });
- }
-}
diff --git a/src/ui/contact.rs b/src/ui/contact.rs
new file mode 100644
index 0000000..e525541
--- /dev/null
+++ b/src/ui/contact.rs
@@ -0,0 +1,100 @@
+use crate::body::BodyId;
+use crate::fleet::{Fleet, FleetId, FleetsManager};
+use crate::solar_system::{Kilometers, SolarSystem};
+use crate::solar_system::body::OrbitalBody;
+use crate::timeman::Second;
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum ContactType
+{
+ Body,
+ Fleet
+}
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct MapContact
+{
+ obj_type: ContactType,
+ id: usize
+}
+
+impl MapContact
+{
+ pub fn from_body(
+ id: BodyId)
+ -> Self {
+ Self {
+ obj_type: ContactType::Body,
+ id: id
+ }
+ }
+
+ pub fn from_fleet(
+ id: FleetId)
+ -> Self {
+ Self {
+ obj_type: ContactType::Fleet,
+ id: id
+ }
+ }
+
+ pub fn body(&self)
+ -> Option<BodyId>
+ {
+ match self.obj_type {
+ ContactType::Body => Some(self.id),
+ _ => None
+ }
+ }
+
+ pub fn fleet(&self)
+ -> Option<FleetId>
+ {
+ match self.obj_type {
+ ContactType::Fleet => Some(self.id),
+ _ => None
+ }
+ }
+
+ pub fn name<'ss>(
+ &self,
+ star_system: &'ss SolarSystem,
+ fleets_man: &'ss FleetsManager)
+ -> &'ss String
+ {
+ match self.obj_type {
+ ContactType::Body => star_system.body(self.id).name(),
+ ContactType::Fleet => fleets_man.fleet(self.id).unwrap().name()
+ }
+ }
+
+ pub fn origin_position<'ss>(
+ &self,
+ star_system: &'ss SolarSystem,
+ fleets_man: &'ss FleetsManager,
+ time: Second)
+ -> cgmath::Vector3<Kilometers>
+ {
+ match self.obj_type {
+ ContactType::Body =>
+ star_system.body(self.id).origin_position(star_system, time),
+ ContactType::Fleet =>
+ *fleets_man.fleet(self.id).unwrap().origin_position()
+ }
+ }
+
+ pub fn offset_position<'ss>(
+ &self,
+ star_system: &'ss SolarSystem,
+ fleets_man: &'ss FleetsManager,
+ time: Second)
+ -> cgmath::Vector3<Kilometers>
+ {
+ match self.obj_type {
+ ContactType::Body =>
+ star_system.body(self.id).offset_position(time),
+ ContactType::Fleet =>
+ *fleets_man.fleet(self.id).unwrap().offset_position()
+ }
+ }
+}
diff --git a/src/ui/contacts_widget.rs b/src/ui/contacts_widget.rs
new file mode 100644
index 0000000..70b046c
--- /dev/null
+++ b/src/ui/contacts_widget.rs
@@ -0,0 +1,146 @@
+use crate::solar_system::SolarSystem;
+use crate::fleet::FleetsManager;
+use crate::ui::contact::MapContact;
+
+pub struct ContactsListWidget<'f>
+{
+ contacts: Vec<(MapContact, &'f String)>,
+ selected: &'f mut Option<MapContact>
+}
+
+pub struct FocusedContactWidget<'f>
+{
+ contact: Option<MapContact>,
+ star_system: &'f SolarSystem,
+ fleets_man: &'f FleetsManager
+}
+
+impl<'f> ContactsListWidget<'f>
+{
+ pub fn new(
+ selected: &'f mut Option<MapContact>,
+ star_system: &'f SolarSystem,
+ fleets_man: &'f FleetsManager)
+ -> Self {
+ let system_fleets = fleets_man.all_in_system(star_system.id());
+ let system_bodies = star_system.bodies();
+
+ let mut contacts = system_fleets.iter().map(|id| {
+ let fleet = fleets_man.fleet(*id);
+ (MapContact::from_fleet(*id), fleet.unwrap().name())
+ }).collect::<Vec<_>>();
+ contacts.extend(system_bodies.iter().map(|body| {
+ (MapContact::from_body(body.id()), body.name())
+ }));
+
+ Self {
+ contacts: contacts,
+ selected
+ }
+ }
+
+ pub fn show_ui(
+ mut self,
+ ui: &mut egui::Ui)
+ -> egui::InnerResponse<Option<MapContact>>
+ {
+ self.show_entries_box(ui)
+ }
+
+ fn show_entries_box(
+ &mut self,
+ ui: &mut egui::Ui)
+ -> egui::InnerResponse<Option<MapContact>>
+ {
+ let mut ret = None;
+ egui::Frame::default()
+ .stroke(egui::Stroke::NONE)
+ .fill(egui::Color32::from_rgb(48, 48, 48))
+ .outer_margin(egui::Margin::same(4))
+ .inner_margin(egui::Margin::same(4))
+ .show(ui, |frame| {
+ frame.take_available_width();
+ frame.set_max_height(frame.available_height() / 2.0);
+
+ frame.vertical(|vertical| {
+ let mut table = egui_extras::TableBuilder::new(vertical)
+ .striped(true)
+ .resizable(false)
+ .auto_shrink(false)
+ .column(egui_extras::Column::remainder())
+ .body(
+ |body| {
+ let row_height = 20.0;
+ body.rows(row_height, self.contacts.len(), |mut row| {
+ let resp = self.show_contact(&mut row);
+ if resp.is_some() {
+ ret = resp;
+ }
+ });
+ });
+ });
+ ret
+ })
+ }
+
+ fn show_contact(
+ &mut self,
+ row: &mut egui_extras::TableRow)
+ -> Option<MapContact>
+ {
+ let contact = &self.contacts[row.index()];
+ let selected = self.selected.is_some_and(|sel| { sel == contact.0 });
+
+ let mut ret = None;
+ row.col(|col| {
+ let resp = col.selectable_label(selected, contact.1);
+
+ if resp.double_clicked() {
+ ret = Some(contact.0);
+ }else if resp.clicked() {
+ *self.selected = match selected {
+ true => None,
+ false => Some(contact.0)
+ };
+ }
+ });
+ ret
+ }
+}
+
+impl<'f> FocusedContactWidget<'f>
+{
+ pub fn new(
+ contact: Option<MapContact>,
+ star_system: &'f SolarSystem,
+ fleets_man: &'f FleetsManager)
+ -> Self {
+ Self {
+ contact,
+ star_system,
+ fleets_man
+ }
+ }
+}
+
+impl egui::Widget for FocusedContactWidget<'_>
+{
+ fn ui(self, ui: &mut egui::Ui) -> egui::Response {
+ ui.vertical(|vertical| {
+ vertical.label("Focused contact");
+ vertical.shrink_width_to_current();
+ egui::Frame::new()
+ .fill(egui::Color32::from_gray(48))
+ .stroke(egui::Stroke::NONE)
+ .inner_margin(egui::Margin::same(2))
+ .show(vertical,
+ |frame| {
+ frame.take_available_width();
+ match self.contact {
+ Some(contact) => frame.label(contact.name(self.star_system, self.fleets_man)),
+ None => frame.label("None")
+ }
+ }).inner
+ }).inner
+ }
+}
diff --git a/src/ui/current_system_widget.rs b/src/ui/current_system_widget.rs
new file mode 100644
index 0000000..f7aa940
--- /dev/null
+++ b/src/ui/current_system_widget.rs
@@ -0,0 +1,47 @@
+use crate::solar_system::{SolarSystem, SystemId};
+
+pub struct CurrentSystemWidget<'frame>
+{
+ star_systems: &'frame [SolarSystem],
+ current_system: &'frame mut Option<SystemId>,
+}
+
+impl<'frame> CurrentSystemWidget<'frame> {
+ pub fn new(
+ star_systems: &'frame [SolarSystem],
+ current_system: &'frame mut Option<SystemId>)
+ -> Self {
+ Self {
+ star_systems,
+ current_system
+ }
+ }
+}
+
+impl<'frame> egui::Widget for CurrentSystemWidget<'frame>
+{
+ fn ui(self, ui: &mut egui::Ui) -> egui::Response {
+ let selected_label = match self.current_system {
+ Some(id) => &self.star_systems[*id].name(),
+ None => "Click to open"
+ };
+
+ ui.vertical(|vertical| {
+ vertical.label("Current System");
+ let resp = egui::ComboBox::new("current_system", egui::WidgetText::default())
+ .selected_text(selected_label)
+ .show_ui(vertical,
+ |combo_box| {
+ for system in self.star_systems.iter() {
+ let entry_value = Some(system.id());
+ combo_box.selectable_value(
+ self.current_system,
+ entry_value,
+ system.name());
+ }
+ });
+
+ resp.response
+ }).inner
+ }
+}
diff --git a/src/ui/fleet_schedule.rs b/src/ui/fleet_schedule.rs
deleted file mode 100644
index b0e5dd7..0000000
--- a/src/ui/fleet_schedule.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use crate::solar_system::SolarSystem;
-use crate::solar_system::body::BodyId;
-use crate::fleet::{Fleet, FleetId, FleetsManager};
-
-#[derive(Clone)]
-pub struct FleetScheduleWindow
-{
- pub target: (Option<BodyId>, Option<FleetId>),
- pub linear_acceleration_limit: f32,
- pub angular_acceleration_limit: f32,
-}
-
-impl Default for FleetScheduleWindow
-{
- fn default() -> Self {
- Self {
- target: Default::default(),
- linear_acceleration_limit: 9.81,
- angular_acceleration_limit: 15.0
- }
- }
-}
-
-impl FleetScheduleWindow
-{
- pub fn paint(
- &mut self,
- star_systems: &[SolarSystem],
- fleets_man: &FleetsManager,
- fleet: &Fleet,
- ui: &mut egui::Ui)
- {
- ui.horizontal(|limits_ui| {
- limits_ui.vertical(|linear_limit_ui| {
- linear_limit_ui.label("Linear acceleration limit");
- linear_limit_ui.add(
- egui::DragValue::new(&mut self.linear_acceleration_limit)
- .range(0.0..=(9.8*2.0))
- .clamp_existing_to_range(true)
- .suffix(" m/s^2")
- .speed(0.1)
- .max_decimals_opt(Some(2))
- );
- });
- limits_ui.separator();
- limits_ui.vertical(|angular_limit_ui| {
- angular_limit_ui.label("Angular acceleration limit");
- angular_limit_ui.add(
- egui::DragValue::new(&mut self.angular_acceleration_limit)
- .range(0.0..=30.0)
- .clamp_existing_to_range(true)
- .suffix(" °/s^2")
- .speed(0.1)
- .max_decimals_opt(Some(2))
- );
- });
- });
- ui.separator();
-
- let fleet_system = &star_systems[fleet.system().unwrap_or(0)];
- let bodies = fleet_system.bodies();
- let bodies_order = fleet_system.heirarchy().traverse_preorder();
- let system_fleets = fleet_system.fleets(fleets_man);
-
- let row_height = ui.spacing().interact_size.y;
- ui.horizontal(|panel_ui|
- {
- panel_ui.vertical(|vertical_ui| {
- vertical_ui.set_max_width(128.0);
- vertical_ui.vertical_centered(|centered_ui| {
- centered_ui.label("Target");
- });
- egui::ScrollArea::vertical()
- .id_salt("fleet_schedule_objs")
- .auto_shrink(false)
- .min_scrolled_height(256.0)
- .show_rows(vertical_ui, row_height, bodies.len() + system_fleets.len(),
- |objs_ui, rows| {
- let fleet_rows_start = 0;
- let bodies_rows_start = system_fleets.len();
-
- for row in rows {
- match row < bodies_rows_start {
- true => {
- let id = system_fleets[row - fleet_rows_start];
- let selected = self.target.1
- .is_some_and(|v| { v == id });
- let fleet = fleets_man.fleet(id);
- if let Some(fleet) = fleet {
- if objs_ui.selectable_label(selected, fleet.name()).clicked() {
- self.select_fleet_from_schedule_objs(id, selected);
- }
- }
- },
- false => {
- let id = *bodies_order[row - bodies_rows_start];
- let selected = self.target.0
- .is_some_and(|v| { v == id });
- let body = &bodies[id];
- if objs_ui.selectable_label(selected, body.name()).clicked() {
- self.select_body_from_schedule_objs(id, selected);
- }
- }
- }
- }
- });
- });
-
- panel_ui.vertical(|vertical_ui| {
- vertical_ui.set_max_width(128.0);
- vertical_ui.vertical_centered(|centered_ui| {
- centered_ui.label("Commands");
- });
- egui::ScrollArea::vertical()
- .id_salt("fleet_schedule_commands")
- .auto_shrink(false)
- .min_scrolled_height(256.0)
- .show_rows(vertical_ui, row_height, 0,
- |cmd_ui, rows| {
-
- });
- });
-
- panel_ui.vertical(|vertical_ui| {
- vertical_ui.set_max_width(128.0);
- vertical_ui.vertical_centered(|centered_ui| {
- centered_ui.label("Schedule");
- });
- });
- });
- }
-
- fn select_body_from_schedule_objs(
- &mut self,
- id: BodyId,
- selected: bool)
- {
- self.target =
- (if selected { None } else { Some(id) }, None);
- }
-
- fn select_fleet_from_schedule_objs(
- &mut self,
- id: FleetId,
- selected: bool)
- {
- self.target =
- (None, if selected { None } else { Some(id) });
- }
-}
diff --git a/src/ui/fleet_window.rs b/src/ui/fleet_window.rs
deleted file mode 100644
index 4db4ce8..0000000
--- a/src/ui/fleet_window.rs
+++ /dev/null
@@ -1,314 +0,0 @@
-use crate::GameState;
-use crate::fleet::{Fleet, FleetId, FleetsManager};
-use crate::solar_system::orbit::StaticOrbiter;
-use crate::solar_system::{Kilometers, SolarSystem, SystemId};
-use crate::solar_system::body::BodyId;
-use crate::ui::fleet_schedule::FleetScheduleWindow;
-
-#[derive(Default, Clone, PartialEq)]
-enum FleetMenuPanelSel {
- #[default]
- Info,
- Schedule
-}
-
-#[derive(Default, Clone)]
-pub struct FleetWindowState
-{
- pub open: bool,
- pub selected_fleet: Option<FleetId>,
-
- menu_panel: FleetMenuPanelSel,
- fleet_schedule_window: FleetScheduleWindow,
-
- pub new_fleet_window: Option<NewFleetWindowState>
-}
-
-#[derive(Default, Clone)]
-pub struct FleetWindowAction
-{
- pub new_fleet: Option<NewFleet>
-}
-
-#[derive(Default, Clone)]
-pub struct NewFleetWindowState
-{
- pub open: bool,
- pub parent_system: SystemId,
- pub parent_body: BodyId,
-
- pub fleet_name: String,
- pub fleet_sma: Kilometers
-}
-
-#[derive(Clone)]
-pub struct NewFleet
-{
- pub system: SystemId,
- pub orbiting: BodyId,
- pub name: String,
- pub sma: Kilometers
-}
-
-impl FleetWindowState
-{
- pub fn paint(
- &mut self,
- ui: &mut egui::Ui,
- game_state: &GameState,
- focused_system: &Option<SystemId>,
- focused_body: &Option<BodyId>)
- -> FleetWindowAction
- {
- let mut action = FleetWindowAction::default();
-
- let star_systems = game_state.solar_systems();
- let fleets_manager = game_state.fleets();
-
- let mut mgr_open = self.open;
- egui::Window::new("Fleet Manager")
- .open(&mut mgr_open)
- .show(ui.ctx(), |ui| {
-
- ui.vertical(|ui| {
- ui.horizontal(|ui| {
- self.paint_fleet_list(fleets_manager, ui);
- self.paint_fleet_menu(star_systems, fleets_manager, ui);
- });
-
- if focused_system.is_some() {
- if ui.button("New Fleet").clicked() {
- if self.new_fleet_window.is_none() {
- self.new_fleet_window = Some(NewFleetWindowState::new(
- focused_system.unwrap(),
- focused_body.unwrap_or(0)
- ));
- }
- }
- }
- });
- });
-
- match &mut self.new_fleet_window {
- Some(new_fleet_window) => {
- action.new_fleet = new_fleet_window.paint(ui, game_state);
- if action.new_fleet.is_some() || !new_fleet_window.open {
- self.new_fleet_window = None;
- }
- }
- None => {}
- }
-
- self.open = mgr_open;
- action
- }
-
- fn paint_fleet_menu(
- &mut self,
- star_systems: &[SolarSystem],
- fleets_man: &FleetsManager,
- ui: &mut egui::Ui)
- {
- let fleet = match self.selected_fleet {
- Some(id) => { fleets_man.fleet(id) },
- None => { return; }
- };
- let fleet = match fleet {
- Some(entry) => entry,
- None => {
- self.selected_fleet = None;
- return;
- }
- };
-
- ui.separator();
- ui.vertical(|ui| {
- ui.horizontal(|ui| {
- if ui.button("Info").clicked() {
- self.menu_panel = FleetMenuPanelSel::Info;
- }
- if ui.button("Schedule").clicked() {
- self.menu_panel = FleetMenuPanelSel::Schedule;
- }
- });
-
- egui::Frame::new()
- .stroke(egui::Stroke::new(1.0, egui::Color32::LIGHT_GRAY))
- .show(ui, |ui| {
- ui.set_min_width(200.0);
- ui.set_min_height(200.0);
- ui.style_mut().interaction.selectable_labels = false;
-
- match self.menu_panel {
- FleetMenuPanelSel::Info =>
- self.paint_fleet_menu_info(star_systems, fleets_man, fleet, ui),
- FleetMenuPanelSel::Schedule =>
- self.fleet_schedule_window.paint(star_systems, fleets_man, fleet, ui)
- };
- });
- });
- }
-
- fn paint_fleet_menu_info(
- &mut self,
- star_systems: &[SolarSystem],
- _fleets_man: &FleetsManager,
- fleet: &Fleet,
- ui: &mut egui::Ui)
- {
- if self.menu_panel != FleetMenuPanelSel::Info {
- return;
- }
-
- if let Some(orbit) = fleet.orbit() {
- let star_system = &star_systems[orbit.system()];
- let parent = star_system.body(orbit.parent());
-
- ui.push_id("orbital_info_table", |ui| {
- 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("System"); });
- row.col(|col| { col.label(star_system.name()); });
- });
- 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("Radius"); });
- row.col(|col| { col.label(format!("{:.1}", orbit.sma())); });
- });
- });
- });
-
- ui.separator();
- }
-
- let heading = fleet.heading();
- 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("Heading"); });
- row.col(|col| {
- col.label(format!("{:?} by {:?}",
- cgmath::Deg::<f32>::from(heading.x),
- cgmath::Deg::<f32>::from(heading.y)
- ));
- });
- });
- });
- }
-
- fn paint_fleet_list(
- &mut self,
- fleets_man: &FleetsManager,
- ui: &mut egui::Ui)
- {
- let fleet_ids = fleets_man.all();
- ui.vertical(|ui| {
- for id in fleet_ids {
- if let Some(fleet) = fleets_man.fleet(id) {
- self.paint_fleet_entry(fleet, ui);
- }
- }
- });
- }
-
- fn paint_fleet_entry(
- &mut self,
- fleet: &Fleet,
- ui: &mut egui::Ui)
- {
- let selected = self.selected_fleet.is_some_and(|id| { id == fleet.id() });
- if ui.selectable_label(selected, fleet.name()).clicked() {
- self.select_fleet_from_entries_list(fleet, selected);
- }
- }
-
- fn select_fleet_from_entries_list(
- &mut self,
- fleet: &Fleet,
- selected: bool)
- {
- self.selected_fleet = if selected { None } else { Some(fleet.id()) };
- self.fleet_schedule_window.target = (None, None);
- }
-} // FleetWindowAction
-
-impl NewFleetWindowState
-{
- pub fn new(
- parent_system: SystemId,
- parent_body: BodyId)
- -> Self {
- Self {
- open: true,
- parent_system: parent_system,
- parent_body: parent_body,
- ..Default::default()
- }
- }
-
- pub fn paint(
- &mut self,
- ui: &mut egui::Ui,
- game_state: &GameState,)
- -> Option<NewFleet>
- {
- let solar_system = &game_state.solar_systems()[self.parent_system];
- let orbiting = solar_system.body(self.parent_body);
-
- match egui::Window::new("New Fleet")
- .collapsible(false)
- .open(&mut self.open)
- .show(ui.ctx(), |ui| {
- ui.vertical(|ui| {
- ui.horizontal(|ui| {
- ui.label("Name: ");
- ui.add(egui::TextEdit::singleline(&mut self.fleet_name));
- });
- ui.label(format!("Orbiting: {}", orbiting.name()));
- ui.horizontal(|ui| {
- ui.label("SMA: ");
- ui.add(egui::DragValue::new(&mut self.fleet_sma)
- .range(orbiting.radius()..=f32::INFINITY)
- .clamp_existing_to_range(true)
- .suffix("km"));
- });
- if ui.button("Create").clicked() {
- return Some(NewFleet {
- system: self.parent_system,
- orbiting: self.parent_body,
- name: self.fleet_name.clone(),
- sma: self.fleet_sma,
- });
- }
- None
- }).inner
- }) {
- Some(resp) => {
- resp.inner?
- },
- None => None
- }
- }
-} // impl FleetWindowState
-
-impl Default for NewFleet
-{
- fn default() -> Self {
- Self {
- system: Default::default(),
- orbiting: Default::default(),
- name: "New Fleet".to_string(),
- sma: Default::default()
- }
- }
-} // impl NewFleet
-
-
diff --git a/src/ui/fleets_widget.rs b/src/ui/fleets_widget.rs
new file mode 100644
index 0000000..b31a04b
--- /dev/null
+++ b/src/ui/fleets_widget.rs
@@ -0,0 +1,246 @@
+use std::str::FromStr;
+
+use crate::GameState;
+use crate::fleet::FleetsManager;
+use crate::solar_system::body::{BodyId, OrbitalBody};
+use crate::solar_system::{Kilometers, SolarSystem, SystemId};
+use crate::ui::contact::MapContact;
+
+pub struct FleetsListWidget<'f>
+{
+ fleets_man: &'f FleetsManager,
+ selected: &'f mut Option<MapContact>,
+}
+
+pub struct FleetsListResponse
+{
+ pub response: egui::Response,
+ pub refocus: Option<(SystemId, MapContact)>
+}
+
+pub struct FleetsControlPanel
+{
+ new_fleet_modal: Option<NewFleetModal>
+}
+
+pub struct FleetsControlResponse
+{
+ pub new_fleet: Option<NewFleetModal>
+}
+
+#[derive(Clone)]
+pub struct NewFleetModal
+{
+ pub name: String,
+ pub sma: Kilometers,
+}
+
+impl<'f> FleetsListWidget<'f>
+{
+ pub fn new(
+ fleets_man: &'f FleetsManager,
+ selected: &'f mut Option<MapContact>)
+ -> Self
+ {
+ Self {
+ fleets_man,
+ selected
+ }
+ }
+
+ pub fn show_ui(
+ mut self,
+ ui: &mut egui::Ui)
+ -> FleetsListResponse
+ {
+ let frame_resp = egui::Frame::new()
+ .fill(egui::Color32::from_gray(48))
+ .outer_margin(egui::Margin::same(4))
+ .inner_margin(egui::Margin::same(4))
+ .show(ui,
+ |frame| {
+ frame.set_max_height(frame.available_height() / 2.0);
+ self.show_fleets_list(frame)
+ });
+
+ FleetsListResponse {
+ response: frame_resp.response,
+ refocus: frame_resp.inner
+ }
+ }
+
+ fn show_fleets_list(
+ &mut self,
+ ui: &mut egui::Ui)
+ -> Option<(SystemId, MapContact)>
+ {
+ egui::ScrollArea::vertical()
+ .auto_shrink(false)
+ .show(ui,
+ |scrollarea| {
+ let mut refocus: Option<(SystemId, MapContact)> = None;
+ for id in self.fleets_man.all() {
+ let fleet = match self.fleets_man.fleet(id) {
+ Some(v) => v,
+ None => { continue; }
+ };
+ let contact = MapContact::from_fleet(id);
+ let selected = self.selected.is_some_and(|v| { v == contact });
+ let resp = scrollarea.selectable_label(selected, fleet.name());
+
+ if resp.double_clicked() {
+ refocus = Some((fleet.system(), contact));
+ }else if resp.clicked() {
+ *self.selected = if selected { None } else { Some(contact) };
+ }
+ }
+ refocus
+ }).inner
+ }
+} // impl FleetsListWidget
+
+impl FleetsControlPanel
+{
+ pub fn show(
+ &mut self,
+ selected_contact: &Option<MapContact>,
+ fleets_man: &FleetsManager,
+ star_system: &SolarSystem,
+ ui: &mut egui::Ui)
+ -> FleetsControlResponse
+ {
+ let mut response = FleetsControlResponse {
+ new_fleet: None
+ };
+
+ egui::Frame::new()
+ .inner_margin(egui::Margin::same(4))
+ .outer_margin(egui::Margin::same(4))
+ .fill(egui::Color32::from_gray(48))
+ .show(ui,
+ |frame| {
+ frame.horizontal(|horizontal| {
+ horizontal.take_available_width();
+ if horizontal.button("New Fleet").clicked() {
+ self.new_fleet_modal = Some(NewFleetModal::default());
+ }
+ });
+ });
+
+ if let Some(new_fleet_modal) = &mut self.new_fleet_modal {
+ let modal = new_fleet_modal.show(
+ selected_contact,
+ fleets_man,
+ star_system,
+ ui.ctx());
+
+ if modal.inner {
+ response.new_fleet = self.new_fleet_modal.take();
+ }
+
+ if modal.should_close() {
+ self.new_fleet_modal = None;
+ }
+ }
+
+ response
+ }
+}
+
+impl Default for FleetsControlPanel
+{
+ fn default()
+ -> Self
+ {
+ Self {
+ new_fleet_modal: None
+ }
+ }
+}
+
+impl NewFleetModal
+{
+ pub fn show(
+ &mut self,
+ selected_contact: &Option<MapContact>,
+ fleets_man: &FleetsManager,
+ star_system: &SolarSystem,
+ ctx: &egui::Context)
+ -> egui::ModalResponse<bool>
+ {
+ let sma_min = match selected_contact.unwrap_or(MapContact::from_body(0)).body() {
+ Some(id) => star_system.body(id).radius(),
+ None => 0.0
+ };
+
+ egui::Modal::new(egui::Id::new("NewFleetModal")).show(ctx, |ui| {
+ ui.set_width(256.0);
+ ui.heading("Create new debug fleet");
+
+ ui.horizontal(|ui| {
+ ui.label("Name: ");
+ ui.text_edit_singleline(&mut self.name);
+ });
+
+ ui.horizontal(|ui| {
+ ui.label("Orbiting: ");
+ ui.label(selected_contact.unwrap_or(MapContact::from_body(0)).name(star_system, fleets_man));
+ ui.label(" above ");
+ ui.add(
+ egui::DragValue::new(&mut self.sma).range(sma_min..=f32::MAX).suffix("km")
+ );
+ });
+
+ if ui.button("Create").clicked() {
+ ui.close();
+ return true;
+ }
+ false
+ })
+ }
+}
+
+impl Default for NewFleetModal
+{
+ fn default()
+ -> Self
+ {
+ Self {
+ name: String::from_str("New Fleet").unwrap(),
+ sma: 0.0
+ }
+ }
+}
+
+impl GameState
+{
+ pub fn new_fleet_from_modal(
+ &mut self,
+ modal: NewFleetModal,
+ star_system: SystemId,
+ contact: Option<MapContact>)
+ {
+ let contact = contact.unwrap_or(MapContact::from_body(0));
+ let star_system = &self.solar_systems[star_system];
+ let body = match contact.body() {
+ Some(id) => id,
+ None => 0
+ };
+ self.fleets.new_fleet_from_modal(modal, star_system, body);
+ }
+}
+
+impl FleetsManager
+{
+ pub fn new_fleet_from_modal(
+ &mut self,
+ modal: NewFleetModal,
+ star_system: &SolarSystem,
+ body: BodyId)
+ {
+ let id = self.new_fleet(modal.name, star_system.id());
+ let fleet = self.fleet_mut(id).unwrap();
+
+ fleet.make_orbit(star_system, body, modal.sma);
+ }
+}
diff --git a/src/ui/schedule_widget.rs b/src/ui/schedule_widget.rs
new file mode 100644
index 0000000..7a0fb25
--- /dev/null
+++ b/src/ui/schedule_widget.rs
@@ -0,0 +1,6 @@
+pub struct ScheduleWidget
+{
+
+}
+
+
diff --git a/src/ui/time_control_widget.rs b/src/ui/time_control_widget.rs
new file mode 100644
index 0000000..a534998
--- /dev/null
+++ b/src/ui/time_control_widget.rs
@@ -0,0 +1,89 @@
+use std::hash::Hash;
+
+use crate::timeman::{self, Second};
+
+pub struct TimeControlWidget<'f>
+{
+ manual_tick: &'f mut Second,
+ auto_tick: &'f mut (bool, Second)
+}
+
+impl<'f> TimeControlWidget<'f>
+{
+ pub fn new(
+ manual_tick: &'f mut Second,
+ auto_tick: &'f mut (bool, Second))
+ -> Self
+ {
+ Self {
+ manual_tick,
+ auto_tick
+ }
+ }
+
+ pub fn show_ui(mut self,
+ ui: &mut egui::Ui)
+ -> egui::InnerResponse<Option<Second>>
+ {
+ egui::Frame::new().show(ui, |frame| {
+ frame.set_max_width(224.0);
+ frame.vertical(|vertical| {
+ let ret = self.show_manual(vertical);
+ ret
+ }).inner
+ })
+ }
+
+ const TICK_OPTIONS: [(&'static str, Second);10] = [
+ ("1 second", 1),
+ ("5 seconds", 5),
+ ("30 seconds", 30),
+ ("1 minute", timeman::MINUTE),
+ ("5 minutes", 5 * timeman::MINUTE),
+ ("30 minutes", 30 * timeman::MINUTE),
+ ("1 hour", timeman::HOUR),
+ ("1 day", timeman::DAY),
+ ("5 days", 5 * timeman::DAY),
+ ("30 days", 30 * timeman::DAY)
+ ];
+
+ fn tick_selector_combobox<T: Hash>(
+ ui: &mut egui::Ui,
+ id_salt: T,
+ selected: Second)
+ -> Second
+ {
+ let selected_value = Self::TICK_OPTIONS.iter().find(|v| { v.1 == selected }).unwrap();
+ egui::ComboBox::new(id_salt, "")
+ .selected_text(selected_value.0)
+ .show_ui(ui,
+ |combobox| {
+ let mut ret = selected;
+ for entry in Self::TICK_OPTIONS {
+ combobox.selectable_value(&mut ret, entry.1, entry.0);
+ }
+ ret
+ }).inner.unwrap_or(selected)
+ }
+
+ fn show_manual(&mut self,
+ ui: &mut egui::Ui)
+ -> Option<Second>
+ {
+
+ ui.horizontal(|horizontal| {
+ horizontal.checkbox(&mut self.auto_tick.0, "Auto Advance ");
+ self.auto_tick.1 = Self::tick_selector_combobox(horizontal, "auto-tick-combobox", self.auto_tick.1);
+ });
+ ui.shrink_width_to_current();
+ ui.horizontal(|horizontal| {
+ let mut tick: Option<Second> = None;
+ if horizontal.button("Manual Advance").clicked() {
+ tick = Some(*self.manual_tick);
+ }
+ *self.manual_tick = Self::tick_selector_combobox(horizontal, "manual-tick-combobox", *self.manual_tick);
+
+ tick
+ }).inner
+ }
+}
diff --git a/src/ui/topbar.rs b/src/ui/topbar.rs
deleted file mode 100644
index f560310..0000000
--- a/src/ui/topbar.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-
-use crate::{GameState, solar_system::SystemId, timeman::{self, Second, TimeMan}, ui::{bodies_window::BodiesWindowState, fleet_window::FleetWindowState}};
-
-#[derive(Default, Clone)]
-pub struct TopBarState
-{
- pub current_system: Option<SystemId>,
- pub auto_tick: Option<Second>,
- pub do_auto_tick: bool,
-}
-
-#[derive(Default, Clone)]
-pub struct TopBarAction
-{
- pub advance_tick: Option<Second>,
- pub toggle_bodies_window: bool,
- pub toggle_fleets_window: bool,
-}
-
-impl TopBarState
-{
- pub fn paint(
- &mut self,
- ui: &mut egui::Ui,
- game_state: &GameState,
- bodies_window_state: &BodiesWindowState,
- fleets_window_state: &FleetWindowState)
- -> TopBarAction
- {
- let mut action: TopBarAction = TopBarAction::default();
-
- let solar_systems = game_state.solar_systems();
- let timeman = game_state.timeman();
-
- egui::Panel::top("topbar").show_inside(
- ui,
- |ui| {
-
- ui.horizontal(|ui| {
- ui.vertical(|ui| {
- let selected_system_label = match self.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 self.current_system,
- Some(i),
- system.name()
- );
- }
- });
-
- ui.horizontal(|ui| {
- self.paint_empire_buttons(ui, &mut action, bodies_window_state, fleets_window_state);
- });
- });
-
- self.paint_tick_buttons(&mut action, ui);
-
- });
- ui.vertical_centered_justified(|ui| {
- let time_str = TimeMan::format_duration(timeman.seconds());
- ui.label(
- egui::RichText::new(time_str)
- .font(egui::FontId::monospace(16.0))
- );
- });
- });
- action
- }
-
- pub fn paint_empire_buttons(
- &mut self,
- ui: &mut egui::Ui,
- action: &mut TopBarAction,
- bodies_window_state: &BodiesWindowState,
- fleets_window_state: &FleetWindowState
- )
- {
- if ui.add(egui::Button::new("Bodies").selected(bodies_window_state.open)).clicked() {
- action.toggle_bodies_window = true;
- }
- if ui.add(egui::Button::new("Fleets").selected(fleets_window_state.open)).clicked() {
- action.toggle_fleets_window = true;
- }
- }
-
- pub fn paint_tick_buttons(
- &mut self,
- action: &mut TopBarAction,
- 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);
- }
- });
- });
- }
-}