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}