Skip to main content

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}