@@ -29,6 +29,8 @@ use serde::{
2929#[ cfg( feature = "schemars" ) ]
3030use serde_json:: { Map as SchemaMap , Value as SchemaValue } ;
3131
32+ pub use uuid:: Uuid ;
33+
3234mod geometry;
3335pub use geometry:: { Affine , Point , Rect , Size , Vec2 } ;
3436
@@ -626,6 +628,11 @@ pub enum TextDecoration {
626628pub type NodeIdContent = u64 ;
627629
628630/// The stable identity of a [`Node`], unique within the node's tree.
631+ ///
632+ /// Each tree (root or subtree) has its own independent ID space. The same
633+ /// `NodeId` value can exist in different trees without conflict. When working
634+ /// with multiple trees, the combination of `NodeId` and [`TreeId`] uniquely
635+ /// identifies a node.
629636#[ derive( Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
630637#[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
631638#[ cfg_attr( feature = "schemars" , derive( JsonSchema ) ) ]
@@ -652,6 +659,21 @@ impl fmt::Debug for NodeId {
652659 }
653660}
654661
662+ /// The stable identity of a [`Tree`].
663+ ///
664+ /// Use [`TreeId::ROOT`] for the main/root tree. For subtrees, use a random
665+ /// UUID (version 4) to avoid collisions between independently created trees.
666+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
667+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
668+ #[ cfg_attr( feature = "schemars" , derive( JsonSchema ) ) ]
669+ #[ repr( transparent) ]
670+ pub struct TreeId ( pub Uuid ) ;
671+
672+ impl TreeId {
673+ /// A reserved tree ID for the root tree. This uses a nil UUID.
674+ pub const ROOT : Self = Self ( Uuid :: nil ( ) ) ;
675+ }
676+
655677/// Defines a custom action for a UI element.
656678///
657679/// For example, a list UI can allow a user to reorder items in the list by dragging the
@@ -774,6 +796,7 @@ enum PropertyValue {
774796 Rect ( Rect ) ,
775797 TextSelection ( Box < TextSelection > ) ,
776798 CustomActionVec ( Vec < CustomAction > ) ,
799+ TreeId ( TreeId ) ,
777800}
778801
779802#[ derive( Clone , Copy , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
@@ -892,6 +915,7 @@ enum PropertyId {
892915 Bounds ,
893916 TextSelection ,
894917 CustomActions ,
918+ TreeId ,
895919
896920 // This MUST be last.
897921 Unset ,
@@ -1728,7 +1752,8 @@ copy_type_getters! {
17281752 ( get_usize_property, usize , Usize ) ,
17291753 ( get_color_property, Color , Color ) ,
17301754 ( get_text_decoration_property, TextDecoration , TextDecoration ) ,
1731- ( get_bool_property, bool , Bool )
1755+ ( get_bool_property, bool , Bool ) ,
1756+ ( get_tree_id_property, TreeId , TreeId )
17321757}
17331758
17341759box_type_setters ! {
@@ -1747,7 +1772,8 @@ copy_type_setters! {
17471772 ( set_usize_property, usize , Usize ) ,
17481773 ( set_color_property, Color , Color ) ,
17491774 ( set_text_decoration_property, TextDecoration , TextDecoration ) ,
1750- ( set_bool_property, bool , Bool )
1775+ ( set_bool_property, bool , Bool ) ,
1776+ ( set_tree_id_property, TreeId , TreeId )
17511777}
17521778
17531779vec_type_methods ! {
@@ -2051,11 +2077,20 @@ property_methods! {
20512077 /// [`transform`]: Node::transform
20522078 ( Bounds , bounds, get_rect_property, Option <Rect >, set_bounds, set_rect_property, Rect , clear_bounds) ,
20532079
2054- ( TextSelection , text_selection, get_text_selection_property, Option <& TextSelection >, set_text_selection, set_text_selection_property, impl Into <Box <TextSelection >>, clear_text_selection)
2080+ ( TextSelection , text_selection, get_text_selection_property, Option <& TextSelection >, set_text_selection, set_text_selection_property, impl Into <Box <TextSelection >>, clear_text_selection) ,
2081+
2082+ /// The tree that this node grafts. When set, this node acts as a graft
2083+ /// point, and its child is the root of the specified subtree.
2084+ ///
2085+ /// A graft node must be created before its subtree is pushed.
2086+ ///
2087+ /// Removing a graft node or clearing this property removes its subtree,
2088+ /// unless a new graft node is provided in the same update.
2089+ ( TreeId , tree_id, get_tree_id_property, Option <TreeId >, set_tree_id, set_tree_id_property, TreeId , clear_tree_id)
20552090}
20562091
20572092impl Node {
2058- option_properties_debug_method ! { debug_option_properties, [ transform, bounds, text_selection, ] }
2093+ option_properties_debug_method ! { debug_option_properties, [ transform, bounds, text_selection, tree_id , ] }
20592094}
20602095
20612096#[ cfg( test) ]
@@ -2160,6 +2195,31 @@ mod text_selection {
21602195 }
21612196}
21622197
2198+ #[ cfg( test) ]
2199+ mod tree_id {
2200+ use super :: { Node , Role , TreeId , Uuid } ;
2201+
2202+ #[ test]
2203+ fn getter_should_return_default_value ( ) {
2204+ let node = Node :: new ( Role :: GenericContainer ) ;
2205+ assert ! ( node. tree_id( ) . is_none( ) ) ;
2206+ }
2207+ #[ test]
2208+ fn setter_should_update_the_property ( ) {
2209+ let mut node = Node :: new ( Role :: GenericContainer ) ;
2210+ let value = TreeId ( Uuid :: nil ( ) ) ;
2211+ node. set_tree_id ( value) ;
2212+ assert_eq ! ( node. tree_id( ) , Some ( value) ) ;
2213+ }
2214+ #[ test]
2215+ fn clearer_should_reset_the_property ( ) {
2216+ let mut node = Node :: new ( Role :: GenericContainer ) ;
2217+ node. set_tree_id ( TreeId ( Uuid :: nil ( ) ) ) ;
2218+ node. clear_tree_id ( ) ;
2219+ assert ! ( node. tree_id( ) . is_none( ) ) ;
2220+ }
2221+ }
2222+
21632223vec_property_methods ! {
21642224 ( CustomActions , CustomAction , custom_actions, get_custom_action_vec, set_custom_actions, set_custom_action_vec, push_custom_action, push_to_custom_action_vec, clear_custom_actions)
21652225}
@@ -2330,7 +2390,8 @@ impl Serialize for Properties {
23302390 Affine ,
23312391 Rect ,
23322392 TextSelection ,
2333- CustomActionVec
2393+ CustomActionVec ,
2394+ TreeId
23342395 } ) ;
23352396 }
23362397 map. end ( )
@@ -2462,7 +2523,8 @@ impl<'de> Visitor<'de> for PropertiesVisitor {
24622523 Affine { Transform } ,
24632524 Rect { Bounds } ,
24642525 TextSelection { TextSelection } ,
2465- CustomActionVec { CustomActions }
2526+ CustomActionVec { CustomActions } ,
2527+ TreeId { TreeId }
24662528 } ) ;
24672529 }
24682530
@@ -2694,11 +2756,26 @@ pub struct TreeUpdate {
26942756 /// a tree.
26952757 pub tree : Option < Tree > ,
26962758
2759+ /// The identifier of the tree that this update applies to.
2760+ ///
2761+ /// Use [`TreeId::ROOT`] for the main/root tree. For subtrees, use a unique
2762+ /// [`TreeId`] that identifies the subtree.
2763+ ///
2764+ /// When updating a subtree (non-ROOT tree_id):
2765+ /// - A graft node with [`Node::tree_id`] set to this tree's ID must already
2766+ /// exist in the parent tree before the first subtree update.
2767+ /// - The first update for a subtree must include [`tree`](Self::tree) data.
2768+ pub tree_id : TreeId ,
2769+
26972770 /// The node within this tree that has keyboard focus when the native
26982771 /// host (e.g. window) has focus. If no specific node within the tree
26992772 /// has keyboard focus, this must be set to the root. The latest focus state
27002773 /// must be provided with every tree update, even if the focus state
27012774 /// didn't change in a given update.
2775+ ///
2776+ /// For subtrees, this specifies which node has focus when the subtree
2777+ /// itself is focused (i.e., when focus is on the graft node in the parent
2778+ /// tree).
27022779 pub focus : NodeId ,
27032780}
27042781
@@ -2772,7 +2849,8 @@ pub enum ActionData {
27722849#[ cfg_attr( feature = "serde" , serde( rename_all = "camelCase" ) ) ]
27732850pub struct ActionRequest {
27742851 pub action : Action ,
2775- pub target : NodeId ,
2852+ pub target_tree : TreeId ,
2853+ pub target_node : NodeId ,
27762854 pub data : Option < ActionData > ,
27772855}
27782856
0 commit comments