Generic window system
This commit is contained in:
parent
489733bd50
commit
2ffdd87712
10 changed files with 200 additions and 34 deletions
|
@ -1,13 +1,26 @@
|
|||
#!/bin/env /bin/sh
|
||||
echo "Debug-build features: minimal"
|
||||
cargo build --no-default-features --features= &&
|
||||
echo "Debug-build features: logging" &&
|
||||
cargo build --no-default-features --features=logging &&
|
||||
echo "Debug-build features: render" &&
|
||||
cargo build --no-default-features --features=render &&
|
||||
echo "Debug-build features: logging,render" &&
|
||||
cargo build --no-default-features --features=logging,render &&
|
||||
echo "Debug-build features: globe_view" &&
|
||||
cargo build --no-default-features --features=globe_view &&
|
||||
echo "Debug-build features: logging,globe_view" &&
|
||||
cargo build --no-default-features --features=logging,globe_view &&
|
||||
echo "Release-build features: minimal"
|
||||
cargo build --release --no-default-features --features= &&
|
||||
echo "Release-build features: logging" &&
|
||||
cargo build --release --no-default-features --features=logging &&
|
||||
echo "Release-build features: render" &&
|
||||
cargo build --release --no-default-features --features=render &&
|
||||
echo "Release-build features: logging,render" &&
|
||||
cargo build --release --no-default-features --features=logging,render &&
|
||||
echo "Release-build features: globe_view" &&
|
||||
cargo build --release --no-default-features --features=globe_view &&
|
||||
cargo build --release --no-default-features --features=logging,globe_view
|
||||
echo "Release-build features: logging,globe_view" &&
|
||||
cargo build --release --no-default-features --features=logging,globe_view &&
|
||||
echo "Done!"
|
|
@ -1,3 +1,7 @@
|
|||
pub(crate) mod widget;
|
||||
pub(crate) mod widgets;
|
||||
pub(crate) use widget::*;
|
||||
pub(crate) mod window;
|
||||
pub(crate) use window::*;
|
||||
|
||||
pub(crate) mod widgets;
|
||||
pub(crate) mod windows;
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
pub(crate) mod info_panel;
|
||||
pub(crate) use info_panel::InfoPanel;
|
||||
pub(crate) mod toolbar;
|
||||
mod toolbar;
|
||||
pub(crate) use toolbar::ToolbarWidget;
|
||||
|
|
|
@ -9,8 +9,9 @@ use {
|
|||
};
|
||||
use {
|
||||
crate::{
|
||||
gui::{WidgetId, WidgetSystem},
|
||||
gui::{open_window, windows::Overlay, WidgetId, WidgetSystem},
|
||||
macros::iterable_enum,
|
||||
resources::OpenedWindows,
|
||||
},
|
||||
bevy::{
|
||||
asset::Assets,
|
||||
|
@ -35,7 +36,8 @@ iterable_enum!(ToolbarButton {
|
|||
LoadWorld,
|
||||
Rainfall,
|
||||
Temperature,
|
||||
PlanetView,
|
||||
Overlays,
|
||||
ToggleBiomes,
|
||||
Contours,
|
||||
});
|
||||
#[cfg(feature = "globe_view")]
|
||||
|
@ -45,7 +47,8 @@ iterable_enum!(ToolbarButton {
|
|||
LoadWorld,
|
||||
Rainfall,
|
||||
Temperature,
|
||||
PlanetView,
|
||||
Overlays,
|
||||
ToggleBiomes,
|
||||
Contours,
|
||||
GlobeView,
|
||||
});
|
||||
|
@ -98,7 +101,10 @@ impl ToolbarButton {
|
|||
world_manager.toggle_temperature();
|
||||
update_textures(&world_manager, &mut world.resource_mut::<Assets<Image>>());
|
||||
},
|
||||
ToolbarButton::PlanetView => {
|
||||
ToolbarButton::Overlays => {
|
||||
open_window::<Overlay>(&mut world.resource_mut::<OpenedWindows>());
|
||||
},
|
||||
ToolbarButton::ToggleBiomes => {
|
||||
world_manager.cycle_view();
|
||||
update_textures(&world_manager, &mut world.resource_mut::<Assets<Image>>());
|
||||
},
|
||||
|
@ -129,7 +135,8 @@ impl From<ToolbarButton> for &'static str {
|
|||
ToolbarButton::Rainfall => "Toggle rainfall",
|
||||
ToolbarButton::Temperature => "Toggle temperature",
|
||||
ToolbarButton::Contours => "Toggle contours",
|
||||
ToolbarButton::PlanetView => "Cycle view",
|
||||
ToolbarButton::Overlays => "Overlays",
|
||||
ToolbarButton::ToggleBiomes => "Toggle biome view",
|
||||
ToolbarButton::GenerateWorld => "Generate new world",
|
||||
ToolbarButton::SaveWorld => "Save",
|
||||
ToolbarButton::LoadWorld => "Load",
|
||||
|
|
105
src/gui/window.rs
Normal file
105
src/gui/window.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
use {
|
||||
super::windows,
|
||||
crate::resources::OpenedWindows,
|
||||
bevy::{
|
||||
ecs::{
|
||||
change_detection::Mut,
|
||||
system::{SystemParam, SystemState},
|
||||
world::World,
|
||||
},
|
||||
log::debug,
|
||||
utils::HashMap,
|
||||
},
|
||||
bevy_egui::egui::{Context, Ui, Window},
|
||||
fxhash::FxHasher32,
|
||||
std::hash::Hasher,
|
||||
};
|
||||
|
||||
pub(crate) trait WindowSystem: SystemParam {
|
||||
fn draw_contents(world: &mut World, state: &mut SystemState<Self>, ui: &mut Ui);
|
||||
fn name() -> &'static str;
|
||||
}
|
||||
|
||||
pub(crate) fn render_windows(world: &mut World, ctx: &Context) {
|
||||
// TODO: Windows are hard-coded here instead of being iterable.
|
||||
// Is that good enough? Probably, yea.
|
||||
window::<windows::Overlay>(world, ctx);
|
||||
window::<windows::TileInfo>(world, ctx);
|
||||
}
|
||||
|
||||
pub(crate) fn open_window<S: 'static + WindowSystem>(windows: &mut OpenedWindows) {
|
||||
windows.open(S::name().into());
|
||||
}
|
||||
pub(crate) fn close_window<S: 'static + WindowSystem>(windows: &mut OpenedWindows) {
|
||||
windows.close(&S::name().into());
|
||||
}
|
||||
|
||||
fn window<S: 'static + WindowSystem>(world: &mut World, ctx: &Context) {
|
||||
// We need to cache `SystemState` to allow for a system's locally tracked state
|
||||
if !world.contains_resource::<StateInstances<S>>() {
|
||||
// Note, this message should only appear once! If you see it twice in the logs,
|
||||
// the function may have been called recursively, and will panic.
|
||||
debug!("Init system state {}", std::any::type_name::<S>());
|
||||
world.insert_resource(StateInstances::<S> {
|
||||
instances: HashMap::new(),
|
||||
});
|
||||
}
|
||||
world.resource_scope(|world, mut states: Mut<'_, StateInstances<S>>| {
|
||||
let id: WindowId = S::name().into();
|
||||
if !states.instances.contains_key(&id) {
|
||||
debug!(
|
||||
"Registering system state for window {id:?} of type {}",
|
||||
std::any::type_name::<S>()
|
||||
);
|
||||
_ = states.instances.insert(id, SystemState::new(world));
|
||||
}
|
||||
// Instead of passing this to open, don't render manually.
|
||||
// Saves fetching states, but might fuck up states?
|
||||
// TODO: Check that
|
||||
if world.resource::<OpenedWindows>().is_open(&id) {
|
||||
let cached_state = states.instances.get_mut(&id).unwrap();
|
||||
|
||||
let mut still_open = true;
|
||||
Window::new(S::name())
|
||||
.resizable(false)
|
||||
.open(&mut still_open)
|
||||
.title_bar(true)
|
||||
.show(ctx, |ui| {
|
||||
S::draw_contents(world, cached_state, ui);
|
||||
});
|
||||
if !still_open {
|
||||
close_window::<S>(&mut world.resource_mut::<OpenedWindows>());
|
||||
}
|
||||
|
||||
cached_state.apply(world);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StateInstances<T: WindowSystem> {
|
||||
instances: HashMap<WindowId, SystemState<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct WindowId(pub(crate) u64);
|
||||
impl WindowId {
|
||||
#[must_use]
|
||||
pub(crate) fn new(name: &str) -> Self {
|
||||
let bytes = name.as_bytes();
|
||||
let mut hasher = FxHasher32::default();
|
||||
hasher.write(bytes);
|
||||
WindowId(hasher.finish())
|
||||
}
|
||||
|
||||
// #[must_use]
|
||||
// pub(crate) fn with(&self, name: &str) -> Self {
|
||||
// Self::new(&format!("{}{name}", self.0))
|
||||
// }
|
||||
}
|
||||
impl From<&str> for WindowId {
|
||||
#[must_use]
|
||||
fn from(str: &str) -> Self {
|
||||
Self::new(str)
|
||||
}
|
||||
}
|
4
src/gui/windows/mod.rs
Normal file
4
src/gui/windows/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
mod overlay;
|
||||
pub(crate) use overlay::Overlay;
|
||||
mod tile_info;
|
||||
pub(crate) use tile_info::TileInfo;
|
25
src/gui/windows/overlay.rs
Normal file
25
src/gui/windows/overlay.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use {
|
||||
crate::gui::WindowSystem,
|
||||
bevy::ecs::{
|
||||
system::{SystemParam, SystemState},
|
||||
world::World,
|
||||
},
|
||||
bevy_egui::egui::Ui,
|
||||
std::marker::PhantomData,
|
||||
};
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub(crate) struct Overlay<'w, 's> {
|
||||
#[system_param(ignore)]
|
||||
_phantom: PhantomData<(&'w (), &'s ())>,
|
||||
}
|
||||
|
||||
impl WindowSystem for Overlay<'_, '_> {
|
||||
fn draw_contents(world: &mut World, _state: &mut SystemState<Self>, ui: &mut Ui) {
|
||||
ui.label(format!("{world:#?}"));
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
"Overlay Selection"
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
use {
|
||||
crate::{
|
||||
gui::{WidgetId, WidgetSystem},
|
||||
resources::CursorMapPosition,
|
||||
},
|
||||
crate::{gui::WindowSystem, resources::CursorMapPosition},
|
||||
bevy::ecs::{
|
||||
system::{SystemParam, SystemState},
|
||||
world::World,
|
||||
|
@ -13,15 +10,13 @@ use {
|
|||
};
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub(crate) struct InfoPanel<'w, 's> {
|
||||
pub(crate) struct TileInfo<'w, 's> {
|
||||
#[system_param(ignore)]
|
||||
_phantom: PhantomData<(&'w (), &'s ())>,
|
||||
}
|
||||
impl WidgetSystem for InfoPanel<'_, '_> {
|
||||
fn system(world: &mut World, _state: &mut SystemState<Self>, ui: &mut Ui, _id: WidgetId) {
|
||||
// This will get everything our system/widget requested
|
||||
// let mut params = state.get_mut(world);
|
||||
|
||||
impl WindowSystem for TileInfo<'_, '_> {
|
||||
fn draw_contents(world: &mut World, _state: &mut SystemState<Self>, ui: &mut Ui) {
|
||||
_ = Grid::new("info_panel")
|
||||
.num_columns(2)
|
||||
.striped(false)
|
||||
|
@ -68,4 +63,8 @@ impl WidgetSystem for InfoPanel<'_, '_> {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
"Tile Info"
|
||||
}
|
||||
}
|
17
src/main.rs
17
src/main.rs
|
@ -3,6 +3,7 @@ pub(crate) mod components;
|
|||
pub(crate) mod gui;
|
||||
pub(crate) mod macros;
|
||||
pub(crate) mod plugins;
|
||||
#[cfg(feature = "render")]
|
||||
pub(crate) mod resources;
|
||||
|
||||
#[cfg(all(feature = "render", feature = "logging"))]
|
||||
|
@ -52,11 +53,8 @@ use {
|
|||
EguiContext,
|
||||
},
|
||||
components::panning::Pan2d,
|
||||
gui::{
|
||||
widget,
|
||||
widgets::{InfoPanel, ToolbarWidget},
|
||||
},
|
||||
resources::CursorMapPosition,
|
||||
gui::{render_windows, widget, widgets::ToolbarWidget},
|
||||
resources::{CursorMapPosition, OpenedWindows},
|
||||
};
|
||||
#[cfg(all(feature = "render", feature = "globe_view"))]
|
||||
use {
|
||||
|
@ -257,12 +255,6 @@ fn generate_graphics(
|
|||
fn update_gui(world: &mut World) {
|
||||
world.resource_scope(|world, mut ctx: Mut<'_, EguiContext>| {
|
||||
let ctx = ctx.ctx_mut();
|
||||
_ = bevy_egui::egui::Window::new("Tile Info")
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
widget::<InfoPanel<'_, '_>>(world, ui, "Tile Info Panel".into());
|
||||
});
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
bevy_egui::egui::CentralPanel::default()
|
||||
|
@ -287,6 +279,8 @@ fn update_gui(world: &mut World) {
|
|||
.show(ctx, |ui| {
|
||||
widget::<ToolbarWidget<'_, '_>>(world, ui, "Toolbar".into());
|
||||
});
|
||||
|
||||
render_windows(world, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -310,6 +304,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
..default()
|
||||
})
|
||||
.insert_resource(CursorMapPosition::default())
|
||||
.insert_resource(OpenedWindows::default())
|
||||
.add_startup_system(generate_graphics)
|
||||
.add_system(update_gui.exclusive_system())
|
||||
.add_system(update_cursor_map_position);
|
||||
|
|
|
@ -1,15 +1,31 @@
|
|||
#[cfg(feature = "render")]
|
||||
use std::fmt::Display;
|
||||
use {crate::gui::WindowId, bevy::utils::HashSet, std::fmt::Display};
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct CursorMapPosition {
|
||||
pub(crate) x: i32,
|
||||
pub(crate) y: i32,
|
||||
}
|
||||
#[cfg(feature = "render")]
|
||||
impl Display for CursorMapPosition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct OpenedWindows(HashSet<WindowId>);
|
||||
|
||||
impl OpenedWindows {
|
||||
pub(crate) fn open(&mut self, id: WindowId) {
|
||||
// Ignore opening already opened windows
|
||||
_ = self.0.insert(id);
|
||||
}
|
||||
|
||||
pub(crate) fn close(&mut self, id: &WindowId) {
|
||||
// Ignore closing already closed windows
|
||||
_ = self.0.remove(id);
|
||||
}
|
||||
|
||||
pub(crate) fn is_open(&self, id: &WindowId) -> bool {
|
||||
self.0.contains(id)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue