diff options
| author | Jon Santmyer <jon@jonsantmyer.com> | 2026-05-07 08:50:05 -0400 |
|---|---|---|
| committer | Jon Santmyer <jon@jonsantmyer.com> | 2026-05-07 08:50:05 -0400 |
| commit | c1adf64c1aaecd5a2b9d532d707ef35971f1aa18 (patch) | |
| tree | fc1050becd0576d75a8d6afb8be09fae80c91541 | |
| parent | 9788d9037ad7199701b1710c28559cb96bce5aec (diff) | |
| download | systemic4x-c1adf64c1aaecd5a2b9d532d707ef35971f1aa18.tar.gz systemic4x-c1adf64c1aaecd5a2b9d532d707ef35971f1aa18.tar.bz2 systemic4x-c1adf64c1aaecd5a2b9d532d707ef35971f1aa18.zip | |
begin work on body info window
| -rw-r--r-- | assets/shaders/tacmap/body.wgsl | 8 | ||||
| -rw-r--r-- | src/main.rs | 5 | ||||
| -rw-r--r-- | src/ntree.rs | 81 | ||||
| -rw-r--r-- | src/solar_system.rs | 28 | ||||
| -rw-r--r-- | src/solar_system/body.rs | 4 | ||||
| -rw-r--r-- | src/solar_system/fleet.rs | 60 | ||||
| -rw-r--r-- | src/solar_system/orbit.rs | 4 | ||||
| -rw-r--r-- | src/solar_system/ship.rs | 36 | ||||
| -rw-r--r-- | src/tacmap.rs | 2 | ||||
| -rw-r--r-- | src/ui.rs | 42 | ||||
| -rw-r--r-- | src/ui/bodies_window.rs | 180 | ||||
| -rw-r--r-- | src/ui/camera_info.rs | 3 | ||||
| -rw-r--r-- | src/ui/fleet_window.rs | 80 | ||||
| -rw-r--r-- | src/ui/topbar.rs | 151 |
14 files changed, 565 insertions, 119 deletions
diff --git a/assets/shaders/tacmap/body.wgsl b/assets/shaders/tacmap/body.wgsl index 21dc081..e91c841 100644 --- a/assets/shaders/tacmap/body.wgsl +++ b/assets/shaders/tacmap/body.wgsl @@ -48,11 +48,11 @@ fn vs_main( //Scale the world around the camera scale and translate about the camera's //absolute (/target) position - let relative_pos = instance.position * camera.scale; - let origin_pos = (instance.origin - camera.pos) * camera.scale; + let relative_pos = instance.position; + let origin_pos = (instance.origin - camera.pos); if all(relative_pos != origin_pos) { - if length(relative_pos) < 0.01 { + if length(relative_pos) < 0.01 / camera.scale { if length(relative_pos) != 0.0 { out.clip_position = vec4<f32>(0.0, 0.0, 0.0, 1.0); return out; @@ -60,7 +60,7 @@ fn vs_main( } } - let instance_pos = relative_pos + origin_pos; + let instance_pos = (relative_pos + origin_pos) * camera.scale; let view_proj = camera.proj * view; diff --git a/src/main.rs b/src/main.rs index 923f357..d7c76bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod texture; mod solar_system; mod known_stars; mod timeman; +mod ntree; use std::cell::RefCell; use std::thread; @@ -68,7 +69,7 @@ impl GameState Err(e) => panic!("Unable to create sol system : {}", e) }; - sol_system.update(timeman.seconds()); + sol_system.tick(timeman.seconds()); Self { timeman: timeman, @@ -91,7 +92,7 @@ impl GameState { let ticks = self.timeman.update(); ticks.iter().for_each(|time| { - self.solar_systems.iter_mut().for_each(|system| { system.update(*time) }); + self.solar_systems.iter_mut().for_each(|system| { system.tick(*time) }); }); } } diff --git a/src/ntree.rs b/src/ntree.rs new file mode 100644 index 0000000..26c241c --- /dev/null +++ b/src/ntree.rs @@ -0,0 +1,81 @@ + +#[derive(Clone)] +pub struct NTreeNode<T> +{ + value: T, + children: Vec<NTreeNode<T>> + +} + +#[derive(Default, Clone)] +pub struct NTree<T> +{ + root: Option<NTreeNode<T>> +} + +impl<T> NTree<T> +{ + pub fn set_root_val( + &mut self, + val: T) + { + self.root = Some(NTreeNode { value: val, children: vec![] }); + } + + pub fn set_root( + &mut self, + node: NTreeNode<T>) + { + self.root = Some(node); + } + + pub fn root(&self) -> &Option<NTreeNode<T>> { &self.root } + pub fn root_mut(&mut self) -> &mut Option<NTreeNode<T>> { &mut self.root } +} + +impl<T> NTreeNode<T> +{ + pub fn new(val: T) + -> Self + { + Self { + value: val, + children: vec![] + } + } + + pub fn value(&self) -> &T { &self.value } + pub fn value_mut(&mut self) -> &mut T { &mut self.value } + + pub fn children(&self) -> &[NTreeNode<T>] { &self.children.as_slice() } + + pub fn as_tree(self) + -> NTree<T> + { + NTree { root: Some(self) } + } + + pub fn insert_value( + &mut self, + val: T) + { + self.children.push(NTreeNode { value: val, children: vec![] }); + } + + pub fn insert_node( + &mut self, + subtree: NTreeNode<T>) + { + self.children.push(subtree); + } + + pub fn insert_tree( + &mut self, + subtree: NTree<T>) + { + match subtree.root { + Some(subtree_root) => self.insert_node(subtree_root), + None => { panic!("Tried to put null subtree") } + } + } +} diff --git a/src/solar_system.rs b/src/solar_system.rs index ad1f304..913ca00 100644 --- a/src/solar_system.rs +++ b/src/solar_system.rs @@ -1,9 +1,9 @@ pub mod body; -pub mod ship; +pub mod fleet; pub mod orbit; use self::body::*; -use self::ship::*; +use self::fleet::*; use self::orbit::*; use serde::{Deserialize}; @@ -38,13 +38,18 @@ pub struct CSVOrbitalBody semi_major_axis: Kilometers, } +pub trait HasMass +{ + fn mass() -> Kilograms; +} + pub struct SolarSystem { id: SystemId, name: String, bodies: Vec<OrbitalBody>, - ships: Vec<Ship> + fleets: Vec<Fleet> } impl SolarSystem @@ -79,7 +84,7 @@ impl SolarSystem id: id, name: bodies[0].name().clone(), bodies: bodies, - ships: vec![] + fleets: vec![] }) } @@ -95,7 +100,6 @@ impl SolarSystem } pub fn id(&self) -> SystemId { self.id } - pub fn name(&self) -> &String { &self.name } pub fn bodies(&self) @@ -104,6 +108,9 @@ impl SolarSystem self.bodies.as_slice() } + pub fn body(&self, id: BodyId) -> &OrbitalBody + { &self.bodies[id] } + pub fn body_position( &self, body: &OrbitalBody) @@ -112,12 +119,19 @@ impl SolarSystem body.absolute_position(self) } - pub fn update( + pub fn fleets(&self) + -> &[Fleet] + { self.fleets.as_slice() } + + pub fn tick( &mut self, time: Second) { self.bodies.iter_mut().for_each(|body| { - body.update(time); + body.tick(time); + }); + self.fleets.iter_mut().for_each(|fleet| { + fleet.tick(time); }); } } diff --git a/src/solar_system/body.rs b/src/solar_system/body.rs index 55d3d4a..7618a95 100644 --- a/src/solar_system/body.rs +++ b/src/solar_system/body.rs @@ -81,12 +81,12 @@ impl OrbitalBody -> cgmath::Vector3<Kilometers> { match &self.orbit { - Some(orbit) => orbit.calculate_position_at(self, time), + Some(orbit) => orbit.calculate_position_at(self.sgp(), time), None => cgmath::vec3(0.0, 0.0, 0.0) } } - pub fn update( + pub fn tick( &mut self, time: Second) { diff --git a/src/solar_system/fleet.rs b/src/solar_system/fleet.rs new file mode 100644 index 0000000..c331cd7 --- /dev/null +++ b/src/solar_system/fleet.rs @@ -0,0 +1,60 @@ +use crate::solar_system::{GRAVITATIONAL_CONSTANT, Kilometers, SolarSystem, body::OrbitalBody, orbit::StaticOrbit}; +use crate::timeman::Second; + +pub type FleetId = usize; + +pub struct Fleet +{ + id: FleetId, + name: String, + + position: cgmath::Vector3<Kilometers>, + velocity: cgmath::Vector3<f32>, + acceleration: cgmath::Vector3<f32>, + + baked_orbit: Option<(f64, StaticOrbit)>, +} + +impl Fleet +{ + pub fn new( + id: FleetId, + name: String) + -> Self + { + Self { + id, + name, + position: cgmath::vec3(0.0, 0.0, 0.0), + velocity: cgmath::vec3(0.0, 0.0, 0.0), + acceleration: cgmath::vec3(0.0, 0.0, 0.0), + baked_orbit: None, + } + } + + pub fn id(&self) -> FleetId { self.id } + pub fn name(&self) -> &String { &self.name } + + pub fn make_orbit( + &mut self, + body: &OrbitalBody, + radius: Kilometers) + { + let sgp = body.mass() * GRAVITATIONAL_CONSTANT; + self.baked_orbit = Some((sgp, StaticOrbit::new_circular(body, radius))); + } + + pub fn tick( + &mut self, + time: Second) + { + match &self.baked_orbit { + Some(orbit_info) => { + let sgp = orbit_info.0; + let orbit = &orbit_info.1; + self.position = orbit.calculate_position_at(sgp, time); + }, + None => {} + } + } +} diff --git a/src/solar_system/orbit.rs b/src/solar_system/orbit.rs index 461de7b..1800127 100644 --- a/src/solar_system/orbit.rs +++ b/src/solar_system/orbit.rs @@ -65,7 +65,7 @@ impl StaticOrbit pub fn calculate_position_at( &self, - this_body: &OrbitalBody, + sgp: f64, time: Second) -> cgmath::Vector3<f64> { @@ -75,7 +75,7 @@ impl StaticOrbit let arg_periaps = self.long_periapsis - long_asc_node; let sma_cubed = self.semi_major_axis.powf(3.0); - let mean_motion = (this_body.sgp() / sma_cubed).sqrt(); + let mean_motion = (sgp / sma_cubed).sqrt(); let mean_anomaly_epoch = self.mean_long - self.long_periapsis; let mean_anomaly = mean_anomaly_epoch + (mean_motion * time as f64); diff --git a/src/solar_system/ship.rs b/src/solar_system/ship.rs deleted file mode 100644 index 57c0184..0000000 --- a/src/solar_system/ship.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::solar_system::{Kilograms, Kilometers, SolarSystem, body::BodyId, orbit::StaticOrbit}; - -pub type ShipId = usize; - -pub struct Ship -{ - name: String, - mass: Kilograms, - - position: cgmath::Vector3<Kilometers>, - velocity: cgmath::Vector3<f32>, - acceleration: cgmath::Vector3<f32>, - - baked_orbit: Option<StaticOrbit> -} - -impl Ship -{ - pub fn new( - name: String, - mass: Kilograms, - system: &SolarSystem, - orbiting: BodyId, - orbit_sma: Kilometers) - -> Self - { - Self { - name, - mass, - position: cgmath::vec3(0.0, 0.0, 0.0), - velocity: cgmath::vec3(0.0, 0.0, 0.0), - acceleration: cgmath::vec3(0.0, 0.0, 0.0), - baked_orbit: None - } - } -} diff --git a/src/tacmap.rs b/src/tacmap.rs index c04cc92..b093fc1 100644 --- a/src/tacmap.rs +++ b/src/tacmap.rs @@ -1,5 +1,5 @@ pub mod camera; -pub mod render; +mod render; mod body_render; mod orbit_render; @@ -1,10 +1,12 @@ pub mod topbar; pub mod camera_info; +pub mod bodies_window; +pub mod fleet_window; -use std::cell::RefCell; +use std::{borrow::Borrow, cell::RefCell}; -use crate::{GameState, eguictx::EguiCtx, ui::{camera_info::CameraWindowState, topbar::TopBarState}}; +use crate::{GameState, eguictx::EguiCtx, ui::{bodies_window::BodiesWindowState, camera_info::CameraWindowState, fleet_window::FleetWindowState, topbar::TopBarState}}; mod ui { @@ -14,7 +16,10 @@ mod ui { pub struct State { pub topbar_sate: TopBarState, - pub camera_info: CameraWindowState + pub camera_info: CameraWindowState, + + pub bodies_window: BodiesWindowState, + pub fleet_window: FleetWindowState } impl State @@ -24,14 +29,39 @@ impl State game_state: &RefCell<GameState>, eguictx: &EguiCtx) { - TopBarState::render( + let mut game_state = game_state.borrow_mut(); + + let topbar_action = TopBarState::render( &mut self.topbar_sate, - game_state, + &game_state, eguictx); + if let Some(by) = topbar_action.advance_tick { + game_state.timeman_mut().advance(by) + } + + let current_system = match self.topbar_sate.current_system { + Some(id) => &game_state.solar_systems()[id], + None => return + }; + CameraWindowState::render( self, - game_state, + &game_state, eguictx); + + if self.topbar_sate.bodies_window_visible { + let bodies_window_action = + self.bodies_window.render(current_system, eguictx); + + if let Some(body) = bodies_window_action.focus_body { + self.camera_info.target = Some(body); + } + } + + if self.topbar_sate.fleet_window_visible { + let fleet_window_action = + self.fleet_window.render(game_state.borrow(), eguictx); + } } } 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<SystemId>, + system_heirarchy: NTree<SystemId>, + selected_body: Option<BodyId> +} + +#[derive(Default, Clone)] +pub struct BodiesWindowAction +{ + pub focus_body: Option<BodyId> +} + +impl BodiesWindowState +{ + fn build_system_heirarchy_rec( + bodies: &[OrbitalBody], + node: &mut NTreeNode<BodyId>) + { + 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::<BodyId>::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<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() + .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<GameState>, + 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<SystemId>, pub auto_tick: Option<Second>, - 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<Second>, } impl TopBarState { pub fn render( - state: &mut TopBarState, - game_state: &RefCell<GameState>, + &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); + } + }); + }); } } |
