Skip to main content

dear_imgui_rs/widget/multi_select/
ui.rs

1use crate::{Ui, sys};
2
3use super::MultiSelectOptions;
4use super::basic_selection::BasicSelection;
5use super::requests::MultiSelectResult;
6use super::scope::MultiSelectScope;
7use super::storage::MultiSelectIndexStorage;
8
9impl Ui {
10    /// Run an advanced multi-select block and return an owned copy of its final requests.
11    ///
12    /// The scope exposes only operations that are valid between `BeginMultiSelect()` and
13    /// `EndMultiSelect()`. `EndMultiSelect()` runs exactly once even if `render` panics, and the
14    /// returned [`MultiSelectResult`] contains no native pointers.
15    #[doc(alias = "BeginMultiSelect", alias = "EndMultiSelect")]
16    pub fn with_multi_select(
17        &self,
18        flags: impl Into<MultiSelectOptions>,
19        selection_size: Option<i32>,
20        items_count: usize,
21        render: impl FnOnce(&mut MultiSelectScope<'_>),
22    ) -> MultiSelectResult {
23        let mut scope = MultiSelectScope::new(self, flags, selection_size, items_count);
24        render(&mut scope);
25        scope.finish()
26    }
27
28    /// Multi-select helper for index-based storage.
29    ///
30    /// This wraps `BeginMultiSelect()` / `EndMultiSelect()` and applies
31    /// selection requests to an index-addressable selection container.
32    ///
33    /// Typical usage:
34    ///
35    /// ```no_run
36    /// # use dear_imgui_rs::*;
37    /// # let mut ctx = Context::create();
38    /// # let ui = ctx.frame();
39    /// let mut selected = vec![false; 128];
40    ///
41    /// ui.multi_select_indexed(&mut selected, MultiSelectOptions::new(), |ui, idx, is_selected| {
42    ///     ui.text(format!(
43    ///         "{} {}",
44    ///         if is_selected { "[x]" } else { "[ ]" },
45    ///         idx
46    ///     ));
47    /// });
48    /// ```
49    ///
50    /// Notes:
51    /// - `storage.len()` defines `items_count`.
52    /// - This helper uses the "external storage" pattern where selection is
53    ///   stored entirely on the application side.
54    /// - Per-item selection toggles can be queried via
55    ///   [`Ui::is_item_toggled_selection`].
56    #[doc(alias = "SetNextItemSelectionUserData")]
57    pub fn multi_select_indexed<S, F>(
58        &self,
59        storage: &mut S,
60        flags: impl Into<MultiSelectOptions>,
61        mut render_item: F,
62    ) where
63        S: MultiSelectIndexStorage,
64        F: FnMut(&Ui, usize, bool),
65    {
66        let items_count = storage.len();
67        let selection_size_i32 = storage
68            .selected_count_hint()
69            .and_then(|n| i32::try_from(n).ok())
70            .unwrap_or(-1);
71
72        let result =
73            self.with_multi_select(flags, Some(selection_size_i32), items_count, |scope| {
74                scope.apply_begin_requests_indexed(storage);
75
76                for idx in 0..items_count {
77                    scope.set_next_item_selection_user_data(idx as sys::ImGuiSelectionUserData);
78                    let is_selected = storage.is_selected(idx);
79                    render_item(self, idx, is_selected);
80                }
81            });
82        result.apply_requests_indexed(storage);
83    }
84
85    /// Multi-select helper for index-based storage inside an active table.
86    ///
87    /// This is a convenience wrapper over [`Ui::multi_select_indexed`] that
88    /// automatically advances table rows and starts each row at column 0.
89    ///
90    /// It expects to be called between `BeginTable`/`EndTable`.
91    pub fn table_multi_select_indexed<S, F>(
92        &self,
93        storage: &mut S,
94        flags: impl Into<MultiSelectOptions>,
95        mut build_row: F,
96    ) where
97        S: MultiSelectIndexStorage,
98        F: FnMut(&Ui, usize, bool),
99    {
100        let row_count = storage.len();
101        let selection_size_i32 = storage
102            .selected_count_hint()
103            .and_then(|n| i32::try_from(n).ok())
104            .unwrap_or(-1);
105
106        let result = self.with_multi_select(flags, Some(selection_size_i32), row_count, |scope| {
107            scope.apply_begin_requests_indexed(storage);
108
109            for row in 0..row_count {
110                scope.set_next_item_selection_user_data(row as sys::ImGuiSelectionUserData);
111                self.table_next_row();
112                self.table_next_column();
113
114                let is_selected = storage.is_selected(row);
115                build_row(self, row, is_selected);
116            }
117        });
118        result.apply_requests_indexed(storage);
119    }
120
121    /// Multi-select helper using [`BasicSelection`] as underlying storage.
122    ///
123    /// This variant is suitable when items are naturally identified by `ImGuiID`
124    /// (e.g. stable ids for rows or tree nodes).
125    ///
126    /// - `items_count`: number of items in the scope.
127    /// - `id_at_index`: maps `[0, items_count)` to the corresponding item id.
128    /// - `render_item`: called once per index to emit widgets for that item.
129    pub fn multi_select_basic<G, F>(
130        &self,
131        selection: &mut BasicSelection,
132        flags: impl Into<MultiSelectOptions>,
133        items_count: usize,
134        mut id_at_index: G,
135        mut render_item: F,
136    ) where
137        G: FnMut(usize) -> crate::Id,
138        F: FnMut(&Ui, usize, crate::Id, bool),
139    {
140        let selection_size_i32 = i32::try_from(selection.len()).unwrap_or(-1);
141
142        let result =
143            self.with_multi_select(flags, Some(selection_size_i32), items_count, |scope| {
144                scope.apply_begin_requests_basic(selection, &mut id_at_index);
145
146                for idx in 0..items_count {
147                    scope.set_next_item_selection_user_data(idx as sys::ImGuiSelectionUserData);
148                    let id = id_at_index(idx);
149                    let is_selected = selection.contains(id);
150                    render_item(self, idx, id, is_selected);
151                }
152            });
153        result.apply_requests_basic(selection, id_at_index);
154    }
155}