mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 13:38:35 +00:00
Redesign tree node / collapsing header API
This commit is contained in:
parent
2915741ac9
commit
a5a0be44e3
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Redesigned tree / collapsing header API
|
||||||
|
|
||||||
## [0.3.0] - 2020-02-15
|
## [0.3.0] - 2020-02-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
70
imgui-examples/examples/collapsing_header.rs
Normal file
70
imgui-examples/examples/collapsing_header.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use imgui::*;
|
||||||
|
|
||||||
|
mod support;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut state = State {
|
||||||
|
render_closable: true,
|
||||||
|
};
|
||||||
|
let system = support::init(file!());
|
||||||
|
system.main_loop(move |run, ui| {
|
||||||
|
let w = Window::new(im_str!("Collapsing header"))
|
||||||
|
.opened(run)
|
||||||
|
.position([20.0, 20.0], Condition::Appearing)
|
||||||
|
.size([700.0, 500.0], Condition::Appearing);
|
||||||
|
w.build(&ui, || {
|
||||||
|
if CollapsingHeader::new(im_str!("I'm a collapsing header. Click me!")).build(&ui) {
|
||||||
|
ui.text(
|
||||||
|
"A collapsing header can be used to toggle rendering of a group of widgets",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.spacing();
|
||||||
|
if CollapsingHeader::new(im_str!("I'm open by default"))
|
||||||
|
.default_open(true)
|
||||||
|
.build(&ui)
|
||||||
|
{
|
||||||
|
ui.text("You can still close me with a click!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.spacing();
|
||||||
|
if CollapsingHeader::new(im_str!("I only open with double-click"))
|
||||||
|
.open_on_double_click(true)
|
||||||
|
.build(&ui)
|
||||||
|
{
|
||||||
|
ui.text("Double the clicks, double the fun!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.spacing();
|
||||||
|
if CollapsingHeader::new(im_str!("I don't have an arrow"))
|
||||||
|
.bullet(true)
|
||||||
|
.build(&ui)
|
||||||
|
{
|
||||||
|
ui.text("Collapsing headers can use a bullet instead of an arrow");
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.spacing();
|
||||||
|
if CollapsingHeader::new(im_str!("I only open if you click the arrow"))
|
||||||
|
.open_on_arrow(true)
|
||||||
|
.build(&ui)
|
||||||
|
{
|
||||||
|
ui.text("You clicked the arrow");
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.spacing();
|
||||||
|
ui.checkbox(
|
||||||
|
im_str!("Toggle rendering of the next example"),
|
||||||
|
&mut state.render_closable,
|
||||||
|
);
|
||||||
|
if CollapsingHeader::new(im_str!("I've got a separate close button"))
|
||||||
|
.build_with_close_button(&ui, &mut state.render_closable)
|
||||||
|
{
|
||||||
|
ui.text("I've got contents just like any other collapsing header");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
render_closable: bool,
|
||||||
|
}
|
||||||
@ -327,7 +327,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
menu_bar.end(ui);
|
menu_bar.end(ui);
|
||||||
}
|
}
|
||||||
ui.spacing();
|
ui.spacing();
|
||||||
if ui.collapsing_header(im_str!("Help")).build() {
|
if CollapsingHeader::new(im_str!("Help")).build(&ui) {
|
||||||
ui.text_wrapped(im_str!(
|
ui.text_wrapped(im_str!(
|
||||||
"This window is being created by the show_test_window() \
|
"This window is being created by the show_test_window() \
|
||||||
function. Please refer to the code for programming \
|
function. Please refer to the code for programming \
|
||||||
@ -336,7 +336,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
show_user_guide(ui);
|
show_user_guide(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.collapsing_header(im_str!("Window options")).build() {
|
if CollapsingHeader::new(im_str!("Window options")).build(&ui) {
|
||||||
ui.checkbox(im_str!("No titlebar"), &mut state.no_titlebar);
|
ui.checkbox(im_str!("No titlebar"), &mut state.no_titlebar);
|
||||||
ui.same_line(150.0);
|
ui.same_line(150.0);
|
||||||
ui.checkbox(im_str!("No scrollbar"), &mut state.no_scrollbar);
|
ui.checkbox(im_str!("No scrollbar"), &mut state.no_scrollbar);
|
||||||
@ -349,13 +349,14 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
ui.checkbox(im_str!("No collapse"), &mut state.no_collapse);
|
ui.checkbox(im_str!("No collapse"), &mut state.no_collapse);
|
||||||
ui.checkbox(im_str!("No close"), &mut state.no_close);
|
ui.checkbox(im_str!("No close"), &mut state.no_close);
|
||||||
|
|
||||||
ui.tree_node(im_str!("Style"))
|
TreeNode::new(im_str!("Style")).build(&ui, || {
|
||||||
.build(|| ui.show_default_style_editor());
|
ui.show_default_style_editor();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if ui.collapsing_header(im_str!("Widgets")).build() {
|
if CollapsingHeader::new(im_str!("Widgets")).build(&ui) {
|
||||||
ui.tree_node(im_str!("Tree")).build(|| {
|
TreeNode::new(im_str!("Tree")).build(&ui, || {
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
ui.tree_node(&im_str!("Child {}", i)).build(|| {
|
TreeNode::new(&im_str!("Child {}", i)).build(&ui, || {
|
||||||
ui.text(im_str!("blah blah"));
|
ui.text(im_str!("blah blah"));
|
||||||
ui.same_line(0.0);
|
ui.same_line(0.0);
|
||||||
if ui.small_button(im_str!("print")) {
|
if ui.small_button(im_str!("print")) {
|
||||||
@ -364,7 +365,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.tree_node(im_str!("Bullets")).build(|| {
|
TreeNode::new(im_str!("Bullets")).build(&ui, || {
|
||||||
ui.bullet_text(im_str!("Bullet point 1"));
|
ui.bullet_text(im_str!("Bullet point 1"));
|
||||||
ui.bullet_text(im_str!("Bullet point 2\nOn multiple lines"));
|
ui.bullet_text(im_str!("Bullet point 2\nOn multiple lines"));
|
||||||
ui.bullet();
|
ui.bullet();
|
||||||
@ -373,13 +374,13 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
ui.bullet();
|
ui.bullet();
|
||||||
ui.small_button(im_str!("Button"));
|
ui.small_button(im_str!("Button"));
|
||||||
});
|
});
|
||||||
ui.tree_node(im_str!("Colored text")).build(|| {
|
TreeNode::new(im_str!("Colored text")).build(&ui, || {
|
||||||
ui.text_colored([1.0, 0.0, 1.0, 1.0], im_str!("Pink"));
|
ui.text_colored([1.0, 0.0, 1.0, 1.0], im_str!("Pink"));
|
||||||
ui.text_colored([1.0, 1.0, 0.0, 1.0], im_str!("Yellow"));
|
ui.text_colored([1.0, 1.0, 0.0, 1.0], im_str!("Yellow"));
|
||||||
ui.text_disabled(im_str!("Disabled"));
|
ui.text_disabled(im_str!("Disabled"));
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.tree_node(im_str!("Multi-line text")).build(|| {
|
TreeNode::new(im_str!("Multi-line text")).build(&ui, || {
|
||||||
ui.input_text_multiline(
|
ui.input_text_multiline(
|
||||||
im_str!("multiline"),
|
im_str!("multiline"),
|
||||||
&mut state.text_multiline,
|
&mut state.text_multiline,
|
||||||
@ -387,7 +388,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
).build();
|
).build();
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.tree_node(im_str!("Word Wrapping")).build(|| {
|
TreeNode::new(im_str!("Word wrapping")).build(&ui, || {
|
||||||
ui.text_wrapped(im_str!(
|
ui.text_wrapped(im_str!(
|
||||||
"This text should automatically wrap on the edge of \
|
"This text should automatically wrap on the edge of \
|
||||||
the window.The current implementation for text \
|
the window.The current implementation for text \
|
||||||
@ -406,7 +407,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
ui.text(im_str!("Test paragraph 2:"));
|
ui.text(im_str!("Test paragraph 2:"));
|
||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
ui.tree_node(im_str!("UTF-8 Text")).build(|| {
|
TreeNode::new(im_str!("UTF-8 Text")).build(&ui, || {
|
||||||
ui.text_wrapped(im_str!(
|
ui.text_wrapped(im_str!(
|
||||||
"CJK text will only appear if the font was loaded \
|
"CJK text will only appear if the font was loaded \
|
||||||
with theappropriate CJK character ranges. Call \
|
with theappropriate CJK character ranges. Call \
|
||||||
@ -469,7 +470,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
ColorEdit::new(im_str!("color 1"), &mut state.col1).build(ui);
|
ColorEdit::new(im_str!("color 1"), &mut state.col1).build(ui);
|
||||||
ColorEdit::new(im_str!("color 2"), &mut state.col2).build(ui);
|
ColorEdit::new(im_str!("color 2"), &mut state.col2).build(ui);
|
||||||
|
|
||||||
ui.tree_node(im_str!("Multi-component Widgets")).build(|| {
|
TreeNode::new(im_str!("Multi-component Widgets")).build(&ui, || {
|
||||||
ui.input_float2(im_str!("input float2"), &mut state.vec2f)
|
ui.input_float2(im_str!("input float2"), &mut state.vec2f)
|
||||||
.build();
|
.build();
|
||||||
ui.input_int2(im_str!("input int2"), &mut state.vec2i)
|
ui.input_int2(im_str!("input int2"), &mut state.vec2i)
|
||||||
@ -483,7 +484,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
|||||||
ui.spacing();
|
ui.spacing();
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.tree_node(im_str!("Color/Picker Widgets")).build(|| {
|
TreeNode::new(im_str!("Color/Picker Widgets")).build(&ui, || {
|
||||||
let s = &mut state.color_edit;
|
let s = &mut state.color_edit;
|
||||||
ui.checkbox(im_str!("With HDR"), &mut s.hdr);
|
ui.checkbox(im_str!("With HDR"), &mut s.hdr);
|
||||||
ui.same_line(0.0);
|
ui.same_line(0.0);
|
||||||
@ -584,11 +585,8 @@ CTRL+click on individual component to input value.\n",
|
|||||||
b.build(ui);
|
b.build(ui);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ui
|
if CollapsingHeader::new(im_str!("Popups & Modal windows")).build(&ui) {
|
||||||
.collapsing_header(im_str!("Popups & Modal windows"))
|
TreeNode::new(im_str!("Popups")).build(&ui, || {
|
||||||
.build()
|
|
||||||
{
|
|
||||||
ui.tree_node(im_str!("Popups")).build(|| {
|
|
||||||
ui.text_wrapped(im_str!(
|
ui.text_wrapped(im_str!(
|
||||||
"When a popup is active, it inhibits interacting \
|
"When a popup is active, it inhibits interacting \
|
||||||
with windows that are behind the popup. Clicking \
|
with windows that are behind the popup. Clicking \
|
||||||
@ -620,7 +618,7 @@ CTRL+click on individual component to input value.\n",
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.tree_node(im_str!("Modals")).build(|| {
|
TreeNode::new(im_str!("Modals")).build(&ui, || {
|
||||||
ui.text_wrapped(im_str!(
|
ui.text_wrapped(im_str!(
|
||||||
"Modal windows are like popups but the user cannot close \
|
"Modal windows are like popups but the user cannot close \
|
||||||
them by clicking outside the window."
|
them by clicking outside the window."
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
|
use crate::string::ImStr;
|
||||||
|
use crate::widget::tree::{CollapsingHeader, TreeNode, TreeNodeFlags};
|
||||||
|
use crate::Ui;
|
||||||
|
|
||||||
bitflags!(
|
bitflags!(
|
||||||
/// Flags for igBeginDragDropSource(), igAcceptDragDropPayload()
|
/// Flags for igBeginDragDropSource(), igAcceptDragDropPayload()
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -116,48 +120,21 @@ bitflags!(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
bitflags!(
|
pub type ImGuiTreeNodeFlags = TreeNodeFlags;
|
||||||
/// Flags for trees and collapsing headers
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ImGuiTreeNodeFlags: c_int {
|
|
||||||
/// Draw as selected
|
|
||||||
const Selected = 1;
|
|
||||||
/// Full colored frame (e.g. for collapsing header)
|
|
||||||
const Framed = 1 << 1;
|
|
||||||
/// Hit testing to allow subsequent widgets to overlap this one
|
|
||||||
const AllowItemOverlap = 1 << 2;
|
|
||||||
/// Don't do a tree push when open (e.g. for collapsing header) = no extra indent nor
|
|
||||||
/// pushing on ID stack
|
|
||||||
const NoTreePushOnOpen = 1 << 3;
|
|
||||||
/// Don't automatically and temporarily open node when Logging is active (by default
|
|
||||||
/// logging will automatically open tree nodes)
|
|
||||||
const NoAutoOpenOnLog = 1 << 4;
|
|
||||||
/// Default node to be open
|
|
||||||
const DefaultOpen = 1 << 5;
|
|
||||||
/// Need double-click to open node
|
|
||||||
const OpenOnDoubleClick = 1 << 6;
|
|
||||||
/// Only open when clicking on the arrow part. If OpenOnDoubleClick is also set,
|
|
||||||
/// single-click arrow or double-click all box to open.
|
|
||||||
const OpenOnArrow = 1 << 7;
|
|
||||||
/// No collapsing, no arrow (use as a convenience for leaf nodes).
|
|
||||||
const Leaf = 1 << 8;
|
|
||||||
/// Display a bullet instead of arrow
|
|
||||||
const Bullet = 1 << 9;
|
|
||||||
/// Use FramePadding (even for an unframed text node) to vertically align text baseline to
|
|
||||||
/// regular widget height.
|
|
||||||
const FramePadding = 1 << 10;
|
|
||||||
/// Extend hit box to the right-most edge, even if not framed.
|
|
||||||
///
|
|
||||||
/// This is not the default in order to allow adding other items on the same line. In the
|
|
||||||
/// future we may refactor the hit system to be front-to-back, allowing natural overlaps
|
|
||||||
/// and then this can become the default.
|
|
||||||
const SpanAvailWidth = 1 << 11;
|
|
||||||
/// Extend hit box to the left-most and right-most edges (bypass the indented area)
|
|
||||||
const SpanFullWidth = 1 << 12;
|
|
||||||
const NavLeftJumpsBackHere = 1 << 13;
|
|
||||||
|
|
||||||
const CollapsingHeader =
|
impl<'ui> Ui<'ui> {
|
||||||
ImGuiTreeNodeFlags::Framed.bits | ImGuiTreeNodeFlags::NoTreePushOnOpen.bits |
|
#[deprecated(
|
||||||
ImGuiTreeNodeFlags::NoAutoOpenOnLog.bits;
|
since = "0.4.0",
|
||||||
|
note = "use imgui::TreeNode::new(...), and build() instead"
|
||||||
|
)]
|
||||||
|
pub fn tree_node<'a>(&self, id: &'a ImStr) -> TreeNode<'a> {
|
||||||
|
TreeNode::new(id)
|
||||||
}
|
}
|
||||||
);
|
#[deprecated(
|
||||||
|
since = "0.4.0",
|
||||||
|
note = "use imgui::CollapsingHeader::new(...), and build() instead"
|
||||||
|
)]
|
||||||
|
pub fn collapsing_header<'a>(&self, label: &'a ImStr) -> CollapsingHeader<'a> {
|
||||||
|
CollapsingHeader::new(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
19
src/lib.rs
19
src/lib.rs
@ -35,7 +35,6 @@ pub use self::render::renderer::*;
|
|||||||
pub use self::stacks::*;
|
pub use self::stacks::*;
|
||||||
pub use self::string::*;
|
pub use self::string::*;
|
||||||
pub use self::style::*;
|
pub use self::style::*;
|
||||||
pub use self::trees::{CollapsingHeader, TreeNode};
|
|
||||||
pub use self::utils::*;
|
pub use self::utils::*;
|
||||||
pub use self::widget::color_editors::*;
|
pub use self::widget::color_editors::*;
|
||||||
pub use self::widget::combo_box::*;
|
pub use self::widget::combo_box::*;
|
||||||
@ -44,6 +43,7 @@ pub use self::widget::menu::*;
|
|||||||
pub use self::widget::progress_bar::*;
|
pub use self::widget::progress_bar::*;
|
||||||
pub use self::widget::selectable::*;
|
pub use self::widget::selectable::*;
|
||||||
pub use self::widget::slider::*;
|
pub use self::widget::slider::*;
|
||||||
|
pub use self::widget::tree::*;
|
||||||
pub use self::window::child_window::*;
|
pub use self::window::child_window::*;
|
||||||
pub use self::window::*;
|
pub use self::window::*;
|
||||||
pub use self::window_draw_list::{ChannelsSplit, ImColor, WindowDrawList};
|
pub use self::window_draw_list::{ChannelsSplit, ImColor, WindowDrawList};
|
||||||
@ -69,7 +69,6 @@ mod string;
|
|||||||
mod style;
|
mod style;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
mod trees;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
mod widget;
|
mod widget;
|
||||||
mod window;
|
mod window;
|
||||||
@ -109,12 +108,6 @@ pub struct Ui<'ui> {
|
|||||||
font_atlas: Option<cell::RefMut<'ui, SharedFontAtlas>>,
|
font_atlas: Option<cell::RefMut<'ui, SharedFontAtlas>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static FMT: &[u8] = b"%s\0";
|
|
||||||
|
|
||||||
fn fmt_ptr() -> *const c_char {
|
|
||||||
FMT.as_ptr() as *const c_char
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ui> Ui<'ui> {
|
impl<'ui> Ui<'ui> {
|
||||||
/// Returns an immutable reference to the inputs/outputs object
|
/// Returns an immutable reference to the inputs/outputs object
|
||||||
pub fn io(&self) -> &Io {
|
pub fn io(&self) -> &Io {
|
||||||
@ -334,16 +327,6 @@ impl<'ui> Ui<'ui> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widgets: Trees
|
|
||||||
impl<'ui> Ui<'ui> {
|
|
||||||
pub fn tree_node<'p>(&self, id: &'p ImStr) -> TreeNode<'ui, 'p> {
|
|
||||||
TreeNode::new(self, id)
|
|
||||||
}
|
|
||||||
pub fn collapsing_header<'p>(&self, label: &'p ImStr) -> CollapsingHeader<'ui, 'p> {
|
|
||||||
CollapsingHeader::new(self, label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Tooltips
|
/// # Tooltips
|
||||||
impl<'ui> Ui<'ui> {
|
impl<'ui> Ui<'ui> {
|
||||||
/// Construct a tooltip window that can have any kind of content.
|
/// Construct a tooltip window that can have any kind of content.
|
||||||
|
|||||||
173
src/trees.rs
173
src/trees.rs
@ -1,173 +0,0 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
use sys;
|
|
||||||
|
|
||||||
use super::{Condition, ImStr, Ui};
|
|
||||||
use crate::legacy::ImGuiTreeNodeFlags;
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub struct TreeNode<'ui, 'p> {
|
|
||||||
id: &'p ImStr,
|
|
||||||
label: Option<&'p ImStr>,
|
|
||||||
opened: bool,
|
|
||||||
opened_cond: Condition,
|
|
||||||
flags: ImGuiTreeNodeFlags,
|
|
||||||
_phantom: PhantomData<&'ui Ui<'ui>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ui, 'p> TreeNode<'ui, 'p> {
|
|
||||||
pub fn new(_: &Ui<'ui>, id: &'p ImStr) -> Self {
|
|
||||||
TreeNode {
|
|
||||||
id,
|
|
||||||
label: None,
|
|
||||||
opened: false,
|
|
||||||
opened_cond: Condition::Never,
|
|
||||||
flags: ImGuiTreeNodeFlags::empty(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn label(mut self, label: &'p ImStr) -> Self {
|
|
||||||
self.label = Some(label);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
|
|
||||||
self.opened = opened;
|
|
||||||
self.opened_cond = cond;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn flags(mut self, flags: ImGuiTreeNodeFlags) -> Self {
|
|
||||||
self.flags = flags;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn selected(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Selected, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn framed(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Framed, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn allow_item_overlap(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::AllowItemOverlap, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn no_tree_push_on_open(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::NoTreePushOnOpen, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn no_auto_open_on_log(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::NoAutoOpenOnLog, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn default_open(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::DefaultOpen, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::OpenOnDoubleClick, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::OpenOnArrow, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn leaf(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Leaf, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn bullet(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Bullet, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn frame_padding(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::FramePadding, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn build<F: FnOnce()>(self, f: F) {
|
|
||||||
let render = unsafe {
|
|
||||||
if self.opened_cond != Condition::Never {
|
|
||||||
sys::igSetNextItemOpen(self.opened, self.opened_cond as _);
|
|
||||||
}
|
|
||||||
sys::igTreeNodeExStrStr(
|
|
||||||
self.id.as_ptr(),
|
|
||||||
self.flags.bits(),
|
|
||||||
super::fmt_ptr(),
|
|
||||||
self.label.unwrap_or(self.id).as_ptr(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if render {
|
|
||||||
f();
|
|
||||||
unsafe { sys::igTreePop() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub struct CollapsingHeader<'ui, 'p> {
|
|
||||||
label: &'p ImStr,
|
|
||||||
// Some flags are automatically set in ImGui::CollapsingHeader, so
|
|
||||||
// we only support a sensible subset here
|
|
||||||
flags: ImGuiTreeNodeFlags,
|
|
||||||
_phantom: PhantomData<&'ui Ui<'ui>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ui, 'p> CollapsingHeader<'ui, 'p> {
|
|
||||||
pub fn new(_: &Ui<'ui>, label: &'p ImStr) -> Self {
|
|
||||||
CollapsingHeader {
|
|
||||||
label,
|
|
||||||
flags: ImGuiTreeNodeFlags::empty(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn flags(mut self, flags: ImGuiTreeNodeFlags) -> Self {
|
|
||||||
self.flags = flags;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn selected(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Selected, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn default_open(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::DefaultOpen, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::OpenOnDoubleClick, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::OpenOnArrow, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn leaf(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Leaf, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn bullet(mut self, value: bool) -> Self {
|
|
||||||
self.flags.set(ImGuiTreeNodeFlags::Bullet, value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn build(self) -> bool {
|
|
||||||
unsafe { sys::igCollapsingHeader(self.label.as_ptr(), self.flags.bits()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,3 +7,4 @@ pub mod progress_bar;
|
|||||||
pub mod selectable;
|
pub mod selectable;
|
||||||
pub mod slider;
|
pub mod slider;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
pub mod tree;
|
||||||
|
|||||||
407
src/widget/tree.rs
Normal file
407
src/widget/tree.rs
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
use bitflags::bitflags;
|
||||||
|
use std::os::raw::{c_char, c_void};
|
||||||
|
use std::ptr;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use crate::context::Context;
|
||||||
|
use crate::string::ImStr;
|
||||||
|
use crate::sys;
|
||||||
|
use crate::{Condition, Ui};
|
||||||
|
|
||||||
|
bitflags!(
|
||||||
|
/// Flags for tree nodes
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct TreeNodeFlags: u32 {
|
||||||
|
/// Draw as selected
|
||||||
|
const SELECTED = sys::ImGuiTreeNodeFlags_Selected;
|
||||||
|
/// Full colored frame (e.g. for CollapsingHeader)
|
||||||
|
const FRAMED = sys::ImGuiTreeNodeFlags_Framed;
|
||||||
|
/// Hit testing to allow subsequent widgets to overlap this one
|
||||||
|
const ALLOW_ITEM_OVERLAP = sys::ImGuiTreeNodeFlags_AllowItemOverlap;
|
||||||
|
/// Don't push a tree node when open (e.g. for CollapsingHeader) = no extra indent nor
|
||||||
|
/// pushing on ID stack
|
||||||
|
const NO_TREE_PUSH_ON_OPEN = sys::ImGuiTreeNodeFlags_NoTreePushOnOpen;
|
||||||
|
/// Don't automatically and temporarily open node when Logging is active (by default
|
||||||
|
/// logging will automatically open tree nodes)
|
||||||
|
const NO_AUTO_OPEN_ON_LOG = sys::ImGuiTreeNodeFlags_NoAutoOpenOnLog;
|
||||||
|
/// Default node to be open
|
||||||
|
const DEFAULT_OPEN = sys::ImGuiTreeNodeFlags_DefaultOpen;
|
||||||
|
/// Need double-click to open node
|
||||||
|
const OPEN_ON_DOUBLE_CLICK = sys::ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||||
|
/// Only open when clicking on the arrow part.
|
||||||
|
///
|
||||||
|
/// If `TreeNodeFlags::OPEN_ON_DOUBLE_CLICK` is also set, single-click arrow or
|
||||||
|
/// double-click all box to open.
|
||||||
|
const OPEN_ON_ARROW = sys::ImGuiTreeNodeFlags_OpenOnArrow;
|
||||||
|
/// No collapsing, no arrow (use as a convenience for leaf nodes)
|
||||||
|
const LEAF = sys::ImGuiTreeNodeFlags_Leaf;
|
||||||
|
/// Display a bullet instead of arrow
|
||||||
|
const BULLET = sys::ImGuiTreeNodeFlags_Bullet;
|
||||||
|
/// Use `Style::frame_padding` (even for an unframed text node) to vertically align text
|
||||||
|
/// baseline to regular widget height.
|
||||||
|
///
|
||||||
|
/// Equivalent to calling `Ui::align_text_to_frame_padding`.
|
||||||
|
const FRAME_PADDING = sys::ImGuiTreeNodeFlags_FramePadding;
|
||||||
|
/// Extend hit box to the right-most edge, even if not framed.
|
||||||
|
///
|
||||||
|
/// This is not the default in order to allow adding other items on the same line. In the
|
||||||
|
/// future we may refactor the hit system to be front-to-back, allowing natural overlaps
|
||||||
|
/// and then this can become the default.
|
||||||
|
const SPAN_AVAIL_WIDTH = sys::ImGuiTreeNodeFlags_SpanAvailWidth;
|
||||||
|
/// Extend hit box to the left-most and right-most edges (bypass the indented area)
|
||||||
|
const SPAN_FULL_WIDTH = sys::ImGuiTreeNodeFlags_SpanFullWidth;
|
||||||
|
/// (WIP) Nav: left direction may move to this tree node from any of its child
|
||||||
|
const NAV_LEFT_JUMPS_BACK_HERE = sys::ImGuiTreeNodeFlags_NavLeftJumpsBackHere;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
static FMT: &'static [u8] = b"%s\0";
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fmt_ptr() -> *const c_char {
|
||||||
|
FMT.as_ptr() as *const c_char
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unique ID used by tree nodes
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum TreeNodeId<'a> {
|
||||||
|
Str(&'a ImStr),
|
||||||
|
Ptr(*const c_void),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + AsRef<ImStr>> From<&'a T> for TreeNodeId<'a> {
|
||||||
|
fn from(s: &'a T) -> Self {
|
||||||
|
TreeNodeId::Str(s.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<*const T> for TreeNodeId<'static> {
|
||||||
|
fn from(p: *const T) -> Self {
|
||||||
|
TreeNodeId::Ptr(p as *const c_void)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<*mut T> for TreeNodeId<'static> {
|
||||||
|
fn from(p: *mut T) -> Self {
|
||||||
|
TreeNodeId::Ptr(p as *const T as *const c_void)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder for a tree node widget
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct TreeNode<'a> {
|
||||||
|
id: TreeNodeId<'a>,
|
||||||
|
label: Option<&'a ImStr>,
|
||||||
|
opened: bool,
|
||||||
|
opened_cond: Condition,
|
||||||
|
flags: TreeNodeFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TreeNode<'a> {
|
||||||
|
/// Constructs a new tree node builder
|
||||||
|
pub fn new<I: Into<TreeNodeId<'a>>>(id: I) -> TreeNode<'a> {
|
||||||
|
TreeNode {
|
||||||
|
id: id.into(),
|
||||||
|
label: None,
|
||||||
|
opened: false,
|
||||||
|
opened_cond: Condition::Never,
|
||||||
|
flags: TreeNodeFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Sets the tree node label
|
||||||
|
#[inline]
|
||||||
|
pub fn label(mut self, label: &'a ImStr) -> Self {
|
||||||
|
self.label = Some(label);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the opened state of the tree node, which is applied based on the given condition value
|
||||||
|
#[inline]
|
||||||
|
pub fn opened(mut self, opened: bool, cond: Condition) -> Self {
|
||||||
|
self.opened = opened;
|
||||||
|
self.opened_cond = cond;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Replaces all current settings with the given flags.
|
||||||
|
#[inline]
|
||||||
|
pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
|
||||||
|
self.flags = flags;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enables/disables drawing the tree node in selected state.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn selected(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::SELECTED, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enables/disables full-colored frame.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn framed(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::FRAMED, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enables/disables allowing the tree node to overlap subsequent widgets.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn allow_item_overlap(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enables/disables automatic tree push when the tree node is open (= adds extra indentation
|
||||||
|
/// and pushes to the ID stack).
|
||||||
|
///
|
||||||
|
/// Enabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn tree_push_on_open(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, !value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enables/disables automatic opening of the tree node when logging is active.
|
||||||
|
///
|
||||||
|
/// By default, logging will automatically open all tree nodes.
|
||||||
|
///
|
||||||
|
/// Enabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn auto_open_on_log(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, !value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the default open state for the tree node.
|
||||||
|
///
|
||||||
|
/// Tree nodes are closed by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn default_open(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Only open when the tree node is double-clicked.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Only open when clicking the arrow part of the tree node.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enable/disables leaf mode (no collapsing, no arrow).
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn leaf(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::LEAF, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Display a bullet instead of arrow.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn bullet(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::BULLET, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Use `frame_padding` to vertically align text baseline to regular widget height.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn frame_padding(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Left direction may move to this tree node from any of its child.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn nav_left_jumps_back_here(mut self, value: bool) -> Self {
|
||||||
|
self.flags
|
||||||
|
.set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Pushes a tree node and starts appending to it.
|
||||||
|
///
|
||||||
|
/// Returns `Some(TreeNodeToken)` if the tree node is open. After content has been
|
||||||
|
/// rendered, the token must be popped by calling `.pop()`.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the tree node is not open and no content should be rendered.
|
||||||
|
#[must_use]
|
||||||
|
pub fn push(self, ui: &Ui) -> Option<TreeNodeToken> {
|
||||||
|
let open = unsafe {
|
||||||
|
if self.opened_cond != Condition::Never {
|
||||||
|
sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
|
||||||
|
}
|
||||||
|
match self.id {
|
||||||
|
TreeNodeId::Str(id) => sys::igTreeNodeExStrStr(
|
||||||
|
id.as_ptr(),
|
||||||
|
self.flags.bits() as i32,
|
||||||
|
fmt_ptr(),
|
||||||
|
self.label.unwrap_or(id).as_ptr(),
|
||||||
|
),
|
||||||
|
TreeNodeId::Ptr(id) => sys::igTreeNodeExPtr(
|
||||||
|
id,
|
||||||
|
self.flags.bits() as i32,
|
||||||
|
fmt_ptr(),
|
||||||
|
self.label.unwrap_or_default().as_ptr(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if open {
|
||||||
|
Some(TreeNodeToken {
|
||||||
|
ctx: if self.flags.contains(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN) {
|
||||||
|
ptr::null()
|
||||||
|
} else {
|
||||||
|
ui.ctx
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates a tree node and runs a closure to construct the contents.
|
||||||
|
///
|
||||||
|
/// Note: the closure is not called if the tree node is not open.
|
||||||
|
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
|
||||||
|
if let Some(node) = self.push(ui) {
|
||||||
|
f();
|
||||||
|
node.pop(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tracks a tree node that must be popped by calling `.pop()`.
|
||||||
|
///
|
||||||
|
/// If `TreeNodeFlags::NO_TREE_PUSH_ON_OPEN` was used when this token was created, calling `.pop()`
|
||||||
|
/// is not mandatory and is a no-op.
|
||||||
|
#[must_use]
|
||||||
|
pub struct TreeNodeToken {
|
||||||
|
ctx: *const Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNodeToken {
|
||||||
|
/// Pops a tree node
|
||||||
|
pub fn pop(mut self, _: &Ui) {
|
||||||
|
if !self.ctx.is_null() {
|
||||||
|
self.ctx = ptr::null();
|
||||||
|
unsafe { sys::igTreePop() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TreeNodeToken {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.ctx.is_null() && !thread::panicking() {
|
||||||
|
panic!("A TreeNodeToken was leaked. Did you call .pop()?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder for a collapsing header widget
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct CollapsingHeader<'a> {
|
||||||
|
label: &'a ImStr,
|
||||||
|
flags: TreeNodeFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CollapsingHeader<'a> {
|
||||||
|
/// Constructs a new collapsing header builder
|
||||||
|
pub fn new(label: &ImStr) -> CollapsingHeader {
|
||||||
|
CollapsingHeader {
|
||||||
|
label,
|
||||||
|
flags: TreeNodeFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Replaces all current settings with the given flags.
|
||||||
|
#[inline]
|
||||||
|
pub fn flags(mut self, flags: TreeNodeFlags) -> Self {
|
||||||
|
self.flags = flags;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enables/disables allowing the collapsing header to overlap subsequent widgets.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn allow_item_overlap(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the default open state for the collapsing header.
|
||||||
|
///
|
||||||
|
/// Collapsing headers are closed by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn default_open(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Only open when the collapsing header is double-clicked.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn open_on_double_click(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Only open when clicking the arrow part of the collapsing header.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn open_on_arrow(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enable/disables leaf mode (no collapsing, no arrow).
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn leaf(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::LEAF, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Display a bullet instead of arrow.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn bullet(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::BULLET, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Use `frame_padding` to vertically align text baseline to regular widget height.
|
||||||
|
///
|
||||||
|
/// Disabled by default.
|
||||||
|
#[inline]
|
||||||
|
pub fn frame_padding(mut self, value: bool) -> Self {
|
||||||
|
self.flags.set(TreeNodeFlags::FRAME_PADDING, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Builds the collapsing header.
|
||||||
|
///
|
||||||
|
/// Returns true if the collapsing header is open and content should be rendered.
|
||||||
|
#[must_use]
|
||||||
|
pub fn build(self, _: &Ui) -> bool {
|
||||||
|
unsafe { sys::igCollapsingHeader(self.label.as_ptr(), self.flags.bits() as i32) }
|
||||||
|
}
|
||||||
|
/// Builds the collapsing header, and adds an additional close button that changes the value of
|
||||||
|
/// the given mutable reference when clicked.
|
||||||
|
///
|
||||||
|
/// Returns true if the collapsing header is open and content should be rendered.
|
||||||
|
#[must_use]
|
||||||
|
pub fn build_with_close_button(self, _: &Ui, opened: &mut bool) -> bool {
|
||||||
|
unsafe {
|
||||||
|
sys::igCollapsingHeaderBoolPtr(
|
||||||
|
self.label.as_ptr(),
|
||||||
|
opened as *mut bool,
|
||||||
|
self.flags.bits() as i32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user