1#![allow(
7 clippy::cast_possible_truncation,
8 clippy::cast_sign_loss,
9 clippy::as_conversions
10)]
11use crate::sys;
12use crate::ui::Ui;
13use std::ptr;
14
15bitflags::bitflags! {
16 #[repr(transparent)]
21 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22 pub struct TabBarFlags: i32 {
23 const NONE = 0;
25 const REORDERABLE = sys::ImGuiTabBarFlags_Reorderable as i32;
27 const AUTO_SELECT_NEW_TABS = sys::ImGuiTabBarFlags_AutoSelectNewTabs as i32;
29 const TAB_LIST_POPUP_BUTTON = sys::ImGuiTabBarFlags_TabListPopupButton as i32;
31 const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabBarFlags_NoCloseWithMiddleMouseButton as i32;
33 const NO_TAB_LIST_SCROLLING_BUTTONS = sys::ImGuiTabBarFlags_NoTabListScrollingButtons as i32;
35 const NO_TOOLTIP = sys::ImGuiTabBarFlags_NoTooltip as i32;
37 const DRAW_SELECTED_OVERLINE = sys::ImGuiTabBarFlags_DrawSelectedOverline as i32;
39 }
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44pub enum TabBarFittingPolicy {
45 Mixed,
47 Shrink,
49 Scroll,
51}
52
53impl TabBarFittingPolicy {
54 #[inline]
55 const fn raw(self) -> i32 {
56 match self {
57 Self::Mixed => sys::ImGuiTabBarFlags_FittingPolicyMixed as i32,
58 Self::Shrink => sys::ImGuiTabBarFlags_FittingPolicyShrink as i32,
59 Self::Scroll => sys::ImGuiTabBarFlags_FittingPolicyScroll as i32,
60 }
61 }
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
67pub struct TabBarOptions {
68 pub flags: TabBarFlags,
69 pub fitting_policy: Option<TabBarFittingPolicy>,
70}
71
72impl TabBarOptions {
73 pub const fn new() -> Self {
74 Self {
75 flags: TabBarFlags::NONE,
76 fitting_policy: None,
77 }
78 }
79
80 pub fn flags(mut self, flags: TabBarFlags) -> Self {
81 self.flags = flags;
82 self
83 }
84
85 pub fn fitting_policy(mut self, policy: TabBarFittingPolicy) -> Self {
86 self.fitting_policy = Some(policy);
87 self
88 }
89
90 pub fn bits(self) -> i32 {
91 self.raw()
92 }
93
94 #[inline]
95 pub(crate) fn raw(self) -> i32 {
96 self.flags.bits() | self.fitting_policy.map_or(0, TabBarFittingPolicy::raw)
97 }
98}
99
100impl Default for TabBarOptions {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106impl From<TabBarFlags> for TabBarOptions {
107 fn from(flags: TabBarFlags) -> Self {
108 Self::new().flags(flags)
109 }
110}
111
112bitflags::bitflags! {
113 #[repr(transparent)]
117 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
118 pub struct TabItemFlags: i32 {
119 const NONE = 0;
121 const UNSAVED_DOCUMENT = sys::ImGuiTabItemFlags_UnsavedDocument as i32;
123 const SET_SELECTED = sys::ImGuiTabItemFlags_SetSelected as i32;
125 const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabItemFlags_NoCloseWithMiddleMouseButton as i32;
127 const NO_PUSH_ID = sys::ImGuiTabItemFlags_NoPushId as i32;
129 const NO_TOOLTIP = sys::ImGuiTabItemFlags_NoTooltip as i32;
131 const NO_REORDER = sys::ImGuiTabItemFlags_NoReorder as i32;
133 }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
138pub enum TabItemPlacement {
139 Leading,
141 Trailing,
143}
144
145impl TabItemPlacement {
146 #[inline]
147 const fn raw(self) -> i32 {
148 match self {
149 Self::Leading => sys::ImGuiTabItemFlags_Leading as i32,
150 Self::Trailing => sys::ImGuiTabItemFlags_Trailing as i32,
151 }
152 }
153}
154
155#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
158pub struct TabItemOptions {
159 pub flags: TabItemFlags,
160 pub placement: Option<TabItemPlacement>,
161}
162
163impl TabItemOptions {
164 pub const fn new() -> Self {
165 Self {
166 flags: TabItemFlags::NONE,
167 placement: None,
168 }
169 }
170
171 pub fn flags(mut self, flags: TabItemFlags) -> Self {
172 self.flags = flags;
173 self
174 }
175
176 pub fn placement(mut self, placement: TabItemPlacement) -> Self {
177 self.placement = Some(placement);
178 self
179 }
180
181 pub fn bits(self) -> i32 {
182 self.raw()
183 }
184
185 #[inline]
186 pub(crate) fn raw(self) -> i32 {
187 self.flags.bits() | self.placement.map_or(0, TabItemPlacement::raw)
188 }
189}
190
191impl Default for TabItemOptions {
192 fn default() -> Self {
193 Self::new()
194 }
195}
196
197impl From<TabItemFlags> for TabItemOptions {
198 fn from(flags: TabItemFlags) -> Self {
199 Self::new().flags(flags)
200 }
201}
202
203#[derive(Debug)]
205#[must_use]
206pub struct TabBar<T> {
207 id: T,
208 options: TabBarOptions,
209}
210
211impl<T: AsRef<str>> TabBar<T> {
212 #[doc(alias = "BeginTabBar")]
214 pub fn new(id: T) -> Self {
215 Self {
216 id,
217 options: TabBarOptions::new(),
218 }
219 }
220
221 pub fn reorderable(mut self, value: bool) -> Self {
225 if value {
226 self.options.flags |= TabBarFlags::REORDERABLE;
227 } else {
228 self.options.flags &= !TabBarFlags::REORDERABLE;
229 }
230 self
231 }
232
233 pub fn flags(mut self, flags: impl Into<TabBarOptions>) -> Self {
237 self.options = flags.into();
238 self
239 }
240
241 pub fn fitting_policy(mut self, policy: TabBarFittingPolicy) -> Self {
243 self.options.fitting_policy = Some(policy);
244 self
245 }
246
247 pub fn begin(self, ui: &Ui) -> Option<TabBarToken<'_>> {
249 ui.tab_bar_with_flags(self.id, self.options)
250 }
251
252 pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
257 self.begin(ui).map(|_tab| f())
258 }
259}
260
261#[derive(Debug)]
263#[must_use]
264pub struct TabBarToken<'ui> {
265 _ui: &'ui Ui,
266}
267
268impl<'ui> TabBarToken<'ui> {
269 pub(crate) fn new(ui: &'ui Ui) -> Self {
271 Self { _ui: ui }
272 }
273
274 pub fn end(self) {
276 }
278}
279
280impl<'ui> Drop for TabBarToken<'ui> {
281 fn drop(&mut self) {
282 unsafe {
283 sys::igEndTabBar();
284 }
285 }
286}
287
288#[derive(Debug)]
290#[must_use]
291pub struct TabItem<'a, T> {
292 label: T,
293 opened: Option<&'a mut bool>,
294 options: TabItemOptions,
295}
296
297impl<'a, T: AsRef<str>> TabItem<'a, T> {
298 #[doc(alias = "BeginTabItem")]
300 pub fn new(label: T) -> Self {
301 Self {
302 label,
303 opened: None,
304 options: TabItemOptions::new(),
305 }
306 }
307
308 pub fn opened(mut self, opened: &'a mut bool) -> Self {
312 self.opened = Some(opened);
313 self
314 }
315
316 pub fn flags(mut self, flags: impl Into<TabItemOptions>) -> Self {
320 self.options = flags.into();
321 self
322 }
323
324 pub fn placement(mut self, placement: TabItemPlacement) -> Self {
326 self.options.placement = Some(placement);
327 self
328 }
329
330 pub fn begin(self, ui: &Ui) -> Option<TabItemToken<'_>> {
332 ui.tab_item_with_flags(self.label, self.opened, self.options)
333 }
334
335 pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
340 self.begin(ui).map(|_tab| f())
341 }
342}
343
344#[derive(Debug)]
346#[must_use]
347pub struct TabItemToken<'ui> {
348 _ui: &'ui Ui,
349}
350
351impl<'ui> TabItemToken<'ui> {
352 pub(crate) fn new(ui: &'ui Ui) -> Self {
354 Self { _ui: ui }
355 }
356
357 pub fn end(self) {
359 }
361}
362
363impl<'ui> Drop for TabItemToken<'ui> {
364 fn drop(&mut self) {
365 unsafe {
366 sys::igEndTabItem();
367 }
368 }
369}
370
371impl Ui {
373 #[doc(alias = "BeginTabBar")]
377 pub fn tab_bar(&self, id: impl AsRef<str>) -> Option<TabBarToken<'_>> {
378 self.tab_bar_with_flags(id, TabBarFlags::NONE)
379 }
380
381 #[doc(alias = "BeginTabBar")]
384 pub fn tab_bar_with_flags(
385 &self,
386 id: impl AsRef<str>,
387 flags: impl Into<TabBarOptions>,
388 ) -> Option<TabBarToken<'_>> {
389 let options = flags.into();
390 let id_ptr = self.scratch_txt(id);
391 let should_render = unsafe { sys::igBeginTabBar(id_ptr, options.raw()) };
392
393 if should_render {
394 Some(TabBarToken::new(self))
395 } else {
396 None
397 }
398 }
399
400 #[doc(alias = "BeginTabItem")]
408 pub fn tab_item(&self, label: impl AsRef<str>) -> Option<TabItemToken<'_>> {
409 self.tab_item_with_flags(label, None, TabItemOptions::new())
410 }
411
412 #[doc(alias = "BeginTabItem")]
416 pub fn tab_item_with_opened(
417 &self,
418 label: impl AsRef<str>,
419 opened: &mut bool,
420 ) -> Option<TabItemToken<'_>> {
421 self.tab_item_with_flags(label, Some(opened), TabItemOptions::new())
422 }
423
424 #[doc(alias = "BeginTabItem")]
426 pub fn tab_item_with_flags(
427 &self,
428 label: impl AsRef<str>,
429 opened: Option<&mut bool>,
430 flags: impl Into<TabItemOptions>,
431 ) -> Option<TabItemToken<'_>> {
432 let options = flags.into();
433 let label_ptr = self.scratch_txt(label);
434 let opened_ptr = opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut());
435
436 let should_render = unsafe { sys::igBeginTabItem(label_ptr, opened_ptr, options.raw()) };
437
438 if should_render {
439 Some(TabItemToken::new(self))
440 } else {
441 None
442 }
443 }
444
445 #[doc(alias = "TabItemButton")]
447 pub fn tab_item_button(&self, label: impl AsRef<str>) -> bool {
448 self.tab_item_button_with_flags(label, TabItemOptions::new())
449 }
450
451 #[doc(alias = "TabItemButton")]
453 pub fn tab_item_button_with_flags(
454 &self,
455 label: impl AsRef<str>,
456 flags: impl Into<TabItemOptions>,
457 ) -> bool {
458 unsafe { sys::igTabItemButton(self.scratch_txt(label), flags.into().raw()) }
459 }
460
461 #[doc(alias = "SetTabItemClosed")]
463 pub fn set_tab_item_closed(&self, tab_or_docked_window_label: impl AsRef<str>) {
464 unsafe { sys::igSetTabItemClosed(self.scratch_txt(tab_or_docked_window_label)) }
465 }
466}