Bladeren bron

implement mounted event for TUI

Evan Almloff 2 jaren geleden
bovenliggende
commit
e0d46d9820
2 gewijzigde bestanden met toevoegingen van 102 en 0 verwijderingen
  1. 92 0
      packages/tui/src/element.rs
  2. 10 0
      packages/tui/src/lib.rs

+ 92 - 0
packages/tui/src/element.rs

@@ -0,0 +1,92 @@
+use std::{
+    fmt::{Display, Formatter},
+    rc::Rc,
+};
+
+use dioxus_core::{ElementId, Mutations, VirtualDom};
+use dioxus_html::{
+    geometry::euclid::{Point2D, Rect, Size2D},
+    MountedData, MountedError, RenderedElementBacking,
+};
+
+use crate::query::{ElementRef, Query};
+
+pub(crate) fn find_mount_events(mutations: &Mutations) -> Vec<ElementId> {
+    let mut mount_events = Vec::new();
+    for mutation in &mutations.edits {
+        if let dioxus_core::Mutation::NewEventListener {
+            name: "mounted",
+            id,
+        } = mutation
+        {
+            mount_events.push(*id);
+        }
+    }
+    mount_events
+}
+
+pub(crate) fn send_mounted_events(vdom: &mut VirtualDom, mount_events: Vec<ElementId>) {
+    let query: Query = vdom
+        .base_scope()
+        .consume_context()
+        .expect("Query should be in context");
+    for id in mount_events {
+        let element = TuiElement {
+            query: query.clone(),
+            id,
+        };
+        vdom.handle_event("mounted", Rc::new(MountedData::new(element)), id, false);
+    }
+}
+
+struct TuiElement {
+    query: Query,
+    id: ElementId,
+}
+
+impl TuiElement {
+    pub(crate) fn element(&self) -> ElementRef {
+        self.query.get(self.id)
+    }
+}
+
+impl RenderedElementBacking for TuiElement {
+    fn get_client_rect(
+        &self,
+    ) -> std::pin::Pin<
+        Box<
+            dyn futures::Future<
+                Output = dioxus_html::MountedResult<dioxus_html::geometry::euclid::Rect<f64, f64>>,
+            >,
+        >,
+    > {
+        let layout = self.element().layout();
+        Box::pin(async move {
+            match layout {
+                Some(layout) => {
+                    let x = layout.location.x as f64;
+                    let y = layout.location.y as f64;
+                    let width = layout.size.width as f64;
+                    let height = layout.size.height as f64;
+                    Ok(Rect::new(Point2D::new(x, y), Size2D::new(width, height)))
+                }
+                None => Err(MountedError::OperationFailed(Box::new(TuiElementNotFound))),
+            }
+        })
+    }
+
+    fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> {
+        Ok(self)
+    }
+}
+
+#[derive(Debug)]
+struct TuiElementNotFound;
+
+impl Display for TuiElementNotFound {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "TUI element not found")
+    }
+}
+
+impl std::error::Error for TuiElementNotFound {}

+ 10 - 0
packages/tui/src/lib.rs

@@ -7,6 +7,7 @@ use crossterm::{
 };
 use dioxus_core::*;
 use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, NodeMask, SendAnyMap};
+use element::{find_mount_events, send_mounted_events};
 use focus::FocusState;
 use futures::{
     channel::mpsc::{UnboundedReceiver, UnboundedSender},
@@ -26,6 +27,7 @@ use tokio::select;
 use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
 
 mod config;
+mod element;
 mod focus;
 mod hooks;
 mod layout;
@@ -114,7 +116,11 @@ pub fn launch_cfg_with_props<Props: 'static>(app: Component<Props>, props: Props
     {
         let mut rdom = rdom.borrow_mut();
         let mutations = dom.rebuild();
+        // Search for mount events that happened during the rebuild
+        let mounted = find_mount_events(&mutations);
         let (to_update, _) = rdom.apply_mutations(mutations);
+        // Send the mount events
+        send_mounted_events(&mut dom, mounted);
         let mut any_map = SendAnyMap::new();
         any_map.insert(taffy.clone());
         let _to_rerender = rdom.update_state(to_update, any_map);
@@ -307,8 +313,12 @@ fn render_vdom(
                     let mut rdom = rdom.borrow_mut();
                     let mutations = vdom.render_immediate();
                     handler.prune(&mutations, &rdom);
+                    // Search for mount events that happened during the rebuild
+                    let mounted = find_mount_events(&mutations);
                     // updates the dom's nodes
                     let (to_update, dirty) = rdom.apply_mutations(mutations);
+                    // Send the mount events
+                    send_mounted_events(vdom, mounted);
                     // update the style and layout
                     let mut any_map = SendAnyMap::new();
                     any_map.insert(taffy.clone());