1#![allow(
2 clippy::cast_possible_truncation,
3 clippy::cast_sign_loss,
4 clippy::as_conversions
5)]
6use crate::Condition;
7use crate::sys;
8use crate::ui::Ui;
9use crate::widget::TreeNodeFlags;
10
11#[derive(Copy, Clone, Debug)]
13pub enum TreeNodeId<T> {
14 Str(T),
15 Ptr(*const u8),
16 Int(i32),
17}
18
19impl<T> From<T> for TreeNodeId<T>
20where
21 T: AsRef<str>,
22{
23 fn from(s: T) -> Self {
24 TreeNodeId::Str(s)
25 }
26}
27
28impl From<*const u8> for TreeNodeId<&'static str> {
29 fn from(ptr: *const u8) -> Self {
30 TreeNodeId::Ptr(ptr)
31 }
32}
33
34impl From<i32> for TreeNodeId<&'static str> {
35 fn from(i: i32) -> Self {
36 TreeNodeId::Int(i)
37 }
38}
39
40impl Ui {
42 pub fn tree_node<I, T>(&self, id: I) -> Option<TreeNodeToken<'_>>
49 where
50 I: Into<TreeNodeId<T>>,
51 T: AsRef<str>,
52 {
53 self.tree_node_config(id).push()
54 }
55
56 pub fn tree_node_config<I, T>(&self, id: I) -> TreeNode<'_, T>
62 where
63 I: Into<TreeNodeId<T>>,
64 T: AsRef<str>,
65 {
66 TreeNode {
67 id: id.into(),
68 label: None,
69 opened: false,
70 opened_cond: Condition::Never,
71 flags: TreeNodeFlags::NONE,
72 ui: self,
73 }
74 }
75
76 #[doc(alias = "CollapsingHeader")]
78 pub fn collapsing_header(&self, label: impl AsRef<str>, flags: TreeNodeFlags) -> bool {
79 let label_ptr = self.scratch_txt(label);
80 unsafe { sys::igCollapsingHeader_TreeNodeFlags(label_ptr, flags.bits()) }
81 }
82}
83
84#[derive(Clone, Debug)]
86#[must_use]
87pub struct TreeNode<'a, T, L = &'static str> {
88 id: TreeNodeId<T>,
89 label: Option<L>,
90 opened: bool,
91 opened_cond: Condition,
92 flags: TreeNodeFlags,
93 ui: &'a Ui,
94}
95
96impl<'a, T: AsRef<str>> TreeNode<'a, T, &'static str> {
97 pub fn label<L: AsRef<str>>(self, label: L) -> TreeNode<'a, T, L> {
99 TreeNode {
100 id: self.id,
101 label: Some(label),
102 opened: self.opened,
103 opened_cond: self.opened_cond,
104 flags: self.flags,
105 ui: self.ui,
106 }
107 }
108}
109
110impl<'a, T: AsRef<str>, L: AsRef<str>> TreeNode<'a, T, L> {
111 pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
113 self.opened = opened;
114 self.opened_cond = cond;
115 self
116 }
117
118 pub fn selected(mut self, selected: bool) -> Self {
120 self.flags.set(TreeNodeFlags::SELECTED, selected);
121 self
122 }
123
124 pub fn framed(mut self, framed: bool) -> Self {
126 self.flags.set(TreeNodeFlags::FRAMED, framed);
127 self
128 }
129
130 pub fn allow_item_overlap(mut self, allow: bool) -> Self {
132 self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, allow);
133 self
134 }
135
136 pub fn no_tree_push_on_open(mut self, no_push: bool) -> Self {
138 self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, no_push);
139 self
140 }
141
142 pub fn no_auto_open_on_log(mut self, no_auto: bool) -> Self {
144 self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, no_auto);
145 self
146 }
147
148 pub fn default_open(mut self, default_open: bool) -> Self {
150 self.flags.set(TreeNodeFlags::DEFAULT_OPEN, default_open);
151 self
152 }
153
154 pub fn open_on_double_click(mut self, double_click: bool) -> Self {
156 self.flags
157 .set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, double_click);
158 self
159 }
160
161 pub fn open_on_arrow(mut self, arrow_only: bool) -> Self {
163 self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, arrow_only);
164 self
165 }
166
167 pub fn leaf(mut self, leaf: bool) -> Self {
169 self.flags.set(TreeNodeFlags::LEAF, leaf);
170 self
171 }
172
173 pub fn bullet(mut self, bullet: bool) -> Self {
175 self.flags.set(TreeNodeFlags::BULLET, bullet);
176 self
177 }
178
179 pub fn frame_padding(mut self, frame_padding: bool) -> Self {
181 self.flags.set(TreeNodeFlags::FRAME_PADDING, frame_padding);
182 self
183 }
184
185 pub fn span_avail_width(mut self, span: bool) -> Self {
187 self.flags.set(TreeNodeFlags::SPAN_AVAIL_WIDTH, span);
188 self
189 }
190
191 pub fn span_full_width(mut self, span: bool) -> Self {
193 self.flags.set(TreeNodeFlags::SPAN_FULL_WIDTH, span);
194 self
195 }
196
197 pub fn nav_left_jumps_back_here(mut self, nav: bool) -> Self {
199 self.flags.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, nav);
200 self
201 }
202
203 pub fn push(self) -> Option<TreeNodeToken<'a>> {
210 let open = unsafe {
211 if self.opened_cond != Condition::Never {
212 sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
213 }
214
215 match &self.id {
216 TreeNodeId::Str(s) => {
217 let id_ptr = self.ui.scratch_txt(s);
218 let label_ptr = self
219 .label
220 .as_ref()
221 .map(|l| self.ui.scratch_txt(l))
222 .unwrap_or(id_ptr);
223 sys::igTreeNodeEx_StrStr(id_ptr, self.flags.bits(), label_ptr)
224 }
225 TreeNodeId::Ptr(ptr) => {
226 let label_ptr = self
227 .label
228 .as_ref()
229 .map(|l| self.ui.scratch_txt(l))
230 .unwrap_or(std::ptr::null());
231 sys::igTreeNodeEx_Ptr(
232 *ptr as *const std::os::raw::c_void,
233 self.flags.bits(),
234 label_ptr,
235 )
236 }
237 TreeNodeId::Int(i) => {
238 let label_ptr = self
239 .label
240 .as_ref()
241 .map(|l| self.ui.scratch_txt(l))
242 .unwrap_or(std::ptr::null());
243 sys::igTreeNodeEx_Ptr(
244 *i as *const std::os::raw::c_void,
245 self.flags.bits(),
246 label_ptr,
247 )
248 }
249 }
250 };
251
252 if open {
253 Some(TreeNodeToken::new(self.ui))
254 } else {
255 None
256 }
257 }
258}
259
260#[must_use]
262pub struct TreeNodeToken<'ui> {
263 ui: &'ui Ui,
264}
265
266impl<'ui> TreeNodeToken<'ui> {
267 fn new(ui: &'ui Ui) -> Self {
269 TreeNodeToken { ui }
270 }
271
272 pub fn pop(self) {
274 }
276}
277
278impl<'ui> Drop for TreeNodeToken<'ui> {
279 fn drop(&mut self) {
280 unsafe {
281 sys::igTreePop();
282 }
283 }
284}