1use bitflags::bitflags;
2use std::borrow::Cow;
3
4use crate::sys;
5use crate::Ui;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum ComboBoxHeight {
12 Small,
14 Regular,
16 Large,
18 Largest,
20}
21
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24pub enum ComboBoxPreviewMode {
25 Label,
27 ArrowButton,
29 Full,
31}
32
33bitflags!(
34#[repr(transparent)]
36pub struct ComboBoxFlags: u32 {
37 const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft;
39 const HEIGHT_SMALL = sys::ImGuiComboFlags_HeightSmall;
41 const HEIGHT_REGULAR = sys::ImGuiComboFlags_HeightRegular;
43 const HEIGHT_LARGE = sys::ImGuiComboFlags_HeightLarge;
45 const HEIGHT_LARGEST = sys::ImGuiComboFlags_HeightLargest;
47 const NO_ARROW_BUTTON = sys::ImGuiComboFlags_NoArrowButton;
49 const NO_PREVIEW = sys::ImGuiComboFlags_NoPreview;
51}
52);
53
54#[derive(Copy, Clone, Debug)]
56#[must_use]
57pub struct ComboBox<Label, Preview = &'static str> {
58 label: Label,
59 preview_value: Option<Preview>,
60 flags: ComboBoxFlags,
61}
62
63impl<Label: AsRef<str>> ComboBox<Label> {
64 #[doc(alias = "BeginCombo")]
66 pub fn new(label: Label) -> Self {
67 ComboBox {
68 label,
69 preview_value: None,
70 flags: ComboBoxFlags::empty(),
71 }
72 }
73}
74
75impl<T: AsRef<str>, Preview: AsRef<str>> ComboBox<T, Preview> {
76 pub fn preview_value<Preview2: AsRef<str>>(
77 self,
78 preview_value: Preview2,
79 ) -> ComboBox<T, Preview2> {
80 ComboBox {
81 label: self.label,
82 preview_value: Some(preview_value),
83 flags: self.flags,
84 }
85 }
86
87 pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
89 self.flags = flags;
90 self
91 }
92
93 pub fn popup_align_left(mut self, popup_align_left: bool) -> Self {
97 self.flags
98 .set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left);
99 self
100 }
101
102 #[inline]
106 pub fn height(mut self, height: ComboBoxHeight) -> Self {
107 self.flags
108 .set(ComboBoxFlags::HEIGHT_SMALL, height == ComboBoxHeight::Small);
109 self.flags.set(
110 ComboBoxFlags::HEIGHT_REGULAR,
111 height == ComboBoxHeight::Regular,
112 );
113 self.flags
114 .set(ComboBoxFlags::HEIGHT_LARGE, height == ComboBoxHeight::Large);
115 self.flags.set(
116 ComboBoxFlags::HEIGHT_LARGEST,
117 height == ComboBoxHeight::Largest,
118 );
119 self
120 }
121
122 #[inline]
126 pub fn preview_mode(mut self, preview_mode: ComboBoxPreviewMode) -> Self {
127 self.flags.set(
128 ComboBoxFlags::NO_ARROW_BUTTON,
129 preview_mode == ComboBoxPreviewMode::Label,
130 );
131 self.flags.set(
132 ComboBoxFlags::NO_PREVIEW,
133 preview_mode == ComboBoxPreviewMode::ArrowButton,
134 );
135 self
136 }
137
138 #[must_use]
145 pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ComboBoxToken<'ui>> {
146 let should_render = unsafe {
147 if let Some(preview_value) = self.preview_value {
148 let (ptr_one, ptr_two) = ui.scratch_txt_two(self.label, preview_value);
149 sys::igBeginCombo(ptr_one, ptr_two, self.flags.bits() as i32)
150 } else {
151 let ptr_one = ui.scratch_txt(self.label);
152 sys::igBeginCombo(ptr_one, std::ptr::null(), self.flags.bits() as i32)
153 }
154 };
155 if should_render {
156 Some(ComboBoxToken::new(ui))
157 } else {
158 None
159 }
160 }
161 pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
166 self.begin(ui).map(|_combo| f())
167 }
168}
169
170create_token!(
171 pub struct ComboBoxToken<'ui>;
174
175 drop { sys::igEndCombo() }
177);
178
179impl<'ui> Ui<'ui> {
181 #[must_use]
191 #[doc(alias = "BeginCombo")]
192 pub fn begin_combo(
193 &self,
194 label: impl AsRef<str>,
195 preview_value: impl AsRef<str>,
196 ) -> Option<ComboBoxToken<'ui>> {
197 self.begin_combo_with_flags(label, preview_value, ComboBoxFlags::empty())
198 }
199
200 #[must_use]
210 #[doc(alias = "BeginCombo")]
211 pub fn begin_combo_with_flags(
212 &self,
213 label: impl AsRef<str>,
214 preview_value: impl AsRef<str>,
215 flags: ComboBoxFlags,
216 ) -> Option<ComboBoxToken<'ui>> {
217 self._begin_combo(label, Some(preview_value), flags)
218 }
219
220 #[must_use]
233 #[doc(alias = "BeginCombo")]
234 pub fn begin_combo_no_preview(&self, label: impl AsRef<str>) -> Option<ComboBoxToken<'ui>> {
235 self.begin_combo_no_preview_with_flags(label, ComboBoxFlags::empty())
236 }
237
238 #[must_use]
248 #[doc(alias = "BeginCombo")]
249 pub fn begin_combo_no_preview_with_flags(
250 &self,
251 label: impl AsRef<str>,
252 flags: ComboBoxFlags,
253 ) -> Option<ComboBoxToken<'ui>> {
254 self._begin_combo(label, Option::<&'static str>::None, flags)
255 }
256
257 fn _begin_combo(
259 &self,
260 label: impl AsRef<str>,
261 preview_value: Option<impl AsRef<str>>,
262 flags: ComboBoxFlags,
263 ) -> Option<ComboBoxToken<'ui>> {
264 let should_render = unsafe {
265 let (ptr_one, ptr_two) = self.scratch_txt_with_opt(label, preview_value);
266 sys::igBeginCombo(ptr_one, ptr_two, flags.bits() as i32)
267 };
268 if should_render {
269 Some(ComboBoxToken::new(self))
270 } else {
271 None
272 }
273 }
274 #[doc(alias = "Combo")]
276 pub fn combo<V, L>(
277 &self,
278 label: impl AsRef<str>,
279 current_item: &mut usize,
280 items: &[V],
281 label_fn: L,
282 ) -> bool
283 where
284 for<'b> L: Fn(&'b V) -> Cow<'b, str>,
285 {
286 use crate::widget::selectable::Selectable;
287 let label_fn = &label_fn;
288 let mut result = false;
289 let preview_value = items.get(*current_item).map(label_fn);
290
291 if let Some(_cb) = self._begin_combo(label, preview_value, ComboBoxFlags::empty()) {
292 for (idx, item) in items.iter().enumerate() {
293 let text = label_fn(item);
294 let selected = idx == *current_item;
295 if Selectable::new(&text).selected(selected).build(self) {
296 *current_item = idx;
297 result = true;
298 }
299 if selected {
300 self.set_item_default_focus();
301 }
302 }
303 }
304 result
305 }
306
307 #[doc(alias = "Combo")]
309 pub fn combo_simple_string(
310 &self,
311 label: impl AsRef<str>,
312 current_item: &mut usize,
313 items: &[impl AsRef<str>],
314 ) -> bool {
315 self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
316 }
317}