dear_imgui_rs/widget/
combo.rs1use std::borrow::Cow;
7
8use crate::sys;
9use crate::ui::Ui;
10use crate::widget::{ComboBoxFlags, ComboBoxHeight, ComboBoxOptions, ComboBoxPreviewMode};
11
12impl Ui {
14 #[must_use]
21 #[doc(alias = "BeginCombo")]
22 pub fn begin_combo(
23 &self,
24 label: impl AsRef<str>,
25 preview_value: impl AsRef<str>,
26 ) -> Option<ComboBoxToken<'_>> {
27 self.begin_combo_with_flags(label, preview_value, ComboBoxFlags::NONE)
28 }
29
30 #[must_use]
37 #[doc(alias = "BeginCombo")]
38 pub fn begin_combo_with_flags(
39 &self,
40 label: impl AsRef<str>,
41 preview_value: impl AsRef<str>,
42 flags: impl Into<ComboBoxOptions>,
43 ) -> Option<ComboBoxToken<'_>> {
44 let options = flags.into();
45 let (label_ptr, preview_ptr) = self.scratch_txt_two(label, preview_value);
46
47 let should_render = unsafe { sys::igBeginCombo(label_ptr, preview_ptr, options.raw()) };
48
49 if should_render {
50 Some(ComboBoxToken::new(self))
51 } else {
52 None
53 }
54 }
55
56 #[must_use]
63 #[doc(alias = "BeginCombo")]
64 pub fn begin_combo_no_preview(&self, label: impl AsRef<str>) -> Option<ComboBoxToken<'_>> {
65 self.begin_combo_no_preview_with_flags(label, ComboBoxFlags::NONE)
66 }
67
68 #[must_use]
75 #[doc(alias = "BeginCombo")]
76 pub fn begin_combo_no_preview_with_flags(
77 &self,
78 label: impl AsRef<str>,
79 flags: impl Into<ComboBoxOptions>,
80 ) -> Option<ComboBoxToken<'_>> {
81 let mut options = flags.into();
82 options.preview_mode = ComboBoxPreviewMode::NoPreview;
83 let label_ptr = self.scratch_txt(label);
84
85 let should_render =
86 unsafe { sys::igBeginCombo(label_ptr, std::ptr::null(), options.raw()) };
87
88 if should_render {
89 Some(ComboBoxToken::new(self))
90 } else {
91 None
92 }
93 }
94
95 #[doc(alias = "Combo")]
97 pub fn combo<V, L>(
98 &self,
99 label: impl AsRef<str>,
100 current_item: &mut usize,
101 items: &[V],
102 label_fn: L,
103 ) -> bool
104 where
105 for<'b> L: Fn(&'b V) -> Cow<'b, str>,
106 {
107 let label_fn = &label_fn;
108 let mut result = false;
109 let preview_value = items.get(*current_item).map(label_fn);
110
111 if let Some(combo_token) = self.begin_combo(
112 label,
113 preview_value.as_ref().map(|s| s.as_ref()).unwrap_or(""),
114 ) {
115 for (idx, item) in items.iter().enumerate() {
116 let is_selected = idx == *current_item;
117 if is_selected {
118 self.set_item_default_focus();
119 }
120
121 let clicked = self.selectable(label_fn(item).as_ref());
122
123 if clicked {
124 *current_item = idx;
125 result = true;
126 }
127 }
128 combo_token.end();
129 }
130
131 result
132 }
133
134 #[doc(alias = "Combo")]
139 pub fn combo_i32<V, L>(
140 &self,
141 label: impl AsRef<str>,
142 current_item: &mut i32,
143 items: &[V],
144 label_fn: L,
145 ) -> bool
146 where
147 for<'b> L: Fn(&'b V) -> Cow<'b, str>,
148 {
149 let label_fn = &label_fn;
150 let mut result = false;
151
152 let preview_value = if *current_item >= 0 {
153 items.get(*current_item as usize).map(|v| label_fn(v))
154 } else {
155 None
156 };
157
158 if let Some(combo_token) = self.begin_combo(
159 label,
160 preview_value.as_ref().map(|s| s.as_ref()).unwrap_or(""),
161 ) {
162 for (idx, item) in items.iter().enumerate() {
163 if idx > i32::MAX as usize {
164 break;
165 }
166 let idx_i32 = idx as i32;
167 let is_selected = idx_i32 == *current_item;
168 if is_selected {
169 self.set_item_default_focus();
170 }
171
172 let clicked = self.selectable(label_fn(item).as_ref());
173 if clicked {
174 *current_item = idx_i32;
175 result = true;
176 }
177 }
178 combo_token.end();
179 }
180
181 result
182 }
183
184 #[doc(alias = "Combo")]
186 pub fn combo_simple_string(
187 &self,
188 label: impl AsRef<str>,
189 current_item: &mut usize,
190 items: &[impl AsRef<str>],
191 ) -> bool {
192 self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
193 }
194
195 #[doc(alias = "Combo")]
197 pub fn combo_simple_string_i32(
198 &self,
199 label: impl AsRef<str>,
200 current_item: &mut i32,
201 items: &[impl AsRef<str>],
202 ) -> bool {
203 self.combo_i32(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
204 }
205
206 pub fn set_item_default_focus(&self) {
208 unsafe {
209 sys::igSetItemDefaultFocus();
210 }
211 }
212}
213
214#[derive(Clone, Debug)]
216#[must_use]
217pub struct ComboBox<'ui, Label, Preview = &'static str> {
218 pub label: Label,
219 pub preview_value: Option<Preview>,
220 pub options: ComboBoxOptions,
221 pub ui: &'ui Ui,
222}
223
224impl<'ui, Label: AsRef<str>> ComboBox<'ui, Label> {
225 pub fn preview_value<P: AsRef<str>>(self, preview: P) -> ComboBox<'ui, Label, P> {
227 ComboBox {
228 label: self.label,
229 preview_value: Some(preview),
230 options: self.options,
231 ui: self.ui,
232 }
233 }
234
235 pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
237 self.options.flags = flags;
238 self
239 }
240
241 pub fn height(mut self, height: ComboBoxHeight) -> Self {
243 self.options.height = Some(height);
244 self
245 }
246
247 pub fn preview_mode(mut self, mode: ComboBoxPreviewMode) -> Self {
249 self.options.preview_mode = mode;
250 self
251 }
252
253 #[must_use]
260 pub fn begin(self) -> Option<ComboBoxToken<'ui>> {
261 let (label_ptr, preview_ptr) = self
262 .ui
263 .scratch_txt_with_opt(self.label.as_ref(), self.preview_value.as_deref());
264
265 let should_render =
266 unsafe { sys::igBeginCombo(label_ptr, preview_ptr, self.options.raw()) };
267
268 if should_render {
269 Some(ComboBoxToken::new(self.ui))
270 } else {
271 None
272 }
273 }
274}
275
276#[must_use]
278pub struct ComboBoxToken<'ui> {
279 _ui: &'ui Ui,
280}
281
282impl<'ui> ComboBoxToken<'ui> {
283 fn new(ui: &'ui Ui) -> Self {
285 ComboBoxToken { _ui: ui }
286 }
287
288 pub fn end(self) {
290 }
292}
293
294impl<'ui> Drop for ComboBoxToken<'ui> {
295 fn drop(&mut self) {
296 unsafe {
297 sys::igEndCombo();
298 }
299 }
300}