diff --git a/src/auto_reload.rs b/src/auto_reload.rs index 9d4f55d..66a619b 100644 --- a/src/auto_reload.rs +++ b/src/auto_reload.rs @@ -7,6 +7,7 @@ pub struct AutoReload { pub reload_after: Option, pub reload_count: u32, } + impl AutoReload { /// Increase the current reload count and return bool based on if it is equal or above the count it is /// supposed to reload at @@ -55,6 +56,13 @@ where /// occur at a rate of 1000 rows per second, a `count` between 500 and 1000 may offer the best balance between /// performance and up-to-date display. /// + /// # When to use: + /// This can be useful when streaming table data from a source and it gets updated + /// frequently. If for example 1k rows are added/modified per second, it might be a good idea + /// to set the count at 5k so it auto reloads the table every 5 seconds. This + /// allows not having to keep track of when to reload the table and reloading once at the end + /// of stream can result in the table showing the latest data. + /// /// # Example: /// ```rust,ignore /// let table = SelectableTable::new(vec![col1, col2, col3]) diff --git a/src/auto_scroll.rs b/src/auto_scroll.rs index 47c145e..e922b2f 100644 --- a/src/auto_scroll.rs +++ b/src/auto_scroll.rs @@ -117,7 +117,6 @@ impl AutoScroll { if let Some(loc) = pointer { let pointer_y = loc.y; - // Min gets a bit more space as the header is along the way let min_y = max_rect.min.y + self.distance_from_min; let max_y = max_rect.max.y - self.distance_from_max; diff --git a/src/lib.rs b/src/lib.rs index c0ab630..3893b46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,6 +246,9 @@ where add_serial_column: bool, /// The row height for the table, defaults to 25.0 row_height: f32, + /// The header height for the table, defaults to 20.0 + header_height: f32, + /// The matcher used for fuzzy searching #[cfg(feature = "fuzzy-matching")] matcher: Matcher, @@ -309,6 +312,7 @@ where config: Conf::default(), add_serial_column: false, row_height: 25.0, + header_height: 20.0, #[cfg(feature = "fuzzy-matching")] matcher: Matcher::default(), no_ctrl_a_capture: false, @@ -347,7 +351,8 @@ where self } - /// Clears all rows from the table, including the displayed ones + /// Clears all rows from the table, including the displayed ones. No need for any additional + /// call to update the UI. /// /// # Example: /// ```rust,ignore @@ -365,7 +370,8 @@ where /// /// # Parameters: /// - `ui`: The UI context where the table will be rendered. - /// - `table_builder`: A closure that receives and modifies the `TableBuilder`. + /// - A closure that provides and allows modification of `TableBuilder`. Build your own table + /// and return it. /// /// # Example: /// ```rust,ignore @@ -408,7 +414,7 @@ where } let output = table - .header(20.0, |header| { + .header(self.header_height, |header| { self.build_head(header); }) .body(|body| { @@ -437,7 +443,7 @@ where } let output = table - .header(20.0, |header| { + .header(self.header_height, |header| { self.build_head(header); }) .body(|body| { @@ -701,9 +707,9 @@ where &self.rows } - /// Adds a serial column to the table. + /// Adds a serial column UI to the table. /// - /// The serial column is automatically generated and displayed at the very left of the table. + /// The serial column UI is automatically generated and displayed at the very left of the table. /// It shows the row number (starting from 1) for each row. /// /// # Returns: @@ -755,6 +761,25 @@ where self } + /// Sets the height of the header in the table. Defaults to 20.0. + /// + /// # Parameters: + /// - `height`: The desired height for the header + /// + /// # Returns: + /// - `Self`: The modified table with the specified header height applied. + /// + /// # Example: + /// ```rust,ignore + /// let table = SelectableTable::new(vec![col1, col2, col3]) + /// .header_height(24.0); + /// ``` + #[must_use] + pub const fn header_height(mut self, height: f32) -> Self { + self.header_height = height; + self + } + /// Disables Ctrl+A keyboard shortcut capturing for selecting all rows /// /// # Returns: diff --git a/src/row_modification.rs b/src/row_modification.rs index a8b031e..d6a0784 100644 --- a/src/row_modification.rs +++ b/src/row_modification.rs @@ -21,8 +21,15 @@ where /// 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: - /// - `table`: A closure that takes a mutable reference to the rows and optionally returns a new row. + /// - 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: @@ -77,15 +84,31 @@ where /// This provides direct access to the currently formatted rows for lightweight updates. /// /// # Important: - /// - This does **not** require calling `recreate_rows` to reflect changes in the UI. + /// - 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 alongside [`add_modify_row`](#method.add_modify_row) to show updated data immediately. - /// When row recreation happens, the modified data will be preserved as long as it's updated via [`add_modify_row`](#method.add_modify_row). + /// - 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: - /// - `table`: A closure that takes a mutable reference to the currently formatted rows and an index map. + /// - 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 @@ -109,6 +132,11 @@ where /// 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. /// diff --git a/src/row_selection.rs b/src/row_selection.rs index 7a7999c..de46da5 100644 --- a/src/row_selection.rs +++ b/src/row_selection.rs @@ -1,5 +1,6 @@ use egui::Ui; use egui::ahash::{HashMap, HashMapExt, HashSet, HashSetExt}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::fmt::Write as _; use std::hash::Hash; @@ -100,7 +101,7 @@ where let drag_start = self.drag_started_on.clone().expect("Drag start not found"); - // number of the column of drag starting point and the current cell that we are trying to select + // Number of the column of drag starting point and the current cell that we are trying to select let drag_start_num = self.column_to_num(&drag_start.1); let ongoing_column_num = self.column_to_num(column_name); @@ -363,7 +364,7 @@ where /// Selects all rows and columns in the table. /// - /// After calling this method, all rows will have all columns selected. + /// After calling this method, all rows will have all columns selected and visible immediately. /// /// # Example: /// ```rust,ignore @@ -418,6 +419,44 @@ where selected_rows } + /// Retrieves the currently selected rows but in no particular order. Can be faster than + /// [`get_selected_rows`](#method.get_selected_rows) as it uses rayon for parallel processing + /// and only checks the active rows instead of every single row. + /// + /// This method returns a vector of the rows that have one or more columns selected. + /// + /// If the `select_full_row` flag is enabled, it will ensure that all columns are selected for + /// each active row. + /// + /// # Returns: + /// A `Vec` of `SelectableRow` instances that are currently selected. + /// + /// # Example: + /// ```rust,ignore + /// let selected_rows = table.get_selected_rows(); + /// ``` + pub fn get_selected_rows_unsorted(&mut self) -> Vec> { + if self.select_full_row { + self.active_columns.extend(self.all_columns.clone()); + } + + self.active_rows + .par_iter() + .map(|row_id| { + let row_index = self + .indexed_ids + .get(row_id) + .expect("Could not get id index"); + let target_row = self + .formatted_rows + .get(*row_index) + .expect("Could not get row"); + + target_row.clone() + }) + .collect() + } + /// Copies selected cells to the system clipboard in a tabular format. /// /// This method copies only the selected cells from each row to the clipboard, and ensures @@ -441,7 +480,7 @@ where // Iter through all the rows and find the rows that have at least one column as selected. // Keep track of the biggest length of a value of a column // active rows cannot be used here because hashset does not maintain an order. - // So itering will give the rows in a different order than what is shown in the ui + // So iterating will give the rows in a different order than what is shown in the ui for row in &self.formatted_rows { if row.selected_columns.is_empty() { continue;