mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 21:48:36 +00:00
Merge pull request #318 from SnoozeTime/tabbar-wrapper
implementation of TabBar and TabItem
This commit is contained in:
commit
5dea51c55b
@ -47,6 +47,8 @@ struct State {
|
||||
dont_ask_me_next_time: bool,
|
||||
stacked_modals_item: usize,
|
||||
stacked_modals_color: [f32; 4],
|
||||
|
||||
tabs: TabState,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
@ -102,6 +104,41 @@ impl Default for State {
|
||||
dont_ask_me_next_time: false,
|
||||
stacked_modals_item: 0,
|
||||
stacked_modals_color: [0.4, 0.7, 0.0, 0.5],
|
||||
tabs: TabState::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TabState {
|
||||
// flags for the advanced tab example
|
||||
reorderable: bool,
|
||||
autoselect: bool,
|
||||
listbutton: bool,
|
||||
noclose_middlebutton: bool,
|
||||
fitting_resizedown: bool,
|
||||
fitting_scroll: bool,
|
||||
|
||||
// opened state for tab items
|
||||
artichoke_tab: bool,
|
||||
beetroot_tab: bool,
|
||||
celery_tab: bool,
|
||||
daikon_tab: bool,
|
||||
}
|
||||
|
||||
impl Default for TabState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
reorderable: true,
|
||||
autoselect: false,
|
||||
listbutton: false,
|
||||
noclose_middlebutton: false,
|
||||
fitting_resizedown: true,
|
||||
fitting_scroll: false,
|
||||
|
||||
artichoke_tab: true,
|
||||
beetroot_tab: true,
|
||||
celery_tab: true,
|
||||
daikon_tab: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -365,6 +402,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
TreeNode::new(im_str!("Bullets")).build(&ui, || {
|
||||
ui.bullet_text(im_str!("Bullet point 1"));
|
||||
ui.bullet_text(im_str!("Bullet point 2\nOn multiple lines"));
|
||||
@ -585,6 +623,80 @@ CTRL+click on individual component to input value.\n",
|
||||
b.build(ui);
|
||||
});
|
||||
}
|
||||
|
||||
if CollapsingHeader::new(im_str!("Layout")).build(&ui) {
|
||||
TreeNode::new(im_str!("Tabs")).build(&ui, || {
|
||||
TreeNode::new(im_str!("Basic")).build(&ui, || {
|
||||
TabBar::new(im_str!("basictabbar")).build(&ui, || {
|
||||
TabItem::new(im_str!("Avocado")).build(&ui, || {
|
||||
ui.text(im_str!("This is the Avocado tab!"));
|
||||
ui.text(im_str!("blah blah blah blah blah"));
|
||||
});
|
||||
TabItem::new(im_str!("Broccoli")).build(&ui, || {
|
||||
ui.text(im_str!("This is the Broccoli tab!"));
|
||||
ui.text(im_str!("blah blah blah blah blah"));
|
||||
});
|
||||
TabItem::new(im_str!("Cucumber")).build(&ui, || {
|
||||
ui.text(im_str!("This is the Cucumber tab!"));
|
||||
ui.text(im_str!("blah blah blah blah blah"));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
TreeNode::new(im_str!("Advanced & Close button")).build(&ui, || {
|
||||
|
||||
ui.separator();
|
||||
let s = &mut state.tabs;
|
||||
|
||||
ui.checkbox(im_str!("ImGuiTabBarFlags_Reorderable"), &mut s.reorderable);
|
||||
ui.checkbox(im_str!("ImGuiTabBarFlags_AutoSelectNewTabs"), &mut s.autoselect);
|
||||
ui.checkbox(im_str!("ImGuiTabBarFlags_TabListPopupButton"), &mut s.listbutton);
|
||||
ui.checkbox(im_str!("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton"), &mut s.noclose_middlebutton);
|
||||
if ui.checkbox(im_str!("ImGuiTabBarFlags_FittingPolicyResizeDown"), &mut s.fitting_resizedown) {
|
||||
s.fitting_scroll = !s.fitting_resizedown;
|
||||
}
|
||||
if ui.checkbox(im_str!("ImGuiTabBarFlags_FittingPolicyScroll"), &mut s.fitting_scroll) {
|
||||
s.fitting_resizedown = !s.fitting_scroll;
|
||||
}
|
||||
let style = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0]));
|
||||
ui.checkbox(im_str!("Artichoke"), &mut s.artichoke_tab);
|
||||
ui.same_line(0.0);
|
||||
ui.checkbox(im_str!("Beetroot"), &mut s.beetroot_tab);
|
||||
ui.same_line(0.0);
|
||||
ui.checkbox(im_str!("Celery"), &mut s.celery_tab);
|
||||
ui.same_line(0.0);
|
||||
ui.checkbox(im_str!("Daikon"), &mut s.daikon_tab);
|
||||
style.pop(ui);
|
||||
|
||||
let flags = {
|
||||
let mut f = TabBarFlags::empty();
|
||||
f.set(TabBarFlags::REORDERABLE, s.reorderable);
|
||||
f.set(TabBarFlags::AUTO_SELECT_NEW_TABS, s.autoselect);
|
||||
f.set(TabBarFlags::TAB_LIST_POPUP_BUTTON, s.listbutton);
|
||||
f.set(TabBarFlags::NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON, s.noclose_middlebutton);
|
||||
f.set(TabBarFlags::FITTING_POLICY_RESIZE_DOWN, s.fitting_resizedown);
|
||||
f.set(TabBarFlags::FITTING_POLICY_SCROLL, s.fitting_scroll);
|
||||
f
|
||||
};
|
||||
|
||||
TabBar::new(im_str!("tabbar")).flags(flags).build(&ui, || {
|
||||
TabItem::new(im_str!("Artichoke")).opened(&mut s.artichoke_tab).build(&ui, || {
|
||||
ui.text(im_str!("This is the Artichoke tab!"));
|
||||
});
|
||||
TabItem::new(im_str!("Beetroot")).opened(&mut s.beetroot_tab).build(&ui, || {
|
||||
ui.text(im_str!("This is the Beetroot tab!"));
|
||||
});
|
||||
TabItem::new(im_str!("Celery")).opened(&mut s.celery_tab).build(&ui, || {
|
||||
ui.text(im_str!("This is the Celery tab!"));
|
||||
});
|
||||
TabItem::new(im_str!("Daikon")).opened(&mut s.daikon_tab).build(&ui, || {
|
||||
ui.text(im_str!("This is the Daikon tab!"));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
if CollapsingHeader::new(im_str!("Popups & Modal windows")).build(&ui) {
|
||||
TreeNode::new(im_str!("Popups")).build(&ui, || {
|
||||
ui.text_wrapped(im_str!(
|
||||
|
||||
@ -43,6 +43,7 @@ pub use self::widget::menu::*;
|
||||
pub use self::widget::progress_bar::*;
|
||||
pub use self::widget::selectable::*;
|
||||
pub use self::widget::slider::*;
|
||||
pub use self::widget::tab::*;
|
||||
pub use self::widget::tree::*;
|
||||
pub use self::window::child_window::*;
|
||||
pub use self::window::*;
|
||||
|
||||
@ -6,5 +6,6 @@ pub mod misc;
|
||||
pub mod progress_bar;
|
||||
pub mod selectable;
|
||||
pub mod slider;
|
||||
pub mod tab;
|
||||
pub mod text;
|
||||
pub mod tree;
|
||||
|
||||
188
src/widget/tab.rs
Normal file
188
src/widget/tab.rs
Normal file
@ -0,0 +1,188 @@
|
||||
//! Safe wrapper around imgui-sys for tab menu.
|
||||
//!
|
||||
//! # Examples
|
||||
//
|
||||
//! ```no_run
|
||||
//! # use imgui::*;
|
||||
//! # let mut ctx = Context::create();
|
||||
//! # let ui = ctx.frame();
|
||||
//!
|
||||
//! // During UI construction
|
||||
//! TabBar::new(im_str!("tabbar")).build(&ui, || {
|
||||
//! TabItem::new(im_str!("a tab")).build(&ui, || {
|
||||
//! ui.text(im_str!("tab content 1"));
|
||||
//! });
|
||||
//! TabItem::new(im_str!("2tab")).build(&ui, || {
|
||||
//! ui.text(im_str!("tab content 2"));
|
||||
//! });
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! See `test_window_impl.rs` for a more complicated example.
|
||||
use crate::context::Context;
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
use bitflags::bitflags;
|
||||
use std::{ptr, thread};
|
||||
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
pub struct TabBarFlags: u32 {
|
||||
const REORDERABLE = sys::ImGuiTabBarFlags_Reorderable;
|
||||
const AUTO_SELECT_NEW_TABS = sys::ImGuiTabBarFlags_AutoSelectNewTabs;
|
||||
const TAB_LIST_POPUP_BUTTON = sys::ImGuiTabBarFlags_TabListPopupButton;
|
||||
const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabBarFlags_NoCloseWithMiddleMouseButton;
|
||||
const NO_TAB_LIST_SCROLLING_BUTTONS = sys::ImGuiTabBarFlags_NoTabListScrollingButtons;
|
||||
const NO_TOOLTIP = sys::ImGuiTabBarFlags_NoTooltip;
|
||||
const FITTING_POLICY_RESIZE_DOWN = sys::ImGuiTabBarFlags_FittingPolicyResizeDown;
|
||||
const FITTING_POLICY_SCROLL = sys::ImGuiTabBarFlags_FittingPolicyScroll;
|
||||
const FITTING_POLICY_MASK = sys::ImGuiTabBarFlags_FittingPolicyMask_;
|
||||
const FITTING_POLICY_DEFAULT = sys::ImGuiTabBarFlags_FittingPolicyDefault_;
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a tab bar.
|
||||
pub struct TabBar<'a> {
|
||||
id: &'a ImStr,
|
||||
flags: TabBarFlags,
|
||||
}
|
||||
|
||||
impl<'a> TabBar<'a> {
|
||||
pub fn new(id: &'a ImStr) -> Self {
|
||||
Self {
|
||||
id,
|
||||
flags: TabBarFlags::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/Disable the reorderable property
|
||||
///
|
||||
/// Disabled by default
|
||||
#[inline]
|
||||
pub fn reorderable(mut self, value: bool) -> Self {
|
||||
self.flags.set(TabBarFlags::REORDERABLE, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the flags of the tab bar.
|
||||
///
|
||||
/// Flags are empty by default
|
||||
#[inline]
|
||||
pub fn flags(mut self, flags: TabBarFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn begin(self, ui: &Ui) -> Option<TabBarToken> {
|
||||
let shoud_render =
|
||||
unsafe { sys::igBeginTabBar(self.id.as_ptr(), self.flags.bits() as i32) };
|
||||
|
||||
if shoud_render {
|
||||
Some(TabBarToken { ctx: ui.ctx })
|
||||
} else {
|
||||
unsafe { sys::igEndTabBar() };
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a tab bar and runs a closure to construct the contents.
|
||||
///
|
||||
/// Note: the closure is not called if no tabbar content is visible
|
||||
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
|
||||
if let Some(tab) = self.begin(ui) {
|
||||
f();
|
||||
tab.end(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks a window that must be ended by calling `.end()`
|
||||
pub struct TabBarToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl TabBarToken {
|
||||
/// Ends a tab bar
|
||||
pub fn end(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igEndTabBar() };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TabBarToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A TabBarToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TabItem<'a> {
|
||||
name: &'a ImStr,
|
||||
opened: Option<&'a mut bool>,
|
||||
}
|
||||
|
||||
impl<'a> TabItem<'a> {
|
||||
pub fn new(name: &'a ImStr) -> Self {
|
||||
Self { name, opened: None }
|
||||
}
|
||||
|
||||
/// Will open or close the tab.\
|
||||
///
|
||||
/// True to display the tab. Tab item is visible by default.
|
||||
#[inline]
|
||||
pub fn opened(mut self, opened: &'a mut bool) -> Self {
|
||||
self.opened = Some(opened);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn begin(self, ui: &Ui) -> Option<TabItemToken> {
|
||||
let shoud_render = unsafe {
|
||||
sys::igBeginTabItem(
|
||||
self.name.as_ptr(),
|
||||
self.opened
|
||||
.map(|x| x as *mut bool)
|
||||
.unwrap_or(ptr::null_mut()),
|
||||
0 as ::std::os::raw::c_int,
|
||||
)
|
||||
};
|
||||
|
||||
if shoud_render {
|
||||
Some(TabItemToken { ctx: ui.ctx })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a tab item and runs a closure to construct the contents.
|
||||
///
|
||||
/// Note: the closure is not called if the tab item is not selected
|
||||
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
|
||||
if let Some(tab) = self.begin(ui) {
|
||||
f();
|
||||
tab.end(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TabItemToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl TabItemToken {
|
||||
pub fn end(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igEndTabItem() };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TabItemToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A TabItemToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user