freya_components/scrollviews/
shared.rs

1use freya_core::prelude::*;
2use torin::{
3    prelude::Direction,
4    size::Size,
5};
6
7#[derive(Debug, PartialEq, Eq, Clone, Copy)]
8pub enum Axis {
9    X,
10    Y,
11}
12
13#[doc(hidden)]
14pub fn get_scroll_position_from_wheel(
15    wheel_movement: f32,
16    inner_size: f32,
17    viewport_size: f32,
18    scroll_position: f32,
19) -> i32 {
20    if viewport_size >= inner_size {
21        return 0;
22    }
23
24    let new_position = scroll_position + wheel_movement;
25
26    if new_position >= 0.0 && wheel_movement > 0.0 {
27        return 0;
28    }
29
30    if new_position <= -(inner_size - viewport_size) && wheel_movement < 0.0 {
31        return -(inner_size - viewport_size) as i32;
32    }
33
34    new_position as i32
35}
36
37#[doc(hidden)]
38pub fn get_corrected_scroll_position(
39    inner_size: f32,
40    viewport_size: f32,
41    scroll_position: f32,
42) -> f32 {
43    // Considering it was a vertical scroll view, the start would be on top and the end on bottom.
44    let overscrolled_start = scroll_position > 0.0;
45    let overscrolled_end = (-scroll_position + viewport_size) > inner_size;
46
47    if overscrolled_start {
48        0f32
49    } else if overscrolled_end {
50        if viewport_size < inner_size {
51            -(inner_size - viewport_size)
52        } else {
53            0f32
54        }
55    } else {
56        scroll_position
57    }
58}
59
60#[doc(hidden)]
61pub fn get_container_sizes(size: Size) -> (Size, Size) {
62    if size == Size::Inner {
63        (size.clone(), size)
64    } else {
65        (Size::percent(100.), Size::fill())
66    }
67}
68
69#[doc(hidden)]
70pub fn is_scrollbar_visible(
71    is_scrollbar_enabled: bool,
72    inner_size: f32,
73    viewport_size: f32,
74) -> bool {
75    if is_scrollbar_enabled {
76        viewport_size > 0. && viewport_size < inner_size
77    } else {
78        false
79    }
80}
81
82const MIN_SCROLLBAR_SIZE: f32 = 50.0;
83
84#[doc(hidden)]
85pub fn get_scrollbar_pos_and_size(
86    inner_size: f32,
87    viewport_size: f32,
88    scroll_position: f32,
89) -> (f32, f32) {
90    if viewport_size >= inner_size {
91        return (0.0, inner_size);
92    }
93
94    let viewable_ratio = viewport_size / inner_size;
95    let mut scrollbar_size = viewport_size * viewable_ratio;
96
97    if scrollbar_size < MIN_SCROLLBAR_SIZE {
98        scrollbar_size = MIN_SCROLLBAR_SIZE;
99    }
100
101    let available_scroll_range = inner_size - viewport_size;
102    let available_thumb_range = viewport_size - scrollbar_size;
103
104    let normalized_scroll = -scroll_position / available_scroll_range;
105    let scrollbar_position = normalized_scroll * available_thumb_range;
106
107    (scrollbar_position, scrollbar_size)
108}
109#[doc(hidden)]
110pub fn get_scroll_position_from_cursor(
111    cursor_position: f32,
112    inner_size: f32,
113    viewport_size: f32,
114) -> i32 {
115    if viewport_size >= inner_size {
116        return 0;
117    }
118
119    let viewable_ratio = viewport_size / inner_size;
120    let mut scrollbar_size = viewport_size * viewable_ratio;
121
122    if scrollbar_size < MIN_SCROLLBAR_SIZE {
123        scrollbar_size = MIN_SCROLLBAR_SIZE;
124    }
125
126    let available_scroll_range = inner_size - viewport_size;
127    let available_thumb_range = viewport_size - scrollbar_size;
128
129    // Clamp cursor position
130    let cursor_clamped = cursor_position.clamp(0.0, available_thumb_range);
131
132    let normalized_scroll = cursor_clamped / available_thumb_range;
133    let new_position = -(normalized_scroll * available_scroll_range);
134
135    new_position as i32
136}
137
138pub fn handle_key_event(
139    key: &Key,
140    (mut x, mut y): (f32, f32),
141    inner_height: f32,
142    inner_width: f32,
143    viewport_height: f32,
144    viewport_width: f32,
145    direction: Direction,
146) -> Option<(f32, f32)> {
147    let y_page_delta = viewport_height;
148    let y_line_delta = y_page_delta / 5.0;
149    let x_page_delta = viewport_width;
150    let x_line_delta = x_page_delta / 5.0;
151
152    // TODO(tropix126): Handle spacebar and spacebar + shift as Home and End
153
154    match key {
155        Key::ArrowUp => {
156            y = get_corrected_scroll_position(inner_height, viewport_height, y + y_line_delta)
157        }
158        Key::ArrowDown => {
159            y = get_corrected_scroll_position(inner_height, viewport_height, y - y_line_delta)
160        }
161        Key::PageUp => {
162            y = get_corrected_scroll_position(inner_height, viewport_height, y + y_line_delta)
163        }
164        Key::PageDown => {
165            y = get_corrected_scroll_position(inner_height, viewport_height, y - y_line_delta)
166        }
167        Key::ArrowLeft => {
168            x = get_corrected_scroll_position(inner_width, viewport_width, x + x_line_delta)
169        }
170        Key::ArrowRight => {
171            x = get_corrected_scroll_position(inner_width, viewport_width, x - x_line_delta)
172        }
173        Key::Home => {
174            if direction == Direction::Vertical {
175                y = 0.0;
176            } else {
177                x = 0.0;
178            }
179        }
180        Key::End => {
181            if direction == Direction::Vertical {
182                y = -inner_height;
183            } else {
184                x = -inner_width;
185            }
186        }
187        _ => return None,
188    };
189    Some((x, y))
190}