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
|
#!/bin/env /bin/sh
|
||||||
|
echo "Debug-build features: minimal"
|
||||||
cargo build --no-default-features --features= &&
|
cargo build --no-default-features --features= &&
|
||||||
|
echo "Debug-build features: logging" &&
|
||||||
cargo build --no-default-features --features=logging &&
|
cargo build --no-default-features --features=logging &&
|
||||||
|
echo "Debug-build features: render" &&
|
||||||
cargo build --no-default-features --features=render &&
|
cargo build --no-default-features --features=render &&
|
||||||
|
echo "Debug-build features: logging,render" &&
|
||||||
cargo build --no-default-features --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 &&
|
cargo build --no-default-features --features=globe_view &&
|
||||||
|
echo "Debug-build features: logging,globe_view" &&
|
||||||
cargo build --no-default-features --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= &&
|
cargo build --release --no-default-features --features= &&
|
||||||
|
echo "Release-build features: logging" &&
|
||||||
cargo build --release --no-default-features --features=logging &&
|
cargo build --release --no-default-features --features=logging &&
|
||||||
|
echo "Release-build features: render" &&
|
||||||
cargo build --release --no-default-features --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 &&
|
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=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 widget;
|
||||||
pub(crate) mod widgets;
|
|
||||||
pub(crate) use widget::*;
|
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;
|
mod toolbar;
|
||||||
pub(crate) use info_panel::InfoPanel;
|
|
||||||
pub(crate) mod toolbar;
|
|
||||||
pub(crate) use toolbar::ToolbarWidget;
|
pub(crate) use toolbar::ToolbarWidget;
|
||||||
|
|
|
@ -9,8 +9,9 @@ use {
|
||||||
};
|
};
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gui::{WidgetId, WidgetSystem},
|
gui::{open_window, windows::Overlay, WidgetId, WidgetSystem},
|
||||||
macros::iterable_enum,
|
macros::iterable_enum,
|
||||||
|
resources::OpenedWindows,
|
||||||
},
|
},
|
||||||
bevy::{
|
bevy::{
|
||||||
asset::Assets,
|
asset::Assets,
|
||||||
|
@ -35,7 +36,8 @@ iterable_enum!(ToolbarButton {
|
||||||
LoadWorld,
|
LoadWorld,
|
||||||
Rainfall,
|
Rainfall,
|
||||||
Temperature,
|
Temperature,
|
||||||
PlanetView,
|
Overlays,
|
||||||
|
ToggleBiomes,
|
||||||
Contours,
|
Contours,
|
||||||
});
|
});
|
||||||
#[cfg(feature = "globe_view")]
|
#[cfg(feature = "globe_view")]
|
||||||
|
@ -45,7 +47,8 @@ iterable_enum!(ToolbarButton {
|
||||||
LoadWorld,
|
LoadWorld,
|
||||||
Rainfall,
|
Rainfall,
|
||||||
Temperature,
|
Temperature,
|
||||||
PlanetView,
|
Overlays,
|
||||||
|
ToggleBiomes,
|
||||||
Contours,
|
Contours,
|
||||||
GlobeView,
|
GlobeView,
|
||||||
});
|
});
|
||||||
|
@ -98,7 +101,10 @@ impl ToolbarButton {
|
||||||
world_manager.toggle_temperature();
|
world_manager.toggle_temperature();
|
||||||
update_textures(&world_manager, &mut world.resource_mut::<Assets<Image>>());
|
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();
|
world_manager.cycle_view();
|
||||||
update_textures(&world_manager, &mut world.resource_mut::<Assets<Image>>());
|
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::Rainfall => "Toggle rainfall",
|
||||||
ToolbarButton::Temperature => "Toggle temperature",
|
ToolbarButton::Temperature => "Toggle temperature",
|
||||||
ToolbarButton::Contours => "Toggle contours",
|
ToolbarButton::Contours => "Toggle contours",
|
||||||
ToolbarButton::PlanetView => "Cycle view",
|
ToolbarButton::Overlays => "Overlays",
|
||||||
|
ToolbarButton::ToggleBiomes => "Toggle biome view",
|
||||||
ToolbarButton::GenerateWorld => "Generate new world",
|
ToolbarButton::GenerateWorld => "Generate new world",
|
||||||
ToolbarButton::SaveWorld => "Save",
|
ToolbarButton::SaveWorld => "Save",
|
||||||
ToolbarButton::LoadWorld => "Load",
|
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 {
|
use {
|
||||||
crate::{
|
crate::{gui::WindowSystem, resources::CursorMapPosition},
|
||||||
gui::{WidgetId, WidgetSystem},
|
|
||||||
resources::CursorMapPosition,
|
|
||||||
},
|
|
||||||
bevy::ecs::{
|
bevy::ecs::{
|
||||||
system::{SystemParam, SystemState},
|
system::{SystemParam, SystemState},
|
||||||
world::World,
|
world::World,
|
||||||
|
@ -13,15 +10,13 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(SystemParam)]
|
#[derive(SystemParam)]
|
||||||
pub(crate) struct InfoPanel<'w, 's> {
|
pub(crate) struct TileInfo<'w, 's> {
|
||||||
#[system_param(ignore)]
|
#[system_param(ignore)]
|
||||||
_phantom: PhantomData<(&'w (), &'s ())>,
|
_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")
|
_ = Grid::new("info_panel")
|
||||||
.num_columns(2)
|
.num_columns(2)
|
||||||
.striped(false)
|
.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 gui;
|
||||||
pub(crate) mod macros;
|
pub(crate) mod macros;
|
||||||
pub(crate) mod plugins;
|
pub(crate) mod plugins;
|
||||||
|
#[cfg(feature = "render")]
|
||||||
pub(crate) mod resources;
|
pub(crate) mod resources;
|
||||||
|
|
||||||
#[cfg(all(feature = "render", feature = "logging"))]
|
#[cfg(all(feature = "render", feature = "logging"))]
|
||||||
|
@ -52,11 +53,8 @@ use {
|
||||||
EguiContext,
|
EguiContext,
|
||||||
},
|
},
|
||||||
components::panning::Pan2d,
|
components::panning::Pan2d,
|
||||||
gui::{
|
gui::{render_windows, widget, widgets::ToolbarWidget},
|
||||||
widget,
|
resources::{CursorMapPosition, OpenedWindows},
|
||||||
widgets::{InfoPanel, ToolbarWidget},
|
|
||||||
},
|
|
||||||
resources::CursorMapPosition,
|
|
||||||
};
|
};
|
||||||
#[cfg(all(feature = "render", feature = "globe_view"))]
|
#[cfg(all(feature = "render", feature = "globe_view"))]
|
||||||
use {
|
use {
|
||||||
|
@ -257,12 +255,6 @@ fn generate_graphics(
|
||||||
fn update_gui(world: &mut World) {
|
fn update_gui(world: &mut World) {
|
||||||
world.resource_scope(|world, mut ctx: Mut<'_, EguiContext>| {
|
world.resource_scope(|world, mut ctx: Mut<'_, EguiContext>| {
|
||||||
let ctx = ctx.ctx_mut();
|
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")]
|
#[cfg(feature = "logging")]
|
||||||
{
|
{
|
||||||
bevy_egui::egui::CentralPanel::default()
|
bevy_egui::egui::CentralPanel::default()
|
||||||
|
@ -287,6 +279,8 @@ fn update_gui(world: &mut World) {
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
widget::<ToolbarWidget<'_, '_>>(world, ui, "Toolbar".into());
|
widget::<ToolbarWidget<'_, '_>>(world, ui, "Toolbar".into());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
render_windows(world, ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +304,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.insert_resource(CursorMapPosition::default())
|
.insert_resource(CursorMapPosition::default())
|
||||||
|
.insert_resource(OpenedWindows::default())
|
||||||
.add_startup_system(generate_graphics)
|
.add_startup_system(generate_graphics)
|
||||||
.add_system(update_gui.exclusive_system())
|
.add_system(update_gui.exclusive_system())
|
||||||
.add_system(update_cursor_map_position);
|
.add_system(update_cursor_map_position);
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
#[cfg(feature = "render")]
|
use {crate::gui::WindowId, bevy::utils::HashSet, std::fmt::Display};
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub(crate) struct CursorMapPosition {
|
pub(crate) struct CursorMapPosition {
|
||||||
pub(crate) x: i32,
|
pub(crate) x: i32,
|
||||||
pub(crate) y: i32,
|
pub(crate) y: i32,
|
||||||
}
|
}
|
||||||
#[cfg(feature = "render")]
|
|
||||||
impl Display for CursorMapPosition {
|
impl Display for CursorMapPosition {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
|
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