implement on mounted for desktop
This commit is contained in:
1 parent
cb5cb56ad3
commit
7636c046fa
8 files changed
+450
-24
No files matched your search
@@ -3,6 +3,7 @@ use std::rc::Rc;
|
||||
use std::rc::Weak;
|
||||
|
||||
use crate::create_new_window;
|
||||
use crate::element::QueryEngine;
|
||||
use crate::eval::EvalResult;
|
||||
use crate::events::IpcMessage;
|
||||
use crate::shortcut::IntoKeyCode;
|
||||
@@ -62,6 +63,9 @@ pub struct DesktopContext {
|
||||
/// The receiver for eval results since eval is async
|
||||
pub(super) eval: tokio::sync::broadcast::Sender<Value>,
|
||||
|
||||
/// The receiver for queries about elements
|
||||
pub(super) query: QueryEngine,
|
||||
|
||||
pub(super) pending_windows: WebviewQueue,
|
||||
|
||||
pub(crate) event_loop: EventLoopWindowTarget<UserWindowEvent>,
|
||||
@@ -97,6 +101,7 @@ impl DesktopContext {
|
||||
proxy,
|
||||
event_loop,
|
||||
eval: tokio::sync::broadcast::channel(8).0,
|
||||
query: Default::default(),
|
||||
pending_windows: webviews,
|
||||
event_handlers,
|
||||
shortcut_manager,
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use dioxus_core::ElementId;
|
||||
use dioxus_html::{
|
||||
MountedResult, MountedReturn, MountedReturnData, NodeUpdate, NodeUpdateData,
|
||||
RenderedElementBacking,
|
||||
};
|
||||
use slab::Slab;
|
||||
use wry::webview::WebView;
|
||||
|
||||
/// A mounted element passed to onmounted events
|
||||
pub struct DesktopElement {
|
||||
id: ElementId,
|
||||
webview: Rc<WebView>,
|
||||
query: QueryEngine,
|
||||
}
|
||||
|
||||
impl DesktopElement {
|
||||
pub(crate) fn new(id: ElementId, webview: Rc<WebView>, query: QueryEngine) -> Self {
|
||||
Self { id, webview, query }
|
||||
}
|
||||
|
||||
/// Get the id of the element
|
||||
pub fn id(&self) -> ElementId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Get the webview the element is mounted in
|
||||
pub fn webview(&self) -> &Rc<WebView> {
|
||||
&self.webview
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderedElementBacking for DesktopElement {
|
||||
fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn get_client_rect(
|
||||
&self,
|
||||
) -> std::pin::Pin<
|
||||
Box<
|
||||
dyn futures_util::Future<
|
||||
Output = dioxus_html::MountedResult<dioxus_html::geometry::euclid::Rect<f64, f64>>,
|
||||
>,
|
||||
>,
|
||||
> {
|
||||
let fut = self
|
||||
.query
|
||||
.new_query(self.id, NodeUpdateData::GetClientRect {}, &self.webview)
|
||||
.resolve();
|
||||
Box::pin(async move {
|
||||
match fut.await {
|
||||
Some(MountedReturnData::GetClientRect(rect)) => Ok(rect),
|
||||
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
||||
Box::new(DesktopQueryError::MismatchedReturn),
|
||||
)),
|
||||
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
|
||||
DesktopQueryError::FailedToQuery,
|
||||
))),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn scroll_to(
|
||||
&self,
|
||||
behavior: dioxus_html::ScrollBehavior,
|
||||
) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
|
||||
let fut = self
|
||||
.query
|
||||
.new_query(
|
||||
self.id,
|
||||
NodeUpdateData::ScrollTo { behavior },
|
||||
&self.webview,
|
||||
)
|
||||
.resolve();
|
||||
Box::pin(async move {
|
||||
match fut.await {
|
||||
Some(MountedReturnData::ScrollTo(())) => Ok(()),
|
||||
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
||||
Box::new(DesktopQueryError::MismatchedReturn),
|
||||
)),
|
||||
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
|
||||
DesktopQueryError::FailedToQuery,
|
||||
))),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_focus(
|
||||
&self,
|
||||
focus: bool,
|
||||
) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
|
||||
let fut = self
|
||||
.query
|
||||
.new_query(self.id, NodeUpdateData::SetFocus { focus }, &self.webview)
|
||||
.resolve();
|
||||
Box::pin(async move {
|
||||
match fut.await {
|
||||
Some(MountedReturnData::SetFocus(())) => Ok(()),
|
||||
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
||||
Box::new(DesktopQueryError::MismatchedReturn),
|
||||
)),
|
||||
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
|
||||
DesktopQueryError::FailedToQuery,
|
||||
))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct SharedSlab {
|
||||
slab: Rc<RefCell<Slab<()>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct QueryEngine {
|
||||
sender: Rc<tokio::sync::broadcast::Sender<MountedReturn>>,
|
||||
active_requests: SharedSlab,
|
||||
}
|
||||
|
||||
impl Default for QueryEngine {
|
||||
fn default() -> Self {
|
||||
let (sender, _) = tokio::sync::broadcast::channel(8);
|
||||
Self {
|
||||
sender: Rc::new(sender),
|
||||
active_requests: SharedSlab::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryEngine {
|
||||
fn new_query(&self, id: ElementId, update: NodeUpdateData, webview: &WebView) -> Query {
|
||||
let request_id = self.active_requests.slab.borrow_mut().insert(());
|
||||
|
||||
let update = NodeUpdate {
|
||||
id: id.0 as u32,
|
||||
request_id,
|
||||
data: update,
|
||||
};
|
||||
|
||||
// start the query
|
||||
webview
|
||||
.evaluate_script(&format!(
|
||||
"window.interpreter.handleNodeUpdate({})",
|
||||
serde_json::to_string(&update).unwrap()
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
Query {
|
||||
slab: self.active_requests.clone(),
|
||||
id: request_id,
|
||||
reciever: self.sender.subscribe(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&self, data: MountedReturn) {
|
||||
self.sender.send(data).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct Query {
|
||||
slab: SharedSlab,
|
||||
id: usize,
|
||||
reciever: tokio::sync::broadcast::Receiver<MountedReturn>,
|
||||
}
|
||||
|
||||
impl Query {
|
||||
async fn resolve(mut self) -> Option<MountedReturnData> {
|
||||
let result = loop {
|
||||
match self.reciever.recv().await {
|
||||
Ok(result) => {
|
||||
if result.id == self.id {
|
||||
break result.data;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
break None;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Remove the query from the slab
|
||||
self.slab.slab.borrow_mut().remove(self.id);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DesktopQueryError {
|
||||
FailedToQuery,
|
||||
MismatchedReturn,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DesktopQueryError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DesktopQueryError::FailedToQuery => write!(f, "Failed to query the element"),
|
||||
DesktopQueryError::MismatchedReturn => {
|
||||
write!(f, "The return type did not match the query")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for DesktopQueryError {}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
mod cfg;
|
||||
mod desktop_context;
|
||||
mod element;
|
||||
mod escape;
|
||||
mod eval;
|
||||
mod events;
|
||||
@@ -19,7 +20,8 @@ pub use desktop_context::{
|
||||
};
|
||||
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
||||
use dioxus_core::*;
|
||||
use dioxus_html::HtmlEvent;
|
||||
use dioxus_html::{HtmlEvent, MountedData, MountedReturn};
|
||||
use element::DesktopElement;
|
||||
pub use eval::{use_eval, EvalResult};
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
use shortcut::ShortcutRegistry;
|
||||
@@ -220,19 +222,69 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
||||
}
|
||||
|
||||
EventData::Ipc(msg) if msg.method() == "user_event" => {
|
||||
let evt = match serde_json::from_value::<HtmlEvent>(msg.params()) {
|
||||
let params = msg.params();
|
||||
|
||||
let evt = match serde_json::from_value::<HtmlEvent>(params) {
|
||||
Ok(value) => value,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let HtmlEvent {
|
||||
element,
|
||||
name,
|
||||
bubbles,
|
||||
data,
|
||||
} = evt;
|
||||
|
||||
let view = webviews.get_mut(&event.1).unwrap();
|
||||
|
||||
view.dom
|
||||
.handle_event(&evt.name, evt.data.into_any(), evt.element, evt.bubbles);
|
||||
// check for a mounted event placeholder and replace it with a desktop specific element
|
||||
let as_any = if let dioxus_html::EventData::Mounted = &data {
|
||||
let query = view
|
||||
.dom
|
||||
.base_scope()
|
||||
.consume_context::<DesktopContext>()
|
||||
.unwrap()
|
||||
.query;
|
||||
|
||||
let element = DesktopElement::new(element, view.webview.clone(), query);
|
||||
|
||||
Rc::new(MountedData::new(element))
|
||||
} else {
|
||||
data.into_any()
|
||||
};
|
||||
|
||||
view.dom.handle_event(&name, as_any, element, bubbles);
|
||||
|
||||
send_edits(view.dom.render_immediate(), &view.webview);
|
||||
}
|
||||
|
||||
EventData::Ipc(msg) if msg.method() == "node_update" => {
|
||||
let params = msg.params();
|
||||
println!("node_update: {:?}", params);
|
||||
|
||||
// check for a mounted event
|
||||
let evt = match serde_json::from_value::<MountedReturn>(params) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
println!("node_update: {:?}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let view = webviews.get(&event.1).unwrap();
|
||||
let query = view
|
||||
.dom
|
||||
.base_scope()
|
||||
.consume_context::<DesktopContext>()
|
||||
.unwrap()
|
||||
.query;
|
||||
|
||||
println!("node_update: {:?}", evt);
|
||||
|
||||
query.send(evt);
|
||||
}
|
||||
|
||||
EventData::Ipc(msg) if msg.method() == "initialize" => {
|
||||
let view = webviews.get_mut(&event.1).unwrap();
|
||||
send_edits(view.dom.rebuild(), &view.webview);
|
||||
|
||||
Reference in new issue
Block a user