Skip to main content

dear_imgui_rs/widget/combo/
options.rs

1use crate::sys;
2
3bitflags::bitflags! {
4    /// Independent flags for combo box widgets.
5    ///
6    /// Mutually exclusive preview and height choices are represented by
7    /// [`ComboBoxPreviewMode`] and [`ComboBoxHeight`].
8    #[repr(transparent)]
9    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10    pub struct ComboBoxFlags: i32 {
11        /// No flags
12        const NONE = 0;
13        /// Align the popup toward the left by default
14        const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft as i32;
15    }
16}
17
18/// Height policy for combo box popups.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub enum ComboBoxHeight {
21    /// Max roughly 4 items visible.
22    Small,
23    /// Max roughly 8 items visible.
24    Regular,
25    /// Max roughly 20 items visible.
26    Large,
27    /// As many fitting items as possible.
28    Largest,
29}
30
31impl ComboBoxHeight {
32    #[inline]
33    const fn raw(self) -> i32 {
34        match self {
35            Self::Small => sys::ImGuiComboFlags_HeightSmall as i32,
36            Self::Regular => sys::ImGuiComboFlags_HeightRegular as i32,
37            Self::Large => sys::ImGuiComboFlags_HeightLarge as i32,
38            Self::Largest => sys::ImGuiComboFlags_HeightLargest as i32,
39        }
40    }
41}
42
43/// Preview/arrow layout for a combo box.
44#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
45pub enum ComboBoxPreviewMode {
46    /// Standard preview box with arrow button.
47    #[default]
48    Preview,
49    /// Standard preview box without the square arrow button.
50    PreviewNoArrowButton,
51    /// Width dynamically calculated from preview contents.
52    PreviewFit,
53    /// Fit preview width without the square arrow button.
54    PreviewFitNoArrowButton,
55    /// Display only a square arrow button.
56    NoPreview,
57}
58
59impl ComboBoxPreviewMode {
60    #[inline]
61    const fn raw(self) -> i32 {
62        match self {
63            Self::Preview => 0,
64            Self::PreviewNoArrowButton => sys::ImGuiComboFlags_NoArrowButton as i32,
65            Self::PreviewFit => sys::ImGuiComboFlags_WidthFitPreview as i32,
66            Self::PreviewFitNoArrowButton => {
67                sys::ImGuiComboFlags_WidthFitPreview as i32
68                    | sys::ImGuiComboFlags_NoArrowButton as i32
69            }
70            Self::NoPreview => sys::ImGuiComboFlags_NoPreview as i32,
71        }
72    }
73}
74
75/// Complete combo box options assembled from independent flags and exclusive
76/// mode selections.
77#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
78pub struct ComboBoxOptions {
79    pub flags: ComboBoxFlags,
80    pub height: Option<ComboBoxHeight>,
81    pub preview_mode: ComboBoxPreviewMode,
82}
83
84impl Default for ComboBoxOptions {
85    fn default() -> Self {
86        Self::new()
87    }
88}
89
90impl ComboBoxOptions {
91    pub const fn new() -> Self {
92        Self {
93            flags: ComboBoxFlags::NONE,
94            height: None,
95            preview_mode: ComboBoxPreviewMode::Preview,
96        }
97    }
98
99    pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
100        self.flags = flags;
101        self
102    }
103
104    pub fn height(mut self, height: ComboBoxHeight) -> Self {
105        self.height = Some(height);
106        self
107    }
108
109    pub fn preview_mode(mut self, mode: ComboBoxPreviewMode) -> Self {
110        self.preview_mode = mode;
111        self
112    }
113
114    pub fn bits(self) -> i32 {
115        self.raw()
116    }
117
118    #[inline]
119    pub(crate) fn raw(self) -> i32 {
120        self.flags.bits() | self.height.map_or(0, ComboBoxHeight::raw) | self.preview_mode.raw()
121    }
122
123    #[inline]
124    pub(crate) fn validate(self, caller: &str) {
125        let unsupported_flags = self.flags.bits() & !ComboBoxFlags::all().bits();
126        assert!(
127            unsupported_flags == 0,
128            "{caller} received non-independent ImGuiComboFlags bits: 0x{unsupported_flags:X}"
129        );
130        let bits = self.raw();
131        let height_mask = sys::ImGuiComboFlags_HeightMask_ as i32;
132        let no_arrow_button = sys::ImGuiComboFlags_NoArrowButton as i32;
133        let no_preview = sys::ImGuiComboFlags_NoPreview as i32;
134        let width_fit_preview = sys::ImGuiComboFlags_WidthFitPreview as i32;
135        let supported = ComboBoxFlags::all().bits() | height_mask | combo_preview_mask();
136        let unsupported = bits & !supported;
137        assert!(
138            unsupported == 0,
139            "{caller} received unsupported ImGuiComboFlags bits: 0x{unsupported:X}"
140        );
141        assert!(
142            bits & (no_arrow_button | no_preview) != (no_arrow_button | no_preview),
143            "{caller} cannot combine NO_ARROW_BUTTON with NO_PREVIEW"
144        );
145        assert!(
146            bits & width_fit_preview == 0 || bits & no_preview == 0,
147            "{caller} cannot combine WIDTH_FIT_PREVIEW with NO_PREVIEW"
148        );
149        assert!(
150            (bits & height_mask).count_ones() <= 1,
151            "{caller} accepts at most one combo height policy"
152        );
153    }
154}
155
156#[inline]
157const fn combo_preview_mask() -> i32 {
158    (sys::ImGuiComboFlags_NoArrowButton
159        | sys::ImGuiComboFlags_NoPreview
160        | sys::ImGuiComboFlags_WidthFitPreview) as i32
161}
162
163impl From<ComboBoxFlags> for ComboBoxOptions {
164    fn from(flags: ComboBoxFlags) -> Self {
165        Self::new().flags(flags)
166    }
167}