Generalize Query system for use in use_eval and node querys
This commit is contained in:
1 parent
7636c046fa
commit
fa9f0d0f6c
8 files changed
+186
-282
No files matched your search
@@ -3,9 +3,9 @@ use std::rc::Rc;
|
|||||||
use std::rc::Weak;
|
use std::rc::Weak;
|
||||||
|
|
||||||
use crate::create_new_window;
|
use crate::create_new_window;
|
||||||
use crate::element::QueryEngine;
|
|
||||||
use crate::eval::EvalResult;
|
use crate::eval::EvalResult;
|
||||||
use crate::events::IpcMessage;
|
use crate::events::IpcMessage;
|
||||||
|
use crate::query::QueryEngine;
|
||||||
use crate::shortcut::IntoKeyCode;
|
use crate::shortcut::IntoKeyCode;
|
||||||
use crate::shortcut::IntoModifersState;
|
use crate::shortcut::IntoModifersState;
|
||||||
use crate::shortcut::ShortcutId;
|
use crate::shortcut::ShortcutId;
|
||||||
@@ -17,7 +17,6 @@ use dioxus_core::ScopeState;
|
|||||||
use dioxus_core::VirtualDom;
|
use dioxus_core::VirtualDom;
|
||||||
#[cfg(all(feature = "hot-reload", debug_assertions))]
|
#[cfg(all(feature = "hot-reload", debug_assertions))]
|
||||||
use dioxus_hot_reload::HotReloadMsg;
|
use dioxus_hot_reload::HotReloadMsg;
|
||||||
use serde_json::Value;
|
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use wry::application::event::Event;
|
use wry::application::event::Event;
|
||||||
use wry::application::event_loop::EventLoopProxy;
|
use wry::application::event_loop::EventLoopProxy;
|
||||||
@@ -60,10 +59,7 @@ pub struct DesktopContext {
|
|||||||
/// The proxy to the event loop
|
/// The proxy to the event loop
|
||||||
pub proxy: ProxyType,
|
pub proxy: ProxyType,
|
||||||
|
|
||||||
/// The receiver for eval results since eval is async
|
/// The receiver for queries about the current window
|
||||||
pub(super) eval: tokio::sync::broadcast::Sender<Value>,
|
|
||||||
|
|
||||||
/// The receiver for queries about elements
|
|
||||||
pub(super) query: QueryEngine,
|
pub(super) query: QueryEngine,
|
||||||
|
|
||||||
pub(super) pending_windows: WebviewQueue,
|
pub(super) pending_windows: WebviewQueue,
|
||||||
@@ -100,7 +96,6 @@ impl DesktopContext {
|
|||||||
webview,
|
webview,
|
||||||
proxy,
|
proxy,
|
||||||
event_loop,
|
event_loop,
|
||||||
eval: tokio::sync::broadcast::channel(8).0,
|
|
||||||
query: Default::default(),
|
query: Default::default(),
|
||||||
pending_windows: webviews,
|
pending_windows: webviews,
|
||||||
event_handlers,
|
event_handlers,
|
||||||
@@ -215,28 +210,10 @@ impl DesktopContext {
|
|||||||
|
|
||||||
/// Evaluate a javascript expression
|
/// Evaluate a javascript expression
|
||||||
pub fn eval(&self, code: &str) -> EvalResult {
|
pub fn eval(&self, code: &str) -> EvalResult {
|
||||||
// Embed the return of the eval in a function so we can send it back to the main thread
|
// the query id lets us keep track of the eval result and send it back to the main thread
|
||||||
let script = format!(
|
let query = self.query.new_query(code, &self.webview);
|
||||||
r#"
|
|
||||||
window.ipc.postMessage(
|
|
||||||
JSON.stringify({{
|
|
||||||
"method":"eval_result",
|
|
||||||
"params": (
|
|
||||||
function(){{
|
|
||||||
{code}
|
|
||||||
}}
|
|
||||||
)()
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = self.webview.evaluate_script(&script) {
|
EvalResult::new(query)
|
||||||
// send an error to the eval receiver
|
|
||||||
log::warn!("Eval script error: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
EvalResult::new(self.eval.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a wry event handler that listens for wry events.
|
/// Create a wry event handler that listens for wry events.
|
||||||
|
|||||||
+41
-124
@@ -1,13 +1,11 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
use dioxus_html::{
|
use dioxus_html::{geometry::euclid::Rect, MountedResult, RenderedElementBacking};
|
||||||
MountedResult, MountedReturn, MountedReturnData, NodeUpdate, NodeUpdateData,
|
|
||||||
RenderedElementBacking,
|
|
||||||
};
|
|
||||||
use slab::Slab;
|
|
||||||
use wry::webview::WebView;
|
use wry::webview::WebView;
|
||||||
|
|
||||||
|
use crate::query::QueryEngine;
|
||||||
|
|
||||||
/// A mounted element passed to onmounted events
|
/// A mounted element passed to onmounted events
|
||||||
pub struct DesktopElement {
|
pub struct DesktopElement {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
@@ -19,16 +17,6 @@ impl DesktopElement {
|
|||||||
pub(crate) fn new(id: ElementId, webview: Rc<WebView>, query: QueryEngine) -> Self {
|
pub(crate) fn new(id: ElementId, webview: Rc<WebView>, query: QueryEngine) -> Self {
|
||||||
Self { id, webview, query }
|
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 {
|
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
|
let fut = self
|
||||||
.query
|
.query
|
||||||
.new_query(self.id, NodeUpdateData::GetClientRect {}, &self.webview)
|
.new_query::<Option<Rect<f64, f64>>>(&script, &self.webview)
|
||||||
.resolve();
|
.resolve();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
match fut.await {
|
match fut.await {
|
||||||
Some(MountedReturnData::GetClientRect(rect)) => Ok(rect),
|
Ok(Some(rect)) => Ok(rect),
|
||||||
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
||||||
Box::new(DesktopQueryError::MismatchedReturn),
|
Box::new(DesktopQueryError::FailedToQuery),
|
||||||
)),
|
)),
|
||||||
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
|
Err(err) => {
|
||||||
DesktopQueryError::FailedToQuery,
|
MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
|
||||||
))),
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -66,23 +56,25 @@ impl RenderedElementBacking for DesktopElement {
|
|||||||
&self,
|
&self,
|
||||||
behavior: dioxus_html::ScrollBehavior,
|
behavior: dioxus_html::ScrollBehavior,
|
||||||
) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
|
) -> 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
|
let fut = self
|
||||||
.query
|
.query
|
||||||
.new_query(
|
.new_query::<bool>(&script, &self.webview)
|
||||||
self.id,
|
|
||||||
NodeUpdateData::ScrollTo { behavior },
|
|
||||||
&self.webview,
|
|
||||||
)
|
|
||||||
.resolve();
|
.resolve();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
match fut.await {
|
match fut.await {
|
||||||
Some(MountedReturnData::ScrollTo(())) => Ok(()),
|
Ok(true) => Ok(()),
|
||||||
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
||||||
Box::new(DesktopQueryError::MismatchedReturn),
|
Box::new(DesktopQueryError::FailedToQuery),
|
||||||
)),
|
)),
|
||||||
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
|
Err(err) => {
|
||||||
DesktopQueryError::FailedToQuery,
|
MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
|
||||||
))),
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -91,116 +83,41 @@ impl RenderedElementBacking for DesktopElement {
|
|||||||
&self,
|
&self,
|
||||||
focus: bool,
|
focus: bool,
|
||||||
) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
|
) -> 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
|
let fut = self
|
||||||
.query
|
.query
|
||||||
.new_query(self.id, NodeUpdateData::SetFocus { focus }, &self.webview)
|
.new_query::<bool>(&script, &self.webview)
|
||||||
.resolve();
|
.resolve();
|
||||||
|
println!("fut");
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
match fut.await {
|
match fut.await {
|
||||||
Some(MountedReturnData::SetFocus(())) => Ok(()),
|
Ok(true) => Ok(()),
|
||||||
Some(_) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
|
||||||
Box::new(DesktopQueryError::MismatchedReturn),
|
Box::new(DesktopQueryError::FailedToQuery),
|
||||||
)),
|
)),
|
||||||
None => MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(
|
Err(err) => {
|
||||||
DesktopQueryError::FailedToQuery,
|
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)]
|
#[derive(Debug)]
|
||||||
enum DesktopQueryError {
|
enum DesktopQueryError {
|
||||||
FailedToQuery,
|
FailedToQuery,
|
||||||
MismatchedReturn,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for DesktopQueryError {
|
impl std::fmt::Display for DesktopQueryError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
DesktopQueryError::FailedToQuery => write!(f, "Failed to query the element"),
|
DesktopQueryError::FailedToQuery => write!(f, "Failed to query the element"),
|
||||||
DesktopQueryError::MismatchedReturn => {
|
|
||||||
write!(f, "The return type did not match the query")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,32 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::query::Query;
|
||||||
|
use crate::query::QueryError;
|
||||||
use crate::use_window;
|
use crate::use_window;
|
||||||
use dioxus_core::ScopeState;
|
use dioxus_core::ScopeState;
|
||||||
use serde::de::Error;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::future::IntoFuture;
|
use std::future::IntoFuture;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
/// A future that resolves to the result of a JavaScript evaluation.
|
/// A future that resolves to the result of a JavaScript evaluation.
|
||||||
pub struct EvalResult {
|
pub struct EvalResult {
|
||||||
pub(crate) broadcast: tokio::sync::broadcast::Sender<serde_json::Value>,
|
pub(crate) query: Query<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalResult {
|
impl EvalResult {
|
||||||
pub(crate) fn new(sender: tokio::sync::broadcast::Sender<serde_json::Value>) -> Self {
|
pub(crate) fn new(query: Query<serde_json::Value>) -> Self {
|
||||||
Self { broadcast: sender }
|
Self { query }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoFuture for EvalResult {
|
impl IntoFuture for EvalResult {
|
||||||
type Output = Result<serde_json::Value, serde_json::Error>;
|
type Output = Result<serde_json::Value, QueryError>;
|
||||||
|
|
||||||
type IntoFuture = Pin<Box<dyn Future<Output = Result<serde_json::Value, serde_json::Error>>>>;
|
type IntoFuture = Pin<Box<dyn Future<Output = Result<serde_json::Value, QueryError>>>>;
|
||||||
|
|
||||||
fn into_future(self) -> Self::IntoFuture {
|
fn into_future(self) -> Self::IntoFuture {
|
||||||
Box::pin(async move {
|
Box::pin(self.query.resolve())
|
||||||
let mut reciever = self.broadcast.subscribe();
|
as Pin<Box<dyn Future<Output = Result<serde_json::Value, QueryError>>>>
|
||||||
match reciever.recv().await {
|
|
||||||
Ok(result) => Ok(result),
|
|
||||||
Err(_) => Err(serde_json::Error::custom("No result returned")),
|
|
||||||
}
|
|
||||||
}) as Pin<Box<dyn Future<Output = Result<serde_json::Value, serde_json::Error>>>>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-37
@@ -10,17 +10,19 @@ mod escape;
|
|||||||
mod eval;
|
mod eval;
|
||||||
mod events;
|
mod events;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
|
mod query;
|
||||||
mod shortcut;
|
mod shortcut;
|
||||||
mod waker;
|
mod waker;
|
||||||
mod webview;
|
mod webview;
|
||||||
|
|
||||||
|
use crate::query::QueryResult;
|
||||||
pub use cfg::Config;
|
pub use cfg::Config;
|
||||||
pub use desktop_context::{
|
pub use desktop_context::{
|
||||||
use_window, use_wry_event_handler, DesktopContext, WryEventHandler, WryEventHandlerId,
|
use_window, use_wry_event_handler, DesktopContext, WryEventHandler, WryEventHandlerId,
|
||||||
};
|
};
|
||||||
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
use dioxus_html::{HtmlEvent, MountedData, MountedReturn};
|
use dioxus_html::{HtmlEvent, MountedData};
|
||||||
use element::DesktopElement;
|
use element::DesktopElement;
|
||||||
pub use eval::{use_eval, EvalResult};
|
pub use eval::{use_eval, EvalResult};
|
||||||
use futures_util::{pin_mut, FutureExt};
|
use futures_util::{pin_mut, FutureExt};
|
||||||
@@ -259,30 +261,21 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
|||||||
send_edits(view.dom.render_immediate(), &view.webview);
|
send_edits(view.dom.render_immediate(), &view.webview);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventData::Ipc(msg) if msg.method() == "node_update" => {
|
// When the webview sends a query, we need to send it to the query manager which handles dispatching the data to the correct pending query
|
||||||
|
EventData::Ipc(msg) if msg.method() == "query" => {
|
||||||
let params = msg.params();
|
let params = msg.params();
|
||||||
println!("node_update: {:?}", params);
|
|
||||||
|
|
||||||
// check for a mounted event
|
if let Ok(result) = dbg!(serde_json::from_value::<QueryResult>(params)) {
|
||||||
let evt = match serde_json::from_value::<MountedReturn>(params) {
|
let view = webviews.get(&event.1).unwrap();
|
||||||
Ok(value) => value,
|
let query = view
|
||||||
Err(err) => {
|
.dom
|
||||||
println!("node_update: {:?}", err);
|
.base_scope()
|
||||||
return;
|
.consume_context::<DesktopContext>()
|
||||||
}
|
.unwrap()
|
||||||
};
|
.query;
|
||||||
|
|
||||||
let view = webviews.get(&event.1).unwrap();
|
query.send(result);
|
||||||
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" => {
|
EventData::Ipc(msg) if msg.method() == "initialize" => {
|
||||||
@@ -290,21 +283,6 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
|
|||||||
send_edits(view.dom.rebuild(), &view.webview);
|
send_edits(view.dom.rebuild(), &view.webview);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the webview chirps back with the result of the eval, we send it to the active receiver
|
|
||||||
//
|
|
||||||
// This currently doesn't perform any targeting to the callsite, so if you eval multiple times at once,
|
|
||||||
// you might the wrong result. This should be fixed
|
|
||||||
EventData::Ipc(msg) if msg.method() == "eval_result" => {
|
|
||||||
webviews[&event.1]
|
|
||||||
.dom
|
|
||||||
.base_scope()
|
|
||||||
.consume_context::<DesktopContext>()
|
|
||||||
.unwrap()
|
|
||||||
.eval
|
|
||||||
.send(msg.params())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
EventData::Ipc(msg) if msg.method() == "browser_open" => {
|
EventData::Ipc(msg) if msg.method() == "browser_open" => {
|
||||||
if let Some(temp) = msg.params().as_object() {
|
if let Some(temp) = msg.params().as_object() {
|
||||||
if temp.contains_key("href") {
|
if temp.contains_key("href") {
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use slab::Slab;
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::sync::broadcast::error::RecvError;
|
||||||
|
use wry::webview::WebView;
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
struct SharedSlab {
|
||||||
|
slab: Rc<RefCell<Slab<()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct QueryEngine {
|
||||||
|
sender: Rc<tokio::sync::broadcast::Sender<QueryResult>>,
|
||||||
|
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 {
|
||||||
|
pub fn new_query<V: DeserializeOwned>(&self, script: &str, webview: &WebView) -> Query<V> {
|
||||||
|
let request_id = self.active_requests.slab.borrow_mut().insert(());
|
||||||
|
|
||||||
|
// start the query
|
||||||
|
// We embed the return of the eval in a function so we can send it back to the main thread
|
||||||
|
if let Err(err) = webview.evaluate_script(&format!(
|
||||||
|
r#"window.ipc.postMessage(
|
||||||
|
JSON.stringify({{
|
||||||
|
"method":"query",
|
||||||
|
"params": {{
|
||||||
|
"id": {request_id},
|
||||||
|
"data": (function(){{{script}}})()
|
||||||
|
}}
|
||||||
|
}})
|
||||||
|
);"#
|
||||||
|
)) {
|
||||||
|
log::warn!("Query error: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Query {
|
||||||
|
slab: self.active_requests.clone(),
|
||||||
|
id: request_id,
|
||||||
|
reciever: self.sender.subscribe(),
|
||||||
|
phantom: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&self, data: QueryResult) {
|
||||||
|
let _ = self.sender.send(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Query<V: DeserializeOwned> {
|
||||||
|
slab: SharedSlab,
|
||||||
|
id: usize,
|
||||||
|
reciever: tokio::sync::broadcast::Receiver<QueryResult>,
|
||||||
|
phantom: std::marker::PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: DeserializeOwned> Query<V> {
|
||||||
|
pub async fn resolve(mut self) -> Result<V, QueryError> {
|
||||||
|
let result = loop {
|
||||||
|
match self.reciever.recv().await {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.id == self.id {
|
||||||
|
break V::deserialize(result.data).map_err(QueryError::DeserializeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
break Err(QueryError::RecvError(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove the query from the slab
|
||||||
|
self.slab.slab.borrow_mut().remove(self.id);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum QueryError {
|
||||||
|
#[error("Error receiving query result: {0}")]
|
||||||
|
RecvError(RecvError),
|
||||||
|
#[error("Error deserializing query result: {0}")]
|
||||||
|
DeserializeError(serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub(crate) struct QueryResult {
|
||||||
|
id: usize,
|
||||||
|
data: Value,
|
||||||
|
}
|
||||||
@@ -79,13 +79,16 @@ impl MountedData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Scroll to make the element visible
|
/// Scroll to make the element visible
|
||||||
pub async fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> {
|
pub fn scroll_to(
|
||||||
self.inner.scroll_to(behavior).await
|
&self,
|
||||||
|
behavior: ScrollBehavior,
|
||||||
|
) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||||
|
self.inner.scroll_to(behavior)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the focus on the element
|
/// Set the focus on the element
|
||||||
pub async fn set_focus(&self, focus: bool) -> MountedResult<()> {
|
pub fn set_focus(&self, focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||||
self.inner.set_focus(focus).await
|
self.inner.set_focus(focus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use std::{any::Any, rc::Rc};
|
|||||||
|
|
||||||
use crate::events::*;
|
use crate::events::*;
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
use euclid::Rect;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||||
@@ -221,49 +220,3 @@ fn test_back_and_forth() {
|
|||||||
|
|
||||||
assert_eq!(data, p);
|
assert_eq!(data, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to update a node to support MountedData
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct NodeUpdate {
|
|
||||||
/// The id of the node to update
|
|
||||||
pub id: u32,
|
|
||||||
/// The id of the request
|
|
||||||
pub request_id: usize,
|
|
||||||
/// The data to update the node with
|
|
||||||
pub data: NodeUpdateData,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Message to update a node to support MountedData
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serialize",
|
|
||||||
derive(serde::Serialize, serde::Deserialize),
|
|
||||||
serde(tag = "type")
|
|
||||||
)]
|
|
||||||
pub enum NodeUpdateData {
|
|
||||||
SetFocus { focus: bool },
|
|
||||||
GetClientRect {},
|
|
||||||
ScrollTo { behavior: ScrollBehavior },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The result of a element query
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct MountedReturn {
|
|
||||||
/// A unique id for the query
|
|
||||||
pub id: usize,
|
|
||||||
/// The result of the query
|
|
||||||
pub data: Option<MountedReturnData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The data of a element query
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serialize",
|
|
||||||
derive(serde::Serialize, serde::Deserialize),
|
|
||||||
serde(tag = "type")
|
|
||||||
)]
|
|
||||||
pub enum MountedReturnData {
|
|
||||||
SetFocus(()),
|
|
||||||
GetClientRect(Rect<f64, f64>),
|
|
||||||
ScrollTo(()),
|
|
||||||
}
|
|
||||||
@@ -227,51 +227,26 @@ class Interpreter {
|
|||||||
ScrollTo(id, behavior) {
|
ScrollTo(id, behavior) {
|
||||||
const node = this.nodes[id];
|
const node = this.nodes[id];
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
node.scrollIntoView({
|
node.scrollIntoView({
|
||||||
behavior: behavior
|
behavior: behavior
|
||||||
});
|
});
|
||||||
return {
|
return true;
|
||||||
type: "ScrollTo",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the focus on the element
|
/// Set the focus on the element
|
||||||
SetFocus(id, focus) {
|
SetFocus(id, focus) {
|
||||||
const node = this.nodes[id];
|
const node = this.nodes[id];
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (focus) {
|
if (focus) {
|
||||||
node.focus();
|
node.focus();
|
||||||
} else {
|
} else {
|
||||||
node.blur();
|
node.blur();
|
||||||
}
|
}
|
||||||
return {
|
return true;
|
||||||
type: "SetFocus",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
handleNodeUpdate(edit) {
|
|
||||||
let data;
|
|
||||||
switch (edit.data.type) {
|
|
||||||
case "SetFocus":
|
|
||||||
data = this.SetFocus(edit.id, edit.data.focus);
|
|
||||||
break;
|
|
||||||
case "ScrollTo":
|
|
||||||
data = this.ScrollTo(edit.id, edit.data.behavior);
|
|
||||||
break;
|
|
||||||
case "GetClientRect":
|
|
||||||
data = this.GetClientRect(edit.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
window.ipc.postMessage(
|
|
||||||
serializeIpcMessage("node_update", {
|
|
||||||
id: edit.request_id,
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEdits(edits) {
|
handleEdits(edits) {
|
||||||
|
|||||||
Reference in new issue
Block a user