bevy_dioxus/examples/demo.rs

213 lines
7.9 KiB
Rust
Raw Normal View History

2023-12-13 03:47:14 +00:00
use bevy::{
app::{App, Startup},
2023-12-17 05:21:32 +00:00
core::{DebugName, Name},
2023-12-13 03:47:14 +00:00
core_pipeline::core_2d::Camera2dBundle,
2023-12-26 20:52:02 +00:00
ecs::{
entity::Entity, query::Without, reflect::AppTypeRegistry, system::Commands, world::World,
},
reflect::TypeInfo,
2023-12-13 03:47:14 +00:00
ui::{node_bundles::NodeBundle, Node},
DefaultPlugins,
};
2023-12-19 05:24:06 +00:00
use bevy_dioxus::{colors::*, prelude::*};
use bevy_mod_picking::DefaultPickingPlugins;
2023-12-13 03:47:14 +00:00
fn main() {
App::new()
.add_plugins((DefaultPlugins, DioxusUiPlugin, DefaultPickingPlugins))
.add_systems(Startup, |mut commands: Commands| {
commands.spawn(DioxusUiBundle {
dioxus_ui_root: DioxusUiRoot(Editor),
2023-12-13 03:47:14 +00:00
node_bundle: NodeBundle::default(),
});
2023-12-17 05:21:32 +00:00
commands.spawn((Camera2dBundle::default(), Name::new("Camera")));
2023-12-13 03:47:14 +00:00
})
.run();
}
#[component]
fn Editor(cx: Scope) -> Element {
2023-12-16 04:31:00 +00:00
// TODO: When selected entity is despawned, need to reset this to None
2023-12-26 23:00:35 +00:00
let selected_entity = use_state_send(cx, || Option::<Entity>::None);
2023-12-13 03:47:14 +00:00
render! {
2023-12-19 05:24:06 +00:00
node {
2023-12-17 04:59:19 +00:00
width: "100vw",
height: "100vh",
2023-12-21 06:35:53 +00:00
justify_content: "space_between",
2023-12-17 04:59:19 +00:00
SceneTree { selected_entity: selected_entity }
EntityInspector { selected_entity: selected_entity }
}
2023-12-13 03:47:14 +00:00
}
}
#[component]
2023-12-26 23:00:35 +00:00
fn SceneTree<'a>(cx: Scope, selected_entity: &'a UseStateSend<Option<Entity>>) -> Element {
2023-12-13 03:47:14 +00:00
let entities = use_query_filtered::<(Entity, DebugName), Without<Node>>(cx);
let entities = entities.query();
let mut entities = entities.into_iter().collect::<Vec<_>>();
entities.sort_by_key(|(entity, _)| *entity);
2023-12-27 19:19:02 +00:00
let system_scheduler = use_system_scheduler(cx);
2023-12-17 05:33:13 +00:00
2023-12-13 03:47:14 +00:00
render! {
2023-12-19 05:24:06 +00:00
node {
2023-12-26 23:00:35 +00:00
onclick: move |_| selected_entity.write(None),
2023-12-13 03:47:14 +00:00
flex_direction: "column",
if entities.is_empty() {
rsx! { "No entities exist" }
} else {
rsx! {
for (entity, name) in entities {
2023-12-23 05:36:05 +00:00
Button {
2023-12-24 21:51:05 +00:00
onclick: move |event: Event<PointerButton>| if *event.data == PointerButton::Primary {
2023-12-26 23:00:35 +00:00
if Some(entity) == *selected_entity.read() {
selected_entity.write(None);
2023-12-17 20:19:08 +00:00
} else {
2023-12-26 23:00:35 +00:00
selected_entity.write(Some(entity));
}
2023-12-22 05:37:53 +00:00
event.stop_propagation();
},
2023-12-26 23:00:35 +00:00
base_color: if Some(entity) == *selected_entity.read() { Some(VIOLET_700) } else { None },
click_color: if Some(entity) == *selected_entity.read() { Some(VIOLET_400) } else { None },
hover_color: if Some(entity) == *selected_entity.read() { Some(VIOLET_500) } else { None },
2023-12-17 05:21:32 +00:00
match name.name {
Some(name) => format!("{name}"),
_ => format!("Entity ({:?})", name.entity)
}
2023-12-13 03:47:14 +00:00
}
}
}
}
2023-12-23 05:36:05 +00:00
Button {
2023-12-24 21:51:05 +00:00
onclick: move |event: Event<PointerButton>| if *event.data == PointerButton::Primary {
2023-12-27 19:19:02 +00:00
system_scheduler.schedule({
let selected_entity = (*selected_entity).clone();
move |world: &mut World| {
let new_entity = world.spawn_empty();
selected_entity.write(Some(new_entity.id()));
}
});
2023-12-23 02:12:39 +00:00
event.stop_propagation();
},
2023-12-23 05:36:05 +00:00
text { text: "Spawn Entity", text_size: "18" }
2023-12-17 05:33:13 +00:00
}
2023-12-13 03:47:14 +00:00
}
}
}
#[component]
2023-12-26 23:00:35 +00:00
fn EntityInspector<'a>(cx: Scope, selected_entity: &'a UseStateSend<Option<Entity>>) -> Element {
let world = use_world(cx);
2023-12-26 20:52:02 +00:00
let type_registry = use_resource::<AppTypeRegistry>(cx).read();
let components = selected_entity
2023-12-26 23:00:35 +00:00
.read()
2023-12-26 20:52:02 +00:00
.map(|selected_entity| {
let entity_ref = world.get_entity(selected_entity).unwrap();
let mut components = entity_ref
.archetype()
.components()
.map(|component_id| {
let component_info = world.components().get_info(component_id).unwrap();
let type_info = component_info
.type_id()
.and_then(|type_id| type_registry.get_type_info(type_id));
let (module, name) = component_info.name().rsplit_once("::").unwrap();
(name, module, type_info)
})
.collect::<Vec<_>>();
components.sort_by_key(|(name, _, _)| *name);
components
})
.unwrap_or_default();
2023-12-13 03:47:14 +00:00
render! {
2023-12-26 23:00:35 +00:00
if selected_entity.read().is_none() {
rsx! {
2023-12-24 21:57:01 +00:00
node {
2023-12-26 20:52:02 +00:00
margin: "8",
2023-12-24 21:57:01 +00:00
"Select an entity to view its components"
}
}
2023-12-13 03:47:14 +00:00
} else {
rsx! {
2023-12-19 05:24:06 +00:00
node {
flex_direction: "column",
2023-12-26 20:52:02 +00:00
margin: "8",
text { text: "Entity Inspector", text_size: "24" }
for (name, module, type_info) in components {
2023-12-19 05:24:06 +00:00
node {
2023-12-26 20:52:02 +00:00
flex_direction: "column",
margin_bottom: "6",
node {
column_gap: "6",
align_items: "baseline",
text { text: name, text_size: "18" }
text { text: module, text_size: "14", text_color: NEUTRAL_400 }
}
if let Some(type_info) = type_info {
rsx!{ ComponentInspector { type_info: type_info } }
}
}
}
}
}
2023-12-13 03:47:14 +00:00
}
}
}
2023-12-23 05:36:05 +00:00
2023-12-26 20:52:02 +00:00
#[component]
fn ComponentInspector<'a>(cx: Scope, type_info: &'a TypeInfo) -> Element {
render! {
match type_info {
TypeInfo::Struct(info) => rsx! {
for field in info.iter() {
format!("{}: {}", field.name(), field.type_path())
}
},
TypeInfo::TupleStruct(_) => rsx! { "TODO" },
TypeInfo::Tuple(_) => rsx! { "TODO" },
TypeInfo::List(_) => rsx! { "TODO" },
TypeInfo::Array(_) => rsx! { "TODO" },
TypeInfo::Map(_) => rsx! { "TODO" },
TypeInfo::Enum(_) => rsx! { "TODO" },
TypeInfo::Value(_) => rsx! { "TODO" },
}
}
}
2023-12-23 05:36:05 +00:00
#[allow(non_snake_case)]
fn Button<'a>(cx: Scope<'a, ButtonProps<'a>>) -> Element<'a> {
let clicked = use_state(cx, || false);
let hovered = use_state(cx, || false);
let background_color = if **clicked {
cx.props.click_color.unwrap_or(NEUTRAL_500)
} else if **hovered {
cx.props.hover_color.unwrap_or(NEUTRAL_600)
} else {
cx.props.base_color.unwrap_or(NEUTRAL_800)
};
render! {
node {
onclick: move |event| cx.props.onclick.call(event),
2023-12-24 21:51:05 +00:00
onclick_down: |event| if *event.data == PointerButton::Primary { clicked.set(true) },
onclick_up: |event| if *event.data == PointerButton::Primary { clicked.set(false) },
2023-12-23 05:36:05 +00:00
onmouse_enter: |_| hovered.set(true),
onmouse_exit: |_| { hovered.set(false); clicked.set(false) },
padding: "8",
background_color: background_color,
&cx.props.children
}
}
}
#[derive(Props)]
struct ButtonProps<'a> {
2023-12-24 21:51:05 +00:00
onclick: EventHandler<'a, Event<PointerButton>>,
2023-12-23 05:36:05 +00:00
base_color: Option<&'a str>,
click_color: Option<&'a str>,
hover_color: Option<&'a str>,
children: Element<'a>,
}