egui_selectable_table/
auto_scroll.rs

1use egui::{Pos2, Rect};
2use std::hash::Hash;
3
4use crate::{ColumnOperations, ColumnOrdering, SelectableTable};
5
6/// Handles automatic scrolling when dragging items near the edges of the table's view.
7///
8/// The `AutoScroll` struct allows the table to automatically scroll when the user drags items
9/// near the top or bottom edge of the view. It provides configurable parameters such as
10/// the speed of scrolling and the distances from the edges at which scrolling is triggered.
11pub struct AutoScroll {
12    /// The current vertical scroll offset.
13    pub scroll_offset: f32,
14    /// Whether auto-scrolling is enabled or disabled.
15    pub enabled: bool,
16    /// The minimum distance from the top edge before auto-scrolling starts. Extra space due to the
17    /// header being in the way. Default: 200.0
18    pub distance_from_min: f32,
19    /// The minimum distance from the bottom edge before auto-scrolling starts. Default: 120.0
20    pub distance_from_max: f32,
21    /// The maximum speed at which auto-scrolling occurs. Default: 30.0
22    pub max_speed: f32,
23}
24
25impl Default for AutoScroll {
26    fn default() -> Self {
27        Self {
28            scroll_offset: 0.0,
29            enabled: false,
30            distance_from_min: 200.0,
31            distance_from_max: 120.0,
32            max_speed: 30.0,
33        }
34    }
35}
36
37impl AutoScroll {
38    /// Creates a new instance of `AutoScroll` with the option to enable or disable auto-scrolling.
39    ///
40    /// # Parameters:
41    /// - `enabled`: Whether auto-scrolling should be enabled.
42    ///
43    /// # Example:
44    /// ```rust,ignore
45    /// let auto_scroll = AutoScroll::new(true); // Enables auto-scrolling
46    /// ```
47    #[must_use]
48    pub fn new(enabled: bool) -> Self {
49        Self {
50            enabled,
51            ..Default::default()
52        }
53    }
54
55    /// Sets the minimum distance from the top edge at which auto-scrolling is triggered.
56    ///
57    /// # Parameters:
58    /// - `distance`: The distance from the top edge in pixels.
59    ///
60    /// # Returns:
61    /// An updated instance of `AutoScroll` with the specified `distance_from_min` value.
62    ///
63    /// # Considerations:
64    /// - Add some extra distance due to the header being in the way of selection
65    ///
66    /// # Example:
67    /// ```rust,ignore
68    /// let auto_scroll = AutoScroll::new(true).distance_from_min(100.0); // Auto-scrolls when 100 pixels from top
69    /// ```
70    #[must_use]
71    pub const fn distance_from_min(mut self, distance: f32) -> Self {
72        self.distance_from_min = distance;
73        self
74    }
75    /// Sets the minimum distance from the bottom edge at which auto-scrolling is triggered.
76    ///
77    /// # Parameters:
78    /// - `distance`: The distance from the bottom edge in pixels.
79    ///
80    /// # Returns:
81    /// An updated instance of `AutoScroll` with the specified `distance_from_max` value.
82    ///
83    /// # Example:
84    /// ```rust,ignore
85    /// let auto_scroll = AutoScroll::new(true).distance_from_max(80.0); // Auto-scrolls when 80 pixels from bottom
86    /// ```
87    #[must_use]
88    pub const fn distance_from_max(mut self, distance: f32) -> Self {
89        self.distance_from_max = distance;
90        self
91    }
92
93    /// Sets the maximum scroll speed when auto-scrolling is triggered.
94    ///
95    /// # Parameters:
96    /// - `speed`: The maximum scroll speed
97    ///
98    /// # Returns:
99    /// An updated instance of `AutoScroll` with the specified `max_speed`.
100    ///
101    /// # Example:
102    /// ```rust,ignore
103    /// let auto_scroll = AutoScroll::new(true).max_speed(50.0); // Sets the max scroll speed to 50.0
104    /// ```
105    #[must_use]
106    pub const fn max_speed(mut self, speed: f32) -> Self {
107        self.max_speed = speed;
108        self
109    }
110
111    /// Calculate the position based on the rectangle and return the new vertical offset
112    pub(crate) fn start_scroll(&mut self, max_rect: Rect, pointer: Option<Pos2>) -> Option<f32> {
113        if !self.enabled {
114            return None;
115        }
116
117        if let Some(loc) = pointer {
118            let pointer_y = loc.y;
119
120            // Min gets a bit more space as the header is along the way
121            let min_y = max_rect.min.y + self.distance_from_min;
122            let max_y = max_rect.max.y - self.distance_from_max;
123
124            // Check if the pointer is within the allowed Y range
125            let within_y = pointer_y >= min_y && pointer_y <= max_y;
126
127            // Whether the mouse is above the minimum y point
128            let above_y = pointer_y < min_y;
129            // Whether the mouse is below the maximum y point
130            let below_y = pointer_y > max_y;
131
132            let max_speed = self.max_speed;
133
134            // Only scroll if the pointer is outside the allowed Y range
135            if !within_y {
136                let distance: f32;
137                let direction: f32; // -1 for upwards, 1 for downwards
138
139                if above_y {
140                    // If above, calculate distance from min_y and scroll upwards
141                    distance = (min_y - pointer_y).abs();
142                    direction = -1.0; // Scroll up
143                } else if below_y {
144                    // If below, calculate distance from max_y and scroll downwards
145                    distance = (pointer_y - max_y).abs();
146                    direction = 1.0; // Scroll down
147                } else {
148                    return None;
149                }
150
151                // Scale the speed by distance, with a cap at max_speed
152                let speed_factor = max_speed * (distance / 100.0).clamp(0.1, 1.0);
153
154                self.scroll_offset += direction * speed_factor;
155
156                // Ensure scroll offset doesn't go negative
157                if self.scroll_offset < 0.0 {
158                    self.scroll_offset = 0.0;
159                }
160
161                return Some(self.scroll_offset);
162            }
163        }
164        None
165    }
166}
167
168/// Enables or configures auto-scrolling behavior in the table view.
169impl<Row, F, Conf> SelectableTable<Row, F, Conf>
170where
171    Row: Clone + Send + Sync,
172    F: Eq
173        + Hash
174        + Clone
175        + Ord
176        + Send
177        + Sync
178        + Default
179        + ColumnOperations<Row, F, Conf>
180        + ColumnOrdering<Row>,
181    Conf: Default,
182{
183    pub(crate) const fn update_scroll_offset(&mut self, offset: f32) {
184        self.auto_scroll.scroll_offset = offset;
185    }
186
187    /// Enables auto-scrolling when dragging near the edges of the view.
188    ///
189    /// # Returns:
190    /// An updated instance of the table with auto-scrolling enabled.
191    ///
192    /// # Example:
193    /// ```rust,ignore
194    /// let table = SelectableTable::new(vec![col1, col2, col3]).auto_scroll()
195    /// ```
196    #[must_use]
197    pub const fn auto_scroll(mut self) -> Self {
198        self.auto_scroll.enabled = true;
199        self
200    }
201    /// Sets the maximum scroll speed for auto-scrolling.
202    ///
203    /// # Parameters:
204    /// - `speed`: The maximum scroll speed (in pixels per frame) when auto-scrolling is active.
205    ///
206    /// # Returns:
207    /// An updated instance of the table with the new scroll speed.
208    ///
209    /// # Example:
210    /// ```rust,ignore
211    /// let table = SelectableTable::new(vec![col1, col2, col3])
212    ///     .auto_scroll().scroll_speed(50.0);
213    /// ```
214    #[must_use]
215    pub const fn scroll_speed(mut self, speed: f32) -> Self {
216        self.auto_scroll.max_speed = speed;
217        self
218    }
219
220    /// Configures the auto-scrolling behavior by providing a new `AutoScroll` instance.
221    ///
222    /// # Parameters:
223    /// - `scroll`: A custom `AutoScroll` instance with defined scroll behavior.
224    ///
225    /// # Returns:
226    /// An updated instance of the table with the provided `AutoScroll` configuration.
227    ///
228    /// # Example:
229    /// ```rust,ignore
230    /// let scroll_settings = AutoScroll::new(true).max_speed(50.0);
231    /// let table = SelectableTable::new(vec![col1, col2, col3])
232    ///     .set_auto_scroll(scroll_settings);
233    /// ```
234    #[must_use]
235    pub const fn set_auto_scroll(mut self, scroll: AutoScroll) -> Self {
236        self.auto_scroll = scroll;
237        self
238    }
239    /// Updates the table's auto-scrolling settings with a new `AutoScroll` instance.
240    ///
241    /// # Parameters:
242    /// - `scroll`: The new `AutoScroll` settings to apply.
243    ///
244    /// This method is used when you need to change the auto-scroll behavior at runtime.
245    ///
246    /// # Example:
247    /// ```rust,ignore
248    /// let new_scroll_settings = AutoScroll::new(true).max_speed(60.0);
249    /// table.update_auto_scroll(new_scroll_settings); // Update the auto-scroll settings during runtime
250    /// ```
251    pub const fn update_auto_scroll(&mut self, scroll: AutoScroll) {
252        self.auto_scroll = scroll;
253    }
254}