mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-11 07:04:13 +00:00
implement for web and desktop
This commit is contained in:
parent
ab743e01b7
commit
0d9c350d5e
8 changed files with 69 additions and 30 deletions
|
@ -102,7 +102,7 @@ impl<'a> VNode<'a> {
|
||||||
///
|
///
|
||||||
/// For this to work properly, the [`Template::name`] *must* be unique across your entire project. This can be done via variety of
|
/// For this to work properly, the [`Template::name`] *must* be unique across your entire project. This can be done via variety of
|
||||||
/// ways, with the suggested approach being the unique code location (file, line, col, etc).
|
/// ways, with the suggested approach being the unique code location (file, line, col, etc).
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||||
pub struct Template<'a> {
|
pub struct Template<'a> {
|
||||||
/// The name of the template. This must be unique across your entire program for template diffing to work properly
|
/// The name of the template. This must be unique across your entire program for template diffing to work properly
|
||||||
|
@ -113,26 +113,47 @@ pub struct Template<'a> {
|
||||||
/// The list of template nodes that make up the template
|
/// The list of template nodes that make up the template
|
||||||
///
|
///
|
||||||
/// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
|
/// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
|
||||||
|
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||||
pub roots: &'a [TemplateNode<'a>],
|
pub roots: &'a [TemplateNode<'a>],
|
||||||
|
|
||||||
/// The paths of each node relative to the root of the template.
|
/// The paths of each node relative to the root of the template.
|
||||||
///
|
///
|
||||||
/// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
|
/// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
|
||||||
/// topmost element, not the `roots` field.
|
/// topmost element, not the `roots` field.
|
||||||
|
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||||
pub node_paths: &'a [&'a [u8]],
|
pub node_paths: &'a [&'a [u8]],
|
||||||
|
|
||||||
/// The paths of each dynamic attribute relative to the root of the template
|
/// The paths of each dynamic attribute relative to the root of the template
|
||||||
///
|
///
|
||||||
/// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
|
/// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
|
||||||
/// topmost element, not the `roots` field.
|
/// topmost element, not the `roots` field.
|
||||||
|
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||||
pub attr_paths: &'a [&'a [u8]],
|
pub attr_paths: &'a [&'a [u8]],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
fn deserialize_leaky<'a, 'de, T: serde::Deserialize<'de>, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<&'a [T], D::Error>
|
||||||
|
where
|
||||||
|
T: serde::Deserialize<'de>,
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
let deserialized = Box::<[T]>::deserialize(deserializer)?;
|
||||||
|
Ok(&*Box::leak(deserialized))
|
||||||
|
}
|
||||||
|
|
||||||
/// A statically known node in a layout.
|
/// A statically known node in a layout.
|
||||||
///
|
///
|
||||||
/// This can be created at compile time, saving the VirtualDom time when diffing the tree
|
/// This can be created at compile time, saving the VirtualDom time when diffing the tree
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize), serde(tag = "type"))]
|
#[cfg_attr(
|
||||||
|
feature = "serialize",
|
||||||
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
serde(tag = "type")
|
||||||
|
)]
|
||||||
pub enum TemplateNode<'a> {
|
pub enum TemplateNode<'a> {
|
||||||
/// An statically known element in the dom.
|
/// An statically known element in the dom.
|
||||||
///
|
///
|
||||||
|
@ -152,9 +173,11 @@ pub enum TemplateNode<'a> {
|
||||||
/// A list of possibly dynamic attribues for this element
|
/// A list of possibly dynamic attribues for this element
|
||||||
///
|
///
|
||||||
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
|
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
|
||||||
|
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||||
attrs: &'a [TemplateAttribute<'a>],
|
attrs: &'a [TemplateAttribute<'a>],
|
||||||
|
|
||||||
/// A list of template nodes that define another set of template nodes
|
/// A list of template nodes that define another set of template nodes
|
||||||
|
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||||
children: &'a [TemplateNode<'a>],
|
children: &'a [TemplateNode<'a>],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::desktop_context::{DesktopContext, UserWindowEvent};
|
use crate::desktop_context::{DesktopContext, UserWindowEvent};
|
||||||
use crate::events::{decode_event, EventMessage};
|
use crate::events::{decode_event, EventMessage};
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use futures_channel::mpsc::{unbounded, UnboundedSender};
|
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
|
@ -24,6 +24,8 @@ pub(super) struct DesktopController {
|
||||||
pub(super) is_ready: Arc<AtomicBool>,
|
pub(super) is_ready: Arc<AtomicBool>,
|
||||||
pub(super) proxy: EventLoopProxy<UserWindowEvent>,
|
pub(super) proxy: EventLoopProxy<UserWindowEvent>,
|
||||||
pub(super) event_tx: UnboundedSender<serde_json::Value>,
|
pub(super) event_tx: UnboundedSender<serde_json::Value>,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub(super) templates_tx: UnboundedSender<Template<'static>>,
|
||||||
|
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
pub(super) views: Vec<*mut Object>,
|
pub(super) views: Vec<*mut Object>,
|
||||||
|
@ -39,6 +41,7 @@ impl DesktopController {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let edit_queue = Arc::new(Mutex::new(Vec::new()));
|
let edit_queue = Arc::new(Mutex::new(Vec::new()));
|
||||||
let (event_tx, mut event_rx) = unbounded();
|
let (event_tx, mut event_rx) = unbounded();
|
||||||
|
let (templates_tx, mut templates_rx) = unbounded();
|
||||||
let proxy2 = proxy.clone();
|
let proxy2 = proxy.clone();
|
||||||
|
|
||||||
let pending_edits = edit_queue.clone();
|
let pending_edits = edit_queue.clone();
|
||||||
|
@ -64,6 +67,18 @@ impl DesktopController {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
template = {
|
||||||
|
#[allow(unused)]
|
||||||
|
fn maybe_future<'a>(templates_rx: &'a mut UnboundedReceiver<Template<'static>>) -> impl Future<Output = dioxus_core::Template<'static>> + 'a {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
return templates_rx.select_next_some();
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
return std::future::pending();
|
||||||
|
}
|
||||||
|
maybe_future(&mut templates_rx)
|
||||||
|
} => {
|
||||||
|
dom.replace_template(template);
|
||||||
|
}
|
||||||
_ = dom.wait_for_work() => {}
|
_ = dom.wait_for_work() => {}
|
||||||
Some(json_value) = event_rx.next() => {
|
Some(json_value) = event_rx.next() => {
|
||||||
if let Ok(value) = serde_json::from_value::<EventMessage>(json_value) {
|
if let Ok(value) = serde_json::from_value::<EventMessage>(json_value) {
|
||||||
|
@ -93,6 +108,8 @@ impl DesktopController {
|
||||||
quit_app_on_close: true,
|
quit_app_on_close: true,
|
||||||
proxy: proxy2,
|
proxy: proxy2,
|
||||||
event_tx,
|
event_tx,
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
templates_tx,
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
views: vec![],
|
views: vec![],
|
||||||
}
|
}
|
||||||
|
@ -123,8 +140,4 @@ impl DesktopController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_template(&self, _serialized_template: String) {
|
|
||||||
todo!("hot reloading currently WIP")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,11 +171,6 @@ pub enum UserWindowEvent {
|
||||||
DragWindow,
|
DragWindow,
|
||||||
FocusWindow,
|
FocusWindow,
|
||||||
|
|
||||||
/// Set a new Dioxus template for hot-reloading
|
|
||||||
///
|
|
||||||
/// Is a no-op in release builds. Must fit the right format for templates
|
|
||||||
SetTemplate(String),
|
|
||||||
|
|
||||||
Visible(bool),
|
Visible(bool),
|
||||||
Minimize(bool),
|
Minimize(bool),
|
||||||
Maximize(bool),
|
Maximize(bool),
|
||||||
|
@ -223,7 +218,6 @@ impl DesktopController {
|
||||||
|
|
||||||
match user_event {
|
match user_event {
|
||||||
Initialize | EditsReady => self.try_load_ready_webviews(),
|
Initialize | EditsReady => self.try_load_ready_webviews(),
|
||||||
SetTemplate(template) => self.set_template(template),
|
|
||||||
CloseWindow => *control_flow = ControlFlow::Exit,
|
CloseWindow => *control_flow = ControlFlow::Exit,
|
||||||
DragWindow => {
|
DragWindow => {
|
||||||
// if the drag_window has any errors, we don't do anything
|
// if the drag_window has any errors, we don't do anything
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use dioxus_core::VirtualDom;
|
use dioxus_core::Template;
|
||||||
|
|
||||||
use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
|
use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
@ -13,7 +13,7 @@ fn handle_error(connection: std::io::Result<LocalSocketStream>) -> Option<LocalS
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(_dom: &VirtualDom) {
|
pub(crate) fn init(proxy: futures_channel::mpsc::UnboundedSender<Template<'static>>) {
|
||||||
let latest_in_connection: Arc<Mutex<Option<BufReader<LocalSocketStream>>>> =
|
let latest_in_connection: Arc<Mutex<Option<BufReader<LocalSocketStream>>>> =
|
||||||
Arc::new(Mutex::new(None));
|
Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
@ -36,11 +36,9 @@ pub(crate) fn init(_dom: &VirtualDom) {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
match conn.read_line(&mut buf) {
|
match conn.read_line(&mut buf) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
todo!()
|
let msg: Template<'static> =
|
||||||
// let msg: SetTemplateMsg = serde_json::from_str(&buf).unwrap();
|
serde_json::from_str(Box::leak(buf.into_boxed_str())).unwrap();
|
||||||
// channel
|
proxy.unbounded_send(msg).unwrap();
|
||||||
// .start_send(SchedulerMsg::SetTemplate(Box::new(msg)))
|
|
||||||
// .unwrap();
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if err.kind() != std::io::ErrorKind::WouldBlock {
|
if err.kind() != std::io::ErrorKind::WouldBlock {
|
||||||
|
|
|
@ -106,6 +106,9 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
||||||
let event_loop = EventLoop::with_user_event();
|
let event_loop = EventLoop::with_user_event();
|
||||||
let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
|
let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
hot_reload::init(desktop.templates_tx.clone());
|
||||||
|
|
||||||
event_loop.run(move |window_event, event_loop, control_flow| {
|
event_loop.run(move |window_event, event_loop, control_flow| {
|
||||||
*control_flow = ControlFlow::Wait;
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ fn create_rows(c: &mut Criterion) {
|
||||||
|
|
||||||
c.bench_function("create rows", |b| {
|
c.bench_function("create rows", |b| {
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
dom.rebuild();
|
let _ = dom.rebuild();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let g = dom.rebuild();
|
let g = dom.rebuild();
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
use futures_channel::mpsc::UnboundedReceiver;
|
use futures_channel::mpsc::UnboundedReceiver;
|
||||||
|
|
||||||
|
use dioxus_core::Template;
|
||||||
use wasm_bindgen::closure::Closure;
|
use wasm_bindgen::closure::Closure;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::{MessageEvent, WebSocket};
|
use web_sys::{MessageEvent, WebSocket};
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub(crate) fn init() -> UnboundedReceiver<String> {
|
pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
|
||||||
let (tx, rx) = futures_channel::mpsc::unbounded();
|
let (tx, rx) = futures_channel::mpsc::unbounded();
|
||||||
|
|
||||||
std::mem::forget(tx);
|
std::mem::forget(tx);
|
||||||
|
@ -16,7 +17,7 @@ pub(crate) fn init() -> UnboundedReceiver<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub(crate) fn init() -> UnboundedReceiver<String> {
|
pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
let window = web_sys::window().unwrap();
|
let window = web_sys::window().unwrap();
|
||||||
|
@ -39,8 +40,11 @@ pub(crate) fn init() -> UnboundedReceiver<String> {
|
||||||
// change the rsx when new data is received
|
// change the rsx when new data is received
|
||||||
let cl = Closure::wrap(Box::new(move |e: MessageEvent| {
|
let cl = Closure::wrap(Box::new(move |e: MessageEvent| {
|
||||||
if let Ok(text) = e.data().dyn_into::<js_sys::JsString>() {
|
if let Ok(text) = e.data().dyn_into::<js_sys::JsString>() {
|
||||||
if let Ok(val) = text.try_into() {
|
let text: Result<String, _> = text.try_into();
|
||||||
_ = tx.unbounded_send(val);
|
if let Ok(string) = text {
|
||||||
|
if let Ok(template) = serde_json::from_str(Box::leak(string.into_boxed_str())) {
|
||||||
|
_ = tx.unbounded_send(template);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) as Box<dyn FnMut(MessageEvent)>);
|
}) as Box<dyn FnMut(MessageEvent)>);
|
||||||
|
|
|
@ -202,19 +202,23 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
|
||||||
|
|
||||||
// if virtualdom has nothing, wait for it to have something before requesting idle time
|
// if virtualdom has nothing, wait for it to have something before requesting idle time
|
||||||
// if there is work then this future resolves immediately.
|
// if there is work then this future resolves immediately.
|
||||||
let mut res = {
|
let (mut res, template) = {
|
||||||
let work = dom.wait_for_work().fuse();
|
let work = dom.wait_for_work().fuse();
|
||||||
pin_mut!(work);
|
pin_mut!(work);
|
||||||
|
|
||||||
futures_util::select! {
|
futures_util::select! {
|
||||||
_ = work => None,
|
_ = work => (None, None),
|
||||||
_new_template = hotreload_rx.next() => {
|
new_template = hotreload_rx.next() => {
|
||||||
todo!("Implement hot reload");
|
(None, new_template)
|
||||||
}
|
}
|
||||||
evt = rx.next() => evt
|
evt = rx.next() => (evt, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(template) = template {
|
||||||
|
dom.replace_template(template);
|
||||||
|
}
|
||||||
|
|
||||||
// Dequeue all of the events from the channel in send order
|
// Dequeue all of the events from the channel in send order
|
||||||
// todo: we should re-order these if possible
|
// todo: we should re-order these if possible
|
||||||
while let Some(evt) = res {
|
while let Some(evt) = res {
|
||||||
|
|
Loading…
Reference in a new issue