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