arcdps_imgui\widget/
list_box.rs

1use std::borrow::Cow;
2
3use crate::sys;
4use crate::Ui;
5
6/// Builder for a list box widget
7#[derive(Copy, Clone, Debug)]
8#[must_use]
9pub struct ListBox<T> {
10    label: T,
11    size: sys::ImVec2,
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: sys::ImVec2::zero(),
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: [f32; 2]) -> Self {
32        self.size = sys::ImVec2::new(size[0], size[1]);
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<'ui>(self, ui: &Ui<'ui>) -> Option<ListBoxToken<'ui>> {
43        let should_render = unsafe { sys::igListBoxHeader_Vec2(ui.scratch_txt(self.label), self.size) };
44        if should_render {
45            Some(ListBoxToken::new(ui))
46        } else {
47            None
48        }
49    }
50    /// Creates a list box and runs a closure to construct the list contents.
51    /// Returns the result of the closure, if it is called.
52    ///
53    /// Note: the closure is not called if the list box is not open.
54    pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
55        self.begin(ui).map(|_list| f())
56    }
57}
58
59create_token!(
60    /// Tracks a list box that can be ended by calling `.end()`
61    /// or by dropping
62    pub struct ListBoxToken<'ui>;
63
64    /// Ends a list box
65    drop { sys::igListBoxFooter() }
66);
67
68/// # Convenience functions
69impl<T: AsRef<str>> ListBox<T> {
70    /// Builds a simple list box for choosing from a slice of values
71    pub fn build_simple<V, L>(
72        self,
73        ui: &Ui<'_>,
74        current_item: &mut usize,
75        items: &[V],
76        label_fn: &L,
77    ) -> bool
78    where
79        for<'b> L: Fn(&'b V) -> Cow<'b, str>,
80    {
81        use crate::widget::selectable::Selectable;
82        let mut result = false;
83        let lb = self;
84        if let Some(_cb) = lb.begin(ui) {
85            for (idx, item) in items.iter().enumerate() {
86                let text = label_fn(item);
87                let selected = idx == *current_item;
88                if Selectable::new(&text).selected(selected).build(ui) {
89                    *current_item = idx;
90                    result = true;
91                }
92            }
93        }
94        result
95    }
96}