diff --git a/packages/desktop/src/element.rs b/packages/desktop/src/element.rs index 4c9dfbbe2..505b45c43 100644 --- a/packages/desktop/src/element.rs +++ b/packages/desktop/src/element.rs @@ -1,5 +1,8 @@ use dioxus_core::ElementId; -use dioxus_html::{geometry::euclid::Rect, MountedResult, RenderedElementBacking}; +use dioxus_html::{ + geometry::{PixelsRect, PixelsSize, PixelsVector2D}, + MountedResult, RenderedElementBacking, +}; use crate::{desktop_context::DesktopContext, query::QueryEngine}; @@ -17,38 +20,56 @@ impl DesktopElement { } } +macro_rules! scripted_getter { + ($meth_name:ident, $script:literal, $output_type:path) => { + fn $meth_name( + &self, + ) -> std::pin::Pin< + Box>>, + > { + let script = format!($script, id = self.id.0); + + let fut = self + .query + .new_query::>(&script, self.webview.clone()) + .resolve(); + Box::pin(async move { + match fut.await { + Ok(Some(res)) => Ok(res), + Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(err), + )), + } + }) + } + }; +} + impl RenderedElementBacking for DesktopElement { fn as_any(&self) -> &dyn std::any::Any { self } - fn get_client_rect( - &self, - ) -> std::pin::Pin< - Box< - dyn futures_util::Future< - Output = dioxus_html::MountedResult>, - >, - >, - > { - let script = format!("return window.interpreter.getClientRect({});", self.id.0); + scripted_getter!( + get_scroll_offset, + "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", + PixelsVector2D + ); - let fut = self - .query - .new_query::>>(&script, self.webview.clone()) - .resolve(); - Box::pin(async move { - match fut.await { - Ok(Some(rect)) => Ok(rect), - Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => { - MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - } - } - }) - } + scripted_getter!( + get_scroll_size, + "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", + PixelsSize + ); + + scripted_getter!( + get_client_rect, + "return window.interpreter.getClientRect({id});", + PixelsRect + ); fn scroll_to( &self, diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index 8962766e1..8d49e2b2f 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -1,7 +1,5 @@ //! Handles querying data from the renderer -use euclid::Rect; - use std::{ fmt::{Display, Formatter}, future::Future, @@ -16,9 +14,20 @@ pub trait RenderedElementBacking: std::any::Any { /// return self as Any fn as_any(&self) -> &dyn std::any::Any; + /// Get the number of pixels that an element's content is scrolled + fn get_scroll_offset(&self) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) + } + + /// Get the size of an element's content, including content not visible on the screen due to overflow + #[allow(clippy::type_complexity)] + fn get_scroll_size(&self) -> Pin>>> { + Box::pin(async { Err(MountedError::NotSupported) }) + } + /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) #[allow(clippy::type_complexity)] - fn get_client_rect(&self) -> Pin>>>> { + fn get_client_rect(&self) -> Pin>>> { Box::pin(async { Err(MountedError::NotSupported) }) } @@ -74,8 +83,18 @@ impl MountedData { } } + /// Get the number of pixels that an element's content is scrolled + pub async fn get_scroll_offset(&self) -> MountedResult { + self.inner.get_scroll_offset().await + } + + /// Get the size of an element's content, including content not visible on the screen due to overflow + pub async fn get_scroll_size(&self) -> MountedResult { + self.inner.get_scroll_size().await + } + /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) - pub async fn get_client_rect(&self) -> MountedResult> { + pub async fn get_client_rect(&self) -> MountedResult { self.inner.get_client_rect().await } @@ -100,6 +119,8 @@ impl MountedData { use dioxus_core::Event; +use crate::geometry::{PixelsRect, PixelsSize, PixelsVector2D}; + pub type MountedEvent = Event; impl_event! [ diff --git a/packages/html/src/geometry.rs b/packages/html/src/geometry.rs index 4a0b16f4a..6b072b787 100644 --- a/packages/html/src/geometry.rs +++ b/packages/html/src/geometry.rs @@ -27,8 +27,14 @@ pub type PagePoint = Point2D; /// A pixel unit: one unit corresponds to 1 pixel pub struct Pixels; -/// A vector expressed in Pixels -pub type PixelsVector = Vector3D; +/// A size expressed in Pixels +pub type PixelsSize = Size2D; +/// A rectangle expressed in Pixels +pub type PixelsRect = Rect; +/// A 2D vector expressed in Pixels +pub type PixelsVector2D = Vector2D; +/// A 3D vector expressed in Pixels +pub type PixelsVector3D = Vector3D; /// A unit in terms of Lines /// @@ -51,7 +57,7 @@ pub type PagesVector = Vector3D; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum WheelDelta { /// Movement in Pixels - Pixels(PixelsVector), + Pixels(PixelsVector3D), /// Movement in Lines Lines(LinesVector), /// Movement in Pages @@ -62,7 +68,7 @@ impl WheelDelta { /// Construct from the attributes of the web wheel event pub fn from_web_attributes(delta_mode: u32, delta_x: f64, delta_y: f64, delta_z: f64) -> Self { match delta_mode { - 0 => WheelDelta::Pixels(PixelsVector::new(delta_x, delta_y, delta_z)), + 0 => WheelDelta::Pixels(PixelsVector3D::new(delta_x, delta_y, delta_z)), 1 => WheelDelta::Lines(LinesVector::new(delta_x, delta_y, delta_z)), 2 => WheelDelta::Pages(PagesVector::new(delta_x, delta_y, delta_z)), _ => panic!("Invalid delta mode, {:?}", delta_mode), @@ -71,7 +77,7 @@ impl WheelDelta { /// Convenience function for constructing a WheelDelta with pixel units pub fn pixels(x: f64, y: f64, z: f64) -> Self { - WheelDelta::Pixels(PixelsVector::new(x, y, z)) + WheelDelta::Pixels(PixelsVector3D::new(x, y, z)) } /// Convenience function for constructing a WheelDelta with line units diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index f5fb84760..ae672d4e8 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -4,7 +4,9 @@ use crate::events::{ TransitionData, WheelData, }; use crate::file_data::{FileEngine, HasFileData}; -use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint}; +use crate::geometry::{ + ClientPoint, ElementPoint, PagePoint, PixelsRect, PixelsSize, PixelsVector2D, ScreenPoint, +}; use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; use crate::prelude::*; use keyboard_types::{Code, Key, Modifiers}; @@ -423,13 +425,32 @@ impl From<&web_sys::Element> for MountedData { #[cfg(feature = "mounted")] impl crate::RenderedElementBacking for web_sys::Element { + fn get_scroll_offset( + &self, + ) -> std::pin::Pin>>> + { + let left = self.scroll_left(); + let top = self.scroll_top(); + let result = Ok(PixelsVector2D::new(left as f64, top as f64)); + Box::pin(async { result }) + } + + fn get_scroll_size( + &self, + ) -> std::pin::Pin>>> + { + let left = self.scroll_left(); + let top = self.scroll_top(); + let result = Ok(PixelsSize::new(left as f64, top as f64)); + Box::pin(async { result }) + } + fn get_client_rect( &self, - ) -> std::pin::Pin< - Box>>>, - > { + ) -> std::pin::Pin>>> + { let rect = self.get_bounding_client_rect(); - let result = Ok(euclid::Rect::new( + let result = Ok(PixelsRect::new( euclid::Point2D::new(rect.left(), rect.top()), euclid::Size2D::new(rect.width(), rect.height()), )); diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index dc97efc04..70f987425 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -5713307201725207733 \ No newline at end of file +8520528080524713002 \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index 20f3b5264..4fbb2d85c 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retriveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{sheet.href=sheet.href+"?"+Math.random()})}}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){let files=void 0;if(event.dataTransfer&&event.dataTransfer.files&&event.dataTransfer.files.length>0)files={files:{placeholder:[]}};return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;queuedBytes=[];liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message),event.preventDefault()}}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getScrollHeight(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollHeight}getScrollLeft(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollLeft}getScrollTop(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollTop}getScrollWidth(id){const node=this.nodes[id];if(node instanceof HTMLElement)return node.scrollWidth}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;k{this.flushQueuedBytes(),this.waitForRequest(headless)})}waitForRequest(headless){fetch(new Request(this.editsPath)).then((response)=>response.arrayBuffer()).then((bytes)=>{this.rafEdits(headless,bytes)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{sheet.href=sheet.href+"?"+Math.random()})}}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i { + fn $meth_name( + &self, + ) -> std::pin::Pin< + Box>>, + > { + let script = format!($script, id = self.id.0); + + let fut = self + .query + .new_query::>(&script) + .resolve(); + Box::pin(async move { + match fut.await { + Ok(Some(res)) => Ok(res), + Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(DesktopQueryError::FailedToQuery), + )), + Err(err) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( + Box::new(err), + )), + } + }) + } + }; +} + impl RenderedElementBacking for LiveviewElement { fn as_any(&self) -> &dyn std::any::Any { self } - fn get_client_rect( - &self, - ) -> std::pin::Pin< - Box< - dyn futures_util::Future< - Output = dioxus_html::MountedResult>, - >, - >, - > { - let script = format!("return window.interpreter.getClientRect({});", self.id.0); + scripted_getter!( + get_scroll_offset, + "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]", + PixelsVector2D + ); - let fut = self - .query - .new_query::>>(&script) - .resolve(); - Box::pin(async move { - match fut.await { - Ok(Some(rect)) => Ok(rect), - Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed( - Box::new(DesktopQueryError::FailedToQuery), - )), - Err(err) => { - MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err))) - } - } - }) - } + scripted_getter!( + get_scroll_size, + "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]", + PixelsSize + ); + + scripted_getter!( + get_client_rect, + "return window.interpreter.getClientRect({id});", + PixelsRect + ); fn scroll_to( &self,