mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 22:54:12 +00:00
implement on mounted for desktop
This commit is contained in:
parent
cb5cb56ad3
commit
7636c046fa
8 changed files with 450 additions and 24 deletions
|
@ -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,
|
||||
|
|
208
packages/desktop/src/element.rs
Normal file
208
packages/desktop/src/element.rs
Normal file
|
@ -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);
|
||||
|
|
|
@ -5,12 +5,15 @@ use euclid::Rect;
|
|||
use std::{
|
||||
any::Any,
|
||||
fmt::{Display, Formatter},
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
/// An Element that has been rendered and allows reading and modifying information about it.
|
||||
///
|
||||
/// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries.
|
||||
// we can not use async_trait here because it does not create a trait that is object safe
|
||||
pub trait RenderedElementBacking {
|
||||
/// Get the renderer specific element for the given id
|
||||
fn get_raw_element(&self) -> MountedResult<&dyn Any> {
|
||||
|
@ -18,26 +21,35 @@ pub trait RenderedElementBacking {
|
|||
}
|
||||
|
||||
/// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position)
|
||||
fn get_client_rect(&self) -> MountedResult<Rect<f64, f64>> {
|
||||
Err(MountedError::NotSupported)
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn get_client_rect(&self) -> Pin<Box<dyn Future<Output = MountedResult<Rect<f64, f64>>>>> {
|
||||
Box::pin(async { Err(MountedError::NotSupported) })
|
||||
}
|
||||
|
||||
/// Scroll to make the element visible
|
||||
fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> {
|
||||
Err(MountedError::NotSupported)
|
||||
fn scroll_to(
|
||||
&self,
|
||||
_behavior: ScrollBehavior,
|
||||
) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
Box::pin(async { Err(MountedError::NotSupported) })
|
||||
}
|
||||
|
||||
/// Set the focus on the element
|
||||
fn set_focus(&self, _focus: bool) -> MountedResult<()> {
|
||||
Err(MountedError::NotSupported)
|
||||
fn set_focus(&self, _focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
Box::pin(async { Err(MountedError::NotSupported) })
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderedElementBacking for () {}
|
||||
|
||||
/// The way that scrolling should be performed
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum ScrollBehavior {
|
||||
/// Scroll to the element immediately
|
||||
#[cfg_attr(feature = "serialize", serde(rename = "instant"))]
|
||||
Instant,
|
||||
/// Scroll to the element smoothly
|
||||
#[cfg_attr(feature = "serialize", serde(rename = "smooth"))]
|
||||
Smooth,
|
||||
}
|
||||
|
||||
|
@ -62,18 +74,18 @@ impl MountedData {
|
|||
}
|
||||
|
||||
/// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position)
|
||||
pub fn get_client_rect(&self) -> MountedResult<Rect<f64, f64>> {
|
||||
self.inner.get_client_rect()
|
||||
pub async fn get_client_rect(&self) -> MountedResult<Rect<f64, f64>> {
|
||||
self.inner.get_client_rect().await
|
||||
}
|
||||
|
||||
/// Scroll to make the element visible
|
||||
pub fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> {
|
||||
self.inner.scroll_to(behavior)
|
||||
pub async fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> {
|
||||
self.inner.scroll_to(behavior).await
|
||||
}
|
||||
|
||||
/// Set the focus on the element
|
||||
pub fn set_focus(&self, focus: bool) -> MountedResult<()> {
|
||||
self.inner.set_focus(focus)
|
||||
pub async fn set_focus(&self, focus: bool) -> MountedResult<()> {
|
||||
self.inner.set_focus(focus).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::{any::Any, rc::Rc};
|
|||
|
||||
use crate::events::*;
|
||||
use dioxus_core::ElementId;
|
||||
use euclid::Rect;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
|
@ -113,6 +114,9 @@ fn fun_name(
|
|||
// Toggle
|
||||
"toggle" => Toggle(de(data)?),
|
||||
|
||||
// Mounted
|
||||
"mounted" => Mounted,
|
||||
|
||||
// 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";
|
||||
other => {
|
||||
|
@ -151,6 +155,7 @@ pub enum EventData {
|
|||
Animation(AnimationData),
|
||||
Transition(TransitionData),
|
||||
Toggle(ToggleData),
|
||||
Mounted,
|
||||
}
|
||||
|
||||
impl EventData {
|
||||
|
@ -172,6 +177,7 @@ impl EventData {
|
|||
EventData::Animation(data) => Rc::new(data) as Rc<dyn Any>,
|
||||
EventData::Transition(data) => Rc::new(data) as Rc<dyn Any>,
|
||||
EventData::Toggle(data) => Rc::new(data) as Rc<dyn Any>,
|
||||
EventData::Mounted => Rc::new(MountedData::new(())) as Rc<dyn Any>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,3 +221,49 @@ fn test_back_and_forth() {
|
|||
|
||||
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(()),
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ use crate::{
|
|||
};
|
||||
use keyboard_types::{Code, Key, Modifiers};
|
||||
use std::convert::TryInto;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{
|
||||
|
@ -203,19 +205,25 @@ impl From<&web_sys::Element> for MountedData {
|
|||
}
|
||||
|
||||
impl RenderedElementBacking for web_sys::Element {
|
||||
fn get_client_rect(&self) -> MountedResult<euclid::Rect<f64, f64>> {
|
||||
fn get_client_rect(
|
||||
&self,
|
||||
) -> Pin<Box<dyn Future<Output = MountedResult<euclid::Rect<f64, f64>>>>> {
|
||||
let rect = self.get_bounding_client_rect();
|
||||
Ok(euclid::Rect::new(
|
||||
let result = Ok(euclid::Rect::new(
|
||||
euclid::Point2D::new(rect.left(), rect.top()),
|
||||
euclid::Size2D::new(rect.width(), rect.height()),
|
||||
))
|
||||
));
|
||||
Box::pin(async { result })
|
||||
}
|
||||
|
||||
fn get_raw_element(&self) -> MountedResult<&dyn std::any::Any> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> {
|
||||
fn scroll_to(
|
||||
&self,
|
||||
behavior: ScrollBehavior,
|
||||
) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
match behavior {
|
||||
ScrollBehavior::Instant => self.scroll_into_view_with_scroll_into_view_options(
|
||||
ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Instant),
|
||||
|
@ -225,16 +233,18 @@ impl RenderedElementBacking for web_sys::Element {
|
|||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Box::pin(async { Ok(()) })
|
||||
}
|
||||
|
||||
fn set_focus(&self, focus: bool) -> MountedResult<()> {
|
||||
self.dyn_ref::<web_sys::HtmlElement>()
|
||||
fn set_focus(&self, focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
let result = self
|
||||
.dyn_ref::<web_sys::HtmlElement>()
|
||||
.ok_or_else(|| MountedError::OperationFailed(Box::new(FocusError(self.into()))))
|
||||
.and_then(|e| {
|
||||
(if focus { e.focus() } else { e.blur() })
|
||||
.map_err(|err| MountedError::OperationFailed(Box::new(FocusError(err))))
|
||||
})
|
||||
});
|
||||
Box::pin(async { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,10 @@ js-sys = { version = "0.3.56", optional = true }
|
|||
web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] }
|
||||
sledgehammer_bindgen = { version = "0.1.3", optional = true }
|
||||
sledgehammer_utils = { version = "0.1.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serialize = ["serde"]
|
||||
web = ["wasm-bindgen", "js-sys", "web-sys"]
|
||||
sledgehammer = ["wasm-bindgen", "js-sys", "web-sys", "sledgehammer_bindgen", "sledgehammer_utils"]
|
||||
|
|
|
@ -204,6 +204,76 @@ class Interpreter {
|
|||
node.removeAttribute(name);
|
||||
}
|
||||
}
|
||||
|
||||
GetClientRect(id) {
|
||||
const node= this.nodes[id];
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const rect = node.getBoundingClientRect();
|
||||
return {
|
||||
type: "GetClientRect",
|
||||
origin: [
|
||||
rect.x,
|
||||
rect.y,
|
||||
],
|
||||
size: [
|
||||
rect.width,
|
||||
rect.height,
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
ScrollTo(id, behavior) {
|
||||
const node = this.nodes[id];
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
node.scrollIntoView({
|
||||
behavior: behavior
|
||||
});
|
||||
return {
|
||||
type: "ScrollTo",
|
||||
};
|
||||
}
|
||||
|
||||
/// Set the focus on the element
|
||||
SetFocus(id, focus) {
|
||||
const node = this.nodes[id];
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
if (focus) {
|
||||
node.focus();
|
||||
} else {
|
||||
node.blur();
|
||||
}
|
||||
return {
|
||||
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) {
|
||||
for (let template of edits.templates) {
|
||||
this.SaveTemplate(template);
|
||||
|
@ -345,6 +415,19 @@ class Interpreter {
|
|||
|
||||
let bubbles = event_bubbles(edit.name);
|
||||
|
||||
// if this is a mounted listener, we send the event immediately
|
||||
if (edit.name === "mounted") {
|
||||
window.ipc.postMessage(
|
||||
serializeIpcMessage("user_event", {
|
||||
name: edit.name,
|
||||
element: edit.id,
|
||||
data: null,
|
||||
bubbles,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// this handler is only provided on desktop implementations since this
|
||||
// method is not used by the web implementation
|
||||
let handler = (event) => {
|
||||
|
@ -921,6 +1004,8 @@ function event_bubbles(event) {
|
|||
return true;
|
||||
case "toggle":
|
||||
return true;
|
||||
case "mounted":
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
Loading…
Reference in a new issue