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 /// # Parameters:
25 /// - `table`: A closure that takes a mutable reference to the rows and optionally returns a new row.
26 /// If a row is returned, it will be added to the table.
27 ///
28 /// # Auto Reload:
29 /// - Use [`auto_reload`](#method.auto_reload) to automatically refresh the UI after a specified
30 /// number of row modifications or additions.
31 ///
32 /// # Returns
33 /// * `Option<i64>` - The row id that is used internally for the new row
34 ///
35 /// # Example:
36 /// ```rust,ignore
37 /// let new_row_id = table.add_modify_row(|rows| {
38 /// let my_row = rows.get_mut(row_id).unwrap();
39 /// // modify your row as necessary
40 ///
41 /// let new_row = MyRow {
42 /// // Define your row values
43 /// };
44 /// Some(new_row) // Optionally add a new row
45 /// });
46 /// ```
47 pub fn add_modify_row<Fn>(&mut self, table: Fn) -> Option<i64>
48 where
49 Fn: FnOnce(&mut HashMap<i64, SelectableRow<Row, F>>) -> Option<Row>,
50 {
51 let new_row = table(&mut self.rows);
52
53 let mut to_return = None;
54
55 if let Some(row) = new_row {
56 let selected_columns = HashSet::new();
57 let new_row = SelectableRow {
58 row_data: row,
59 id: self.last_id_used,
60 selected_columns,
61 };
62 to_return = Some(self.last_id_used);
63 self.rows.insert(new_row.id, new_row);
64 self.last_id_used += 1;
65 }
66
67 let reload = self.auto_reload.increment_count();
68
69 if reload {
70 self.recreate_rows();
71 }
72 to_return
73 }
74
75 /// Modify only the rows currently displayed in the UI.
76 ///
77 /// This provides direct access to the currently formatted rows for lightweight updates.
78 ///
79 /// # Important:
80 /// - This does **not** require calling `recreate_rows` to reflect changes in the UI.
81 /// - **Do not delete rows** from inside this closure — doing so will **cause a panic** and break internal assumptions.
82 /// 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).
83 /// - Can be used alongside [`add_modify_row`](#method.add_modify_row) to show updated data immediately.
84 /// When row recreation happens, the modified data will be preserved as long as it's updated via [`add_modify_row`](#method.add_modify_row).
85 /// - Does not contribute toward [`auto_reload`](#method.auto_reload) count.
86 ///
87 /// # Parameters:
88 /// - `table`: A closure that takes a mutable reference to the currently formatted rows and an index map.
89 ///
90 /// # Example:
91 /// ```rust,ignore
92 /// table.modify_shown_row(|formatted_rows, indexed_ids| {
93 /// let row_id = 0;
94 /// let target_index = indexed_ids.get(&row_id).unwrap();
95 /// let row = formatted_rows.get_mut(*target_index).unwrap();
96 /// // Safely modify row contents here
97 /// });
98 /// ```
99 pub fn modify_shown_row<Fn>(&mut self, mut rows: Fn)
100 where
101 Fn: FnMut(&mut Vec<SelectableRow<Row, F>>, &HashMap<i64, usize>),
102 {
103 rows(&mut self.formatted_rows, &self.indexed_ids);
104 }
105
106 /// Adds a new row to the bottom of the table without applying any sorting logic.
107 ///
108 /// This method inserts the row as-is at the end of the table, assigns it a unique ID, and
109 /// returns it as a `SelectableRow`. This does **not**
110 /// require calling [`recreate_rows`](#method.recreate_rows) for the row to appear in the UI.
111 ///
112 /// # Parameters:
113 /// - `row`: The data to insert into the table.
114 ///
115 /// # Returns:
116 /// - `SelectableRow<Row, F>`: The newly added row wrapped in a `SelectableRow`.
117 ///
118 /// # Example:
119 /// ```rust,ignore
120 /// let row = Row::new(vec![cell1, cell2, cell3]);
121 /// let added_row = table.add_unsorted_row(row);
122 /// ```
123 pub fn add_unsorted_row(&mut self, row: Row) -> SelectableRow<Row, F> {
124 let selected_columns = HashSet::new();
125 let new_row = SelectableRow {
126 row_data: row,
127 id: self.last_id_used,
128 selected_columns,
129 };
130
131 self.formatted_rows.push(new_row.clone());
132 self.indexed_ids
133 .insert(new_row.id, self.formatted_rows.len() - 1);
134 self.rows.insert(new_row.id, new_row.clone());
135 self.last_id_used += 1;
136 new_row
137 }
138
139 /// Sort the rows to the current sorting order and column and save them for later reuse
140 pub(crate) fn sort_rows(&mut self) {
141 let mut row_data: Vec<SelectableRow<Row, F>> =
142 self.rows.par_iter().map(|(_, v)| v.clone()).collect();
143
144 row_data.par_sort_by(|a, b| {
145 let ordering = self.sorted_by.order_by(&a.row_data, &b.row_data);
146 match self.sort_order {
147 SortOrder::Ascending => ordering,
148 SortOrder::Descending => ordering.reverse(),
149 }
150 });
151
152 let indexed_data = row_data
153 .par_iter()
154 .enumerate()
155 .map(|(index, row)| (row.id, index))
156 .collect();
157
158 self.indexed_ids = indexed_data;
159 self.formatted_rows = row_data;
160 }
161}