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}