diff --git a/packages/native-core/src/custom_element.rs b/packages/native-core/src/custom_element.rs index 62d7e5368..675e928f1 100644 --- a/packages/native-core/src/custom_element.rs +++ b/packages/native-core/src/custom_element.rs @@ -9,7 +9,7 @@ use shipyard::Component; use crate::{ node::{FromAnyValue, NodeType}, node_ref::AttributeMask, - prelude::{NodeImmutable, NodeMut, RealDom}, + prelude::{NodeImmutable, NodeMut}, tree::TreeMut, NodeId, }; @@ -27,11 +27,15 @@ impl Default for CustomElementRegistry { } impl CustomElementRegistry { - pub fn register>(&mut self) { + pub fn register(&mut self) + where + F: CustomElementFactory, + U: CustomElementUpdater, + { self.builders.insert( - (W::NAME, W::NAMESPACE), + (F::NAME, F::NAMESPACE), CustomElementBuilder { - create: |dom| Box::new(W::create(dom)), + create: |node| Box::new(F::create(node)), }, ); } @@ -44,10 +48,7 @@ impl CustomElementRegistry { }; if let Some((tag, ns)) = element_tag { if let Some(builder) = self.builders.get(&(tag.as_str(), ns.as_deref())) { - let boxed_widget = { - let dom = node.real_dom_mut(); - (builder.create)(dom) - }; + let boxed_widget = { (builder.create)(node.reborrow()) }; let shadow_roots = boxed_widget.roots(); @@ -69,7 +70,7 @@ impl CustomElementRegistry { } struct CustomElementBuilder { - create: fn(&mut RealDom) -> Box>, + create: fn(NodeMut) -> Box>, } /// A controlled element that renders to a shadow DOM @@ -81,7 +82,7 @@ pub trait CustomElement: Send + Sync + 'stat const NAMESPACE: Option<&'static str> = None; /// Create a new widget *without mounting* it. - fn create(dom: &mut RealDom) -> Self; + fn create(node: NodeMut) -> Self; /// The root node of the widget. This must be static once the element is created. fn roots(&self) -> Vec; @@ -96,7 +97,7 @@ pub trait CustomElement: Send + Sync + 'stat } /// A factory for creating widgets -trait ElementFactory, V: FromAnyValue + Send + Sync = ()>: +pub trait CustomElementFactory, V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static { /// The tag the widget is registered under. @@ -106,21 +107,21 @@ trait ElementFactory, V: FromAnyValue + Send + Sync = const NAMESPACE: Option<&'static str> = None; /// Create a new widget. - fn create(dom: &mut RealDom) -> W; + fn create(dom: NodeMut) -> W; } -impl, V: FromAnyValue + Send + Sync> ElementFactory for W { +impl, V: FromAnyValue + Send + Sync> CustomElementFactory for W { const NAME: &'static str = W::NAME; const NAMESPACE: Option<&'static str> = W::NAMESPACE; - fn create(dom: &mut RealDom) -> Self { - Self::create(dom) + fn create(node: NodeMut) -> Self { + Self::create(node) } } /// A trait for updating widgets -trait CustomElementUpdater: Send + Sync + 'static { +pub trait CustomElementUpdater: Send + Sync + 'static { /// Called when the attributes of the widget are changed. fn attributes_changed(&mut self, light_root: NodeMut, attributes: &AttributeMask); diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index e885963d1..61e9644a3 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -10,7 +10,10 @@ use std::collections::VecDeque; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, RwLock}; -use crate::custom_element::{CustomElement, CustomElementManager, CustomElementRegistry}; +use crate::custom_element::{ + CustomElement, CustomElementFactory, CustomElementManager, CustomElementRegistry, + CustomElementUpdater, +}; use crate::node::{ ElementNode, FromAnyValue, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, TextNode, }; @@ -478,7 +481,16 @@ impl RealDom { /// Registers a new custom element. pub fn register_custom_element>(&mut self) { - self.custom_elements.write().unwrap().register::() + self.register_custom_element_factory::() + } + + /// Registers a new custom element with a custom factory. + pub fn register_custom_element_factory(&mut self) + where + F: CustomElementFactory, + U: CustomElementUpdater, + { + self.custom_elements.write().unwrap().register::() } } @@ -678,6 +690,14 @@ impl<'a, V: FromAnyValue + Send + Sync> NodeImmutable for NodeMut<'a, V> { } impl<'a, V: FromAnyValue + Send + Sync> NodeMut<'a, V> { + /// Reborrow the node mutably + pub fn reborrow(&mut self) -> NodeMut<'_, V> { + NodeMut { + id: self.id, + dom: self.dom, + } + } + /// Get the real dom this node was created in mutably #[inline(always)] pub fn real_dom_mut(&mut self) -> &mut RealDom { diff --git a/packages/native-core/tests/custom_element.rs b/packages/native-core/tests/custom_element.rs index b7c6e7c97..7bddeacc9 100644 --- a/packages/native-core/tests/custom_element.rs +++ b/packages/native-core/tests/custom_element.rs @@ -179,7 +179,8 @@ struct CustomElementWithSlot { impl CustomElement for CustomElementWithSlot { const NAME: &'static str = "customelementslot"; - fn create(dom: &mut RealDom<()>) -> Self { + fn create(mut node: NodeMut<()>) -> Self { + let dom = node.real_dom_mut(); let child = dom.create_node(ElementNode { tag: "div".into(), namespace: None, @@ -227,7 +228,8 @@ struct CustomElementWithNoSlot { impl CustomElement for CustomElementWithNoSlot { const NAME: &'static str = "customelementnoslot"; - fn create(dom: &mut RealDom<()>) -> Self { + fn create(mut node: NodeMut<()>) -> Self { + let dom = node.real_dom_mut(); let root = dom.create_node(ElementNode { tag: "div".into(), namespace: None, diff --git a/packages/rink/examples/widgets.rs b/packages/rink/examples/widgets.rs index 3e709313f..1226d5c3d 100644 --- a/packages/rink/examples/widgets.rs +++ b/packages/rink/examples/widgets.rs @@ -77,7 +77,7 @@ impl Driver for Counter { _: bool, ) { match event_type { - "oninput" => { + "input" => { // when the button is clicked, increment the counter if let EventData::Form(input_event) = &*event { if let Ok(value) = input_event.value.parse::() { diff --git a/packages/rink/src/focus.rs b/packages/rink/src/focus.rs index e26f79b3d..4ede95485 100644 --- a/packages/rink/src/focus.rs +++ b/packages/rink/src/focus.rs @@ -183,7 +183,7 @@ impl FocusState { if forward { // find the closest focusable element after the current level - rdom.traverse_depth_first(|n| { + rdom.traverse_depth_first(true, |n| { let node_level = n.get::().unwrap().level; if node_level != *focus_level && node_level.focusable() @@ -200,7 +200,7 @@ impl FocusState { }); } else { // find the closest focusable element before the current level - rdom.traverse_depth_first(|n| { + rdom.traverse_depth_first(true, |n| { let node_level = n.get::().unwrap().level; if node_level != *focus_level && node_level.focusable() diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs index 341ab02ac..b54cc2eaf 100644 --- a/packages/rink/src/hooks.rs +++ b/packages/rink/src/hooks.rs @@ -25,7 +25,7 @@ use taffy::{prelude::Layout, Taffy}; use crate::focus::{Focus, Focused}; use crate::layout::TaffyLayout; -use crate::{layout_to_screen_space, FocusState}; +use crate::{get_abs_layout, layout_to_screen_space, FocusState}; #[derive(Debug, Clone, PartialEq)] pub struct Event { @@ -323,7 +323,7 @@ impl InnerInputState { if old_pos != Some(new_pos) { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousemove") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let previously_contained = old_pos .filter(|pos| layout_contains_point(&node_layout, *pos)) .is_some(); @@ -347,7 +347,7 @@ impl InnerInputState { // mouseenter let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseenter") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let previously_contained = old_pos .filter(|pos| layout_contains_point(&node_layout, *pos)) .is_some(); @@ -370,7 +370,7 @@ impl InnerInputState { // mouseover let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseover") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let previously_contained = old_pos .filter(|pos| layout_contains_point(&node_layout, *pos)) .is_some(); @@ -393,7 +393,7 @@ impl InnerInputState { if was_pressed { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mousedown") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let currently_contains = layout_contains_point(&node_layout, new_pos); if currently_contains { @@ -414,7 +414,7 @@ impl InnerInputState { if was_released { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseup") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let currently_contains = layout_contains_point(&node_layout, new_pos); if currently_contains { @@ -436,7 +436,7 @@ impl InnerInputState { if mouse_data.trigger_button() == Some(DioxusMouseButton::Primary) && was_released { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("click") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let currently_contains = layout_contains_point(&node_layout, new_pos); if currently_contains { @@ -459,7 +459,7 @@ impl InnerInputState { { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("contextmenu") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let currently_contains = layout_contains_point(&node_layout, new_pos); if currently_contains { @@ -482,7 +482,7 @@ impl InnerInputState { if was_scrolled { let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("wheel") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let currently_contains = layout_contains_point(&node_layout, new_pos); @@ -505,7 +505,7 @@ impl InnerInputState { // mouseleave let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseleave") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let previously_contained = old_pos .filter(|pos| layout_contains_point(&node_layout, *pos)) .is_some(); @@ -528,7 +528,7 @@ impl InnerInputState { // mouseout let mut will_bubble = FxHashSet::default(); for node in dom.get_listening_sorted("mouseout") { - let node_layout = get_abs_layout(node, dom, layout); + let node_layout = get_abs_layout(node, layout); let previously_contained = old_pos .filter(|pos| layout_contains_point(&node_layout, *pos)) .is_some(); @@ -550,7 +550,7 @@ impl InnerInputState { // update focus if was_released { let mut focus_id = None; - dom.traverse_depth_first(|node| { + dom.traverse_depth_first(true, |node| { let node_layout = layout .layout(node.get::().unwrap().node.unwrap()) .unwrap(); @@ -572,24 +572,6 @@ impl InnerInputState { // } } -fn get_abs_layout(node: NodeRef, dom: &RealDom, taffy: &Taffy) -> Layout { - let mut node_layout = *taffy - .layout(node.get::().unwrap().node.unwrap()) - .unwrap(); - let mut current = node; - - while let Some(parent) = current.parent_id() { - let parent = dom.get(parent).unwrap(); - current = parent; - let parent_layout = taffy - .layout(parent.get::().unwrap().node.unwrap()) - .unwrap(); - node_layout.location.x += parent_layout.location.x; - node_layout.location.y += parent_layout.location.y; - } - node_layout -} - pub struct RinkInputHandler { state: Rc>, queued_events: Rc>>, diff --git a/packages/rink/src/layout.rs b/packages/rink/src/layout.rs index b7eff57a6..3bb087596 100644 --- a/packages/rink/src/layout.rs +++ b/packages/rink/src/layout.rs @@ -25,12 +25,6 @@ impl PossiblyUninitalized { _ => panic!("uninitalized"), } } - pub fn ok(self) -> Option { - match self { - Self::Initialized(i) => Some(i), - _ => None, - } - } } impl Default for PossiblyUninitalized { fn default() -> Self { @@ -54,6 +48,9 @@ impl State for TaffyLayout { .with_attrs(AttributeMaskBuilder::Some(SORTED_LAYOUT_ATTRS)) .with_text(); + // The layout state should be effected by the shadow dom + const TRAVERSE_SHADOW_DOM: bool = true; + fn update<'a>( &mut self, node_view: NodeView, diff --git a/packages/rink/src/lib.rs b/packages/rink/src/lib.rs index 2aab01664..18b1e62c7 100644 --- a/packages/rink/src/lib.rs +++ b/packages/rink/src/lib.rs @@ -6,7 +6,7 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use dioxus_native_core::prelude::*; +use dioxus_native_core::{prelude::*, tree::TreeRef}; use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, SendAnyMap}; use focus::FocusState; use futures::{channel::mpsc::UnboundedSender, pin_mut, Future, StreamExt}; @@ -262,7 +262,7 @@ pub fn render( } }, Some(evt) = event_reciever.next() => { - event_recieved=Some(evt); + event_recieved = Some(evt); } } } @@ -342,19 +342,50 @@ pub trait Driver { /// Before sending the event to drivers, we need to bubble it up the tree to any widgets that are listening fn bubble_event_to_widgets(rdom: &mut RealDom, event: &Event) { let id = event.id; - let mut node = Some(rdom.get_mut(id).unwrap()); + let mut node = Some(id); + + while let Some(node_id) = node { + let parent_id = { + let tree = rdom.tree_ref(); + tree.parent_id_advanced(node_id, true) + }; - while let Some(mut node_mut) = node { - let parent_id = node_mut.parent_id(); - if let Some(mut widget) = node_mut - .get_mut::() - .map(|w| w.clone()) { - widget.handle_event(event, &mut node_mut) + // println!("@ bubbling event to node {:?}", node_id); + let mut node_mut = rdom.get_mut(node_id).unwrap(); + if let Some(mut widget) = node_mut + .get_mut::() + .map(|w| w.clone()) + { + widget.handle_event(event, node_mut) + } } + if !event.bubbles { + // println!("event does not bubble"); break; } - node = parent_id.map(|id| rdom.get_mut(id).unwrap()); + node = parent_id; } } + +pub(crate) fn get_abs_layout(node: NodeRef, taffy: &Taffy) -> Layout { + let mut node_layout = *taffy + .layout(node.get::().unwrap().node.unwrap()) + .unwrap(); + let mut current = node; + + let dom = node.real_dom(); + let tree = dom.tree_ref(); + + while let Some(parent) = tree.parent_id_advanced(current.id(), true) { + let parent = dom.get(parent).unwrap(); + current = parent; + let parent_layout = taffy + .layout(parent.get::().unwrap().node.unwrap()) + .unwrap(); + node_layout.location.x += parent_layout.location.x; + node_layout.location.y += parent_layout.location.y; + } + node_layout +} diff --git a/packages/rink/src/query.rs b/packages/rink/src/query.rs index c204e1953..3b8f319cf 100644 --- a/packages/rink/src/query.rs +++ b/packages/rink/src/query.rs @@ -8,7 +8,7 @@ use taffy::{ Taffy, }; -use crate::{layout::TaffyLayout, layout_to_screen_space}; +use crate::{get_abs_layout, layout_to_screen_space}; /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered. /// Provided as a root context for all tui applictions. @@ -95,21 +95,8 @@ impl<'a> ElementRef<'a> { } pub(crate) fn get_layout(node: NodeRef, stretch: &Taffy) -> Option { - let layout = stretch - .layout(node.get::().unwrap().node.ok()?) - .ok()?; - - let mut current_node_id = node.parent_id(); - let mut pos = layout.location; - let rdom = node.real_dom(); - while let Some(node) = current_node_id.and_then(|id| rdom.get(id)) { - let current_layout = stretch - .layout(node.get::().unwrap().node.ok()?) - .ok()?; - pos.x += current_layout.location.x; - pos.y += current_layout.location.y; - current_node_id = node.parent_id(); - } + let layout = get_abs_layout(node, stretch); + let pos = layout.location; Some(Layout { order: layout.order, diff --git a/packages/rink/src/render.rs b/packages/rink/src/render.rs index ef433976d..19d642720 100644 --- a/packages/rink/src/render.rs +++ b/packages/rink/src/render.rs @@ -1,4 +1,4 @@ -use dioxus_native_core::prelude::*; +use dioxus_native_core::{prelude::*, tree::TreeRef}; use std::io::Stdout; use taffy::{ geometry::Point, @@ -83,7 +83,10 @@ pub(crate) fn render_vnode( frame.render_widget(WidgetWithContext::new(node, cfg), area); } - for c in node.children() { + let node_id = node.id(); + let rdom = node.real_dom(); + for child_id in rdom.tree_ref().children_ids_advanced(node_id, true) { + let c = rdom.get(child_id).unwrap(); render_vnode(frame, layout, c, cfg, location); } } diff --git a/packages/rink/src/widgets/button.rs b/packages/rink/src/widgets/button.rs index 2eaddfe59..e4d3754f1 100644 --- a/packages/rink/src/widgets/button.rs +++ b/packages/rink/src/widgets/button.rs @@ -2,14 +2,14 @@ use std::collections::HashMap; use dioxus_html::input_data::keyboard_types::Key; use dioxus_native_core::{ + custom_element::CustomElement, node::OwnedAttributeDiscription, node_ref::AttributeMask, prelude::NodeType, - real_dom::{ElementNodeMut, NodeImmutable, NodeTypeMut, RealDom}, - utils::widget_watcher::Widget, + real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom}, NodeId, }; -use shipyard::UniqueViewMut; +use shipyard::UniqueView; use crate::FormData; @@ -17,7 +17,6 @@ use super::{RinkWidget, WidgetContext}; #[derive(Debug, Default)] pub(crate) struct Button { - div_id: NodeId, text_id: NodeId, value: String, } @@ -88,14 +87,14 @@ impl Button { } } - fn switch(&mut self, ctx: &mut WidgetContext) { + fn switch(&mut self, ctx: &mut WidgetContext, node: NodeMut) { let data = FormData { value: self.value.to_string(), values: HashMap::new(), files: None, }; ctx.send(crate::Event { - id: self.div_id, + id: node.id(), name: "input", data: crate::EventData::Form(data), bubbles: true, @@ -103,10 +102,14 @@ impl Button { } } -impl Widget for Button { +impl CustomElement for Button { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + vec![self.text_id] + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; @@ -127,11 +130,8 @@ impl Widget for Button { root.add_event_listener("keydown"); root.add_event_listener("click"); - let div_id = root.id(); - root.add_child(text_id); Self { - div_id, text_id, value: value.unwrap_or_default(), } @@ -139,7 +139,7 @@ impl Widget for Button { fn attributes_changed( &mut self, - mut root: dioxus_native_core::real_dom::NodeMut<()>, + mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { @@ -175,16 +175,18 @@ impl RinkWidget for Button { fn handle_event( &mut self, event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, + mut node: dioxus_native_core::real_dom::NodeMut, ) { - let mut ctx: UniqueViewMut = node - .real_dom_mut() - .raw_world_mut() - .borrow() - .expect("expected widget context"); + let mut ctx: WidgetContext = { + node.real_dom_mut() + .raw_world_mut() + .borrow::>() + .expect("expected widget context") + .clone() + }; match event.name { - "click" => self.switch(&mut ctx), + "click" => self.switch(&mut ctx, node), "keydown" => { if let crate::EventData::Keyboard(data) = &event.data { if !data.is_auto_repeating() @@ -194,7 +196,7 @@ impl RinkWidget for Button { _ => false, } { - self.switch(&mut ctx); + self.switch(&mut ctx, node); } } } diff --git a/packages/rink/src/widgets/checkbox.rs b/packages/rink/src/widgets/checkbox.rs index 943db5213..24b3f6244 100644 --- a/packages/rink/src/widgets/checkbox.rs +++ b/packages/rink/src/widgets/checkbox.rs @@ -2,11 +2,11 @@ use std::collections::HashMap; use dioxus_html::input_data::keyboard_types::Key; use dioxus_native_core::{ + custom_element::CustomElement, node::OwnedAttributeDiscription, node_ref::AttributeMask, prelude::NodeType, real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut}, - utils::widget_watcher::Widget, NodeId, }; use shipyard::UniqueView; @@ -91,7 +91,7 @@ impl CheckBox { == "true"; } - fn write_value(&self, root: &mut NodeMut) { + fn write_value(&self, mut root: NodeMut) { let single_char = { let node_type = root.node_type_mut(); let NodeTypeMut::Element( el) = node_type else { panic!("input must be an element") }; @@ -117,7 +117,7 @@ impl CheckBox { } } - fn switch(&mut self, node: &mut NodeMut) { + fn switch(&mut self, mut node: NodeMut) { let new_state = !self.checked; let data = FormData { @@ -147,10 +147,14 @@ impl CheckBox { } } -impl Widget for CheckBox { +impl CustomElement for CheckBox { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + vec![self.text_id] + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; @@ -172,7 +176,6 @@ impl Widget for CheckBox { root.add_event_listener("click"); root.add_event_listener("keydown"); let div_id = root.id(); - root.add_child(text_id); let myself = Self { div_id, @@ -187,7 +190,7 @@ impl Widget for CheckBox { fn attributes_changed( &mut self, - mut root: dioxus_native_core::real_dom::NodeMut<()>, + mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { @@ -199,7 +202,7 @@ impl Widget for CheckBox { self.update_size_attr(&mut el); self.update_checked_attr(&el); } - self.write_value(&mut root); + self.write_value(root); } AttributeMask::Some(attrs) => { { @@ -216,7 +219,7 @@ impl Widget for CheckBox { } } if attrs.contains("checked") { - self.write_value(&mut root); + self.write_value(root); } } } @@ -224,11 +227,7 @@ impl Widget for CheckBox { } impl RinkWidget for CheckBox { - fn handle_event( - &mut self, - event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, - ) { + fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) { match event.name { "click" => self.switch(node), "keydown" => { diff --git a/packages/rink/src/widgets/input.rs b/packages/rink/src/widgets/input.rs index 2253ae096..c6a3e56b9 100644 --- a/packages/rink/src/widgets/input.rs +++ b/packages/rink/src/widgets/input.rs @@ -1,6 +1,6 @@ use dioxus_native_core::{ - node::OwnedAttributeDiscription, prelude::NodeType, real_dom::NodeImmutable, - utils::widget_watcher::Widget, + custom_element::CustomElement, node::OwnedAttributeDiscription, prelude::NodeType, + real_dom::NodeImmutable, }; use super::{ @@ -18,10 +18,32 @@ pub(crate) enum Input { Slider(Slider), } -impl Widget for Input { +impl CustomElement for Input { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + match self { + Input::Button(button) => button.roots(), + Input::CheckBox(checkbox) => checkbox.roots(), + Input::TextBox(textbox) => textbox.roots(), + Input::Password(password) => password.roots(), + Input::Number(number) => number.roots(), + Input::Slider(slider) => slider.roots(), + } + } + + fn slot(&self) -> Option { + match self { + Input::Button(button) => button.slot(), + Input::CheckBox(checkbox) => checkbox.slot(), + Input::TextBox(textbox) => textbox.slot(), + Input::Password(password) => password.slot(), + Input::Number(number) => number.slot(), + Input::Slider(slider) => slider.slot(), + } + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { { // currently widgets are not allowed to have children let children = root.child_ids(); @@ -42,7 +64,10 @@ impl Widget for Input { namespace: None, }) .and_then(|value| value.as_text()); - match input_type { + match input_type + .map(|type_| type_.trim().to_lowercase()) + .as_deref() + { Some("button") => { drop(node_type); Input::Button(Button::create(root)) @@ -76,7 +101,7 @@ impl Widget for Input { fn attributes_changed( &mut self, - root: dioxus_native_core::real_dom::NodeMut<()>, + root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match self { @@ -103,11 +128,7 @@ impl Widget for Input { } impl RinkWidget for Input { - fn handle_event( - &mut self, - event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, - ) { + fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) { match self { Input::Button(button) => { button.handle_event(event, node); diff --git a/packages/rink/src/widgets/mod.rs b/packages/rink/src/widgets/mod.rs index 283ebbcd9..27dea751d 100644 --- a/packages/rink/src/widgets/mod.rs +++ b/packages/rink/src/widgets/mod.rs @@ -9,8 +9,8 @@ mod textbox; use std::sync::{Arc, RwLock}; use dioxus_native_core::{ - real_dom::RealDom, - utils::widget_watcher::{Widget, WidgetFactory, WidgetUpdater, WidgetWatcher}, + custom_element::{CustomElement, CustomElementUpdater}, + real_dom::{NodeMut, RealDom}, }; use futures_channel::mpsc::UnboundedSender; use shipyard::{Component, Unique}; @@ -21,42 +21,64 @@ pub(crate) fn register_widgets(rdom: &mut RealDom, sender: UnboundedSender, RinkWidgetTraitObject>(); - - widget_watcher.attach(rdom); + rdom.register_custom_element::>(); } -trait RinkWidget: Sync + Send + Widget + 'static { - fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut); +trait RinkWidget: Sync + Send + CustomElement + 'static { + fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut); } -pub trait RinkWidgetResponder: WidgetUpdater { - fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut); +pub trait RinkWidgetResponder: CustomElementUpdater { + fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut); } impl RinkWidgetResponder for W { - fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut) { + fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut) { RinkWidget::handle_event(self, event, node) } } -struct RinkWidgetTraitObjectFactory { +struct RinkWidgetWrapper { + inner: RinkWidgetTraitObject, _marker: std::marker::PhantomData, } -impl WidgetFactory for RinkWidgetTraitObjectFactory { +impl CustomElement for RinkWidgetWrapper { const NAME: &'static str = W::NAME; - fn create(node: &mut dioxus_native_core::real_dom::NodeMut) -> RinkWidgetTraitObject { + const NAMESPACE: Option<&'static str> = W::NAMESPACE; + + fn create(mut node: NodeMut) -> Self { let myself = RinkWidgetTraitObject { - widget: Arc::new(RwLock::new(W::create(node))), + widget: Arc::new(RwLock::new(W::create(node.reborrow()))), }; + + // Insert the widget as an arbitrary data node so that it can be recognized when bubbling events node.insert(myself.clone()); - myself + + RinkWidgetWrapper { + inner: myself, + _marker: std::marker::PhantomData, + } + } + + fn attributes_changed( + &mut self, + root: dioxus_native_core::real_dom::NodeMut, + attributes: &dioxus_native_core::node_ref::AttributeMask, + ) { + let mut widget = self.inner.widget.write().unwrap(); + widget.attributes_changed(root, attributes); + } + + fn roots(&self) -> Vec { + let widget = self.inner.widget.read().unwrap(); + widget.roots() + } + + fn slot(&self) -> Option { + let widget = self.inner.widget.read().unwrap(); + widget.slot() } } @@ -65,25 +87,35 @@ pub(crate) struct RinkWidgetTraitObject { widget: Arc>, } -impl WidgetUpdater for RinkWidgetTraitObject { +impl CustomElementUpdater for RinkWidgetTraitObject { fn attributes_changed( &mut self, - root: dioxus_native_core::real_dom::NodeMut, + light_root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { let mut widget = self.widget.write().unwrap(); - widget.attributes_changed(root, attributes); + widget.attributes_changed(light_root, attributes); + } + + fn roots(&self) -> Vec { + let widget = self.widget.read().unwrap(); + widget.roots() + } + + fn slot(&self) -> Option { + let widget = self.widget.read().unwrap(); + widget.slot() } } impl RinkWidgetResponder for RinkWidgetTraitObject { - fn handle_event(&mut self, event: &Event, node: &mut dioxus_native_core::real_dom::NodeMut) { + fn handle_event(&mut self, event: &Event, node: dioxus_native_core::real_dom::NodeMut) { let mut widget = self.widget.write().unwrap(); widget.handle_event(event, node); } } -#[derive(Unique)] +#[derive(Unique, Clone)] pub(crate) struct WidgetContext { sender: UnboundedSender, } diff --git a/packages/rink/src/widgets/number.rs b/packages/rink/src/widgets/number.rs index 2925e2966..c83d514b5 100644 --- a/packages/rink/src/widgets/number.rs +++ b/packages/rink/src/widgets/number.rs @@ -3,14 +3,12 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData}; use dioxus_native_core::{ + custom_element::CustomElement, node::OwnedAttributeDiscription, node_ref::AttributeMask, prelude::{ElementNode, NodeType}, real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom}, - utils::{ - cursor::{Cursor, Pos}, - widget_watcher::Widget, - }, + utils::cursor::{Cursor, Pos}, NodeId, }; use shipyard::UniqueView; @@ -169,7 +167,7 @@ impl Number { self.text = (num - 1.0).to_string(); } - fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) { + fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) { let key = data.key(); let is_text = match key.clone() { Key::ArrowLeft | Key::ArrowRight | Key::Backspace => true, @@ -235,14 +233,11 @@ impl Number { } } - fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) { if self.dragging { let id = root.id(); let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); - if self.border { - new.col = new.col.saturating_sub(1); - } // textboxs are only one line tall new.row = 0; @@ -254,12 +249,9 @@ impl Number { } } - fn handle_mousedown(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) { let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); - if self.border { - new.col = new.col.saturating_sub(1); - } // textboxs are only one line tall new.row = 0; @@ -297,10 +289,14 @@ impl Number { } } -impl Widget for Number { +impl CustomElement for Number { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + vec![self.div_wrapper] + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; @@ -364,8 +360,6 @@ impl Widget for Number { root.add_event_listener("keydown"); root.add_event_listener("focusout"); - root.add_child(div_wrapper_id); - Self { pre_cursor_text: pre_text_id, highlighted_text: highlighted_text_id, @@ -379,7 +373,7 @@ impl Widget for Number { fn attributes_changed( &mut self, - mut root: dioxus_native_core::real_dom::NodeMut<()>, + mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { @@ -418,11 +412,7 @@ impl Widget for Number { } impl RinkWidget for Number { - fn handle_event( - &mut self, - event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, - ) { + fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) { match event.name { "keydown" => { if let EventData::Keyboard(data) = &event.data { diff --git a/packages/rink/src/widgets/password.rs b/packages/rink/src/widgets/password.rs index 483b1e175..7e570a631 100644 --- a/packages/rink/src/widgets/password.rs +++ b/packages/rink/src/widgets/password.rs @@ -3,14 +3,12 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData}; use dioxus_native_core::{ + custom_element::CustomElement, node::OwnedAttributeDiscription, node_ref::AttributeMask, prelude::{ElementNode, NodeType}, real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom}, - utils::{ - cursor::{Cursor, Pos}, - widget_watcher::Widget, - }, + utils::cursor::{Cursor, Pos}, NodeId, }; use shipyard::UniqueView; @@ -159,7 +157,7 @@ impl Password { } } - fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) { + fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) { let key = data.key(); let modifiers = data.modifiers(); let code = data.code(); @@ -203,14 +201,11 @@ impl Password { } } - fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) { if self.dragging { let id = root.id(); let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); - if self.border { - new.col = new.col.saturating_sub(1); - } // textboxs are only one line tall new.row = 0; @@ -222,12 +217,9 @@ impl Password { } } - fn handle_mousedown(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) { let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); - if self.border { - new.col = new.col.saturating_sub(1); - } // textboxs are only one line tall new.row = 0; @@ -265,10 +257,14 @@ impl Password { } } -impl Widget for Password { +impl CustomElement for Password { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + vec![self.div_wrapper] + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; @@ -332,8 +328,6 @@ impl Widget for Password { root.add_event_listener("keydown"); root.add_event_listener("focusout"); - root.add_child(div_wrapper_id); - Self { pre_cursor_text: pre_text_id, highlighted_text: highlighted_text_id, @@ -347,7 +341,7 @@ impl Widget for Password { fn attributes_changed( &mut self, - mut root: dioxus_native_core::real_dom::NodeMut<()>, + mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { @@ -386,11 +380,7 @@ impl Widget for Password { } impl RinkWidget for Password { - fn handle_event( - &mut self, - event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, - ) { + fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) { match event.name { "keydown" => { if let EventData::Keyboard(data) = &event.data { diff --git a/packages/rink/src/widgets/slider.rs b/packages/rink/src/widgets/slider.rs index e1fa4f980..f97c12bf6 100644 --- a/packages/rink/src/widgets/slider.rs +++ b/packages/rink/src/widgets/slider.rs @@ -2,11 +2,11 @@ use std::collections::HashMap; use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData}; use dioxus_native_core::{ + custom_element::CustomElement, node::{OwnedAttributeDiscription, OwnedAttributeValue}, node_ref::AttributeMask, prelude::{ElementNode, NodeType}, real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom}, - utils::widget_watcher::Widget, NodeId, }; use shipyard::UniqueView; @@ -205,7 +205,7 @@ impl Slider { } } - fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) { + fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) { let key = data.key(); let step = self.step(); @@ -227,7 +227,7 @@ impl Slider { self.write_value(rdom, id); } - fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) { if !data.held_buttons().is_empty() { let id = root.id(); let rdom = root.real_dom_mut(); @@ -250,10 +250,14 @@ impl Slider { } } -impl Widget for Slider { +impl CustomElement for Slider { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + vec![self.div_wrapper] + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; @@ -364,12 +368,10 @@ impl Widget for Slider { div_wrapper.add_child(cursor_span_id); div_wrapper.add_child(post_cursor_div_id); - div_wrapper.add_event_listener("mousemove"); - div_wrapper.add_event_listener("mousedown"); + root.add_event_listener("mousemove"); + root.add_event_listener("mousedown"); root.add_event_listener("keydown"); - root.add_child(div_wrapper_id); - Self { pre_cursor_div: pre_cursor_div_id, post_cursor_div: post_cursor_div_id, @@ -381,7 +383,7 @@ impl Widget for Slider { fn attributes_changed( &mut self, - mut root: dioxus_native_core::real_dom::NodeMut<()>, + mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { @@ -428,11 +430,7 @@ impl Widget for Slider { } impl RinkWidget for Slider { - fn handle_event( - &mut self, - event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, - ) { + fn handle_event(&mut self, event: &crate::Event, node: dioxus_native_core::real_dom::NodeMut) { match event.name { "keydown" => { if let EventData::Keyboard(data) = &event.data { diff --git a/packages/rink/src/widgets/textbox.rs b/packages/rink/src/widgets/textbox.rs index 16f73fd78..8eaadb7a7 100644 --- a/packages/rink/src/widgets/textbox.rs +++ b/packages/rink/src/widgets/textbox.rs @@ -3,14 +3,12 @@ use std::{collections::HashMap, io::stdout}; use crossterm::{cursor::MoveTo, execute}; use dioxus_html::{input_data::keyboard_types::Key, KeyboardData, MouseData}; use dioxus_native_core::{ + custom_element::CustomElement, node::OwnedAttributeDiscription, node_ref::AttributeMask, prelude::{ElementNode, NodeType}, real_dom::{ElementNodeMut, NodeImmutable, NodeMut, NodeTypeMut, RealDom}, - utils::{ - cursor::{Cursor, Pos}, - widget_watcher::Widget, - }, + utils::cursor::{Cursor, Pos}, NodeId, }; use shipyard::UniqueView; @@ -159,7 +157,7 @@ impl TextBox { } } - fn handle_keydown(&mut self, root: &mut NodeMut, data: &KeyboardData) { + fn handle_keydown(&mut self, mut root: NodeMut, data: &KeyboardData) { let key = data.key(); let modifiers = data.modifiers(); let code = data.code(); @@ -203,14 +201,12 @@ impl TextBox { } } - fn handle_mousemove(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousemove(&mut self, mut root: NodeMut, data: &MouseData) { if self.dragging { let id = root.id(); let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); - if self.border { - new.col = new.col.saturating_sub(1); - } + // textboxs are only one line tall new.row = 0; @@ -222,12 +218,9 @@ impl TextBox { } } - fn handle_mousedown(&mut self, root: &mut NodeMut, data: &MouseData) { + fn handle_mousedown(&mut self, mut root: NodeMut, data: &MouseData) { let offset = data.element_coordinates(); let mut new = Pos::new(offset.x as usize, offset.y as usize); - if self.border { - new.col = new.col.saturating_sub(1); - } // textboxs are only one line tall new.row = 0; @@ -265,10 +258,14 @@ impl TextBox { } } -impl Widget for TextBox { +impl CustomElement for TextBox { const NAME: &'static str = "input"; - fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self { + fn roots(&self) -> Vec { + vec![self.div_wrapper] + } + + fn create(mut root: dioxus_native_core::real_dom::NodeMut) -> Self { let node_type = root.node_type(); let NodeType::Element(el) = &*node_type else { panic!("input must be an element") }; @@ -332,8 +329,6 @@ impl Widget for TextBox { root.add_event_listener("keydown"); root.add_event_listener("focusout"); - root.add_child(div_wrapper_id); - Self { pre_cursor_text: pre_text_id, highlighted_text: highlighted_text_id, @@ -347,7 +342,7 @@ impl Widget for TextBox { fn attributes_changed( &mut self, - mut root: dioxus_native_core::real_dom::NodeMut<()>, + mut root: dioxus_native_core::real_dom::NodeMut, attributes: &dioxus_native_core::node_ref::AttributeMask, ) { match attributes { @@ -386,11 +381,7 @@ impl Widget for TextBox { } impl RinkWidget for TextBox { - fn handle_event( - &mut self, - event: &crate::Event, - node: &mut dioxus_native_core::real_dom::NodeMut, - ) { + fn handle_event(&mut self, event: &crate::Event, node: NodeMut) { match event.name { "keydown" => { if let EventData::Keyboard(data) = &event.data {