feat: event handling on desktop
This commit is contained in:
1 parent
073fea9ed3
commit
30ef225812
29 files changed
+424
-205
No files matched your search
@@ -1,10 +1,17 @@
|
||||
use crate::desktop_context::{DesktopContext, UserWindowEvent};
|
||||
|
||||
use dioxus_core::*;
|
||||
use dioxus_html::events::*;
|
||||
use futures_channel::mpsc::UnboundedReceiver;
|
||||
use futures_util::StreamExt;
|
||||
use serde::Deserialize;
|
||||
use serde_json::from_value;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::Arc,
|
||||
sync::{atomic::AtomicBool, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
use wry::{
|
||||
self,
|
||||
@@ -12,6 +19,25 @@ use wry::{
|
||||
webview::WebView,
|
||||
};
|
||||
|
||||
macro_rules! match_data {
|
||||
(
|
||||
$m:ident;
|
||||
$name:ident;
|
||||
$(
|
||||
$tip:ty => $($mname:literal)|* ;
|
||||
)*
|
||||
) => {
|
||||
match $name {
|
||||
$( $($mname)|* => {
|
||||
println!("casting to type {:?}", std::any::TypeId::of::<$tip>());
|
||||
let val: $tip = from_value::<$tip>($m).ok()?;
|
||||
Rc::new(val) as Rc<dyn Any>
|
||||
})*
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) struct DesktopController {
|
||||
pub(super) webviews: HashMap<WindowId, WebView>,
|
||||
pub(super) pending_edits: Arc<Mutex<Vec<String>>>,
|
||||
@@ -26,6 +52,7 @@ impl DesktopController {
|
||||
root: Component<P>,
|
||||
props: P,
|
||||
proxy: EventLoopProxy<UserWindowEvent>,
|
||||
mut event_rx: UnboundedReceiver<serde_json::Value>,
|
||||
) -> Self {
|
||||
let edit_queue = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
@@ -33,55 +60,51 @@ impl DesktopController {
|
||||
let desktop_context_proxy = proxy.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
// We create the runtime as multithreaded, so you can still "spawn" onto multiple threads
|
||||
// We create the runtime as multithreaded, so you can still "tokio::spawn" onto multiple threads
|
||||
// I'd personally not require tokio to be built-in to Dioxus-Desktop, but the DX is worse without it
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
println!("starting vdom");
|
||||
|
||||
let mut dom = VirtualDom::new_with_props(root, props);
|
||||
|
||||
let window_context = DesktopContext::new(desktop_context_proxy);
|
||||
|
||||
dom.base_scope().provide_context(window_context);
|
||||
let mut dom = VirtualDom::new_with_props(root, props)
|
||||
.with_root_context(DesktopContext::new(desktop_context_proxy));
|
||||
|
||||
{
|
||||
let edits = dom.rebuild();
|
||||
let mut queue = edit_queue.lock().unwrap();
|
||||
queue.push(serde_json::to_string(&edits.template_mutations).unwrap());
|
||||
queue.push(serde_json::to_string(&edits.edits).unwrap());
|
||||
proxy.send_event(UserWindowEvent::Update).unwrap();
|
||||
}
|
||||
|
||||
// println!("got muts: {:#?}", edits);
|
||||
|
||||
{
|
||||
let mut queue = edit_queue.lock().unwrap();
|
||||
queue.push(serde_json::to_string(&edits.template_mutations).unwrap());
|
||||
queue.push(serde_json::to_string(&edits.edits).unwrap());
|
||||
proxy.send_event(UserWindowEvent::Update).unwrap();
|
||||
drop(queue);
|
||||
}
|
||||
|
||||
runtime.block_on(async move {
|
||||
loop {
|
||||
// todo: add the channel of the event loop in
|
||||
tokio::select! {
|
||||
_ = dom.wait_for_work() => {}
|
||||
Some(json_value) = event_rx.next() => {
|
||||
if let Ok(value) = serde_json::from_value::<EventMessage>(json_value) {
|
||||
let name = value.event.clone();
|
||||
let el_id = ElementId(value.mounted_dom_id);
|
||||
let evt = decode_event(value);
|
||||
|
||||
if let Some(evt) = evt {
|
||||
dom.handle_event(&name, evt, el_id, true, EventPriority::Medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let muts = dom
|
||||
.render_with_deadline(tokio::time::sleep(
|
||||
tokio::time::Duration::from_millis(16),
|
||||
))
|
||||
.render_with_deadline(tokio::time::sleep(Duration::from_millis(16)))
|
||||
.await;
|
||||
|
||||
{
|
||||
let mut queue = edit_queue.lock().unwrap();
|
||||
|
||||
queue.push(serde_json::to_string(&muts.template_mutations).unwrap());
|
||||
queue.push(serde_json::to_string(&muts.edits).unwrap());
|
||||
|
||||
drop(queue);
|
||||
let _ = proxy.send_event(UserWindowEvent::Update);
|
||||
}
|
||||
|
||||
let _ = proxy.send_event(UserWindowEvent::Update);
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -113,8 +136,6 @@ impl DesktopController {
|
||||
|
||||
let (_id, view) = self.webviews.iter_mut().next().unwrap();
|
||||
|
||||
println!("sending edits {:#?}", new_queue);
|
||||
|
||||
for edit in new_queue.drain(..) {
|
||||
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
|
||||
.unwrap();
|
||||
@@ -122,3 +143,44 @@ impl DesktopController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EventMessage {
|
||||
contents: serde_json::Value,
|
||||
event: String,
|
||||
mounted_dom_id: usize,
|
||||
}
|
||||
|
||||
fn decode_event(value: EventMessage) -> Option<Rc<dyn Any>> {
|
||||
let val = value.contents;
|
||||
let name = value.event.as_str();
|
||||
let el_id = ElementId(value.mounted_dom_id);
|
||||
type DragData = MouseData;
|
||||
|
||||
let evt = match_data! { val; name;
|
||||
MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup";
|
||||
ClipboardData => "copy" | "cut" | "paste";
|
||||
CompositionData => "compositionend" | "compositionstart" | "compositionupdate";
|
||||
KeyboardData => "keydown" | "keypress" | "keyup";
|
||||
FocusData => "blur" | "focus" | "focusin" | "focusout";
|
||||
FormData => "change" | "input" | "invalid" | "reset" | "submit";
|
||||
DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop";
|
||||
PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture";
|
||||
SelectionData => "selectstart" | "selectionchange" | "select";
|
||||
TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart";
|
||||
ScrollData => "scroll";
|
||||
WheelData => "wheel";
|
||||
MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied"
|
||||
| "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata"
|
||||
| "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress"
|
||||
| "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate"
|
||||
| "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout";
|
||||
AnimationData => "animationstart" | "animationend" | "animationiteration";
|
||||
TransitionData => "transitionend";
|
||||
ToggleData => "toggle";
|
||||
// ImageData => "load" | "error";
|
||||
// OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange";
|
||||
};
|
||||
|
||||
Some(evt)
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
//! Convert a serialized event to an event trigger
|
||||
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dioxus_core::ElementId;
|
||||
use dioxus_core::EventPriority;
|
||||
use dioxus_html::events::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::from_value;
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub(crate) struct IpcMessage {
|
||||
@@ -40,3 +39,11 @@ struct ImEvent {
|
||||
mounted_dom_id: ElementId,
|
||||
contents: serde_json::Value,
|
||||
}
|
||||
|
||||
// pub fn make_synthetic_event(name: &str, val: serde_json::Value) -> Option<Rc<dyn Any>> {
|
||||
// // right now we don't support the datatransfer in Drag
|
||||
// type DragData = MouseData;
|
||||
// type ProgressData = MediaData;
|
||||
|
||||
// Some(res)
|
||||
// }
|
||||
@@ -14,6 +14,7 @@ mod protocol;
|
||||
|
||||
use desktop_context::UserWindowEvent;
|
||||
pub use desktop_context::{use_eval, use_window, DesktopContext};
|
||||
use futures_channel::mpsc::unbounded;
|
||||
pub use wry;
|
||||
pub use wry::application as tao;
|
||||
|
||||
@@ -100,7 +101,9 @@ pub fn launch_cfg(root: Component, config_builder: Config) {
|
||||
pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cfg: Config) {
|
||||
let event_loop = EventLoop::with_user_event();
|
||||
|
||||
let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
|
||||
let (event_tx, event_rx) = unbounded();
|
||||
let mut desktop =
|
||||
DesktopController::new_on_tokio(root, props, event_loop.create_proxy(), event_rx);
|
||||
let proxy = event_loop.create_proxy();
|
||||
|
||||
// We assume that if the icon is None, then the user just didnt set it
|
||||
@@ -135,6 +138,7 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
||||
let custom_head = cfg.custom_head.clone();
|
||||
let resource_dir = cfg.resource_dir.clone();
|
||||
let index_file = cfg.custom_index.clone();
|
||||
let event_tx = event_tx.clone();
|
||||
|
||||
let mut webview = WebViewBuilder::new(window)
|
||||
.unwrap()
|
||||
@@ -145,10 +149,7 @@ pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cf
|
||||
parse_ipc_message(&payload)
|
||||
.map(|message| match message.method() {
|
||||
"user_event" => {
|
||||
println!("user event!");
|
||||
// let event = trigger_from_serialized(message.params());
|
||||
// log::trace!("User event: {:?}", event);
|
||||
// sender.unbounded_send(SchedulerMsg::Event(event)).unwrap();
|
||||
_ = event_tx.unbounded_send(message.params());
|
||||
}
|
||||
"initialize" => {
|
||||
is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
Reference in new issue
Block a user