1#![allow(
7 clippy::cast_possible_truncation,
8 clippy::cast_sign_loss,
9 clippy::as_conversions
10)]
11use crate::Condition;
12use crate::sys;
13use crate::ui::Ui;
14use crate::widget::TreeNodeFlags;
15
16#[derive(Copy, Clone, Debug)]
18pub enum TreeNodeId<T> {
19 Str(T),
20 Ptr(*const u8),
21 Int(i32),
22}
23
24impl<T> From<T> for TreeNodeId<T>
25where
26 T: AsRef<str>,
27{
28 fn from(s: T) -> Self {
29 TreeNodeId::Str(s)
30 }
31}
32
33impl From<*const u8> for TreeNodeId<&'static str> {
34 fn from(ptr: *const u8) -> Self {
35 TreeNodeId::Ptr(ptr)
36 }
37}
38
39impl From<i32> for TreeNodeId<&'static str> {
40 fn from(i: i32) -> Self {
41 TreeNodeId::Int(i)
42 }
43}
44
45impl Ui {
47 pub fn tree_node<I, T>(&self, id: I) -> Option<TreeNodeToken<'_>>
54 where
55 I: Into<TreeNodeId<T>>,
56 T: AsRef<str>,
57 {
58 self.tree_node_config(id).push()
59 }
60
61 pub fn tree_node_config<I, T>(&self, id: I) -> TreeNode<'_, T>
67 where
68 I: Into<TreeNodeId<T>>,
69 T: AsRef<str>,
70 {
71 TreeNode {
72 id: id.into(),
73 label: None,
74 opened: false,
75 opened_cond: Condition::Never,
76 flags: TreeNodeFlags::NONE,
77 ui: self,
78 }
79 }
80
81 #[doc(alias = "CollapsingHeader")]
83 pub fn collapsing_header(&self, label: impl AsRef<str>, flags: TreeNodeFlags) -> bool {
84 let label_ptr = self.scratch_txt(label);
85 unsafe { sys::igCollapsingHeader_TreeNodeFlags(label_ptr, flags.bits()) }
86 }
87
88 #[doc(alias = "CollapsingHeader")]
94 pub fn collapsing_header_with_visible(
95 &self,
96 label: impl AsRef<str>,
97 visible: &mut bool,
98 flags: TreeNodeFlags,
99 ) -> bool {
100 let label_ptr = self.scratch_txt(label);
101 unsafe { sys::igCollapsingHeader_BoolPtr(label_ptr, visible as *mut bool, flags.bits()) }
102 }
103
104 #[doc(alias = "GetTreeNodeToLabelSpacing")]
106 pub fn tree_node_to_label_spacing(&self) -> f32 {
107 unsafe { sys::igGetTreeNodeToLabelSpacing() }
108 }
109
110 #[doc(alias = "TreeNodeGetOpen")]
112 pub fn tree_node_get_open(&self, storage_id: crate::Id) -> bool {
113 unsafe { sys::igTreeNodeGetOpen(storage_id.raw()) }
114 }
115}
116
117#[derive(Clone, Debug)]
119#[must_use]
120pub struct TreeNode<'a, T, L = &'static str> {
121 id: TreeNodeId<T>,
122 label: Option<L>,
123 opened: bool,
124 opened_cond: Condition,
125 flags: TreeNodeFlags,
126 ui: &'a Ui,
127}
128
129impl<'a, T: AsRef<str>> TreeNode<'a, T, &'static str> {
130 pub fn label<L: AsRef<str>>(self, label: L) -> TreeNode<'a, T, L> {
132 TreeNode {
133 id: self.id,
134 label: Some(label),
135 opened: self.opened,
136 opened_cond: self.opened_cond,
137 flags: self.flags,
138 ui: self.ui,
139 }
140 }
141}
142
143impl<'a, T: AsRef<str>, L: AsRef<str>> TreeNode<'a, T, L> {
144 pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
146 self.opened = opened;
147 self.opened_cond = cond;
148 self
149 }
150
151 pub fn selected(mut self, selected: bool) -> Self {
153 self.flags.set(TreeNodeFlags::SELECTED, selected);
154 self
155 }
156
157 pub fn framed(mut self, framed: bool) -> Self {
159 self.flags.set(TreeNodeFlags::FRAMED, framed);
160 self
161 }
162
163 pub fn allow_item_overlap(mut self, allow: bool) -> Self {
165 self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, allow);
166 self
167 }
168
169 pub fn no_tree_push_on_open(mut self, no_push: bool) -> Self {
171 self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, no_push);
172 self
173 }
174
175 pub fn no_auto_open_on_log(mut self, no_auto: bool) -> Self {
177 self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, no_auto);
178 self
179 }
180
181 pub fn default_open(mut self, default_open: bool) -> Self {
183 self.flags.set(TreeNodeFlags::DEFAULT_OPEN, default_open);
184 self
185 }
186
187 pub fn open_on_double_click(mut self, double_click: bool) -> Self {
189 self.flags
190 .set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, double_click);
191 self
192 }
193
194 pub fn open_on_arrow(mut self, arrow_only: bool) -> Self {
196 self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, arrow_only);
197 self
198 }
199
200 pub fn leaf(mut self, leaf: bool) -> Self {
202 self.flags.set(TreeNodeFlags::LEAF, leaf);
203 self
204 }
205
206 pub fn bullet(mut self, bullet: bool) -> Self {
208 self.flags.set(TreeNodeFlags::BULLET, bullet);
209 self
210 }
211
212 pub fn frame_padding(mut self, frame_padding: bool) -> Self {
214 self.flags.set(TreeNodeFlags::FRAME_PADDING, frame_padding);
215 self
216 }
217
218 pub fn span_avail_width(mut self, span: bool) -> Self {
220 self.flags.set(TreeNodeFlags::SPAN_AVAIL_WIDTH, span);
221 self
222 }
223
224 pub fn span_full_width(mut self, span: bool) -> Self {
226 self.flags.set(TreeNodeFlags::SPAN_FULL_WIDTH, span);
227 self
228 }
229
230 pub fn nav_left_jumps_back_here(mut self, nav: bool) -> Self {
232 self.flags.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, nav);
233 self
234 }
235
236 pub fn push(self) -> Option<TreeNodeToken<'a>> {
243 let open = unsafe {
244 if self.opened_cond != Condition::Never {
245 sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
246 }
247
248 match &self.id {
249 TreeNodeId::Str(s) => {
250 if let Some(label) = self.label.as_ref() {
251 let (id_ptr, label_ptr) = self.ui.scratch_txt_two(s, label);
252 sys::igPushID_Str(id_ptr);
253 let open = sys::igTreeNodeEx_Str(label_ptr, self.flags.bits());
254 sys::igPopID();
255 open
256 } else {
257 let label_ptr = self.ui.scratch_txt(s);
258 sys::igTreeNodeEx_Str(label_ptr, self.flags.bits())
259 }
260 }
261 TreeNodeId::Ptr(ptr) => {
262 let label = self.label.as_ref().map_or("", |l| l.as_ref());
263 let label_ptr = self.ui.scratch_txt(label);
264 sys::igPushID_Ptr(*ptr as *const std::os::raw::c_void);
265 let open = sys::igTreeNodeEx_Str(label_ptr, self.flags.bits());
266 sys::igPopID();
267 open
268 }
269 TreeNodeId::Int(i) => {
270 let label = self.label.as_ref().map_or("", |l| l.as_ref());
271 let label_ptr = self.ui.scratch_txt(label);
272 sys::igPushID_Int(*i);
273 let open = sys::igTreeNodeEx_Str(label_ptr, self.flags.bits());
274 sys::igPopID();
275 open
276 }
277 }
278 };
279
280 if open {
281 Some(TreeNodeToken::new(self.ui))
282 } else {
283 None
284 }
285 }
286}
287
288#[must_use]
290pub struct TreeNodeToken<'ui> {
291 _ui: &'ui Ui,
292}
293
294impl<'ui> TreeNodeToken<'ui> {
295 fn new(ui: &'ui Ui) -> Self {
297 TreeNodeToken { _ui: ui }
298 }
299
300 pub fn pop(self) {
302 }
304}
305
306impl<'ui> Drop for TreeNodeToken<'ui> {
307 fn drop(&mut self) {
308 unsafe {
309 sys::igTreePop();
310 }
311 }
312}