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
89#[derive(Clone, Debug)]
91#[must_use]
92pub struct TreeNode<'a, T, L = &'static str> {
93 id: TreeNodeId<T>,
94 label: Option<L>,
95 opened: bool,
96 opened_cond: Condition,
97 flags: TreeNodeFlags,
98 ui: &'a Ui,
99}
100
101impl<'a, T: AsRef<str>> TreeNode<'a, T, &'static str> {
102 pub fn label<L: AsRef<str>>(self, label: L) -> TreeNode<'a, T, L> {
104 TreeNode {
105 id: self.id,
106 label: Some(label),
107 opened: self.opened,
108 opened_cond: self.opened_cond,
109 flags: self.flags,
110 ui: self.ui,
111 }
112 }
113}
114
115impl<'a, T: AsRef<str>, L: AsRef<str>> TreeNode<'a, T, L> {
116 pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
118 self.opened = opened;
119 self.opened_cond = cond;
120 self
121 }
122
123 pub fn selected(mut self, selected: bool) -> Self {
125 self.flags.set(TreeNodeFlags::SELECTED, selected);
126 self
127 }
128
129 pub fn framed(mut self, framed: bool) -> Self {
131 self.flags.set(TreeNodeFlags::FRAMED, framed);
132 self
133 }
134
135 pub fn allow_item_overlap(mut self, allow: bool) -> Self {
137 self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, allow);
138 self
139 }
140
141 pub fn no_tree_push_on_open(mut self, no_push: bool) -> Self {
143 self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, no_push);
144 self
145 }
146
147 pub fn no_auto_open_on_log(mut self, no_auto: bool) -> Self {
149 self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, no_auto);
150 self
151 }
152
153 pub fn default_open(mut self, default_open: bool) -> Self {
155 self.flags.set(TreeNodeFlags::DEFAULT_OPEN, default_open);
156 self
157 }
158
159 pub fn open_on_double_click(mut self, double_click: bool) -> Self {
161 self.flags
162 .set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, double_click);
163 self
164 }
165
166 pub fn open_on_arrow(mut self, arrow_only: bool) -> Self {
168 self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, arrow_only);
169 self
170 }
171
172 pub fn leaf(mut self, leaf: bool) -> Self {
174 self.flags.set(TreeNodeFlags::LEAF, leaf);
175 self
176 }
177
178 pub fn bullet(mut self, bullet: bool) -> Self {
180 self.flags.set(TreeNodeFlags::BULLET, bullet);
181 self
182 }
183
184 pub fn frame_padding(mut self, frame_padding: bool) -> Self {
186 self.flags.set(TreeNodeFlags::FRAME_PADDING, frame_padding);
187 self
188 }
189
190 pub fn span_avail_width(mut self, span: bool) -> Self {
192 self.flags.set(TreeNodeFlags::SPAN_AVAIL_WIDTH, span);
193 self
194 }
195
196 pub fn span_full_width(mut self, span: bool) -> Self {
198 self.flags.set(TreeNodeFlags::SPAN_FULL_WIDTH, span);
199 self
200 }
201
202 pub fn nav_left_jumps_back_here(mut self, nav: bool) -> Self {
204 self.flags.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, nav);
205 self
206 }
207
208 pub fn push(self) -> Option<TreeNodeToken<'a>> {
215 let open = unsafe {
216 if self.opened_cond != Condition::Never {
217 sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
218 }
219
220 match &self.id {
221 TreeNodeId::Str(s) => {
222 let id_ptr = self.ui.scratch_txt(s);
223 let label_ptr = self
224 .label
225 .as_ref()
226 .map(|l| self.ui.scratch_txt(l))
227 .unwrap_or(id_ptr);
228 sys::igTreeNodeEx_StrStr(id_ptr, self.flags.bits(), label_ptr)
229 }
230 TreeNodeId::Ptr(ptr) => {
231 let label_ptr = self
232 .label
233 .as_ref()
234 .map(|l| self.ui.scratch_txt(l))
235 .unwrap_or(std::ptr::null());
236 sys::igTreeNodeEx_Ptr(
237 *ptr as *const std::os::raw::c_void,
238 self.flags.bits(),
239 label_ptr,
240 )
241 }
242 TreeNodeId::Int(i) => {
243 let label_ptr = self
244 .label
245 .as_ref()
246 .map(|l| self.ui.scratch_txt(l))
247 .unwrap_or(std::ptr::null());
248 sys::igTreeNodeEx_Ptr(
249 *i as *const std::os::raw::c_void,
250 self.flags.bits(),
251 label_ptr,
252 )
253 }
254 }
255 };
256
257 if open {
258 Some(TreeNodeToken::new(self.ui))
259 } else {
260 None
261 }
262 }
263}
264
265#[must_use]
267pub struct TreeNodeToken<'ui> {
268 ui: &'ui Ui,
269}
270
271impl<'ui> TreeNodeToken<'ui> {
272 fn new(ui: &'ui Ui) -> Self {
274 TreeNodeToken { ui }
275 }
276
277 pub fn pop(self) {
279 }
281}
282
283impl<'ui> Drop for TreeNodeToken<'ui> {
284 fn drop(&mut self) {
285 unsafe {
286 sys::igTreePop();
287 }
288 }
289}