diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs index a5413bb5b..dafbf46c8 100644 --- a/packages/html/src/events/mounted.rs +++ b/packages/html/src/events/mounted.rs @@ -2,30 +2,34 @@ use euclid::Rect; -use std::{any::Any, rc::Rc}; +use std::{ + any::Any, + fmt::{Display, Formatter}, + 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. pub trait RenderedElementBacking { /// Get the renderer specific element for the given id - fn get_raw_element(&self) -> Option<&dyn Any> { - None + fn get_raw_element(&self) -> MountedResult<&dyn Any> { + Err(MountedError::NotSupported) } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) - fn get_client_rect(&self) -> Option> { - None + fn get_client_rect(&self) -> MountedResult> { + Err(MountedError::NotSupported) } /// Scroll to make the element visible - fn scroll_to(&self, _behavior: ScrollBehavior) -> Option<()> { - None + fn scroll_to(&self, _behavior: ScrollBehavior) -> MountedResult<()> { + Err(MountedError::NotSupported) } /// Set the focus on the element - fn set_focus(&self, _focus: bool) -> Option<()> { - None + fn set_focus(&self, _focus: bool) -> MountedResult<()> { + Err(MountedError::NotSupported) } } @@ -53,22 +57,22 @@ impl MountedData { } /// Get the renderer specific element for the given id - pub fn get_raw_element(&self) -> Option<&dyn Any> { + pub fn get_raw_element(&self) -> MountedResult<&dyn Any> { self.inner.get_raw_element() } /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) - pub fn get_client_rect(&self) -> Option> { + pub fn get_client_rect(&self) -> MountedResult> { self.inner.get_client_rect() } /// Scroll to make the element visible - pub fn scroll_to(&self, behavior: ScrollBehavior) -> Option<()> { + pub fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> { self.inner.scroll_to(behavior) } /// Set the focus on the element - pub fn set_focus(&self, focus: bool) -> Option<()> { + pub fn set_focus(&self, focus: bool) -> MountedResult<()> { self.inner.set_focus(focus) } } @@ -83,3 +87,30 @@ impl_event! [ /// mounted onmounted ]; + +/// The MountedResult type for the MountedData +pub type MountedResult = Result; + +#[derive(Debug)] +/// The error type for the MountedData +pub enum MountedError { + /// The renderer does not support the requested operation + NotSupported, + /// The element was not found + OperationFailed(Box), +} + +impl Display for MountedError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + MountedError::NotSupported => { + write!(f, "The renderer does not support the requested operation") + } + MountedError::OperationFailed(e) => { + write!(f, "The operation failed: {}", e) + } + } + } +} + +impl std::error::Error for MountedError {} diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 01b25dfc2..1983a31da 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -4,11 +4,13 @@ use crate::events::{ }; use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; -use crate::{DragData, MountedData, RenderedElementBacking, ScrollBehavior}; +use crate::{ + DragData, MountedData, MountedError, MountedResult, RenderedElementBacking, ScrollBehavior, +}; use keyboard_types::{Code, Key, Modifiers}; use std::convert::TryInto; use std::str::FromStr; -use wasm_bindgen::JsCast; +use wasm_bindgen::{JsCast, JsValue}; use web_sys::{ AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, ScrollIntoViewOptions, TouchEvent, TransitionEvent, WheelEvent, @@ -201,19 +203,19 @@ impl From<&web_sys::Element> for MountedData { } impl RenderedElementBacking for web_sys::Element { - fn get_client_rect(&self) -> Option> { + fn get_client_rect(&self) -> MountedResult> { let rect = self.get_bounding_client_rect(); - Some(euclid::Rect::new( + Ok(euclid::Rect::new( euclid::Point2D::new(rect.left(), rect.top()), euclid::Size2D::new(rect.width(), rect.height()), )) } - fn get_raw_element(&self) -> Option<&dyn std::any::Any> { - Some(self) + fn get_raw_element(&self) -> MountedResult<&dyn std::any::Any> { + Ok(self) } - fn scroll_to(&self, behavior: ScrollBehavior) -> Option<()> { + fn scroll_to(&self, behavior: ScrollBehavior) -> MountedResult<()> { match behavior { ScrollBehavior::Instant => self.scroll_into_view_with_scroll_into_view_options( ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Instant), @@ -223,16 +225,26 @@ impl RenderedElementBacking for web_sys::Element { ), } - Some(()) + Ok(()) } - fn set_focus(&self, focus: bool) -> Option<()> { - self.dyn_ref::().and_then(|e| { - if focus { - e.focus().ok() - } else { - e.blur().ok() - } - }) + fn set_focus(&self, focus: bool) -> MountedResult<()> { + self.dyn_ref::() + .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)))) + }) } } + +#[derive(Debug)] +struct FocusError(JsValue); + +impl std::fmt::Display for FocusError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "failed to focus element {:?}", self.0) + } +} + +impl std::error::Error for FocusError {}