diff --git a/packages/tui/src/element.rs b/packages/tui/src/element.rs new file mode 100644 index 000000000..5ea1e56af --- /dev/null +++ b/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 { + 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) { + 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>, + >, + >, + > { + 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 {} diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 75c865de5..911c7c4db 100644 --- a/packages/tui/src/lib.rs +++ b/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(app: Component, 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());