1use bitflags::bitflags;
2use std::os::raw::{c_char, c_void};
3
4use crate::sys;
6use crate::{Condition, Ui};
7
8bitflags!(
9 #[repr(transparent)]
11 pub struct TreeNodeFlags: u32 {
12 const SELECTED = sys::ImGuiTreeNodeFlags_Selected;
14 const FRAMED = sys::ImGuiTreeNodeFlags_Framed;
16 const ALLOW_ITEM_OVERLAP = sys::ImGuiTreeNodeFlags_AllowItemOverlap;
18 const NO_TREE_PUSH_ON_OPEN = sys::ImGuiTreeNodeFlags_NoTreePushOnOpen;
21 const NO_AUTO_OPEN_ON_LOG = sys::ImGuiTreeNodeFlags_NoAutoOpenOnLog;
24 const DEFAULT_OPEN = sys::ImGuiTreeNodeFlags_DefaultOpen;
26 const OPEN_ON_DOUBLE_CLICK = sys::ImGuiTreeNodeFlags_OpenOnDoubleClick;
28 const OPEN_ON_ARROW = sys::ImGuiTreeNodeFlags_OpenOnArrow;
33 const LEAF = sys::ImGuiTreeNodeFlags_Leaf;
35 const BULLET = sys::ImGuiTreeNodeFlags_Bullet;
37 const FRAME_PADDING = sys::ImGuiTreeNodeFlags_FramePadding;
42 const SPAN_AVAIL_WIDTH = sys::ImGuiTreeNodeFlags_SpanAvailWidth;
48 const SPAN_FULL_WIDTH = sys::ImGuiTreeNodeFlags_SpanFullWidth;
50 const NAV_LEFT_JUMPS_BACK_HERE = sys::ImGuiTreeNodeFlags_NavLeftJumpsBackHere;
52 }
53);
54
55static FMT: &[u8] = b"%s\0";
56
57#[inline]
58fn fmt_ptr() -> *const c_char {
59 FMT.as_ptr() as *const c_char
60}
61
62#[derive(Copy, Clone, Debug, Eq, PartialEq)]
64pub enum TreeNodeId<T> {
65 Str(T),
66 Ptr(*const c_void),
67}
68
69impl<T: AsRef<str>> From<T> for TreeNodeId<T> {
70 fn from(s: T) -> Self {
71 TreeNodeId::Str(s)
72 }
73}
74
75impl<T> From<*const T> for TreeNodeId<T> {
77 fn from(p: *const T) -> Self {
78 TreeNodeId::Ptr(p as *const c_void)
79 }
80}
81
82impl<T> From<*mut T> for TreeNodeId<T> {
84 fn from(p: *mut T) -> Self {
85 TreeNodeId::Ptr(p as *const T as *const c_void)
86 }
87}
88
89#[derive(Copy, Clone, Debug)]
91#[must_use]
92pub struct TreeNode<T, L = &'static str> {
93 id: TreeNodeId<T>,
94 label: Option<L>,
95 opened: bool,
96 opened_cond: Condition,
97 flags: TreeNodeFlags,
98}
99
100impl<T: AsRef<str>> TreeNode<T, &'static str> {
101 pub fn new<I: Into<TreeNodeId<T>>>(id: I) -> TreeNode<T, &'static str> {
103 TreeNode {
104 id: id.into(),
105 label: None,
106 opened: false,
107 opened_cond: Condition::Never,
108 flags: TreeNodeFlags::empty(),
109 }
110 }
111}
112
113impl<T: AsRef<str>, L: AsRef<str>> TreeNode<T, L> {
114 pub fn label<I: Into<TreeNodeId<L2>>, L2: AsRef<str>>(self, label: L2) -> TreeNode<T, L2> {
116 TreeNode {
117 label: Some(label),
118 id: self.id,
119 opened: self.opened,
120 opened_cond: self.opened_cond,
121 flags: self.flags,
122 }
123 }
124
125 pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
127 self.opened = opened;
128 self.opened_cond = cond;
129 self
130 }
131
132 pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
134 self.flags = flags;
135 self
136 }
137
138 pub fn selected(mut self, value: bool) -> Self {
142 self.flags.set(TreeNodeFlags::SELECTED, value);
143 self
144 }
145
146 pub fn framed(mut self, value: bool) -> Self {
150 self.flags.set(TreeNodeFlags::FRAMED, value);
151 self
152 }
153
154 pub fn allow_item_overlap(mut self, value: bool) -> Self {
158 self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
159 self
160 }
161
162 pub fn tree_push_on_open(mut self, value: bool) -> Self {
167 self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, !value);
168 self
169 }
170
171 pub fn auto_open_on_log(mut self, value: bool) -> Self {
177 self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, !value);
178 self
179 }
180
181 pub fn default_open(mut self, value: bool) -> Self {
185 self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
186 self
187 }
188
189 pub fn open_on_double_click(mut self, value: bool) -> Self {
193 self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
194 self
195 }
196
197 pub fn open_on_arrow(mut self, value: bool) -> Self {
201 self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
202 self
203 }
204
205 pub fn leaf(mut self, value: bool) -> Self {
209 self.flags.set(TreeNodeFlags::LEAF, value);
210 self
211 }
212
213 pub fn bullet(mut self, value: bool) -> Self {
217 self.flags.set(TreeNodeFlags::BULLET, value);
218 self
219 }
220
221 pub fn frame_padding(mut self, value: bool) -> Self {
225 self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
226 self
227 }
228
229 pub fn nav_left_jumps_back_here(mut self, value: bool) -> Self {
233 self.flags
234 .set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, value);
235 self
236 }
237
238 pub fn push<'ui>(self, ui: &Ui<'ui>) -> Option<TreeNodeToken<'ui>> {
245 let open = unsafe {
246 if self.opened_cond != Condition::Never {
247 sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
248 }
249 match self.id {
250 TreeNodeId::Str(id) => {
251 let (id, label) = match self.label {
252 Some(label) => ui.scratch_txt_two(id, label),
253 None => {
254 let v = ui.scratch_txt(id);
255 (v, v)
256 }
257 };
258
259 sys::igTreeNodeEx_StrStr(id, self.flags.bits() as i32, fmt_ptr(), label)
260 }
261 TreeNodeId::Ptr(id) => sys::igTreeNodeEx_Ptr(
262 id,
263 self.flags.bits() as i32,
264 fmt_ptr(),
265 match self.label {
266 Some(v) => ui.scratch_txt(v),
267 None => ui.scratch_txt(""),
268 },
269 ),
270 }
271 };
272 if open {
273 Some(TreeNodeToken::new(
274 ui,
275 !self.flags.contains(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN),
276 ))
277 } else {
278 None
279 }
280 }
281 pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
286 self.push(ui).map(|_node| f())
287 }
288}
289
290#[must_use]
295pub struct TreeNodeToken<'a>(core::marker::PhantomData<crate::Ui<'a>>, bool);
296
297impl<'a> TreeNodeToken<'a> {
298 pub(crate) fn new(_: &crate::Ui<'a>, execute_drop: bool) -> Self {
300 Self(std::marker::PhantomData, execute_drop)
301 }
302
303 #[inline]
305 pub fn end(self) {
306 }
308
309 #[inline]
311 pub fn pop(self) {
312 self.end()
313 }
314}
315
316impl Drop for TreeNodeToken<'_> {
317 #[doc(alias = "TreePop")]
318 fn drop(&mut self) {
319 if self.1 {
320 unsafe { sys::igTreePop() }
321 }
322 }
323}
324
325#[derive(Copy, Clone, Debug)]
327#[must_use]
328pub struct CollapsingHeader<T> {
329 label: T,
330 flags: TreeNodeFlags,
331}
332
333impl<T: AsRef<str>> CollapsingHeader<T> {
334 #[doc(alias = "CollapsingHeader")]
336 pub fn new(label: T) -> CollapsingHeader<T> {
337 CollapsingHeader {
338 label,
339 flags: TreeNodeFlags::empty(),
340 }
341 }
342 #[inline]
344 pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
345 self.flags = flags;
346 self
347 }
348 #[inline]
352 pub fn allow_item_overlap(mut self, value: bool) -> Self {
353 self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
354 self
355 }
356 #[inline]
360 pub fn default_open(mut self, value: bool) -> Self {
361 self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
362 self
363 }
364 #[inline]
368 pub fn open_on_double_click(mut self, value: bool) -> Self {
369 self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
370 self
371 }
372 #[inline]
376 pub fn open_on_arrow(mut self, value: bool) -> Self {
377 self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
378 self
379 }
380 #[inline]
384 pub fn leaf(mut self, value: bool) -> Self {
385 self.flags.set(TreeNodeFlags::LEAF, value);
386 self
387 }
388 #[inline]
392 pub fn bullet(mut self, value: bool) -> Self {
393 self.flags.set(TreeNodeFlags::BULLET, value);
394 self
395 }
396 #[inline]
400 pub fn frame_padding(mut self, value: bool) -> Self {
401 self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
402 self
403 }
404
405 #[must_use]
411 pub fn begin(self, ui: &Ui<'_>) -> bool {
412 self.build(ui)
413 }
414
415 #[must_use]
422 pub fn begin_with_close_button(self, ui: &Ui<'_>, opened: &mut bool) -> bool {
423 self.build_with_close_button(ui, opened)
424 }
425
426 #[must_use]
430 #[inline]
431 pub fn build(self, ui: &Ui<'_>) -> bool {
432 unsafe {
433 sys::igCollapsingHeader_TreeNodeFlags(
434 ui.scratch_txt(self.label),
435 self.flags.bits() as i32,
436 )
437 }
438 }
439 #[must_use]
444 #[inline]
445 pub fn build_with_close_button(self, ui: &Ui<'_>, opened: &mut bool) -> bool {
446 unsafe {
447 sys::igCollapsingHeader_BoolPtr(
448 ui.scratch_txt(self.label),
449 opened as *mut bool,
450 self.flags.bits() as i32,
451 )
452 }
453 }
454}
455
456impl Ui<'_> {
457 #[doc(alias = "CollapsingHeader")]
459 pub fn collapsing_header(&self, label: impl AsRef<str>, flags: TreeNodeFlags) -> bool {
460 CollapsingHeader::new(label).flags(flags).build(self)
461 }
462
463 #[doc(alias = "CollapsingHeader")]
465 pub fn collapsing_header_with_close_button(
466 &self,
467 label: impl AsRef<str>,
468 flags: TreeNodeFlags,
469 opened: &mut bool,
470 ) -> bool {
471 CollapsingHeader::new(label)
472 .flags(flags)
473 .build_with_close_button(self, opened)
474 }
475}