diff --git a/examples/clock.rs b/examples/clock.rs new file mode 100644 index 000000000..5df326c1d --- /dev/null +++ b/examples/clock.rs @@ -0,0 +1,41 @@ +//! Example: README.md showcase +//! +//! The example from the README.md. + +use dioxus::prelude::*; +use futures_util::Future; + +#[tokio::main] +async fn main() { + let mut dom = VirtualDom::new(app); + dom.rebuild(); + + loop { + dom.wait_for_work().await; + } + + // dioxus_desktop::launch(app).await; +} + +fn app(cx: Scope) -> Element { + let mut count = use_ref(cx, || 0); + + let mut ct = count.to_owned(); + use_coroutine(cx, |_: UnboundedReceiver<()>| async move { + loop { + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + + *ct.write() += 1; + + let current = *ct.read(); + + println!("current: {}", current); + } + }); + + let mut count = count.read(); + + cx.render(rsx! { + div { "High-Five counter: {count}" } + }) +} diff --git a/examples/todomvc.rs b/examples/todomvc.rs index b5277133e..afd1f36b3 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -3,8 +3,9 @@ use dioxus::prelude::*; use dioxus_elements::input_data::keyboard_types::Key; -fn main() { - dioxus_desktop::launch(app); +#[tokio::main] +async fn main() { + dioxus_desktop::launch(app).await; } #[derive(PartialEq, Eq)] diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index bca8bd031..47652e262 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -23,7 +23,7 @@ rustc-hash = "1.1.0" # Used in diffing longest-increasing-subsequence = "0.1.0" -futures-util = { version = "0.3", default-features = false } +futures-util = { version = "0.3", default-features = false, features = ["alloc"]} slab = "0.4" diff --git a/packages/core/src/scheduler/mod.rs b/packages/core/src/scheduler/mod.rs index 7aed417b1..3996633a2 100644 --- a/packages/core/src/scheduler/mod.rs +++ b/packages/core/src/scheduler/mod.rs @@ -1,14 +1,13 @@ use crate::ScopeId; +use futures_util::stream::FuturesUnordered; use slab::Slab; mod suspense; mod task; mod wait; -mod waker; pub use suspense::*; pub use task::*; -pub use waker::ArcWake; /// The type of message that can be sent to the scheduler. /// @@ -31,17 +30,17 @@ pub(crate) struct Scheduler { pub sender: futures_channel::mpsc::UnboundedSender, /// Tasks created with cx.spawn - pub tasks: RefCell>>, + pub tasks: RefCell>, /// Async components - pub leaves: RefCell>>, + pub leaves: RefCell>, } impl Scheduler { pub fn new(sender: futures_channel::mpsc::UnboundedSender) -> Rc { Rc::new(Scheduler { sender, - tasks: RefCell::new(Slab::new()), + tasks: RefCell::new(FuturesUnordered::new()), leaves: RefCell::new(Slab::new()), }) } diff --git a/packages/core/src/scheduler/suspense.rs b/packages/core/src/scheduler/suspense.rs index c7cd650bb..8449559c8 100644 --- a/packages/core/src/scheduler/suspense.rs +++ b/packages/core/src/scheduler/suspense.rs @@ -1,4 +1,6 @@ -use super::{waker::ArcWake, SchedulerMsg}; +use futures_util::task::ArcWake; + +use super::SchedulerMsg; use crate::ElementId; use crate::{innerlude::Mutations, Element, ScopeId}; use std::future::Future; @@ -40,11 +42,16 @@ pub(crate) struct SuspenseLeaf { pub(crate) tx: futures_channel::mpsc::UnboundedSender, pub(crate) notified: Cell, pub(crate) task: *mut dyn Future>, + pub(crate) waker: Arc, } -impl ArcWake for SuspenseLeaf { +pub struct SuspenseHandle { + pub(crate) id: SuspenseId, + pub(crate) tx: futures_channel::mpsc::UnboundedSender, +} + +impl ArcWake for SuspenseHandle { fn wake_by_ref(arc_self: &Arc) { - arc_self.notified.set(true); _ = arc_self .tx .unbounded_send(SchedulerMsg::SuspenseNotified(arc_self.id)); diff --git a/packages/core/src/scheduler/task.rs b/packages/core/src/scheduler/task.rs index 8c1d7ca76..d79b47cb4 100644 --- a/packages/core/src/scheduler/task.rs +++ b/packages/core/src/scheduler/task.rs @@ -1,9 +1,13 @@ -use super::{waker::ArcWake, Scheduler, SchedulerMsg}; +use futures_util::task::ArcWake; +use futures_util::FutureExt; + +use super::{Scheduler, SchedulerMsg}; use crate::ScopeId; use std::cell::RefCell; use std::future::Future; use std::pin::Pin; use std::sync::Arc; +use std::task::Waker; /// A task's unique identifier. /// @@ -19,6 +23,17 @@ pub(crate) struct LocalTask { pub(super) task: RefCell + 'static>>>, id: TaskId, tx: futures_channel::mpsc::UnboundedSender, + pub waker: Waker, +} +impl Future for LocalTask { + type Output = (); + + fn poll( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.task.borrow_mut().poll_unpin(cx) + } } impl Scheduler { @@ -32,16 +47,23 @@ impl Scheduler { /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which /// will only occur when the VirtuaalDom itself has been dropped. pub fn spawn(&self, scope: ScopeId, task: impl Future + 'static) -> TaskId { - let mut tasks = self.tasks.borrow_mut(); - let entry = tasks.vacant_entry(); - let task_id = TaskId(entry.key()); + // let entry = tasks.vacant_entry(); + let task_id = TaskId(0); - entry.insert(Arc::new(LocalTask { + let task = LocalTask { id: task_id, tx: self.sender.clone(), task: RefCell::new(Box::pin(task)), scope, - })); + waker: futures_util::task::waker(Arc::new(LocalTaskHandle { + id: task_id, + tx: self.sender.clone(), + })), + }; + + self.tasks.borrow_mut().push(task); + + // println!("Spawning task: {:?}", task_id); self.sender .unbounded_send(SchedulerMsg::TaskNotified(task_id)) @@ -54,14 +76,22 @@ impl Scheduler { /// /// This does nto abort the task, so you'll want to wrap it in an aborthandle if that's important to you pub fn remove(&self, id: TaskId) { - self.tasks.borrow_mut().remove(id.0); + // self.tasks.borrow_mut().remove(id.0); } } -impl ArcWake for LocalTask { +pub struct LocalTaskHandle { + id: TaskId, + tx: futures_channel::mpsc::UnboundedSender, +} + +impl ArcWake for LocalTaskHandle { fn wake_by_ref(arc_self: &Arc) { - _ = arc_self + println!("waking task: {:?} {:p}", arc_self.id, arc_self); + + arc_self .tx - .unbounded_send(SchedulerMsg::TaskNotified(arc_self.id)); + .unbounded_send(SchedulerMsg::TaskNotified(arc_self.id)) + .unwrap(); } } diff --git a/packages/core/src/scheduler/wait.rs b/packages/core/src/scheduler/wait.rs index 205bd8ea0..deac7fe6f 100644 --- a/packages/core/src/scheduler/wait.rs +++ b/packages/core/src/scheduler/wait.rs @@ -1,3 +1,4 @@ +use futures_util::task::ArcWake; use futures_util::FutureExt; use std::{ rc::Rc, @@ -10,7 +11,7 @@ use crate::{ ScopeId, TaskId, VNode, VirtualDom, }; -use super::{waker::ArcWake, SuspenseId}; +use super::SuspenseId; impl VirtualDom { /// Handle notifications by tasks inside the scheduler @@ -20,22 +21,27 @@ impl VirtualDom { pub(crate) fn handle_task_wakeup(&mut self, id: TaskId) { let mut tasks = self.scheduler.tasks.borrow_mut(); - let task = match tasks.get(id.0) { - Some(task) => task, - None => return, - }; + // let task = match tasks.get(id.0) { + // Some(task) => task, + // None => { + // // The task was removed from the scheduler, so we can just ignore it + // return; + // } + // }; - let waker = task.waker(); - let mut cx = Context::from_waker(&waker); + // let mut cx = Context::from_waker(&task.waker); - // If the task completes... - if task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() { - // Remove it from the scope so we dont try to double drop it when the scope dropes - self.scopes[task.scope.0].spawned_tasks.remove(&id); + // // If the task completes... + // println!("polling task"); + // if task.task.borrow_mut().as_mut().poll(&mut cx).is_ready() { + // // Remove it from the scope so we dont try to double drop it when the scope dropes + // self.scopes[task.scope.0].spawned_tasks.remove(&id); - // Remove it from the scheduler - tasks.remove(id.0); - } + // // Remove it from the scheduler + // tasks.remove(id.0); + // } else { + // println!("task not ready yet, but we gave it a handle to the waker"); + // } } pub(crate) fn acquire_suspense_boundary(&self, id: ScopeId) -> Rc { @@ -45,69 +51,69 @@ impl VirtualDom { } pub(crate) fn handle_suspense_wakeup(&mut self, id: SuspenseId) { - let leaf = self - .scheduler - .leaves - .borrow_mut() - .get(id.0) - .unwrap() - .clone(); + // let leaf = self + // .scheduler + // .leaves + // .borrow_mut() + // .get(id.0) + // .unwrap() + // .clone(); - let scope_id = leaf.scope_id; + // let scope_id = leaf.scope_id; - // todo: cache the waker - let waker = leaf.waker(); - let mut cx = Context::from_waker(&waker); + // // todo: cache the waker + // let waker = leaf.waker(); + // let mut cx = Context::from_waker(&waker); - // Safety: the future is always pinned to the bump arena - let mut pinned = unsafe { std::pin::Pin::new_unchecked(&mut *leaf.task) }; - let as_pinned_mut = &mut pinned; + // // Safety: the future is always pinned to the bump arena + // let mut pinned = unsafe { std::pin::Pin::new_unchecked(&mut *leaf.task) }; + // let as_pinned_mut = &mut pinned; - // the component finished rendering and gave us nodes - // we should attach them to that component and then render its children - // continue rendering the tree until we hit yet another suspended component - if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) { - let fiber = self.acquire_suspense_boundary(leaf.scope_id); + // // the component finished rendering and gave us nodes + // // we should attach them to that component and then render its children + // // continue rendering the tree until we hit yet another suspended component + // if let Poll::Ready(new_nodes) = as_pinned_mut.poll_unpin(&mut cx) { + // let fiber = self.acquire_suspense_boundary(leaf.scope_id); - let scope = &mut self.scopes[scope_id.0]; - let arena = scope.current_frame(); + // let scope = &mut self.scopes[scope_id.0]; + // let arena = scope.current_frame(); - let ret = arena.bump.alloc(match new_nodes { - Some(new) => RenderReturn::Ready(new), - None => RenderReturn::default(), - }); + // let ret = arena.bump.alloc(match new_nodes { + // Some(new) => RenderReturn::Ready(new), + // None => RenderReturn::default(), + // }); - arena.node.set(ret); + // arena.node.set(ret); - fiber.waiting_on.borrow_mut().remove(&id); + // fiber.waiting_on.borrow_mut().remove(&id); - if let RenderReturn::Ready(template) = ret { - let mutations_ref = &mut fiber.mutations.borrow_mut(); - let mutations = &mut **mutations_ref; - let template: &VNode = unsafe { std::mem::transmute(template) }; - let mutations: &mut Mutations = unsafe { std::mem::transmute(mutations) }; + // if let RenderReturn::Ready(template) = ret { + // let mutations_ref = &mut fiber.mutations.borrow_mut(); + // let mutations = &mut **mutations_ref; + // let template: &VNode = unsafe { std::mem::transmute(template) }; + // let mutations: &mut Mutations = unsafe { std::mem::transmute(mutations) }; - std::mem::swap(&mut self.mutations, mutations); + // std::mem::swap(&mut self.mutations, mutations); - let place_holder_id = scope.placeholder.get().unwrap(); - self.scope_stack.push(scope_id); - let created = self.create(template); - self.scope_stack.pop(); - mutations.push(Mutation::ReplaceWith { - id: place_holder_id, - m: created, - }); + // let place_holder_id = scope.placeholder.get().unwrap(); + // self.scope_stack.push(scope_id); + // let created = self.create(template); + // self.scope_stack.pop(); + // mutations.push(Mutation::ReplaceWith { + // id: place_holder_id, + // m: created, + // }); - for leaf in self.collected_leaves.drain(..) { - fiber.waiting_on.borrow_mut().insert(leaf); - } + // for leaf in self.collected_leaves.drain(..) { + // fiber.waiting_on.borrow_mut().insert(leaf); + // } - std::mem::swap(&mut self.mutations, mutations); + // std::mem::swap(&mut self.mutations, mutations); - if fiber.waiting_on.borrow().is_empty() { - self.finished_fibers.push(fiber.id); - } - } - } + // if fiber.waiting_on.borrow().is_empty() { + // self.finished_fibers.push(fiber.id); + // } + // } + // } } } diff --git a/packages/core/src/scheduler/waker.rs b/packages/core/src/scheduler/waker.rs deleted file mode 100644 index fc75fe8fd..000000000 --- a/packages/core/src/scheduler/waker.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::mem; -use std::sync::Arc; -use std::task::{RawWaker, RawWakerVTable, Waker}; - -pub trait ArcWake: Sized { - /// Create a waker from this self-wakening object - fn waker(self: &Arc) -> Waker { - unsafe fn rc_vtable() -> &'static RawWakerVTable { - &RawWakerVTable::new( - |data| { - let arc = mem::ManuallyDrop::new(Arc::::from_raw(data.cast::())); - let _rc_clone: mem::ManuallyDrop<_> = arc.clone(); - RawWaker::new(data, rc_vtable::()) - }, - |data| Arc::from_raw(data.cast::()).wake(), - |data| { - let arc = mem::ManuallyDrop::new(Arc::::from_raw(data.cast::())); - ArcWake::wake_by_ref(&arc); - }, - |data| drop(Arc::::from_raw(data.cast::())), - ) - } - - unsafe { - Waker::from_raw(RawWaker::new( - Arc::into_raw(self.clone()).cast(), - rc_vtable::(), - )) - } - } - - fn wake_by_ref(arc_self: &Arc); - - fn wake(self: Arc) { - Self::wake_by_ref(&self) - } -} diff --git a/packages/core/src/scope_arena.rs b/packages/core/src/scope_arena.rs index fbe865021..6d574bf7e 100644 --- a/packages/core/src/scope_arena.rs +++ b/packages/core/src/scope_arena.rs @@ -2,9 +2,8 @@ use crate::{ any_props::AnyProps, bump_frame::BumpFrame, innerlude::DirtyScope, - innerlude::{SuspenseId, SuspenseLeaf}, + innerlude::{SuspenseHandle, SuspenseId, SuspenseLeaf}, nodes::RenderReturn, - scheduler::ArcWake, scopes::{ScopeId, ScopeState}, virtual_dom::VirtualDom, }; @@ -90,15 +89,19 @@ impl VirtualDom { let entry = leaves.vacant_entry(); let suspense_id = SuspenseId(entry.key()); - let leaf = Arc::new(SuspenseLeaf { + let leaf = SuspenseLeaf { scope_id, task: task.as_mut(), id: suspense_id, tx: self.scheduler.sender.clone(), notified: Default::default(), - }); + waker: Arc::new(SuspenseHandle { + id: suspense_id, + tx: self.scheduler.sender.clone(), + }), + }; - let waker = leaf.waker(); + let waker = futures_util::task::waker(leaf.waker.clone()); let mut cx = Context::from_waker(&waker); // safety: the task is already pinned in the bump arena diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 47cbe7245..2306073cf 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -13,10 +13,21 @@ use crate::{ scopes::{ScopeId, ScopeState}, AttributeValue, Element, Event, Scope, SuspenseContext, }; -use futures_util::{pin_mut, StreamExt}; +use futures_util::{ + pin_mut, + stream::{futures_unordered, FuturesUnordered}, + StreamExt, +}; use rustc_hash::FxHashMap; use slab::Slab; -use std::{any::Any, borrow::BorrowMut, cell::Cell, collections::BTreeSet, future::Future, rc::Rc}; +use std::{ + any::Any, + borrow::BorrowMut, + cell::Cell, + collections::{BTreeSet, HashSet}, + future::Future, + rc::Rc, +}; /// A virtual node system that progresses user events and diffs UI trees. /// @@ -420,7 +431,7 @@ impl VirtualDom { loop { match some_msg.take() { // If a bunch of messages are ready in a sequence, try to pop them off synchronously - Some(msg) => match msg { + Some(msg) => match dbg!(msg) { SchedulerMsg::Immediate(id) => self.mark_dirty(id), SchedulerMsg::TaskNotified(task) => self.handle_task_wakeup(task), SchedulerMsg::SuspenseNotified(id) => self.handle_suspense_wakeup(id), @@ -430,14 +441,20 @@ impl VirtualDom { None => { match self.rx.try_next() { Ok(Some(val)) => some_msg = Some(val), - Ok(None) => return, + Ok(None) => panic!("Scheduler channel closed"), Err(_) => { // If we have any dirty scopes, or finished fiber trees then we should exit if !self.dirty_scopes.is_empty() || !self.finished_fibers.is_empty() { return; } - some_msg = self.rx.next().await + println!("Waiting for next message..."); + let mut tasks = self.scheduler.tasks.borrow_mut(); + + let takss = &mut *tasks; + + use futures_util::StreamExt; + takss.next().await; } } } diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 929f1ce4a..fdc38db3a 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -13,6 +13,8 @@ use wry::application::event_loop::EventLoopProxy; #[cfg(target_os = "ios")] use wry::application::platform::ios::WindowExtIOS; use wry::application::window::Fullscreen as WryFullscreen; +use wry::application::window::Window; +use wry::webview::WebView; use UserWindowEvent::*; @@ -40,16 +42,32 @@ pub fn use_window(cx: &ScopeState) -> &DesktopContext { #[derive(Clone)] pub struct DesktopContext { /// The wry/tao proxy to the current window + pub webview: Rc, + + /// The proxy to the event loop pub proxy: ProxyType, + + /// The receiver for eval results since eval is async pub(super) eval_reciever: Rc>>, } +/// A smart pointer to the current window. +impl std::ops::Deref for DesktopContext { + type Target = Window; + + fn deref(&self) -> &Self::Target { + &self.webview.window() + } +} + impl DesktopContext { pub(crate) fn new( + webview: Rc, proxy: ProxyType, eval_reciever: tokio::sync::mpsc::UnboundedReceiver, ) -> Self { Self { + webview, proxy, eval_reciever: Rc::new(tokio::sync::Mutex::new(eval_reciever)), } @@ -66,82 +84,21 @@ impl DesktopContext { pub fn drag(&self) { let _ = self.proxy.send_event(DragWindow); } - - /// set window minimize state - pub fn set_minimized(&self, minimized: bool) { - let _ = self.proxy.send_event(Minimize(minimized)); - } - - /// set window maximize state - pub fn set_maximized(&self, maximized: bool) { - let _ = self.proxy.send_event(Maximize(maximized)); - } - /// toggle window maximize state pub fn toggle_maximized(&self) { let _ = self.proxy.send_event(MaximizeToggle); } - /// set window visible or not - pub fn set_visible(&self, visible: bool) { - let _ = self.proxy.send_event(Visible(visible)); - } - /// close window pub fn close(&self) { let _ = self.proxy.send_event(CloseWindow); } - /// set window to focus - pub fn focus(&self) { - let _ = self.proxy.send_event(FocusWindow); - } - /// change window to fullscreen pub fn set_fullscreen(&self, fullscreen: bool) { let _ = self.proxy.send_event(Fullscreen(fullscreen)); } - /// set resizable state - pub fn set_resizable(&self, resizable: bool) { - let _ = self.proxy.send_event(Resizable(resizable)); - } - - /// set the window always on top - pub fn set_always_on_top(&self, top: bool) { - let _ = self.proxy.send_event(AlwaysOnTop(top)); - } - - /// set cursor visible or not - pub fn set_cursor_visible(&self, visible: bool) { - let _ = self.proxy.send_event(CursorVisible(visible)); - } - - /// set cursor grab - pub fn set_cursor_grab(&self, grab: bool) { - let _ = self.proxy.send_event(CursorGrab(grab)); - } - - /// set window title - pub fn set_title(&self, title: &str) { - let _ = self.proxy.send_event(SetTitle(String::from(title))); - } - - /// change window to borderless - pub fn set_decorations(&self, decoration: bool) { - let _ = self.proxy.send_event(SetDecorations(decoration)); - } - - /// set window zoom level - pub fn set_zoom_level(&self, scale_factor: f64) { - let _ = self.proxy.send_event(SetZoomLevel(scale_factor)); - } - - /// modifies the inner size of the window - pub fn set_inner_size(&self, logical_size: LogicalSize) { - let _ = self.proxy.send_event(SetInnerSize(logical_size)); - } - /// launch print modal pub fn print(&self) { let _ = self.proxy.send_event(Print); @@ -177,31 +134,20 @@ pub enum UserWindowEvent { Poll, + UserEvent(serde_json::Value), + CloseWindow, DragWindow, - FocusWindow, - Visible(bool), - Minimize(bool), - Maximize(bool), MaximizeToggle, - Resizable(bool), - AlwaysOnTop(bool), + Fullscreen(bool), - CursorVisible(bool), - CursorGrab(bool), - - SetTitle(String), - SetDecorations(bool), - - SetZoomLevel(f64), - SetInnerSize(LogicalSize), - Print, DevTool, Eval(String), + EvalResult(serde_json::Value), #[cfg(target_os = "ios")] PushView(objc_id::ShareId), @@ -236,18 +182,17 @@ impl DesktopController { // if the drag_window has any errors, we don't do anything window.fullscreen().is_none().then(|| window.drag_window()); } - Visible(state) => window.set_visible(state), - Minimize(state) => window.set_minimized(state), - Maximize(state) => window.set_maximized(state), + MaximizeToggle => window.set_maximized(!window.is_maximized()), Fullscreen(state) => { if let Some(handle) = window.current_monitor() { window.set_fullscreen(state.then_some(WryFullscreen::Borderless(Some(handle)))); } } - FocusWindow => window.set_focus(), - Resizable(state) => window.set_resizable(state), - AlwaysOnTop(state) => window.set_always_on_top(state), + + UserEvent(event) => { + // + } Eval(code) => { let script = format!( @@ -261,17 +206,11 @@ impl DesktopController { log::warn!("Eval script error: {e}"); } } - CursorVisible(state) => window.set_cursor_visible(state), - CursorGrab(state) => { - let _ = window.set_cursor_grab(state); + + EvalResult(result) => { + // todo } - SetTitle(content) => window.set_title(&content), - SetDecorations(state) => window.set_decorations(state), - - SetZoomLevel(scale_factor) => webview.zoom(scale_factor), - SetInnerSize(logical_size) => window.set_inner_size(logical_size), - Poll => { // todo } diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 14ce5f2f7..b620e7857 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -13,6 +13,7 @@ mod protocol; #[cfg(all(feature = "hot-reload", debug_assertions))] mod hot_reload; +use dioxus_html::{a, HtmlEvent}; use futures_util::task::ArcWake; use serde_json::Value; use std::cell::RefCell; @@ -115,23 +116,18 @@ pub async fn launch_cfg(root: Component, config_builder: Config) { /// } /// ``` pub async fn launch_with_props(root: Component

, props: P, mut cfg: Config) { - let mut event_loop = EventLoop::with_user_event(); + let event_loop = EventLoop::with_user_event(); - let is_ready = Arc::new(AtomicBool::new(false)); - let (eval_sender, eval_reciever) = tokio::sync::mpsc::unbounded_channel(); - - let mut dom = VirtualDom::new_with_props(root, props).with_root_context(DesktopContext::new( - event_loop.create_proxy(), - eval_reciever, - )); + let mut dom = VirtualDom::new_with_props(root, props); let proxy = event_loop.create_proxy(); + // We want to poll the virtualdom and the event loop at the same time + // So the waker will be connected to both let waker = futures_util::task::waker(Arc::new(DomHandle { proxy: proxy.clone(), })); - let mut events = Rc::new(RefCell::new(vec![])); let mut webviews = HashMap::new(); event_loop.run(move |window_event, event_loop, control_flow| { @@ -139,20 +135,21 @@ pub async fn launch_with_props(root: Component

, props: P, match window_event { Event::NewEvents(StartCause::Init) => { - let window = build_webview( - &mut cfg, - event_loop, - is_ready.clone(), - proxy.clone(), - eval_sender.clone(), - events.clone(), - ); + let (eval_sender, eval_reciever) = tokio::sync::mpsc::unbounded_channel(); + let window = Rc::new(build_webview(&mut cfg, event_loop, proxy.clone())); + let ctx = DesktopContext::new(window.clone(), proxy.clone(), eval_reciever); + dom.base_scope().provide_context(ctx); + webviews.insert(window.window().id(), window.clone()); - webviews.insert(window.window().id(), window); - - // desktop.start(&mut cfg, event_loop); + proxy.send_event(UserWindowEvent::Poll).unwrap(); } + Event::MainEventsCleared => {} + Event::Resumed => {} + Event::Suspended => {} + Event::LoopDestroyed => {} + Event::RedrawRequested(_id) => {} + Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Destroyed { .. } => { @@ -165,19 +162,65 @@ pub async fn launch_with_props(root: Component

, props: P, println!("user event: {:?}", user_event); match user_event { + UserWindowEvent::UserEvent(json_value) => { + if let Ok(value) = serde_json::from_value::(json_value) { + let HtmlEvent { + name, + element, + bubbles, + data, + } = value; + + dom.handle_event(&name, data.into_any(), element, bubbles); + + let edits = dom.render_immediate(); + + let serialized = serde_json::to_string(&edits).unwrap(); + + let (_id, view) = webviews.iter_mut().next().unwrap(); + + view.evaluate_script(&format!( + "window.interpreter.handleEdits({})", + serialized + )) + .unwrap(); + } + } + UserWindowEvent::Poll => { let mut cx = std::task::Context::from_waker(&waker); - let render = { - let fut = dom.wait_for_work(); - pin_mut!(fut); - matches!(fut.poll_unpin(&mut cx), std::task::Poll::Ready(_)) - }; + println!("polling.."); + + loop { + { + println!("wait for next work"); + + let fut = dom.wait_for_work(); + pin_mut!(fut); + + match fut.poll_unpin(&mut cx) { + std::task::Poll::Ready(_) => { + println!("work ready"); + } + std::task::Poll::Pending => break, + } + } + + println!("rendering.."); - if render { let edits = dom.render_immediate(); // apply the edits + let serialized = serde_json::to_string(&edits).unwrap(); + + let (_id, view) = webviews.iter_mut().next().unwrap(); + + view.evaluate_script(&format!( + "window.interpreter.handleEdits({})", + serialized + )) + .unwrap(); } } @@ -200,11 +243,7 @@ pub async fn launch_with_props(root: Component

, props: P, } } } - Event::MainEventsCleared => {} - Event::Resumed => {} - Event::Suspended => {} - Event::LoopDestroyed => {} - Event::RedrawRequested(_id) => {} + _ => {} } }) @@ -223,10 +262,7 @@ impl ArcWake for DomHandle { fn build_webview( cfg: &mut Config, event_loop: &tao::event_loop::EventLoopWindowTarget, - is_ready: Arc, proxy: tao::event_loop::EventLoopProxy, - eval_sender: tokio::sync::mpsc::UnboundedSender, - event_tx: Rc>>, ) -> wry::webview::WebView { let builder = cfg.window.clone(); let window = builder.build(event_loop).unwrap(); @@ -264,29 +300,23 @@ fn build_webview( match message.method() { "eval_result" => { - let result = message.params(); - eval_sender.send(result).unwrap(); + let _ = proxy.send_event(UserWindowEvent::EvalResult(message.params())); } "user_event" => { - _ = event_tx.borrow_mut().push(message.params()); - let _ = proxy.send_event(UserWindowEvent::Poll); + let _ = proxy.send_event(UserWindowEvent::UserEvent(message.params())); } "initialize" => { - is_ready.store(true, std::sync::atomic::Ordering::Relaxed); let _ = proxy.send_event(UserWindowEvent::EditsReady); } - "browser_open" => { - let data = message.params(); - log::trace!("Open browser: {:?}", data); - if let Some(temp) = data.as_object() { - if temp.contains_key("href") { - let url = temp.get("href").unwrap().as_str().unwrap(); - if let Err(e) = webbrowser::open(url) { - log::error!("Open Browser error: {:?}", e); - } + "browser_open" => match message.params().as_object() { + Some(temp) if temp.contains_key("href") => { + let open = webbrowser::open(temp["href"].as_str().unwrap()); + if let Err(e) = open { + log::error!("Open Browser error: {:?}", e); } } - } + _ => (), + }, _ => (), } })