dear_imgui_rs/widget/
combo.rs1use std::borrow::Cow;
7
8use crate::sys;
9use crate::ui::Ui;
10use crate::widget::ComboBoxFlags;
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: ComboBoxFlags,
43 ) -> Option<ComboBoxToken<'_>> {
44 let (label_ptr, preview_ptr) = self.scratch_txt_two(label, preview_value);
45
46 let should_render = unsafe { sys::igBeginCombo(label_ptr, preview_ptr, flags.bits()) };
47
48 if should_render {
49 Some(ComboBoxToken::new(self))
50 } else {
51 None
52 }
53 }
54
55 #[must_use]
62 #[doc(alias = "BeginCombo")]
63 pub fn begin_combo_no_preview(&self, label: impl AsRef<str>) -> Option<ComboBoxToken<'_>> {
64 self.begin_combo_no_preview_with_flags(label, ComboBoxFlags::NONE)
65 }
66
67 #[must_use]
74 #[doc(alias = "BeginCombo")]
75 pub fn begin_combo_no_preview_with_flags(
76 &self,
77 label: impl AsRef<str>,
78 flags: ComboBoxFlags,
79 ) -> Option<ComboBoxToken<'_>> {
80 let label_ptr = self.scratch_txt(label);
81
82 let should_render = unsafe { sys::igBeginCombo(label_ptr, std::ptr::null(), flags.bits()) };
83
84 if should_render {
85 Some(ComboBoxToken::new(self))
86 } else {
87 None
88 }
89 }
90
91 #[doc(alias = "Combo")]
93 pub fn combo<V, L>(
94 &self,
95 label: impl AsRef<str>,
96 current_item: &mut usize,
97 items: &[V],
98 label_fn: L,
99 ) -> bool
100 where
101 for<'b> L: Fn(&'b V) -> Cow<'b, str>,
102 {
103 let label_fn = &label_fn;
104 let mut result = false;
105 let preview_value = items.get(*current_item).map(label_fn);
106
107 if let Some(combo_token) = self.begin_combo(
108 label,
109 preview_value.as_ref().map(|s| s.as_ref()).unwrap_or(""),
110 ) {
111 for (idx, item) in items.iter().enumerate() {
112 let is_selected = idx == *current_item;
113 if is_selected {
114 self.set_item_default_focus();
115 }
116
117 let clicked = self.selectable(label_fn(item).as_ref());
118
119 if clicked {
120 *current_item = idx;
121 result = true;
122 }
123 }
124 combo_token.end();
125 }
126
127 result
128 }
129
130 #[doc(alias = "Combo")]
135 pub fn combo_i32<V, L>(
136 &self,
137 label: impl AsRef<str>,
138 current_item: &mut i32,
139 items: &[V],
140 label_fn: L,
141 ) -> bool
142 where
143 for<'b> L: Fn(&'b V) -> Cow<'b, str>,
144 {
145 let label_fn = &label_fn;
146 let mut result = false;
147
148 let preview_value = if *current_item >= 0 {
149 items.get(*current_item as usize).map(|v| label_fn(v))
150 } else {
151 None
152 };
153
154 if let Some(combo_token) = self.begin_combo(
155 label,
156 preview_value.as_ref().map(|s| s.as_ref()).unwrap_or(""),
157 ) {
158 for (idx, item) in items.iter().enumerate() {
159 if idx > i32::MAX as usize {
160 break;
161 }
162 let idx_i32 = idx as i32;
163 let is_selected = idx_i32 == *current_item;
164 if is_selected {
165 self.set_item_default_focus();
166 }
167
168 let clicked = self.selectable(label_fn(item).as_ref());
169 if clicked {
170 *current_item = idx_i32;
171 result = true;
172 }
173 }
174 combo_token.end();
175 }
176
177 result
178 }
179
180 #[doc(alias = "Combo")]
182 pub fn combo_simple_string(
183 &self,
184 label: impl AsRef<str>,
185 current_item: &mut usize,
186 items: &[impl AsRef<str>],
187 ) -> bool {
188 self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
189 }
190
191 #[doc(alias = "Combo")]
193 pub fn combo_simple_string_i32(
194 &self,
195 label: impl AsRef<str>,
196 current_item: &mut i32,
197 items: &[impl AsRef<str>],
198 ) -> bool {
199 self.combo_i32(label, current_item, items, |s| Cow::Borrowed(s.as_ref()))
200 }
201
202 pub fn set_item_default_focus(&self) {
204 unsafe {
205 sys::igSetItemDefaultFocus();
206 }
207 }
208}
209
210#[derive(Clone, Debug)]
212#[must_use]
213pub struct ComboBox<'ui, Label, Preview = &'static str> {
214 pub label: Label,
215 pub preview_value: Option<Preview>,
216 pub flags: ComboBoxFlags,
217 pub ui: &'ui Ui,
218}
219
220impl<'ui, Label: AsRef<str>> ComboBox<'ui, Label> {
221 pub fn preview_value<P: AsRef<str>>(self, preview: P) -> ComboBox<'ui, Label, P> {
223 ComboBox {
224 label: self.label,
225 preview_value: Some(preview),
226 flags: self.flags,
227 ui: self.ui,
228 }
229 }
230
231 pub fn flags(mut self, flags: ComboBoxFlags) -> Self {
233 self.flags = flags;
234 self
235 }
236
237 #[must_use]
244 pub fn begin(self) -> Option<ComboBoxToken<'ui>> {
245 let (label_ptr, preview_ptr) = self
246 .ui
247 .scratch_txt_with_opt(self.label.as_ref(), self.preview_value.as_deref());
248
249 let should_render = unsafe { sys::igBeginCombo(label_ptr, preview_ptr, self.flags.bits()) };
250
251 if should_render {
252 Some(ComboBoxToken::new(self.ui))
253 } else {
254 None
255 }
256 }
257}
258
259#[must_use]
261pub struct ComboBoxToken<'ui> {
262 ui: &'ui Ui,
263}
264
265impl<'ui> ComboBoxToken<'ui> {
266 fn new(ui: &'ui Ui) -> Self {
268 ComboBoxToken { ui }
269 }
270
271 pub fn end(self) {
273 }
275}
276
277impl<'ui> Drop for ComboBoxToken<'ui> {
278 fn drop(&mut self) {
279 unsafe {
280 sys::igEndCombo();
281 }
282 }
283}