element.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. use dioxus_core::ElementId;
  2. use dioxus_html::{
  3. geometry::{PixelsRect, PixelsSize, PixelsVector2D},
  4. MountedResult, RenderedElementBacking,
  5. };
  6. use crate::query::QueryEngine;
  7. /// A mounted element passed to onmounted events
  8. #[derive(Clone)]
  9. pub struct LiveviewElement {
  10. id: ElementId,
  11. query: QueryEngine,
  12. }
  13. impl LiveviewElement {
  14. pub(crate) fn new(id: ElementId, query: QueryEngine) -> Self {
  15. Self { id, query }
  16. }
  17. }
  18. macro_rules! scripted_getter {
  19. ($meth_name:ident, $script:literal, $output_type:path) => {
  20. fn $meth_name(
  21. &self,
  22. ) -> std::pin::Pin<
  23. Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<$output_type>>>,
  24. > {
  25. let script = format!($script, id = self.id.0);
  26. let fut = self
  27. .query
  28. .new_query::<Option<$output_type>>(&script)
  29. .resolve();
  30. Box::pin(async move {
  31. match fut.await {
  32. Ok(Some(res)) => Ok(res),
  33. Ok(None) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
  34. Box::new(DesktopQueryError::FailedToQuery),
  35. )),
  36. Err(err) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
  37. Box::new(err),
  38. )),
  39. }
  40. })
  41. }
  42. };
  43. }
  44. impl RenderedElementBacking for LiveviewElement {
  45. fn as_any(&self) -> &dyn std::any::Any {
  46. self
  47. }
  48. scripted_getter!(
  49. get_scroll_offset,
  50. "return [window.interpreter.getScrollLeft({id}), window.interpreter.getScrollTop({id})]",
  51. PixelsVector2D
  52. );
  53. scripted_getter!(
  54. get_scroll_size,
  55. "return [window.interpreter.getScrollWidth({id}), window.interpreter.getScrollHeight({id})]",
  56. PixelsSize
  57. );
  58. scripted_getter!(
  59. get_client_rect,
  60. "return window.interpreter.getClientRect({id});",
  61. PixelsRect
  62. );
  63. fn scroll_to(
  64. &self,
  65. options: dioxus_html::ScrollToOptions,
  66. ) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
  67. let script = format!(
  68. "return window.interpreter.scrollTo({}, {});",
  69. self.id.0,
  70. serde_json::to_string(&options).expect("Failed to serialize ScrollToOptions")
  71. );
  72. let fut = self.query.new_query::<bool>(&script).resolve();
  73. Box::pin(async move {
  74. match fut.await {
  75. Ok(true) => Ok(()),
  76. Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
  77. Box::new(DesktopQueryError::FailedToQuery),
  78. )),
  79. Err(err) => {
  80. MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
  81. }
  82. }
  83. })
  84. }
  85. fn scroll(
  86. &self,
  87. coordinates: PixelsVector2D,
  88. behavior: dioxus_html::ScrollBehavior,
  89. ) -> std::pin::Pin<Box<dyn std::future::Future<Output = MountedResult<()>>>> {
  90. let script = format!(
  91. "return window.interpreter.scroll({}, {}, {}, {});",
  92. self.id.0,
  93. coordinates.x,
  94. coordinates.y,
  95. serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior")
  96. );
  97. let fut = self.query.new_query::<bool>(&script).resolve();
  98. Box::pin(async move {
  99. match fut.await {
  100. Ok(true) => Ok(()),
  101. Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
  102. Box::new(DesktopQueryError::FailedToQuery),
  103. )),
  104. Err(err) => {
  105. MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
  106. }
  107. }
  108. })
  109. }
  110. fn set_focus(
  111. &self,
  112. focus: bool,
  113. ) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
  114. let script = format!(
  115. "return window.interpreter.setFocus({}, {});",
  116. self.id.0, focus
  117. );
  118. let fut = self.query.new_query::<bool>(&script).resolve();
  119. Box::pin(async move {
  120. match fut.await {
  121. Ok(true) => Ok(()),
  122. Ok(false) => MountedResult::Err(dioxus_html::MountedError::OperationFailed(
  123. Box::new(DesktopQueryError::FailedToQuery),
  124. )),
  125. Err(err) => {
  126. MountedResult::Err(dioxus_html::MountedError::OperationFailed(Box::new(err)))
  127. }
  128. }
  129. })
  130. }
  131. }
  132. #[derive(Debug)]
  133. enum DesktopQueryError {
  134. FailedToQuery,
  135. }
  136. impl std::fmt::Display for DesktopQueryError {
  137. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  138. match self {
  139. DesktopQueryError::FailedToQuery => write!(f, "Failed to query the element"),
  140. }
  141. }
  142. }
  143. impl std::error::Error for DesktopQueryError {}