dear_imgui_rs/widget/tree/
entry.rs1use crate::Id;
2use crate::sys;
3use crate::ui::Ui;
4
5use super::{TreeNode, TreeNodeFlags, TreeNodeId, TreeNodeToken};
6
7impl Ui {
9 #[doc(alias = "TreeNode", alias = "TreeNodeEx")]
16 pub fn tree_node<I, T>(&self, id: I) -> Option<TreeNodeToken<'_>>
17 where
18 I: Into<TreeNodeId<T>>,
19 T: AsRef<str>,
20 {
21 self.tree_node_config(id).push()
22 }
23
24 pub fn tree_node_config<I, T>(&self, id: I) -> TreeNode<'_, T>
30 where
31 I: Into<TreeNodeId<T>>,
32 T: AsRef<str>,
33 {
34 TreeNode::new(id.into(), self)
35 }
36
37 #[doc(alias = "TreePush")]
42 pub fn tree_push(&self, id: impl AsRef<str>) -> TreeNodeToken<'_> {
43 let id = self.scratch_txt(id);
44 self.run_with_bound_context(|| unsafe { sys::igTreePush_Str(id) });
45 TreeNodeToken::new(self)
46 }
47
48 #[doc(alias = "TreePush")]
52 pub fn tree_push_ptr<T>(&self, id: *const T) -> TreeNodeToken<'_> {
53 self.run_with_bound_context(|| unsafe { sys::igTreePush_Ptr(id.cast()) });
54 TreeNodeToken::new(self)
55 }
56
57 #[doc(alias = "CollapsingHeader")]
59 pub fn collapsing_header(&self, label: impl AsRef<str>, flags: TreeNodeFlags) -> bool {
60 let label_ptr = self.scratch_txt(label);
61 self.run_with_bound_context(|| unsafe {
62 sys::igCollapsingHeader_TreeNodeFlags(label_ptr, flags.bits())
63 })
64 }
65
66 #[doc(alias = "CollapsingHeader")]
72 pub fn collapsing_header_with_visible(
73 &self,
74 label: impl AsRef<str>,
75 visible: &mut bool,
76 flags: TreeNodeFlags,
77 ) -> bool {
78 let label_ptr = self.scratch_txt(label);
79 self.run_with_bound_context(|| unsafe {
80 sys::igCollapsingHeader_BoolPtr(label_ptr, visible as *mut bool, flags.bits())
81 })
82 }
83
84 #[doc(alias = "GetTreeNodeToLabelSpacing")]
86 pub fn tree_node_to_label_spacing(&self) -> f32 {
87 self.run_with_bound_context(|| unsafe { sys::igGetTreeNodeToLabelSpacing() })
88 }
89
90 #[doc(alias = "TreeNodeGetOpen")]
92 pub fn tree_node_get_open(&self, storage_id: Id) -> bool {
93 self.run_with_bound_context(|| unsafe { sys::igTreeNodeGetOpen(storage_id.raw()) })
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 #[test]
100 fn manual_tree_scopes_restore_tree_depth_and_id_stack() {
101 let mut ctx = crate::Context::create();
102 ctx.io_mut().set_display_size([128.0, 128.0]);
103 ctx.io_mut().set_delta_time(1.0 / 60.0);
104 let _ = ctx.font_atlas().build();
105 let ui = ctx.frame();
106
107 ui.window("tree_push").build(|| {
108 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
109 let initial_depth = unsafe { (*window).DC.TreeDepth };
110 let initial_id_stack_size = unsafe { (*window).IDStack.Size };
111
112 {
113 let _tree = ui.tree_push("string_scope");
114 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth + 1);
115 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size + 1);
116 }
117
118 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth);
119 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size);
120
121 let marker = 0_u8;
122 let tree = ui.tree_push_ptr(&marker);
123 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth + 1);
124 tree.pop();
125 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth);
126 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size);
127 });
128 }
129
130 #[test]
131 fn tree_node_tokens_match_push_flags_and_keep_custom_ids_stable() {
132 let mut ctx = crate::Context::create();
133 ctx.io_mut().set_display_size([128.0, 128.0]);
134 ctx.io_mut().set_delta_time(1.0 / 60.0);
135 let _ = ctx.font_atlas().build();
136 let ui = ctx.frame();
137
138 ui.window("tree_node_tokens").build(|| {
139 let window = unsafe { crate::sys::igGetCurrentWindowRead() };
140 let initial_depth = unsafe { (*window).DC.TreeDepth };
141 let initial_id_stack_size = unsafe { (*window).IDStack.Size };
142
143 let no_push = ui
144 .tree_node_config("no_push")
145 .opened(true, crate::Condition::Always)
146 .no_tree_push_on_open(true)
147 .push()
148 .expect("forced-open tree node");
149 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth);
150 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size);
151 drop(no_push);
152 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth);
153 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size);
154
155 let first = ui
156 .tree_node_config("stable_id")
157 .label("First label")
158 .opened(true, crate::Condition::Always)
159 .push()
160 .expect("forced-open tree node");
161 let first_id = ui.item_id();
162 drop(first);
163
164 let second = ui
165 .tree_node_config("stable_id")
166 .label("Second label")
167 .opened(true, crate::Condition::Always)
168 .push()
169 .expect("forced-open tree node");
170 let second_id = ui.item_id();
171 drop(second);
172 assert_eq!(first_id, second_id);
173
174 let first_int = ui
175 .tree_node_config(7_i32)
176 .label("First integer label")
177 .opened(true, crate::Condition::Always)
178 .nav_left_jumps_back_here(true)
179 .push()
180 .expect("forced-open tree node");
181 let first_int_id = ui.item_id();
182 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth + 1);
183 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size + 1);
184 drop(first_int);
185
186 let second_int = ui
187 .tree_node_config(7_i32)
188 .label("Second integer label")
189 .opened(true, crate::Condition::Always)
190 .push()
191 .expect("forced-open tree node");
192 let second_int_id = ui.item_id();
193 drop(second_int);
194 assert_eq!(first_int_id, second_int_id);
195 assert_eq!(unsafe { (*window).DC.TreeDepth }, initial_depth);
196 assert_eq!(unsafe { (*window).IDStack.Size }, initial_id_stack_size);
197 });
198 }
199}