arcdps_imgui\widget/
tab.rs

1//!   # Examples
2//
3//! ```no_run
4//! # use arcdps_imgui::*;
5//! # let mut ctx = Context::create();
6//! # let ui = ctx.frame();
7//!
8//! // During UI construction
9//! TabBar::new(im_str!("tabbar")).build(&ui, || {
10//!                   TabItem::new(im_str!("a tab")).build(&ui, || {
11//!                       ui.text(im_str!("tab content 1"));
12//!                   });
13//!                   TabItem::new(im_str!("2tab")).build(&ui, || {
14//!                       ui.text(im_str!("tab content 2"));
15//!                   });
16//!               });
17//! ```
18//!
19//! See `test_window_impl.rs` for a more complicated example.
20use crate::sys;
21use crate::Ui;
22use bitflags::bitflags;
23use std::ptr;
24
25bitflags! {
26    #[repr(transparent)]
27    pub struct TabBarFlags: u32 {
28        const REORDERABLE = sys::ImGuiTabBarFlags_Reorderable;
29        const AUTO_SELECT_NEW_TABS = sys::ImGuiTabBarFlags_AutoSelectNewTabs;
30        const TAB_LIST_POPUP_BUTTON = sys::ImGuiTabBarFlags_TabListPopupButton;
31        const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabBarFlags_NoCloseWithMiddleMouseButton;
32        const NO_TAB_LIST_SCROLLING_BUTTONS = sys::ImGuiTabBarFlags_NoTabListScrollingButtons;
33        const NO_TOOLTIP = sys::ImGuiTabBarFlags_NoTooltip;
34        const FITTING_POLICY_RESIZE_DOWN = sys::ImGuiTabBarFlags_FittingPolicyResizeDown;
35        const FITTING_POLICY_SCROLL = sys::ImGuiTabBarFlags_FittingPolicyScroll;
36        const FITTING_POLICY_MASK = sys::ImGuiTabBarFlags_FittingPolicyMask_;
37        const FITTING_POLICY_DEFAULT = sys::ImGuiTabBarFlags_FittingPolicyDefault_;
38    }
39}
40
41bitflags! {
42    #[repr(transparent)]
43    pub struct TabItemFlags: u32 {
44        const UNSAVED_DOCUMENT = sys::ImGuiTabItemFlags_UnsavedDocument;
45        const SET_SELECTED = sys::ImGuiTabItemFlags_SetSelected;
46        const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
47        const NO_PUSH_ID = sys::ImGuiTabItemFlags_NoPushId;
48        const NO_TOOLTIP = sys::ImGuiTabItemFlags_NoTooltip;
49        const NO_REORDER = sys::ImGuiTabItemFlags_NoReorder;
50        const LEADING = sys::ImGuiTabItemFlags_Leading;
51        const TRAILING = sys::ImGuiTabItemFlags_Trailing;
52    }
53}
54
55/// Builder for a tab bar.
56pub struct TabBar<T> {
57    id: T,
58    flags: TabBarFlags,
59}
60
61impl<T: AsRef<str>> TabBar<T> {
62    #[inline]
63    #[doc(alias = "BeginTabBar")]
64    pub fn new(id: T) -> Self {
65        Self {
66            id,
67            flags: TabBarFlags::empty(),
68        }
69    }
70
71    /// Enable/Disable the reorderable property
72    ///
73    /// Disabled by default
74    #[inline]
75    pub fn reorderable(mut self, value: bool) -> Self {
76        self.flags.set(TabBarFlags::REORDERABLE, value);
77        self
78    }
79
80    /// Set the flags of the tab bar.
81    ///
82    /// Flags are empty by default
83    #[inline]
84    pub fn flags(mut self, flags: TabBarFlags) -> Self {
85        self.flags = flags;
86        self
87    }
88
89    #[must_use]
90    pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option<TabBarToken<'ui>> {
91        ui.tab_bar_with_flags(self.id, self.flags)
92    }
93
94    /// Creates a tab bar and runs a closure to construct the contents.
95    /// Returns the result of the closure, if it is called.
96    ///
97    /// Note: the closure is not called if no tabbar content is visible
98    pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
99        self.begin(ui).map(|_tab| f())
100    }
101}
102
103create_token!(
104    /// Tracks a window that can be ended by calling `.end()`
105    /// or by dropping
106    pub struct TabBarToken<'ui>;
107
108    /// Ends a tab bar.
109    drop { sys::igEndTabBar() }
110);
111
112pub struct TabItem<'a, T> {
113    label: T,
114    opened: Option<&'a mut bool>,
115    flags: TabItemFlags,
116}
117
118impl<'a, T: AsRef<str>> TabItem<'a, T> {
119    #[doc(alias = "BeginTabItem")]
120    pub fn new(name: T) -> Self {
121        Self {
122            label: name,
123            opened: None,
124            flags: TabItemFlags::empty(),
125        }
126    }
127
128    /// Will open or close the tab.
129    ///
130    /// True to display the tab. Tab item is visible by default.
131    #[inline]
132    pub fn opened(mut self, opened: &'a mut bool) -> Self {
133        self.opened = Some(opened);
134        self
135    }
136
137    /// Set the flags of the tab item.
138    ///
139    /// Flags are empty by default
140    #[inline]
141    pub fn flags(mut self, flags: TabItemFlags) -> Self {
142        self.flags = flags;
143        self
144    }
145
146    #[must_use]
147    pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option<TabItemToken<'ui>> {
148        ui.tab_item_with_flags(self.label, self.opened, self.flags)
149    }
150
151    /// Creates a tab item and runs a closure to construct the contents.
152    /// Returns the result of the closure, if it is called.
153    ///
154    /// Note: the closure is not called if the tab item is not selected
155    pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
156        self.begin(ui).map(|_tab| f())
157    }
158}
159
160create_token!(
161    /// Tracks a tab bar item that can be ended by calling `.end()`
162    /// or by dropping
163    pub struct TabItemToken<'ui>;
164
165    /// Ends a tab bar item.
166    drop { sys::igEndTabItem() }
167);
168
169impl Ui<'_> {
170    /// Creates a tab bar and returns a tab bar token, allowing you to append
171    /// Tab items afterwards. This passes no flags. To pass flags explicitly,
172    /// use [tab_bar_with_flags](Self::tab_bar_with_flags).
173    pub fn tab_bar(&self, id: impl AsRef<str>) -> Option<TabBarToken<'_>> {
174        self.tab_bar_with_flags(id, TabBarFlags::empty())
175    }
176    //
177    /// Creates a tab bar and returns a tab bar token, allowing you to append
178    /// Tab items afterwards.
179    pub fn tab_bar_with_flags(
180        &self,
181        id: impl AsRef<str>,
182        flags: TabBarFlags,
183    ) -> Option<TabBarToken<'_>> {
184        let should_render =
185            unsafe { sys::igBeginTabBar(self.scratch_txt(id), flags.bits() as i32) };
186
187        if should_render {
188            Some(TabBarToken::new(self))
189        } else {
190            unsafe { sys::igEndTabBar() };
191            None
192        }
193    }
194
195    /// Creates a new tab item and returns a token if its contents are visible.
196    ///
197    /// By default, this doesn't pass an opened bool nor any flags. See [tab_item_with_opened]
198    /// and [tab_item_with_flags] for more.
199    ///
200    /// [tab_item_with_opened]: Self::tab_item_with_opened
201    /// [tab_item_with_flags]: Self::tab_item_with_flags
202    pub fn tab_item(&self, label: impl AsRef<str>) -> Option<TabItemToken<'_>> {
203        self.tab_item_with_flags(label, None, TabItemFlags::empty())
204    }
205
206    /// Creates a new tab item and returns a token if its contents are visible.
207    ///
208    /// By default, this doesn't pass any flags. See `[tab_item_with_flags]` for more.
209    pub fn tab_item_with_opened(
210        &self,
211        label: impl AsRef<str>,
212        opened: &mut bool,
213    ) -> Option<TabItemToken<'_>> {
214        self.tab_item_with_flags(label, Some(opened), TabItemFlags::empty())
215    }
216
217    /// Creates a new tab item and returns a token if its contents are visible.
218    pub fn tab_item_with_flags(
219        &self,
220        label: impl AsRef<str>,
221        opened: Option<&mut bool>,
222        flags: TabItemFlags,
223    ) -> Option<TabItemToken<'_>> {
224        let should_render = unsafe {
225            sys::igBeginTabItem(
226                self.scratch_txt(label),
227                opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut()),
228                flags.bits() as i32,
229            )
230        };
231
232        if should_render {
233            Some(TabItemToken::new(self))
234        } else {
235            None
236        }
237    }
238}