#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::as_conversions
)]
use crate::sys;
use crate::ui::Ui;
use std::ptr;
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TabBarFlags: i32 {
const NONE = 0;
const REORDERABLE = sys::ImGuiTabBarFlags_Reorderable as i32;
const AUTO_SELECT_NEW_TABS = sys::ImGuiTabBarFlags_AutoSelectNewTabs as i32;
const TAB_LIST_POPUP_BUTTON = sys::ImGuiTabBarFlags_TabListPopupButton as i32;
const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabBarFlags_NoCloseWithMiddleMouseButton as i32;
const NO_TAB_LIST_SCROLLING_BUTTONS = sys::ImGuiTabBarFlags_NoTabListScrollingButtons as i32;
const NO_TOOLTIP = sys::ImGuiTabBarFlags_NoTooltip as i32;
const DRAW_SELECTED_OVERLINE = sys::ImGuiTabBarFlags_DrawSelectedOverline as i32;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TabBarFittingPolicy {
Mixed,
Shrink,
Scroll,
}
impl TabBarFittingPolicy {
#[inline]
const fn raw(self) -> i32 {
match self {
Self::Mixed => sys::ImGuiTabBarFlags_FittingPolicyMixed as i32,
Self::Shrink => sys::ImGuiTabBarFlags_FittingPolicyShrink as i32,
Self::Scroll => sys::ImGuiTabBarFlags_FittingPolicyScroll as i32,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TabBarOptions {
pub flags: TabBarFlags,
pub fitting_policy: Option<TabBarFittingPolicy>,
}
impl TabBarOptions {
pub const fn new() -> Self {
Self {
flags: TabBarFlags::NONE,
fitting_policy: None,
}
}
pub fn flags(mut self, flags: TabBarFlags) -> Self {
self.flags = flags;
self
}
pub fn fitting_policy(mut self, policy: TabBarFittingPolicy) -> Self {
self.fitting_policy = Some(policy);
self
}
pub fn bits(self) -> i32 {
self.raw()
}
#[inline]
pub(crate) fn raw(self) -> i32 {
self.flags.bits() | self.fitting_policy.map_or(0, TabBarFittingPolicy::raw)
}
#[inline]
pub(crate) fn validate(self, caller: &str) {
let unsupported_flags = self.flags.bits() & !TabBarFlags::all().bits();
assert!(
unsupported_flags == 0,
"{caller} received non-independent ImGuiTabBarFlags bits: 0x{unsupported_flags:X}"
);
let bits = self.raw();
let fitting_policy_mask = sys::ImGuiTabBarFlags_FittingPolicyMask_ as i32;
let supported = TabBarFlags::all().bits() | fitting_policy_mask;
let unsupported = bits & !supported;
assert!(
unsupported == 0,
"{caller} received unsupported ImGuiTabBarFlags bits: 0x{unsupported:X}"
);
assert!(
(bits & fitting_policy_mask).count_ones() <= 1,
"{caller} accepts at most one tab-bar fitting policy"
);
}
}
impl Default for TabBarOptions {
fn default() -> Self {
Self::new()
}
}
impl From<TabBarFlags> for TabBarOptions {
fn from(flags: TabBarFlags) -> Self {
Self::new().flags(flags)
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TabItemFlags: i32 {
const NONE = 0;
const UNSAVED_DOCUMENT = sys::ImGuiTabItemFlags_UnsavedDocument as i32;
const SET_SELECTED = sys::ImGuiTabItemFlags_SetSelected as i32;
const NO_CLOSE_WITH_MIDDLE_MOUSE_BUTTON = sys::ImGuiTabItemFlags_NoCloseWithMiddleMouseButton as i32;
const NO_PUSH_ID = sys::ImGuiTabItemFlags_NoPushId as i32;
const NO_TOOLTIP = sys::ImGuiTabItemFlags_NoTooltip as i32;
const NO_REORDER = sys::ImGuiTabItemFlags_NoReorder as i32;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TabItemPlacement {
Leading,
Trailing,
}
impl TabItemPlacement {
#[inline]
const fn raw(self) -> i32 {
match self {
Self::Leading => sys::ImGuiTabItemFlags_Leading as i32,
Self::Trailing => sys::ImGuiTabItemFlags_Trailing as i32,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TabItemOptions {
pub flags: TabItemFlags,
pub placement: Option<TabItemPlacement>,
}
impl TabItemOptions {
pub const fn new() -> Self {
Self {
flags: TabItemFlags::NONE,
placement: None,
}
}
pub fn flags(mut self, flags: TabItemFlags) -> Self {
self.flags = flags;
self
}
pub fn placement(mut self, placement: TabItemPlacement) -> Self {
self.placement = Some(placement);
self
}
pub fn bits(self) -> i32 {
self.raw()
}
#[inline]
pub(crate) fn raw(self) -> i32 {
self.flags.bits() | self.placement.map_or(0, TabItemPlacement::raw)
}
#[inline]
pub(crate) fn validate_for_tab_item(self, caller: &str) {
validate_tab_item_options(caller, self, false);
}
#[inline]
pub(crate) fn validate_for_tab_button(self, caller: &str) {
validate_tab_item_options(caller, self, true);
}
}
fn validate_tab_item_options(caller: &str, options: TabItemOptions, allow_button_bits: bool) {
let unsupported_flags = options.flags.bits() & !TabItemFlags::all().bits();
assert!(
unsupported_flags == 0,
"{caller} received non-independent ImGuiTabItemFlags bits: 0x{unsupported_flags:X}"
);
let bits = options.raw();
let mut supported = TabItemFlags::all().bits()
| (sys::ImGuiTabItemFlags_Leading as i32)
| (sys::ImGuiTabItemFlags_Trailing as i32);
if allow_button_bits {
supported |= sys::ImGuiTabItemFlags_Button as i32;
}
let unsupported = bits & !supported;
assert!(
unsupported == 0,
"{caller} received unsupported ImGuiTabItemFlags bits: 0x{unsupported:X}"
);
let placement_mask = (sys::ImGuiTabItemFlags_Leading | sys::ImGuiTabItemFlags_Trailing) as i32;
assert!(
bits & placement_mask != placement_mask,
"{caller} cannot combine LEADING with TRAILING"
);
if !allow_button_bits {
assert!(
bits & (sys::ImGuiTabItemFlags_Button as i32) == 0,
"{caller} cannot use BUTTON; use TabItemButton instead"
);
}
}
impl Default for TabItemOptions {
fn default() -> Self {
Self::new()
}
}
impl From<TabItemFlags> for TabItemOptions {
fn from(flags: TabItemFlags) -> Self {
Self::new().flags(flags)
}
}
#[derive(Debug)]
#[must_use]
pub struct TabBar<T> {
id: T,
options: TabBarOptions,
}
impl<T: AsRef<str>> TabBar<T> {
#[doc(alias = "BeginTabBar")]
pub fn new(id: T) -> Self {
Self {
id,
options: TabBarOptions::new(),
}
}
pub fn reorderable(mut self, value: bool) -> Self {
if value {
self.options.flags |= TabBarFlags::REORDERABLE;
} else {
self.options.flags &= !TabBarFlags::REORDERABLE;
}
self
}
pub fn flags(mut self, flags: impl Into<TabBarOptions>) -> Self {
self.options = flags.into();
self
}
pub fn fitting_policy(mut self, policy: TabBarFittingPolicy) -> Self {
self.options.fitting_policy = Some(policy);
self
}
pub fn begin(self, ui: &Ui) -> Option<TabBarToken<'_>> {
ui.tab_bar_with_flags(self.id, self.options)
}
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
self.begin(ui).map(|_tab| f())
}
}
#[derive(Debug)]
#[must_use]
pub struct TabBarToken<'ui> {
_ui: &'ui Ui,
}
impl<'ui> TabBarToken<'ui> {
pub(crate) fn new(ui: &'ui Ui) -> Self {
Self { _ui: ui }
}
pub fn end(self) {
}
}
impl<'ui> Drop for TabBarToken<'ui> {
fn drop(&mut self) {
unsafe {
sys::igEndTabBar();
}
}
}
#[derive(Debug)]
#[must_use]
pub struct TabItem<'a, T> {
label: T,
opened: Option<&'a mut bool>,
options: TabItemOptions,
}
impl<'a, T: AsRef<str>> TabItem<'a, T> {
#[doc(alias = "BeginTabItem")]
pub fn new(label: T) -> Self {
Self {
label,
opened: None,
options: TabItemOptions::new(),
}
}
pub fn opened(mut self, opened: &'a mut bool) -> Self {
self.opened = Some(opened);
self
}
pub fn flags(mut self, flags: impl Into<TabItemOptions>) -> Self {
self.options = flags.into();
self
}
pub fn placement(mut self, placement: TabItemPlacement) -> Self {
self.options.placement = Some(placement);
self
}
pub fn begin(self, ui: &Ui) -> Option<TabItemToken<'_>> {
ui.tab_item_with_flags(self.label, self.opened, self.options)
}
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
self.begin(ui).map(|_tab| f())
}
}
#[derive(Debug)]
#[must_use]
pub struct TabItemToken<'ui> {
_ui: &'ui Ui,
}
impl<'ui> TabItemToken<'ui> {
pub(crate) fn new(ui: &'ui Ui) -> Self {
Self { _ui: ui }
}
pub fn end(self) {
}
}
impl<'ui> Drop for TabItemToken<'ui> {
fn drop(&mut self) {
unsafe {
sys::igEndTabItem();
}
}
}
impl Ui {
#[doc(alias = "BeginTabBar")]
pub fn tab_bar(&self, id: impl AsRef<str>) -> Option<TabBarToken<'_>> {
self.tab_bar_with_flags(id, TabBarFlags::NONE)
}
#[doc(alias = "BeginTabBar")]
pub fn tab_bar_with_flags(
&self,
id: impl AsRef<str>,
flags: impl Into<TabBarOptions>,
) -> Option<TabBarToken<'_>> {
let options = flags.into();
options.validate("Ui::tab_bar_with_flags()");
let id_ptr = self.scratch_txt(id);
let should_render = unsafe { sys::igBeginTabBar(id_ptr, options.raw()) };
if should_render {
Some(TabBarToken::new(self))
} else {
None
}
}
#[doc(alias = "BeginTabItem")]
pub fn tab_item(&self, label: impl AsRef<str>) -> Option<TabItemToken<'_>> {
self.tab_item_with_flags(label, None, TabItemOptions::new())
}
#[doc(alias = "BeginTabItem")]
pub fn tab_item_with_opened(
&self,
label: impl AsRef<str>,
opened: &mut bool,
) -> Option<TabItemToken<'_>> {
self.tab_item_with_flags(label, Some(opened), TabItemOptions::new())
}
#[doc(alias = "BeginTabItem")]
pub fn tab_item_with_flags(
&self,
label: impl AsRef<str>,
opened: Option<&mut bool>,
flags: impl Into<TabItemOptions>,
) -> Option<TabItemToken<'_>> {
let options = flags.into();
options.validate_for_tab_item("Ui::tab_item_with_flags()");
let label_ptr = self.scratch_txt(label);
let opened_ptr = opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut());
let should_render = unsafe { sys::igBeginTabItem(label_ptr, opened_ptr, options.raw()) };
if should_render {
Some(TabItemToken::new(self))
} else {
None
}
}
#[doc(alias = "TabItemButton")]
pub fn tab_item_button(&self, label: impl AsRef<str>) -> bool {
self.tab_item_button_with_flags(label, TabItemOptions::new())
}
#[doc(alias = "TabItemButton")]
pub fn tab_item_button_with_flags(
&self,
label: impl AsRef<str>,
flags: impl Into<TabItemOptions>,
) -> bool {
let options = flags.into();
options.validate_for_tab_button("Ui::tab_item_button_with_flags()");
unsafe { sys::igTabItemButton(self.scratch_txt(label), options.raw()) }
}
#[doc(alias = "SetTabItemClosed")]
pub fn set_tab_item_closed(&self, tab_or_docked_window_label: impl AsRef<str>) {
unsafe { sys::igSetTabItemClosed(self.scratch_txt(tab_or_docked_window_label)) }
}
}