egui_selectable_table/row_modification.rs
1use egui::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 only the rows currently displayed in the UI.
83 ///
84 /// This provides direct access to the currently formatted rows for lightweight updates.
85 ///
86 /// # Important:
87 /// - This does **not** require calling [`recreate_rows`](#method.recreate_rows) to reflect changes in the UI.
88 /// - **Do not delete rows** from inside this closure — doing so will **cause a panic** and break internal assumptions.
89 /// 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).
90 /// - Can be used to update a row and show it immediately without having to do an expensive [`recreate_rows`](#method.recreate_rows).
91 /// You can then immediately call [`add_modify_row`](#method.add_modify_row) to do the same
92 /// update so later calls to [`recreate_rows`](#method.recreate_rows) or
93 /// [`recreate_rows_no_unselect`](#method.recreate_rows_no_unselect) does not revert the changes.
94 /// - Does not contribute toward [`auto_reload`](#method.auto_reload) count.
95 ///
96 /// # Parameters:
97 /// - A closure that provides a mutable reference to the currently formatted rows and an index map.
98 ///
99 /// # When to use:
100 /// - Use when you need to modify a data that is currently displayed without having to call
101 /// [`recreate_rows`](#method.recreate_rows).
102 /// - When you need to make a temporary change to the data displayed in the UI. To persist any
103 /// changes, use [`add_modify_row`](#method.add_modify_row).
104 ///
105 /// # How to use:
106 /// 1. Get the row ID you want to modify.
107 /// 2. Use the index map to get the index of the row in the formatted rows.
108 /// 3. Use the index to get a mutable reference to the row.
109 /// 4. Safely modify the row contents.
110 /// 5. In the next frame load, the modified data is visible immediately without any additional
111 /// calls.
112 ///
113 /// # Example:
114 /// ```rust,ignore
115 /// table.modify_shown_row(|formatted_rows, indexed_ids| {
116 /// let row_id = 0;
117 /// let target_index = indexed_ids.get(&row_id).unwrap();
118 /// let row = formatted_rows.get_mut(*target_index).unwrap();
119 /// // Safely modify row contents here
120 /// });
121 /// ```
122 pub fn modify_shown_row<Fn>(&mut self, mut rows: Fn)
123 where
124 Fn: FnMut(&mut Vec<SelectableRow<Row, F>>, &HashMap<i64, usize>),
125 {
126 rows(&mut self.formatted_rows, &self.indexed_ids);
127 }
128
129 /// Adds a new row to the bottom of the table without applying any sorting logic.
130 ///
131 /// This method inserts the row as-is at the end of the table, assigns it a unique ID, and
132 /// returns it as a `SelectableRow`. This does **not**
133 /// require calling [`recreate_rows`](#method.recreate_rows) for the row to appear in the UI.
134 ///
135 /// # Important:
136 /// - This method does not contribute toward [`auto_reload`](#method.auto_reload) count.
137 /// - Later calls to [`recreate_rows`](#method.recreate_rows) or
138 /// [`recreate_rows_no_unselect`](#method.recreate_rows_no_unselect) will sort the row again.
139 ///
140 /// # Parameters:
141 /// - `row`: The data to insert into the table.
142 ///
143 /// # Returns:
144 /// - `SelectableRow<Row, F>`: The newly added row wrapped in a `SelectableRow`.
145 ///
146 /// # Example:
147 /// ```rust,ignore
148 /// let row = Row::new(vec![cell1, cell2, cell3]);
149 /// let added_row = table.add_unsorted_row(row);
150 /// ```
151 pub fn add_unsorted_row(&mut self, row: Row) -> SelectableRow<Row, F> {
152 let selected_columns = HashSet::new();
153 let new_row = SelectableRow {
154 row_data: row,
155 id: self.last_id_used,
156 selected_columns,
157 };
158
159 self.formatted_rows.push(new_row.clone());
160 self.indexed_ids
161 .insert(new_row.id, self.formatted_rows.len() - 1);
162 self.rows.insert(new_row.id, new_row.clone());
163 self.last_id_used += 1;
164 new_row
165 }
166
167 /// Sort the rows to the current sorting order and column and save them for later reuse
168 pub(crate) fn sort_rows(&mut self) {
169 let mut row_data: Vec<SelectableRow<Row, F>> =
170 self.rows.par_iter().map(|(_, v)| v.clone()).collect();
171
172 row_data.par_sort_by(|a, b| {
173 let ordering = self.sorted_by.order_by(&a.row_data, &b.row_data);
174 match self.sort_order {
175 SortOrder::Ascending => ordering,
176 SortOrder::Descending => ordering.reverse(),
177 }
178 });
179
180 let indexed_data = row_data
181 .par_iter()
182 .enumerate()
183 .map(|(index, row)| (row.id, index))
184 .collect();
185
186 self.indexed_ids = indexed_data;
187 self.formatted_rows = row_data;
188 }
189}