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(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(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 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(flags, Some(selection_size_i32), row_count);
109
110 scope.apply_begin_requests_indexed(storage);
111
112 for row in 0..row_count {
113 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(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 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}