Skip to main content

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            let min_y = max_rect.min.y + self.distance_from_min;
121            let max_y = max_rect.max.y - self.distance_from_max;
122
123            // Check if the pointer is within the allowed Y range
124            let within_y = pointer_y >= min_y && pointer_y <= max_y;
125
126            // Whether the mouse is above the minimum y point
127            let above_y = pointer_y < min_y;
128            // Whether the mouse is below the maximum y point
129            let below_y = pointer_y > max_y;
130
131            let max_speed = self.max_speed;
132
133            // Only scroll if the pointer is outside the allowed Y range
134            if !within_y {
135                let distance: f32;
136                let direction: f32; // -1 for upwards, 1 for downwards
137
138                if above_y {
139                    // If above, calculate distance from min_y and scroll upwards
140                    distance = (min_y - pointer_y).abs();
141                    direction = -1.0; // Scroll up
142                } else if below_y {
143                    // If below, calculate distance from max_y and scroll downwards
144                    distance = (pointer_y - max_y).abs();
145                    direction = 1.0; // Scroll down
146                } else {
147                    return None;
148                }
149
150                // Scale the speed by distance, with a cap at max_speed
151                let speed_factor = max_speed * (distance / 100.0).clamp(0.1, 1.0);
152
153                self.scroll_offset += direction * speed_factor;
154
155                // Ensure scroll offset doesn't go negative
156                if self.scroll_offset < 0.0 {
157                    self.scroll_offset = 0.0;
158                }
159
160                return Some(self.scroll_offset);
161            }
162        }
163        None
164    }
165}
166
167/// Enables or configures auto-scrolling behavior in the table view.
168impl<Row, F, Conf> SelectableTable<Row, F, Conf>
169where
170    Row: Clone + Send + Sync,
171    F: Eq
172        + Hash
173        + Clone
174        + Ord
175        + Send
176        + Sync
177        + Default
178        + ColumnOperations<Row, F, Conf>
179        + ColumnOrdering<Row>,
180    Conf: Default,
181{
182    pub(crate) const fn update_scroll_offset(&mut self, offset: f32) {
183        self.auto_scroll.scroll_offset = offset;
184    }
185
186    /// Enables auto-scrolling when dragging near the edges of the view.
187    ///
188    /// # Returns:
189    /// An updated instance of the table with auto-scrolling enabled.
190    ///
191    /// # Example:
192    /// ```rust,ignore
193    /// let table = SelectableTable::new(vec![col1, col2, col3]).auto_scroll()
194    /// ```
195    #[must_use]
196    pub const fn auto_scroll(mut self) -> Self {
197        self.auto_scroll.enabled = true;
198        self
199    }
200    /// Sets the maximum scroll speed for auto-scrolling.
201    ///
202    /// # Parameters:
203    /// - `speed`: The maximum scroll speed (in pixels per frame) when auto-scrolling is active.
204    ///
205    /// # Returns:
206    /// An updated instance of the table with the new scroll speed.
207    ///
208    /// # Example:
209    /// ```rust,ignore
210    /// let table = SelectableTable::new(vec![col1, col2, col3])
211    ///     .auto_scroll().scroll_speed(50.0);
212    /// ```
213    #[must_use]
214    pub const fn scroll_speed(mut self, speed: f32) -> Self {
215        self.auto_scroll.max_speed = speed;
216        self
217    }
218
219    /// Configures the auto-scrolling behavior by providing a new `AutoScroll` instance.
220    ///
221    /// # Parameters:
222    /// - `scroll`: A custom `AutoScroll` instance with defined scroll behavior.
223    ///
224    /// # Returns:
225    /// An updated instance of the table with the provided `AutoScroll` configuration.
226    ///
227    /// # Example:
228    /// ```rust,ignore
229    /// let scroll_settings = AutoScroll::new(true).max_speed(50.0);
230    /// let table = SelectableTable::new(vec![col1, col2, col3])
231    ///     .set_auto_scroll(scroll_settings);
232    /// ```
233    #[must_use]
234    pub const fn set_auto_scroll(mut self, scroll: AutoScroll) -> Self {
235        self.auto_scroll = scroll;
236        self
237    }
238    /// Updates the table's auto-scrolling settings with a new `AutoScroll` instance.
239    ///
240    /// # Parameters:
241    /// - `scroll`: The new `AutoScroll` settings to apply.
242    ///
243    /// This method is used when you need to change the auto-scroll behavior at runtime.
244    ///
245    /// # Example:
246    /// ```rust,ignore
247    /// let new_scroll_settings = AutoScroll::new(true).max_speed(60.0);
248    /// table.update_auto_scroll(new_scroll_settings); // Update the auto-scroll settings during runtime
249    /// ```
250    pub const fn update_auto_scroll(&mut self, scroll: AutoScroll) {
251        self.auto_scroll = scroll;
252    }
253}