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::apply_multi_select_requests_basic;
6use super::scope::MultiSelectScope;
7use super::storage::MultiSelectIndexStorage;
8
9impl Ui {
10    /// Low-level entry point: begin a multi-select scope and return a RAII wrapper.
11    ///
12    /// This is the closest safe wrapper to the raw `BeginMultiSelect()` /
13    /// `EndMultiSelect()` pair. It does not drive any selection storage by
14    /// itself; use `begin_io()` / `end().io()` and the helper methods to
15    /// implement custom patterns.
16    pub fn begin_multi_select_raw(
17        &self,
18        flags: impl Into<MultiSelectOptions>,
19        selection_size: Option<i32>,
20        items_count: usize,
21    ) -> MultiSelectScope<'_> {
22        MultiSelectScope::new(self, flags, selection_size, items_count)
23    }
24
25    /// Multi-select helper for index-based storage.
26    ///
27    /// This wraps `BeginMultiSelect()` / `EndMultiSelect()` and applies
28    /// selection requests to an index-addressable selection container.
29    ///
30    /// Typical usage:
31    ///
32    /// ```no_run
33    /// # use dear_imgui_rs::*;
34    /// # let mut ctx = Context::create();
35    /// # let ui = ctx.frame();
36    /// let mut selected = vec![false; 128];
37    ///
38    /// ui.multi_select_indexed(&mut selected, MultiSelectOptions::new(), |ui, idx, is_selected| {
39    ///     ui.text(format!(
40    ///         "{} {}",
41    ///         if is_selected { "[x]" } else { "[ ]" },
42    ///         idx
43    ///     ));
44    /// });
45    /// ```
46    ///
47    /// Notes:
48    /// - `storage.len()` defines `items_count`.
49    /// - This helper uses the "external storage" pattern where selection is
50    ///   stored entirely on the application side.
51    /// - Per-item selection toggles can be queried via
52    ///   [`Ui::is_item_toggled_selection`].
53    pub fn multi_select_indexed<S, F>(
54        &self,
55        storage: &mut S,
56        flags: impl Into<MultiSelectOptions>,
57        mut render_item: F,
58    ) where
59        S: MultiSelectIndexStorage,
60        F: FnMut(&Ui, usize, bool),
61    {
62        let items_count = storage.len();
63        let selection_size_i32 = storage
64            .selected_count_hint()
65            .and_then(|n| i32::try_from(n).ok())
66            .unwrap_or(-1);
67
68        let mut scope = MultiSelectScope::new(self, flags, Some(selection_size_i32), items_count);
69
70        // Apply SetAll requests (if any) before submitting items.
71        scope.apply_begin_requests_indexed(storage);
72
73        // Submit items: for each index we set SelectionUserData and let user
74        // draw widgets, passing the current selection state as `is_selected`.
75        for idx in 0..items_count {
76            self.run_with_bound_context(|| unsafe {
77                sys::igSetNextItemSelectionUserData(idx as sys::ImGuiSelectionUserData);
78            });
79            let is_selected = storage.is_selected(idx);
80            render_item(self, idx, is_selected);
81        }
82
83        // End scope and apply requests generated during item submission.
84        scope.end().apply_requests_indexed(storage);
85    }
86
87    /// Multi-select helper for index-based storage inside an active table.
88    ///
89    /// This is a convenience wrapper over [`Ui::multi_select_indexed`] that
90    /// automatically advances table rows and starts each row at column 0.
91    ///
92    /// It expects to be called between `BeginTable`/`EndTable`.
93    pub fn table_multi_select_indexed<S, F>(
94        &self,
95        storage: &mut S,
96        flags: impl Into<MultiSelectOptions>,
97        mut build_row: F,
98    ) where
99        S: MultiSelectIndexStorage,
100        F: FnMut(&Ui, usize, bool),
101    {
102        let row_count = storage.len();
103        let selection_size_i32 = storage
104            .selected_count_hint()
105            .and_then(|n| i32::try_from(n).ok())
106            .unwrap_or(-1);
107
108        let mut scope = MultiSelectScope::new(self, flags, Some(selection_size_i32), row_count);
109
110        scope.apply_begin_requests_indexed(storage);
111
112        for row in 0..row_count {
113            self.run_with_bound_context(|| unsafe {
114                sys::igSetNextItemSelectionUserData(row as sys::ImGuiSelectionUserData);
115            });
116            // Start a new table row and move to first column.
117            self.table_next_row();
118            self.table_next_column();
119
120            let is_selected = storage.is_selected(row);
121            build_row(self, row, is_selected);
122        }
123
124        scope.end().apply_requests_indexed(storage);
125    }
126
127    /// Multi-select helper using [`BasicSelection`] as underlying storage.
128    ///
129    /// This variant is suitable when items are naturally identified by `ImGuiID`
130    /// (e.g. stable ids for rows or tree nodes).
131    ///
132    /// - `items_count`: number of items in the scope.
133    /// - `id_at_index`: maps `[0, items_count)` to the corresponding item id.
134    /// - `render_item`: called once per index to emit widgets for that item.
135    pub fn multi_select_basic<G, F>(
136        &self,
137        selection: &mut BasicSelection,
138        flags: impl Into<MultiSelectOptions>,
139        items_count: usize,
140        mut id_at_index: G,
141        mut render_item: F,
142    ) where
143        G: FnMut(usize) -> crate::Id,
144        F: FnMut(&Ui, usize, crate::Id, bool),
145    {
146        let selection_size_i32 = i32::try_from(selection.len()).unwrap_or(-1);
147
148        let scope = MultiSelectScope::new(self, flags, Some(selection_size_i32), items_count);
149
150        unsafe {
151            apply_multi_select_requests_basic(
152                scope.ms_io_begin,
153                selection,
154                items_count,
155                &mut id_at_index,
156            );
157        }
158
159        for idx in 0..items_count {
160            self.run_with_bound_context(|| unsafe {
161                sys::igSetNextItemSelectionUserData(idx as sys::ImGuiSelectionUserData);
162            });
163            let id = id_at_index(idx);
164            let is_selected = selection.contains(id);
165            render_item(self, idx, id, is_selected);
166        }
167
168        scope
169            .end()
170            .apply_requests_basic(selection, &mut id_at_index);
171    }
172}