dear_imgui/widget/
tab.rs

1#![allow(
2    clippy::cast_possible_truncation,
3    clippy::cast_sign_loss,
4    clippy::as_conversions
5)]
6use crate::sys;
7use crate::ui::Ui;
8use std::ptr;
9
10bitflags::bitflags! {
11    /// Flags for tab bar widgets
12    #[repr(transparent)]
13    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14    pub struct TabBarFlags: i32 {
15        /// No flags
16        const NONE = 0;
17        /// Allow manually dragging tabs to re-order them + New tabs are appended at the end of list
18        const REORDERABLE = sys::ImGuiTabBarFlags_Reorderable as i32;
19        /// Automatically select new tabs when they appear
20        const AUTO_SELECT_NEW_TABS = sys::ImGuiTabBarFlags_AutoSelectNewTabs as i32;
21        /// Disable buttons to open the tab list popup
22        const TAB_LIST_POPUP_BUTTON = sys::ImGuiTabBarFlags_TabListPopupButton as i32;
23        /// Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
24        const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabBarFlags_NoCloseWithMiddleMouseButton as i32;
25        /// Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll)
26        const NO_TAB_LIST_SCROLLING_BUTTONS = sys::ImGuiTabBarFlags_NoTabListScrollingButtons as i32;
27        /// Disable tooltips when hovering a tab
28        const NO_TOOLTIP = sys::ImGuiTabBarFlags_NoTooltip as i32;
29        /// Draw selected tab with a different color
30        const DRAW_SELECTED_OVERLINE = sys::ImGuiTabBarFlags_DrawSelectedOverline as i32;
31        /// Mixed fitting policy
32        const FITTING_POLICY_MIXED = sys::ImGuiTabBarFlags_FittingPolicyMixed as i32;
33        /// Shrink tabs when they don't fit
34        const FITTING_POLICY_SHRINK = sys::ImGuiTabBarFlags_FittingPolicyShrink as i32;
35        /// Add scroll buttons when tabs don't fit
36        const FITTING_POLICY_SCROLL = sys::ImGuiTabBarFlags_FittingPolicyScroll as i32;
37        /// Mask for fitting policy flags
38        const FITTING_POLICY_MASK = sys::ImGuiTabBarFlags_FittingPolicyMask_ as i32;
39        /// Default fitting policy
40        const FITTING_POLICY_DEFAULT = sys::ImGuiTabBarFlags_FittingPolicyDefault_ as i32;
41    }
42}
43
44bitflags::bitflags! {
45    /// Flags for tab item widgets
46    #[repr(transparent)]
47    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
48    pub struct TabItemFlags: i32 {
49        /// No flags
50        const NONE = 0;
51        /// Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
52        const UNSAVED_DOCUMENT = sys::ImGuiTabItemFlags_UnsavedDocument as i32;
53        /// Trigger flag to programmatically make the tab selected when calling BeginTabItem()
54        const SET_SELECTED = sys::ImGuiTabItemFlags_SetSelected as i32;
55        /// Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
56        const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabItemFlags_NoCloseWithMiddleMouseButton as i32;
57        /// Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
58        const NO_PUSH_ID = sys::ImGuiTabItemFlags_NoPushId as i32;
59        /// Disable tooltip for the given tab
60        const NO_TOOLTIP = sys::ImGuiTabItemFlags_NoTooltip as i32;
61        /// Disable reordering this tab or having another tab cross over this tab
62        const NO_REORDER = sys::ImGuiTabItemFlags_NoReorder as i32;
63        /// Enforce the tab position to the left of the tab bar (after the tab list popup button)
64        const LEADING = sys::ImGuiTabItemFlags_Leading as i32;
65        /// Enforce the tab position to the right of the tab bar (before the scrolling buttons)
66        const TRAILING = sys::ImGuiTabItemFlags_Trailing as i32;
67    }
68}
69
70/// Builder for a tab bar
71#[derive(Debug)]
72#[must_use]
73pub struct TabBar<T> {
74    id: T,
75    flags: TabBarFlags,
76}
77
78impl<T: AsRef<str>> TabBar<T> {
79    /// Creates a new tab bar builder
80    #[doc(alias = "BeginTabBar")]
81    pub fn new(id: T) -> Self {
82        Self {
83            id,
84            flags: TabBarFlags::NONE,
85        }
86    }
87
88    /// Enable/Disable the reorderable property
89    ///
90    /// Disabled by default
91    pub fn reorderable(mut self, value: bool) -> Self {
92        if value {
93            self.flags |= TabBarFlags::REORDERABLE;
94        } else {
95            self.flags &= !TabBarFlags::REORDERABLE;
96        }
97        self
98    }
99
100    /// Set the flags of the tab bar
101    ///
102    /// Flags are empty by default
103    pub fn flags(mut self, flags: TabBarFlags) -> Self {
104        self.flags = flags;
105        self
106    }
107
108    /// Begins the tab bar and returns a token if successful
109    pub fn begin(self, ui: &Ui) -> Option<TabBarToken<'_>> {
110        ui.tab_bar_with_flags(self.id, self.flags)
111    }
112
113    /// Creates a tab bar and runs a closure to construct the contents.
114    /// Returns the result of the closure, if it is called.
115    ///
116    /// Note: the closure is not called if no tabbar content is visible
117    pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
118        self.begin(ui).map(|_tab| f())
119    }
120}
121
122/// Token representing an active tab bar
123#[derive(Debug)]
124#[must_use]
125pub struct TabBarToken<'ui> {
126    ui: &'ui Ui,
127}
128
129impl<'ui> TabBarToken<'ui> {
130    /// Creates a new tab bar token
131    pub(crate) fn new(ui: &'ui Ui) -> Self {
132        Self { ui }
133    }
134
135    /// Ends the tab bar
136    pub fn end(self) {
137        // Token is consumed, destructor will be called
138    }
139}
140
141impl<'ui> Drop for TabBarToken<'ui> {
142    fn drop(&mut self) {
143        unsafe {
144            sys::igEndTabBar();
145        }
146    }
147}
148
149/// Builder for a tab item
150#[derive(Debug)]
151#[must_use]
152pub struct TabItem<'a, T> {
153    label: T,
154    opened: Option<&'a mut bool>,
155    flags: TabItemFlags,
156}
157
158impl<'a, T: AsRef<str>> TabItem<'a, T> {
159    /// Creates a new tab item builder
160    #[doc(alias = "BeginTabItem")]
161    pub fn new(label: T) -> Self {
162        Self {
163            label,
164            opened: None,
165            flags: TabItemFlags::NONE,
166        }
167    }
168
169    /// Will open or close the tab.
170    ///
171    /// True to display the tab. Tab item is visible by default.
172    pub fn opened(mut self, opened: &'a mut bool) -> Self {
173        self.opened = Some(opened);
174        self
175    }
176
177    /// Set the flags of the tab item.
178    ///
179    /// Flags are empty by default
180    pub fn flags(mut self, flags: TabItemFlags) -> Self {
181        self.flags = flags;
182        self
183    }
184
185    /// Begins the tab item and returns a token if successful
186    pub fn begin(self, ui: &Ui) -> Option<TabItemToken<'_>> {
187        ui.tab_item_with_flags(self.label, self.opened, self.flags)
188    }
189
190    /// Creates a tab item and runs a closure to construct the contents.
191    /// Returns the result of the closure, if it is called.
192    ///
193    /// Note: the closure is not called if the tab item is not selected
194    pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
195        self.begin(ui).map(|_tab| f())
196    }
197}
198
199/// Token representing an active tab item
200#[derive(Debug)]
201#[must_use]
202pub struct TabItemToken<'ui> {
203    ui: &'ui Ui,
204}
205
206impl<'ui> TabItemToken<'ui> {
207    /// Creates a new tab item token
208    pub(crate) fn new(ui: &'ui Ui) -> Self {
209        Self { ui }
210    }
211
212    /// Ends the tab item
213    pub fn end(self) {
214        // Token is consumed, destructor will be called
215    }
216}
217
218impl<'ui> Drop for TabItemToken<'ui> {
219    fn drop(&mut self) {
220        unsafe {
221            sys::igEndTabItem();
222        }
223    }
224}
225
226/// # Tab Widgets
227impl Ui {
228    /// Creates a tab bar and returns a tab bar token, allowing you to append
229    /// Tab items afterwards. This passes no flags. To pass flags explicitly,
230    /// use [tab_bar_with_flags](Self::tab_bar_with_flags).
231    #[doc(alias = "BeginTabBar")]
232    pub fn tab_bar(&self, id: impl AsRef<str>) -> Option<TabBarToken<'_>> {
233        self.tab_bar_with_flags(id, TabBarFlags::NONE)
234    }
235
236    /// Creates a tab bar and returns a tab bar token, allowing you to append
237    /// Tab items afterwards.
238    #[doc(alias = "BeginTabBar")]
239    pub fn tab_bar_with_flags(
240        &self,
241        id: impl AsRef<str>,
242        flags: TabBarFlags,
243    ) -> Option<TabBarToken<'_>> {
244        let id_ptr = self.scratch_txt(id);
245        let should_render = unsafe { sys::igBeginTabBar(id_ptr, flags.bits()) };
246
247        if should_render {
248            Some(TabBarToken::new(self))
249        } else {
250            None
251        }
252    }
253
254    /// Creates a new tab item and returns a token if its contents are visible.
255    ///
256    /// By default, this doesn't pass an opened bool nor any flags. See [tab_item_with_opened]
257    /// and [tab_item_with_flags] for more.
258    ///
259    /// [tab_item_with_opened]: Self::tab_item_with_opened
260    /// [tab_item_with_flags]: Self::tab_item_with_flags
261    #[doc(alias = "BeginTabItem")]
262    pub fn tab_item(&self, label: impl AsRef<str>) -> Option<TabItemToken<'_>> {
263        self.tab_item_with_flags(label, None, TabItemFlags::NONE)
264    }
265
266    /// Creates a new tab item and returns a token if its contents are visible.
267    ///
268    /// By default, this doesn't pass any flags. See [tab_item_with_flags] for more.
269    #[doc(alias = "BeginTabItem")]
270    pub fn tab_item_with_opened(
271        &self,
272        label: impl AsRef<str>,
273        opened: &mut bool,
274    ) -> Option<TabItemToken<'_>> {
275        self.tab_item_with_flags(label, Some(opened), TabItemFlags::NONE)
276    }
277
278    /// Creates a new tab item and returns a token if its contents are visible.
279    #[doc(alias = "BeginTabItem")]
280    pub fn tab_item_with_flags(
281        &self,
282        label: impl AsRef<str>,
283        opened: Option<&mut bool>,
284        flags: TabItemFlags,
285    ) -> Option<TabItemToken<'_>> {
286        let label_ptr = self.scratch_txt(label);
287        let opened_ptr = opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut());
288
289        let should_render = unsafe { sys::igBeginTabItem(label_ptr, opened_ptr, flags.bits()) };
290
291        if should_render {
292            Some(TabItemToken::new(self))
293        } else {
294            None
295        }
296    }
297}