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