dear_imgui/widget/list_box.rs
1use std::borrow::Cow;
2
3use crate::Ui;
4use crate::sys;
5
6/// Builder for a list box widget
7#[derive(Clone, Debug)]
8#[must_use]
9pub struct ListBox<T> {
10 label: T,
11 size: [f32; 2],
12}
13
14impl<T: AsRef<str>> ListBox<T> {
15 /// Constructs a new list box builder.
16 #[doc(alias = "ListBoxHeaderVec2", alias = "ListBoxHeaderInt")]
17 pub fn new(label: T) -> ListBox<T> {
18 ListBox {
19 label,
20 size: [0.0, 0.0],
21 }
22 }
23
24 /// Sets the list box size based on the given width and height
25 /// If width or height are 0 or smaller, a default value is calculated
26 /// Helper to calculate the size of a listbox and display a label on the right.
27 /// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty"
28 ///
29 /// Default: [0.0, 0.0], in which case the combobox calculates a sensible width and height
30 #[inline]
31 pub fn size(mut self, size: impl Into<[f32; 2]>) -> Self {
32 self.size = size.into();
33 self
34 }
35 /// Creates a list box and starts appending to it.
36 ///
37 /// Returns `Some(ListBoxToken)` if the list box is open. After content has been
38 /// rendered, the token must be ended by calling `.end()`.
39 ///
40 /// Returns `None` if the list box is not open and no content should be rendered.
41 #[must_use]
42 pub fn begin(self, ui: &Ui) -> Option<ListBoxToken<'_>> {
43 let size_vec = sys::ImVec2 {
44 x: self.size[0],
45 y: self.size[1],
46 };
47 let should_render = unsafe { sys::igBeginListBox(ui.scratch_txt(self.label), size_vec) };
48 if should_render {
49 Some(ListBoxToken::new(ui))
50 } else {
51 None
52 }
53 }
54 /// Creates a list box and runs a closure to construct the list contents.
55 /// Returns the result of the closure, if it is called.
56 ///
57 /// Note: the closure is not called if the list box is not open.
58 pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui, f: F) -> Option<R> {
59 self.begin(ui).map(|_list| f())
60 }
61}
62
63/// Tracks a list box that can be ended by calling `.end()`
64/// or by dropping
65pub struct ListBoxToken<'ui> {
66 ui: &'ui Ui,
67}
68
69impl<'ui> ListBoxToken<'ui> {
70 /// Creates a new list box token
71 pub fn new(ui: &'ui Ui) -> Self {
72 Self { ui }
73 }
74
75 /// Ends the list box
76 pub fn end(self) {
77 // The drop implementation will handle the actual ending
78 }
79}
80
81impl<'ui> Drop for ListBoxToken<'ui> {
82 fn drop(&mut self) {
83 unsafe {
84 sys::igEndListBox();
85 }
86 }
87}
88
89/// # Convenience functions
90impl<T: AsRef<str>> ListBox<T> {
91 /// Builds a simple list box for choosing from a slice of values
92 pub fn build_simple<V, L>(
93 self,
94 ui: &Ui,
95 current_item: &mut usize,
96 items: &[V],
97 label_fn: &L,
98 ) -> bool
99 where
100 for<'b> L: Fn(&'b V) -> Cow<'b, str>,
101 {
102 let mut result = false;
103 let lb = self;
104 if let Some(_cb) = lb.begin(ui) {
105 for (idx, item) in items.iter().enumerate() {
106 let text = label_fn(item);
107 let selected = idx == *current_item;
108 if ui.selectable_config(&text).selected(selected).build() {
109 *current_item = idx;
110 result = true;
111 }
112 }
113 }
114 result
115 }
116}