This commit is contained in:
JMS55 2023-12-06 18:46:50 -08:00
parent 0e76c0284a
commit 98178e9ca9
6 changed files with 181 additions and 111 deletions

6
src/apply_mutations.rs Normal file
View file

@ -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");
}

44
src/deferred_system.rs Normal file
View file

@ -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<DeferredSystemInner>);
pub(crate) struct DeferredSystemInner {
pub id: SystemId,
world: *mut World,
}
impl DeferredSystem {
pub(crate) fn new<S>(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::<DeferredSystemRunQueue>()
.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<DeferredSystem>);

42
src/hooks.rs Normal file
View file

@ -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<S>(&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<S>(&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::<Q, F>().iter(&world)
// }

View file

@ -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<RefCell<Commands<'static, 'static>>>,
}
pub fn use_world<'a>(cx: &'a ScopeState) -> &'a World {
cx.consume_context::<EcsContext>()
.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::<EcsContext>()
.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::<EcsContext>()
// .expect("Must be used from a dioxus component within a DioxusUiRoot bevy component")
// .world_read_only;
// world.query_filtered::<Q, F>().iter(&world)
// }
pub fn use_commands<'a>(cx: &'a ScopeState) -> Rc<RefCell<Commands<'a, 'a>>> {
unsafe {
transmute(Rc::clone(
&cx.consume_context::<EcsContext>()
.expect("Must be used from a dioxus component within a DioxusUiRoot bevy component")
.commands,
))
}
}
#[derive(Deref, DerefMut)]
pub struct VirtualDomUnsafe(pub SyncCell<VirtualDom>);
unsafe impl Send for VirtualDomUnsafe {}
impl VirtualDomUnsafe {
pub fn new(root_component: fn(Scope) -> Element) -> Self {
Self(SyncCell::new(VirtualDom::new(root_component)))
}
}

View file

@ -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::<DeferredSystemRunQueue>()
.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,
}
}
}

76
src/tick.rs Normal file
View file

@ -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::<DeferredSystemRunQueue>().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::<EcsContext>()
.expect("Must be used from a dioxus component within a DioxusUiRoot bevy component")
.world
}
}
}
#[derive(Deref, DerefMut)]
pub struct VirtualDomUnsafe(pub SyncCell<VirtualDom>);
unsafe impl Send for VirtualDomUnsafe {}
impl VirtualDomUnsafe {
pub fn new(root_component: fn(Scope) -> Element) -> Self {
Self(SyncCell::new(VirtualDom::new(root_component)))
}
}