diff --git a/src/apply_mutations.rs b/src/apply_mutations.rs new file mode 100644 index 0000000..fca4a04 --- /dev/null +++ b/src/apply_mutations.rs @@ -0,0 +1,6 @@ +use bevy::ecs::{entity::Entity, world::World}; +use dioxus_core::Mutations; + +pub fn apply_mutations(mutations: Mutations, root_entity: Entity, world: &mut World) { + todo!("Modify bevy_ui entities based on mutations"); +} diff --git a/src/deferred_system.rs b/src/deferred_system.rs new file mode 100644 index 0000000..d9a0128 --- /dev/null +++ b/src/deferred_system.rs @@ -0,0 +1,44 @@ +use bevy::ecs::{ + system::{IntoSystem, Resource, SystemId}, + world::World, +}; +use std::sync::Arc; + +#[derive(Clone)] +pub struct DeferredSystem(pub(crate) Arc); + +pub(crate) struct DeferredSystemInner { + pub id: SystemId, + world: *mut World, +} + +impl DeferredSystem { + pub(crate) fn new(system: S, world: &mut World) -> Self + where + S: IntoSystem<(), (), ()> + 'static, + { + Self(Arc::new(DeferredSystemInner { + id: world.register_system(system), + world, + })) + } + + pub fn schedule(&self) { + unsafe { &mut *self.0.world } + .resource_mut::() + .0 + .push(self.clone()); + } +} + +impl Drop for DeferredSystemInner { + fn drop(&mut self) { + unsafe { &mut *self.world }.remove_system(self.id).unwrap(); + } +} + +unsafe impl Send for DeferredSystemInner {} +unsafe impl Sync for DeferredSystemInner {} + +#[derive(Resource, Default)] +pub struct DeferredSystemRunQueue(pub Vec); diff --git a/src/hooks.rs b/src/hooks.rs new file mode 100644 index 0000000..6ee8901 --- /dev/null +++ b/src/hooks.rs @@ -0,0 +1,42 @@ +use crate::{deferred_system::DeferredSystem, tick::EcsContext}; +use bevy::ecs::{ + system::{IntoSystem, Resource}, + world::World, +}; +use dioxus_core::ScopeState; + +pub trait DioxusUiHooks { + fn use_world<'a>(&'a self) -> &'a World; + fn use_resource<'a, T: Resource>(&'a self) -> &'a T; + fn use_system(&self, system: S) -> DeferredSystem + where + S: IntoSystem<(), (), ()> + 'static; +} + +impl DioxusUiHooks for ScopeState { + fn use_world<'a>(&'a self) -> &'a World { + EcsContext::get_world(self) + } + + fn use_resource<'a, T: Resource>(&'a self) -> &'a T { + EcsContext::get_world(self).resource() + } + + fn use_system(&self, system: S) -> DeferredSystem + where + S: IntoSystem<(), (), ()> + 'static, + { + self.use_hook(|| DeferredSystem::new(system, EcsContext::get_world(self))) + .clone() + } +} + +// TODO +// pub fn use_query<'a, Q, F>(cx: &'a ScopeState) -> QueryIter<'a, '_, Q, F> +// where +// Q: ReadOnlyWorldQuery, +// F: ReadOnlyWorldQuery, +// { +// let world = EcsContext::get_world(cx); +// world.query_filtered::().iter(&world) +// } diff --git a/src/implementation.rs b/src/implementation.rs deleted file mode 100644 index bd50404..0000000 --- a/src/implementation.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::DioxusUiRoot; -use bevy::{ - ecs::{ - entity::Entity, - system::{CommandQueue, Commands, Resource}, - world::World, - }, - prelude::{Deref, DerefMut}, - utils::synccell::SyncCell, -}; -use dioxus_core::{Element, Mutations, Scope, ScopeState, VirtualDom}; -use std::{cell::RefCell, mem::transmute, rc::Rc}; - -pub fn tick_dioxus_ui(world: &mut World) { - let apply_mutations = |mutations: Mutations, root_entity: Entity| { - todo!("Modify bevy_ui entities based on mutations"); - }; - - let mut command_queue = CommandQueue::default(); - let ecs_context = unsafe { - EcsContext { - world_read_only: transmute(&world), - commands: Rc::new(RefCell::new(Commands::new( - transmute(&mut command_queue), - transmute(&world), - ))), - } - }; - - for (root_entity, mut dioxus_ui_root) in - world.query::<(Entity, &mut DioxusUiRoot)>().iter_mut(world) - { - let DioxusUiRoot { - virtual_dom, - initial_build, - } = &mut *dioxus_ui_root; - let virtual_dom = virtual_dom.get(); - - virtual_dom - .base_scope() - .provide_context(ecs_context.clone()); - - if !*initial_build { - apply_mutations(virtual_dom.rebuild(), root_entity); - *initial_build = true; - } - - // TODO: Handle events from winit - // virtual_dom.handle_event(todo!(), todo!(), todo!(), todo!()); - - apply_mutations(virtual_dom.render_immediate(), root_entity); - } - - command_queue.apply(world); -} - -#[derive(Clone)] -struct EcsContext { - world_read_only: &'static World, - commands: Rc>>, -} - -pub fn use_world<'a>(cx: &'a ScopeState) -> &'a World { - cx.consume_context::() - .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component") - .world_read_only -} - -pub fn use_resource<'a, T: Resource>(cx: &'a ScopeState) -> &'a T { - cx.consume_context::() - .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component") - .world_read_only - .resource() -} - -// TODO -// pub fn use_query<'a, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery>( -// cx: &'a ScopeState, -// ) -> QueryIter<'a, '_, Q, F> { -// let world = &mut cx -// .consume_context::() -// .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component") -// .world_read_only; -// world.query_filtered::().iter(&world) -// } - -pub fn use_commands<'a>(cx: &'a ScopeState) -> Rc>> { - unsafe { - transmute(Rc::clone( - &cx.consume_context::() - .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component") - .commands, - )) - } -} - -#[derive(Deref, DerefMut)] -pub struct VirtualDomUnsafe(pub SyncCell); -unsafe impl Send for VirtualDomUnsafe {} - -impl VirtualDomUnsafe { - pub fn new(root_component: fn(Scope) -> Element) -> Self { - Self(SyncCell::new(VirtualDom::new(root_component))) - } -} diff --git a/src/lib.rs b/src/lib.rs index b91ced7..1076921 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,33 +1,40 @@ -mod implementation; +mod apply_mutations; +mod deferred_system; +mod hooks; +mod tick; -use self::implementation::{tick_dioxus_ui, VirtualDomUnsafe}; +use self::{ + deferred_system::DeferredSystemRunQueue, + tick::{tick_dioxus_ui, VirtualDomUnsafe}, +}; use bevy::{ app::{App, Plugin, Update}, ecs::component::Component, }; -pub use self::implementation::{use_commands, use_resource, use_world}; +pub use self::hooks::DioxusUiHooks; pub use dioxus_core::{Element, Scope}; pub struct DioxusUiPlugin; impl Plugin for DioxusUiPlugin { fn build(&self, app: &mut App) { - app.add_systems(Update, tick_dioxus_ui); + app.init_resource::() + .add_systems(Update, tick_dioxus_ui); } } #[derive(Component)] pub struct DioxusUiRoot { virtual_dom: VirtualDomUnsafe, - initial_build: bool, + needs_rebuild: bool, } impl DioxusUiRoot { pub fn new(root_component: fn(Scope) -> Element) -> Self { Self { virtual_dom: VirtualDomUnsafe::new(root_component), - initial_build: false, + needs_rebuild: true, } } } diff --git a/src/tick.rs b/src/tick.rs new file mode 100644 index 0000000..3e24d7a --- /dev/null +++ b/src/tick.rs @@ -0,0 +1,76 @@ +use crate::{ + apply_mutations::apply_mutations, deferred_system::DeferredSystemRunQueue, DioxusUiRoot, +}; +use bevy::{ + ecs::{entity::Entity, world::World}, + prelude::{Deref, DerefMut}, + utils::synccell::SyncCell, +}; +use dioxus_core::{Element, Scope, ScopeState, VirtualDom}; +use std::mem; + +pub fn tick_dioxus_ui(world: &mut World) { + let world_ptr: *mut World = world; + let world_cell = world.as_unsafe_world_cell(); + + for (root_entity, mut dioxus_ui_root) in unsafe { + world_cell + .world_mut() + .query::<(Entity, &mut DioxusUiRoot)>() + .iter_mut(world_cell.world_mut()) + } { + let DioxusUiRoot { + virtual_dom, + needs_rebuild, + } = &mut *dioxus_ui_root; + let virtual_dom = virtual_dom.get(); + + virtual_dom + .base_scope() + .provide_context(EcsContext { world: world_ptr }); + + if *needs_rebuild { + apply_mutations(virtual_dom.rebuild(), root_entity, unsafe { + world_cell.world_mut() + }); + *needs_rebuild = false; + } + + // TODO: Handle events from winit + // virtual_dom.handle_event(todo!(), todo!(), todo!(), todo!()); + + apply_mutations(virtual_dom.render_immediate(), root_entity, unsafe { + world_cell.world_mut() + }); + } + + for system in mem::take(&mut world.resource_mut::().0) { + world.run_system(system.0.id).unwrap(); + } +} + +#[derive(Clone)] +pub(crate) struct EcsContext { + world: *mut World, +} + +impl EcsContext { + pub fn get_world(cx: &ScopeState) -> &mut World { + unsafe { + &mut *cx + .consume_context::() + .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component") + .world + } + } +} + +#[derive(Deref, DerefMut)] +pub struct VirtualDomUnsafe(pub SyncCell); +unsafe impl Send for VirtualDomUnsafe {} + +impl VirtualDomUnsafe { + pub fn new(root_component: fn(Scope) -> Element) -> Self { + Self(SyncCell::new(VirtualDom::new(root_component))) + } +}