Skip to main content

dear_imgui_rs/widget/tree/
builder.rs

1use crate::Condition;
2use crate::sys;
3use crate::ui::Ui;
4
5use super::{TreeNodeFlags, TreeNodeId, TreeNodeToken};
6
7/// Builder for a tree node widget
8#[derive(Clone, Debug)]
9#[must_use]
10pub struct TreeNode<'a, T, L = &'static str> {
11    id: TreeNodeId<T>,
12    label: Option<L>,
13    opened: bool,
14    opened_cond: Option<Condition>,
15    flags: TreeNodeFlags,
16    ui: &'a Ui,
17}
18
19impl<'a, T: AsRef<str>> TreeNode<'a, T, &'static str> {
20    pub(super) fn new(id: TreeNodeId<T>, ui: &'a Ui) -> Self {
21        TreeNode {
22            id,
23            label: None,
24            opened: false,
25            opened_cond: None,
26            flags: TreeNodeFlags::NONE,
27            ui,
28        }
29    }
30
31    /// Sets a custom label for the tree node
32    pub fn label<L: AsRef<str>>(self, label: L) -> TreeNode<'a, T, L> {
33        TreeNode {
34            id: self.id,
35            label: Some(label),
36            opened: self.opened,
37            opened_cond: self.opened_cond,
38            flags: self.flags,
39            ui: self.ui,
40        }
41    }
42}
43
44impl<'a, T: AsRef<str>, L: AsRef<str>> TreeNode<'a, T, L> {
45    /// Sets the opened state
46    pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
47        self.opened = opened;
48        self.opened_cond = Some(cond);
49        self
50    }
51
52    /// Draw as selected
53    pub fn selected(mut self, selected: bool) -> Self {
54        self.flags.set(TreeNodeFlags::SELECTED, selected);
55        self
56    }
57
58    /// Draw frame with background (e.g. for CollapsingHeader)
59    pub fn framed(mut self, framed: bool) -> Self {
60        self.flags.set(TreeNodeFlags::FRAMED, framed);
61        self
62    }
63
64    /// Hit testing to allow subsequent widgets to overlap this one
65    pub fn allow_item_overlap(mut self, allow: bool) -> Self {
66        self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, allow);
67        self
68    }
69
70    /// Don't do a TreePush() when open (e.g. for CollapsingHeader)
71    pub fn no_tree_push_on_open(mut self, no_push: bool) -> Self {
72        self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, no_push);
73        self
74    }
75
76    /// Don't automatically and temporarily open node when Logging is active
77    pub fn no_auto_open_on_log(mut self, no_auto: bool) -> Self {
78        self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, no_auto);
79        self
80    }
81
82    /// Default node to be open
83    pub fn default_open(mut self, default_open: bool) -> Self {
84        self.flags.set(TreeNodeFlags::DEFAULT_OPEN, default_open);
85        self
86    }
87
88    /// Need double-click to open node
89    pub fn open_on_double_click(mut self, double_click: bool) -> Self {
90        self.flags
91            .set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, double_click);
92        self
93    }
94
95    /// Only open when clicking on the arrow part
96    pub fn open_on_arrow(mut self, arrow_only: bool) -> Self {
97        self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, arrow_only);
98        self
99    }
100
101    /// No collapsing, no arrow (use as a convenience for leaf nodes)
102    pub fn leaf(mut self, leaf: bool) -> Self {
103        self.flags.set(TreeNodeFlags::LEAF, leaf);
104        self
105    }
106
107    /// Display a bullet instead of arrow
108    pub fn bullet(mut self, bullet: bool) -> Self {
109        self.flags.set(TreeNodeFlags::BULLET, bullet);
110        self
111    }
112
113    /// Use FramePadding to vertically align text baseline to regular widget height
114    pub fn frame_padding(mut self, frame_padding: bool) -> Self {
115        self.flags.set(TreeNodeFlags::FRAME_PADDING, frame_padding);
116        self
117    }
118
119    /// Extend hit box to the right-most edge
120    pub fn span_avail_width(mut self, span: bool) -> Self {
121        self.flags.set(TreeNodeFlags::SPAN_AVAIL_WIDTH, span);
122        self
123    }
124
125    /// Extend hit box to the left-most and right-most edges
126    pub fn span_full_width(mut self, span: bool) -> Self {
127        self.flags.set(TreeNodeFlags::SPAN_FULL_WIDTH, span);
128        self
129    }
130
131    /// Left direction may move to this tree node from any of its child
132    pub fn nav_left_jumps_back_here(mut self, nav: bool) -> Self {
133        self.flags.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, nav);
134        self
135    }
136
137    /// Pushes a tree node and starts appending to it.
138    ///
139    /// Returns `Some(TreeNodeToken)` if the tree node is open. After content has been
140    /// rendered, the token can be popped by calling `.pop()`.
141    ///
142    /// Returns `None` if the tree node is not open and no content should be rendered.
143    pub fn push(self) -> Option<TreeNodeToken<'a>> {
144        let open = self.ui.run_with_bound_context(|| unsafe {
145            if let Some(opened_cond) = self.opened_cond {
146                sys::igSetNextItemOpen(self.opened, opened_cond as i32);
147            }
148
149            match &self.id {
150                TreeNodeId::Str(s) => {
151                    if let Some(label) = self.label.as_ref() {
152                        let (id_ptr, label_ptr) = self.ui.scratch_txt_two(s, label);
153                        sys::igPushID_Str(id_ptr);
154                        let open = sys::igTreeNodeEx_Str(label_ptr, self.flags.bits());
155                        sys::igPopID();
156                        open
157                    } else {
158                        let label_ptr = self.ui.scratch_txt(s);
159                        sys::igTreeNodeEx_Str(label_ptr, self.flags.bits())
160                    }
161                }
162                TreeNodeId::Ptr(ptr) => {
163                    let label = self.label.as_ref().map_or("", |l| l.as_ref());
164                    let label_ptr = self.ui.scratch_txt(label);
165                    sys::igPushID_Ptr(*ptr as *const std::os::raw::c_void);
166                    let open = sys::igTreeNodeEx_Str(label_ptr, self.flags.bits());
167                    sys::igPopID();
168                    open
169                }
170                TreeNodeId::Int(i) => {
171                    let label = self.label.as_ref().map_or("", |l| l.as_ref());
172                    let label_ptr = self.ui.scratch_txt(label);
173                    sys::igPushID_Int(*i);
174                    let open = sys::igTreeNodeEx_Str(label_ptr, self.flags.bits());
175                    sys::igPopID();
176                    open
177                }
178            }
179        });
180
181        if open {
182            Some(TreeNodeToken::new(self.ui))
183        } else {
184            None
185        }
186    }
187}