Last active
August 18, 2023 23:42
-
-
Save dmlary/a40e29de0e9ec78950bb5f352115710a to your computer and use it in GitHub Desktop.
Bevy v0.10 SystemParam-based egui Widgets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// implemention of SystemParam-based egui Widgets for bevy 0.10 | |
/// adapted from: https://github.com/bevyengine/bevy/discussions/5522 | |
/// | |
/// Effectively widgets can access world components/resources similar | |
/// to systems. | |
#![allow(clippy::type_complexity)] | |
use bevy::ecs::system::{SystemParam, SystemState}; | |
use bevy::prelude::*; | |
use bevy_egui::egui; | |
use bevy_inspector_egui::quick::WorldInspectorPlugin; | |
use std::collections::HashMap; | |
use std::marker::PhantomData; | |
fn main() { | |
App::new() | |
.add_plugins(DefaultPlugins.set(WindowPlugin { | |
primary_window: Some(Window { | |
title: "scene_aabb".to_string(), | |
..default() | |
}), | |
..default() | |
})) | |
.add_plugin(WorldInspectorPlugin::new()) | |
.add_startup_system(setup) | |
.add_system(draw_ui) | |
.run(); | |
} | |
fn setup(mut commands: Commands) { | |
for i in 0..10 { | |
commands.spawn(Tileset { id: i }); | |
} | |
} | |
#[derive(Component, Default)] | |
struct Tileset { | |
id: usize, | |
} | |
pub trait WidgetSystem: SystemParam { | |
fn system(world: &mut World, state: &mut SystemState<Self>, ui: &mut egui::Ui, id: egui::Id); | |
} | |
pub fn widget<S: 'static + WidgetSystem>(world: &mut World, ui: &mut egui::Ui, id: egui::Id) { | |
// 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>>| { | |
if !states.instances.contains_key(&id) { | |
debug!( | |
"Registering system state for widget {id:?} of type {}", | |
std::any::type_name::<S>() | |
); | |
states.instances.insert(id, SystemState::new(world)); | |
} | |
let cached_state = states.instances.get_mut(&id).unwrap(); | |
S::system(world, cached_state, ui, id); | |
cached_state.apply(world); | |
}); | |
} | |
/// A UI widget may have multiple instances. We need to ensure the local state of these instances is | |
/// not shared. This hashmap allows us to dynamically store instance states. | |
#[derive(Default, Resource)] | |
struct StateInstances<T: 'static + WidgetSystem> { | |
instances: HashMap<egui::Id, SystemState<T>>, | |
} | |
pub fn with_world_and_egui_context<T>( | |
world: &mut World, | |
f: impl FnOnce(&mut World, egui::Context) -> T, | |
) -> T { | |
use bevy::window::PrimaryWindow; | |
use bevy_egui::EguiContext; | |
let mut state = world.query_filtered::<Entity, (With<EguiContext>, With<PrimaryWindow>)>(); | |
let entity = state.single(world); | |
let mut egui_context = world.get_mut::<EguiContext>(entity).unwrap(); | |
let ctx = egui_context.get_mut().clone(); | |
f(world, ctx) | |
} | |
fn draw_ui(world: &mut World) { | |
with_world_and_egui_context(world, |world, ctx| { | |
egui::Window::new("window").show(&ctx, |ui| { | |
widget::<TilesetList>(world, ui, ui.id().with("list")); | |
}); | |
}); | |
} | |
#[derive(SystemParam)] | |
pub struct TilesetList<'w, 's> { | |
tilesets: Query<'w, 's, &'static Tileset>, | |
} | |
impl<'w, 's> WidgetSystem for TilesetList<'w, 's> { | |
fn system(world: &mut World, state: &mut SystemState<Self>, ui: &mut egui::Ui, _id: egui::Id) { | |
let params = state.get(world); | |
for tileset in ¶ms.tilesets { | |
ui.label(format!("tileset {}", tileset.id)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment