Generalize Query system for use in use_eval and node querys

This commit is contained in:
Evan Almloff committed 2023-03-23 15:19:00 -05:00
1 parent 7636c046fa
commit fa9f0d0f6c
8 files changed
+186 -282

No files matched your search

+41 -124
View File
@@ -1,13 +1,11 @@
use std::{cell::RefCell, rc::Rc};
use std::rc::Rc;
use dioxus_core::ElementId;
use dioxus_html::{
MountedResult, MountedReturn, MountedReturnData, NodeUpdate, NodeUpdateData,
RenderedElementBacking,
};
use slab::Slab;
use dioxus_html::{geometry::euclid::Rect, MountedResult, RenderedElementBacking};
use wry::webview::WebView;
use crate::query::QueryEngine;
/// A mounted element passed to onmounted events
pub struct DesktopElement {
id: ElementId,
@@ -19,16 +17,6 @@ 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 {
@@ -45,19 +33,21 @@ impl RenderedElementBacking for DesktopElement {
>,
>,
> {
let script = format!("return window.interpreter.GetClientRect({});", self.id.0);
let fut = self
.query
.new_query(self.id, NodeUpdateData::GetClientRect {}, &self.webview)
.new_query::<Option<Rect<f64, f64>>>(&script, &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),
Ok(Some(rect)) => Ok(rect),
Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
Box::new(DesktopQueryError::FailedToQuery),
)),
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
DesktopQueryError::FailedToQuery,
))),
Err(err) => {
MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
}
}
})
}
@@ -66,23 +56,25 @@ impl RenderedElementBacking for DesktopElement {
&self,
behavior: dioxus_html::ScrollBehavior,
) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
let script = format!(
"return window.interpreter.ScrollTo({}, {});",
self.id.0,
serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior")
);
let fut = self
.query
.new_query(
self.id,
NodeUpdateData::ScrollTo { behavior },
&self.webview,
)
.new_query::<bool>(&script, &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),
Ok(true) => Ok(()),
Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
Box::new(DesktopQueryError::FailedToQuery),
)),
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
DesktopQueryError::FailedToQuery,
))),
Err(err) => {
MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
}
}
})
}
@@ -91,116 +83,41 @@ impl RenderedElementBacking for DesktopElement {
&self,
focus: bool,
) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
let script = format!(
"return window.interpreter.SetFocus({}, {});",
self.id.0, focus
);
println!("script: {}", script);
let fut = self
.query
.new_query(self.id, NodeUpdateData::SetFocus { focus }, &self.webview)
.new_query::<bool>(&script, &self.webview)
.resolve();
println!("fut");
Box::pin(async move {
match fut.await {
Some(MountedReturnData::SetFocus(())) => Ok(()),
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
Box::new(DesktopQueryError::MismatchedReturn),
Ok(true) => Ok(()),
Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
Box::new(DesktopQueryError::FailedToQuery),
)),
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
DesktopQueryError::FailedToQuery,
))),
Err(err) => {
MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
}
}
})
}
}
#[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")
}
}
}
}