egui_selectable_table/
auto_scroll.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
use egui::{Pos2, Rect};
use std::hash::Hash;

use crate::{ColumnOperations, ColumnOrdering, SelectableTable};

/// Handles automatic scrolling when dragging items near the edges of the table's view.
///
/// The `AutoScroll` struct allows the table to automatically scroll when the user drags items
/// near the top or bottom edge of the view. It provides configurable parameters such as
/// the speed of scrolling and the distances from the edges at which scrolling is triggered.
pub struct AutoScroll {
    /// The current vertical scroll offset.
    pub scroll_offset: f32,
    /// Whether auto-scrolling is enabled or disabled.
    pub enabled: bool,
    /// The minimum distance from the top edge before auto-scrolling starts. Extra space due to the
    /// header being in the way. Default: 200.0
    pub distance_from_min: f32,
    /// The minimum distance from the bottom edge before auto-scrolling starts. Default: 120.0
    pub distance_from_max: f32,
    /// The maximum speed at which auto-scrolling occurs. Default: 30.0
    pub max_speed: f32,
}

impl Default for AutoScroll {
    fn default() -> Self {
        Self {
            scroll_offset: 0.0,
            enabled: false,
            distance_from_min: 200.0,
            distance_from_max: 120.0,
            max_speed: 30.0,
        }
    }
}

impl AutoScroll {
    /// Creates a new instance of `AutoScroll` with the option to enable or disable auto-scrolling.
    ///
    /// # Parameters:
    /// - `enabled`: Whether auto-scrolling should be enabled.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let auto_scroll = AutoScroll::new(true); // Enables auto-scrolling
    /// ```
    #[must_use]
    pub fn new(enabled: bool) -> Self {
        Self {
            enabled,
            ..Default::default()
        }
    }

    /// Sets the minimum distance from the top edge at which auto-scrolling is triggered.
    ///
    /// # Parameters:
    /// - `distance`: The distance from the top edge in pixels.
    ///
    /// # Returns:
    /// An updated instance of `AutoScroll` with the specified `distance_from_min` value.
    ///
    /// # Considerations:
    /// - Add some extra distance due to the header being in the way of selection
    ///
    /// # Example:
    /// ```rust,ignore
    /// let auto_scroll = AutoScroll::new(true).distance_from_min(100.0); // Auto-scrolls when 100 pixels from top
    /// ```
    #[must_use]
    pub const fn distance_from_min(mut self, distance: f32) -> Self {
        self.distance_from_min = distance;
        self
    }
    /// Sets the minimum distance from the bottom edge at which auto-scrolling is triggered.
    ///
    /// # Parameters:
    /// - `distance`: The distance from the bottom edge in pixels.
    ///
    /// # Returns:
    /// An updated instance of `AutoScroll` with the specified `distance_from_max` value.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let auto_scroll = AutoScroll::new(true).distance_from_max(80.0); // Auto-scrolls when 80 pixels from bottom
    /// ```
    #[must_use]
    pub const fn distance_from_max(mut self, distance: f32) -> Self {
        self.distance_from_max = distance;
        self
    }

    /// Sets the maximum scroll speed when auto-scrolling is triggered.
    ///
    /// # Parameters:
    /// - `speed`: The maximum scroll speed
    ///
    /// # Returns:
    /// An updated instance of `AutoScroll` with the specified `max_speed`.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let auto_scroll = AutoScroll::new(true).max_speed(50.0); // Sets the max scroll speed to 50.0
    /// ```
    #[must_use]
    pub const fn max_speed(mut self, speed: f32) -> Self {
        self.max_speed = speed;
        self
    }

    /// Calculate the position based on the rectangle and return the new vertical offset
    pub(crate) fn start_scroll(&mut self, max_rect: Rect, pointer: Option<Pos2>) -> Option<f32> {
        if !self.enabled {
            return None;
        }

        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;

            // Check if the pointer is within the allowed Y range
            let within_y = pointer_y >= min_y && pointer_y <= max_y;

            // Whether the mouse is above the minimum y point
            let above_y = pointer_y < min_y;
            // Whether the mouse is below the maximum y point
            let below_y = pointer_y > max_y;

            let max_speed = self.max_speed;

            // Only scroll if the pointer is outside the allowed Y range
            if !within_y {
                let distance: f32;
                let direction: f32; // -1 for upwards, 1 for downwards

                if above_y {
                    // If above, calculate distance from min_y and scroll upwards
                    distance = (min_y - pointer_y).abs();
                    direction = -1.0; // Scroll up
                } else if below_y {
                    // If below, calculate distance from max_y and scroll downwards
                    distance = (pointer_y - max_y).abs();
                    direction = 1.0; // Scroll down
                } else {
                    return None;
                }

                // Scale the speed by distance, with a cap at max_speed
                let speed_factor = max_speed * (distance / 100.0).clamp(0.1, 1.0);

                self.scroll_offset += direction * speed_factor;

                // Ensure scroll offset doesn't go negative
                if self.scroll_offset < 0.0 {
                    self.scroll_offset = 0.0;
                }

                return Some(self.scroll_offset);
            }
        }
        None
    }
}

/// Enables or configures auto-scrolling behavior in the table view.
impl<Row, F, Conf> SelectableTable<Row, F, Conf>
where
    Row: Clone + Send + Sync,
    F: Eq
        + Hash
        + Clone
        + Ord
        + Send
        + Sync
        + Default
        + ColumnOperations<Row, F, Conf>
        + ColumnOrdering<Row>,
    Conf: Default,
{
    pub(crate) fn update_scroll_offset(&mut self, offset: f32) {
        self.auto_scroll.scroll_offset = offset;
    }

    /// Enables auto-scrolling when dragging near the edges of the view.
    ///
    /// # Returns:
    /// An updated instance of the table with auto-scrolling enabled.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let table = SelectableTable::new(vec![col1, col2, col3]).auto_scroll()
    /// ```
    #[must_use]
    pub const fn auto_scroll(mut self) -> Self {
        self.auto_scroll.enabled = true;
        self
    }
    /// Sets the maximum scroll speed for auto-scrolling.
    ///
    /// # Parameters:
    /// - `speed`: The maximum scroll speed (in pixels per frame) when auto-scrolling is active.
    ///
    /// # Returns:
    /// An updated instance of the table with the new scroll speed.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let table = SelectableTable::new(vec![col1, col2, col3])
    ///     .auto_scroll().scroll_speed(50.0);
    /// ```
    #[must_use]
    pub const fn scroll_speed(mut self, speed: f32) -> Self {
        self.auto_scroll.max_speed = speed;
        self
    }

    /// Configures the auto-scrolling behavior by providing a new `AutoScroll` instance.
    ///
    /// # Parameters:
    /// - `scroll`: A custom `AutoScroll` instance with defined scroll behavior.
    ///
    /// # Returns:
    /// An updated instance of the table with the provided `AutoScroll` configuration.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let scroll_settings = AutoScroll::new(true).max_speed(50.0);
    /// let table = SelectableTable::new(vec![col1, col2, col3])
    ///     .set_auto_scroll(scroll_settings);
    /// ```
    #[must_use]
    pub const fn set_auto_scroll(mut self, scroll: AutoScroll) -> Self {
        self.auto_scroll = scroll;
        self
    }
    /// Updates the table's auto-scrolling settings with a new `AutoScroll` instance.
    ///
    /// # Parameters:
    /// - `scroll`: The new `AutoScroll` settings to apply.
    ///
    /// This method is used when you need to change the auto-scroll behavior at runtime.
    ///
    /// # Example:
    /// ```rust,ignore
    /// let new_scroll_settings = AutoScroll::new(true).max_speed(60.0);
    /// table.update_auto_scroll(new_scroll_settings); // Update the auto-scroll settings during runtime
    /// ```
    pub fn update_auto_scroll(&mut self, scroll: AutoScroll) {
        self.auto_scroll = scroll;
    }
}