diff --git a/examples/demo.rs b/examples/demo.rs index 6c1a2d3..878608c 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -28,7 +28,7 @@ fn main() { #[component] fn Editor(cx: Scope) -> Element { // TODO: When selected entity is despawned, need to reset this to None - let selected_entity = use_state(cx, || Option::::None); + let selected_entity = use_state_send(cx, || Option::::None); render! { node { @@ -42,19 +42,23 @@ fn Editor(cx: Scope) -> Element { } #[component] -fn SceneTree<'a>(cx: Scope, selected_entity: &'a UseState>) -> Element { +fn SceneTree<'a>(cx: Scope, selected_entity: &'a UseStateSend>) -> Element { let entities = use_query_filtered::<(Entity, DebugName), Without>(cx); let entities = entities.query(); let mut entities = entities.into_iter().collect::>(); entities.sort_by_key(|(entity, _)| *entity); - let spawn_entity = use_system(cx, |world: &mut World| { - world.spawn_empty(); + let spawn_entity = use_system(cx, { + let selected_entity = (*selected_entity).clone(); + move |world: &mut World| { + let new_entity = world.spawn_empty(); + selected_entity.write(Some(new_entity.id())); + } }); render! { node { - onclick: move |_| selected_entity.set(None), + onclick: move |_| selected_entity.write(None), flex_direction: "column", if entities.is_empty() { rsx! { "No entities exist" } @@ -63,16 +67,16 @@ fn SceneTree<'a>(cx: Scope, selected_entity: &'a UseState>) -> El for (entity, name) in entities { Button { onclick: move |event: Event| if *event.data == PointerButton::Primary { - if Some(entity) == ***selected_entity { - selected_entity.set(None); + if Some(entity) == *selected_entity.read() { + selected_entity.write(None); } else { - selected_entity.set(Some(entity)); + selected_entity.write(Some(entity)); } event.stop_propagation(); }, - base_color: if Some(entity) == ***selected_entity { Some(VIOLET_700) } else { None }, - click_color: if Some(entity) == ***selected_entity { Some(VIOLET_400) } else { None }, - hover_color: if Some(entity) == ***selected_entity { Some(VIOLET_500) } else { None }, + 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 }, match name.name { Some(name) => format!("{name}"), _ => format!("Entity ({:?})", name.entity) @@ -93,11 +97,11 @@ fn SceneTree<'a>(cx: Scope, selected_entity: &'a UseState>) -> El } #[component] -fn EntityInspector<'a>(cx: Scope, selected_entity: &'a UseState>) -> Element { +fn EntityInspector<'a>(cx: Scope, selected_entity: &'a UseStateSend>) -> Element { let world = use_world(cx); let type_registry = use_resource::(cx).read(); let components = selected_entity - .get() + .read() .map(|selected_entity| { let entity_ref = world.get_entity(selected_entity).unwrap(); let mut components = entity_ref @@ -118,7 +122,7 @@ fn EntityInspector<'a>(cx: Scope, selected_entity: &'a UseState>) .unwrap_or_default(); render! { - if selected_entity.is_none() { + if selected_entity.read().is_none() { rsx! { node { margin: "8", diff --git a/src/lib.rs b/src/lib.rs index 2a4780a..bd56a6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ mod events; mod hot_reload; mod parse_attributes; mod tick; +mod use_state_send; use self::{ apply_mutations::BevyTemplate, @@ -29,6 +30,7 @@ use dioxus::core::{Element, ElementId, Scope, VirtualDom}; pub mod prelude { pub use super::ecs_hooks::*; pub use super::elements::*; + pub use super::use_state_send::*; pub use super::{DioxusUiBundle, DioxusUiPlugin, DioxusUiRoot}; pub use bevy_mod_picking::pointer::PointerButton; pub use dioxus; diff --git a/src/use_state_send.rs b/src/use_state_send.rs new file mode 100644 index 0000000..29f585d --- /dev/null +++ b/src/use_state_send.rs @@ -0,0 +1,46 @@ +// https://github.com/DioxusLabs/dioxus-std/blob/8db5b1e8a3b8c81f3174a0c9cb951c87058289ca/std/src/utils/rw/use_rw.rs + +use dioxus::prelude::ScopeState; +use std::sync::{Arc, RwLock, RwLockReadGuard}; + +pub fn use_state_send( + cx: &ScopeState, + init_rw: impl FnOnce() -> T, +) -> &mut UseStateSend { + let hook = cx.use_hook(|| UseStateSend { + update: cx.schedule_update(), + value: Arc::new(RwLock::new(init_rw())), + }); + + hook +} + +pub struct UseStateSend { + update: Arc, + value: Arc>, +} + +impl Clone for UseStateSend { + fn clone(&self) -> Self { + Self { + update: self.update.clone(), + value: self.value.clone(), + } + } +} + +impl UseStateSend { + pub fn read(&self) -> RwLockReadGuard<'_, T> { + self.value.read().expect("Lock poisoned") + } + + pub fn write(&self, new_value: T) { + let mut lock = self.value.write().expect("Lock poisoned"); + *lock = new_value; + self.needs_update(); + } + + pub fn needs_update(&self) { + (self.update)() + } +}