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
use egui::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 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
/// });
/// ```
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;
}
}