egui_selectable_table/row_modification.rs
1use ahash::{HashMap, HashSet, HashSetExt};
2use rayon::prelude::*;
3use std::hash::Hash;
4
5use crate::{ColumnOperations, ColumnOrdering, SelectableRow, SelectableTable, SortOrder};
6
7impl<Row, F, Conf> SelectableTable<Row, F, Conf>
8where
9 Row: Clone + Send + Sync,
10 F: Eq
11 + Hash
12 + Clone
13 + Ord
14 + Send
15 + Sync
16 + Default
17 + ColumnOperations<Row, F, Conf>
18 + ColumnOrdering<Row>,
19 Conf: Default,
20{
21 /// Modify or add rows to the table. Changes are not immediately reflected in the UI.
22 /// You must call [`recreate_rows`](#method.recreate_rows) or [`recreate_rows_no_unselect`](#method.recreate_rows_no_unselect) to apply these changes visually.
23 ///
24 /// If modifying existing and visible row, consider using [`modify_shown_row`](#method.modify_shown_row)
25 /// alongside modifying through this function which will effectively mirror [`recreate_rows`](#method.recreate_rows)
26 /// without doing an expensive call.
27 ///
28 /// Bulk additions of rows is possible through this function with a single call to
29 /// [`add_modify_row`](#method.add_modify_row) at the end.
30 ///
31 /// # Parameters:
32 /// - A closure that provides a mutable reference to the rows and optionally returns a new row.
33 /// If a row is returned, it will be added to the table.
34 ///
35 /// # Auto Reload:
36 /// - Use [`auto_reload`](#method.auto_reload) to automatically refresh the UI after a specified
37 /// number of row modifications or additions.
38 ///
39 /// # Returns
40 /// * `Option<i64>` - The row id that is used internally for the new row
41 ///
42 /// # Example:
43 /// ```rust,ignore
44 /// let new_row_id = table.add_modify_row(|rows| {
45 /// let my_row = rows.get_mut(row_id).unwrap();
46 /// // modify your row as necessary
47 ///
48 /// let new_row = MyRow {
49 /// // Define your row values
50 /// };
51 /// Some(new_row) // Optionally add a new row
52 /// });
53 /// ```
54 pub fn add_modify_row<Fn>(&mut self, table: Fn) -> Option<i64>
55 where
56 Fn: FnOnce(&mut HashMap<i64, SelectableRow<Row, F>>) -> Option<Row>,
57 {
58 let new_row = table(&mut self.rows);
59
60 let mut to_return = None;
61
62 if let Some(row) = new_row {
63 let selected_columns = HashSet::new();
64 let new_row = SelectableRow {
65 row_data: row,
66 id: self.last_id_used,
67 selected_columns,
68 };
69 to_return = Some(self.last_id_used);
70 self.rows.insert(new_row.id, new_row);
71 self.last_id_used += 1;
72 }
73
74 let reload = self.auto_reload.increment_count();
75
76 if reload {
77 self.recreate_rows();
78 }
79 to_return
80 }
81
82 /// Modify a single row by its ID
83 ///
84 /// Changes go to the source-of-truth row storage. Call [`recreate_rows`](SelectableTable::recreate_rows)
85 /// afterwards to reflect the change in the UI, or rely on `auto_reload`.
86 ///
87 /// # Example:
88 /// ```rust,ignore
89 /// table.update_row(42, |row| { row.name = "new name".into(); });
90 /// ```
91 pub fn update_row(&mut self, id: i64, f: impl FnOnce(&mut Row)) {
92 self.add_modify_row(|rows| {
93 if let Some(row) = rows.get_mut(&id) {
94 f(&mut row.row_data);
95 }
96 None
97 });
98 }
99
100 /// Modify only the rows currently displayed in the UI.
101 ///
102 /// This provides direct access to the currently formatted rows for lightweight updates.
103 ///
104 /// # Important:
105 /// - This does **not** require calling [`recreate_rows`](SelectableTable::recreate_rows) to reflect changes in the UI.
106 /// - **Do not delete rows** from inside this closure.
107 /// - Changes **evaporate** on the next [`recreate_rows`](SelectableTable::recreate_rows) call.
108 /// To persist changes, also call [`update_row`](Self::update_row) or [`add_modify_row`](Self::add_modify_row).
109 /// - Does not contribute toward `auto_reload` count.
110 ///
111 /// # When to use:
112 /// - When you need a modification visible immediately without an expensive recreate.
113 ///
114 /// # Example:
115 /// ```rust,ignore
116 /// table.patch_visible_row(|formatted_rows, indexed_ids| {
117 /// let row_id = 0;
118 /// let target_index = indexed_ids.get(&row_id).unwrap();
119 /// let row = formatted_rows.get_mut(*target_index).unwrap();
120 /// row.row_data.name = "updated".into();
121 /// });
122 /// ```
123 pub fn patch_visible_row<Fn>(&mut self, mut f: Fn)
124 where
125 Fn: FnMut(&mut Vec<SelectableRow<Row, F>>, &HashMap<i64, usize>),
126 {
127 f(&mut self.formatted_rows, &self.indexed_ids);
128 }
129
130 /// Modify only the rows currently displayed in the UI.
131 ///
132 /// This provides direct access to the currently formatted rows for lightweight updates.
133 ///
134 /// # Important:
135 /// - This does **not** require calling [`recreate_rows`](#method.recreate_rows) to reflect changes in the UI.
136 /// - **Do not delete rows** from inside this closure — doing so will **cause a panic** and break internal assumptions.
137 /// To safely delete a row, use [`add_modify_row`](#method.add_modify_row) and then call [`recreate_rows`](#method.recreate_rows) or [`recreate_rows_no_unselect`](#method.recreate_rows_no_unselect).
138 /// - Can be used to update a row and show it immediately without having to do an expensive [`recreate_rows`](#method.recreate_rows).
139 /// You can then immediately call [`add_modify_row`](#method.add_modify_row) to do the same
140 /// update so later calls to [`recreate_rows`](#method.recreate_rows) or
141 /// [`recreate_rows_no_unselect`](#method.recreate_rows_no_unselect) does not revert the changes.
142 /// - Does not contribute toward [`auto_reload`](#method.auto_reload) count.
143 ///
144 /// # Parameters:
145 /// - A closure that provides a mutable reference to the currently formatted rows and an index map.
146 ///
147 /// # When to use:
148 /// - Use when you need to modify a data that is currently displayed without having to call
149 /// [`recreate_rows`](#method.recreate_rows).
150 /// - When you need to make a temporary change to the data displayed in the UI. To persist any
151 /// changes, use [`add_modify_row`](#method.add_modify_row).
152 ///
153 /// # How to use:
154 /// 1. Get the row ID you want to modify.
155 /// 2. Use the index map to get the index of the row in the formatted rows.
156 /// 3. Use the index to get a mutable reference to the row.
157 /// 4. Safely modify the row contents.
158 /// 5. In the next frame load, the modified data is visible immediately without any additional
159 /// calls.
160 ///
161 /// # Example:
162 /// ```rust,ignore
163 /// table.modify_shown_row(|formatted_rows, indexed_ids| {
164 /// let row_id = 0;
165 /// let target_index = indexed_ids.get(&row_id).unwrap();
166 /// let row = formatted_rows.get_mut(*target_index).unwrap();
167 /// // Safely modify row contents here
168 /// });
169 /// ```
170 #[deprecated = "use `patch_visible_row` instead"]
171 pub fn modify_shown_row<Fn>(&mut self, mut rows: Fn)
172 where
173 Fn: FnMut(&mut Vec<SelectableRow<Row, F>>, &HashMap<i64, usize>),
174 {
175 rows(&mut self.formatted_rows, &self.indexed_ids);
176 }
177
178 /// Adds a new row to the bottom of the table without applying any sorting logic.
179 ///
180 /// This method inserts the row as-is at the end of the table, assigns it a unique ID, and
181 /// returns it as a `SelectableRow`. This does **not**
182 /// require calling [`recreate_rows`](#method.recreate_rows) for the row to appear in the UI.
183 ///
184 /// # Important:
185 /// - This method does not contribute toward [`auto_reload`](#method.auto_reload) count.
186 /// - Later calls to [`recreate_rows`](#method.recreate_rows) or
187 /// [`recreate_rows_no_unselect`](#method.recreate_rows_no_unselect) will sort the row again.
188 ///
189 /// # Parameters:
190 /// - `row`: The data to insert into the table.
191 ///
192 /// # Returns:
193 /// - `SelectableRow<Row, F>`: The newly added row wrapped in a `SelectableRow`.
194 ///
195 /// # Example:
196 /// ```rust,ignore
197 /// let row = Row::new(vec![cell1, cell2, cell3]);
198 /// let added_row = table.add_unsorted_row(row);
199 /// ```
200 pub fn add_unsorted_row(&mut self, row: Row) -> SelectableRow<Row, F> {
201 let selected_columns = HashSet::new();
202 let new_row = SelectableRow {
203 row_data: row,
204 id: self.last_id_used,
205 selected_columns,
206 };
207
208 self.formatted_rows.push(new_row.clone());
209 self.indexed_ids
210 .insert(new_row.id, self.formatted_rows.len() - 1);
211 self.rows.insert(new_row.id, new_row.clone());
212 self.last_id_used += 1;
213 new_row
214 }
215
216 /// Sort the rows to the current sorting order and column and save them for later reuse
217 pub(crate) fn sort_rows(&mut self) {
218 let mut row_data: Vec<SelectableRow<Row, F>> =
219 self.rows.par_iter().map(|(_, v)| v.clone()).collect();
220
221 row_data.par_sort_by(|a, b| {
222 let ordering = self.sorted_by.order_by(&a.row_data, &b.row_data);
223 match self.sort_order {
224 SortOrder::Ascending => ordering,
225 SortOrder::Descending => ordering.reverse(),
226 }
227 });
228
229 let indexed_data = row_data
230 .par_iter()
231 .enumerate()
232 .map(|(index, row)| (row.id, index))
233 .collect();
234
235 self.indexed_ids = indexed_data;
236 self.formatted_rows = row_data;
237 }
238}