1use crate::makepad_draw::*;
2
3live_design!{
4 DrawScrollBar= {{DrawScrollBar}} {}
5 ScrollBarBase= {{ScrollBar}} {}
6}
7
8#[derive(Live, LiveHook)]
9pub struct ScrollBar {
10 #[live] draw_bar: DrawScrollBar,
11 #[live] pub bar_size: f64,
12 #[live] pub min_handle_size: f64, #[live] bar_side_margin: f64,
14 #[live(Axis::Horizontal)] pub axis: Axis,
15
16 #[live] use_vertical_finger_scroll: bool,
17 #[live] smoothing: Option<f64>,
18
19 #[animator] animator: Animator,
20
21 #[rust] next_frame: NextFrame,
22 #[rust(false)] visible: bool,
23 #[rust] view_total: f64, #[rust] view_visible: f64, #[rust] scroll_size: f64, #[rust] scroll_pos: f64, #[rust] scroll_target: f64,
29 #[rust] scroll_delta: f64,
30 #[rust] drag_point: Option<f64>, }
32
33#[derive(Live, LiveHook)]
34#[repr(C)]
35pub struct DrawScrollBar {
36 #[deref] draw_super: DrawQuad,
37 #[live] is_vertical: f32,
38 #[live] norm_handle: f32,
39 #[live] norm_scroll: f32
40}
41
42#[derive(Clone, PartialEq, Debug)]
43pub enum ScrollBarAction {
44 None,
45 Scroll {scroll_pos: f64, view_total: f64, view_visible: f64},
46 ScrollDone
47}
48
49impl ScrollBar {
50 pub fn get_normalized_scroll_pos(&self) -> (f64, f64) {
57 let vy = self.view_visible / self.view_total;
59 if !self.visible {
60 return (0.0, 0.0);
61 }
62 let norm_handle = vy.max(self.min_handle_size / self.scroll_size);
63 let norm_scroll = (1. - norm_handle) * ((self.scroll_pos / self.view_total) / (1. - vy));
64 return (norm_scroll, norm_handle)
65 }
66
67 pub fn set_scroll_pos_from_finger(&mut self,finger: f64) -> bool {
69 let vy = self.view_visible / self.view_total;
70 let norm_handle = vy.max(self.min_handle_size / self.scroll_size);
71
72 let new_scroll_pos = (
73 (self.view_total * (1. - vy) * (finger / self.scroll_size)) / (1. - norm_handle)
74 ).max(0.).min(self.view_total - self.view_visible);
75 let changed = self.scroll_pos != new_scroll_pos;
78 self.scroll_pos = new_scroll_pos;
79 self.scroll_target = new_scroll_pos;
80 changed
81 }
82
83 pub fn update_shader_scroll_pos(&mut self, cx: &mut Cx) {
85 let (norm_scroll, _) = self.get_normalized_scroll_pos();
86 self.draw_bar.apply_over(cx, live!{
87 norm_scroll: (norm_scroll)
88 });
89 }
91
92 pub fn make_scroll_action(&mut self) -> ScrollBarAction {
94 ScrollBarAction::Scroll {
95 scroll_pos: self.scroll_pos,
96 view_total: self.view_total,
97 view_visible: self.view_visible
98 }
99 }
100
101 pub fn move_towards_scroll_target(&mut self, cx: &mut Cx) -> bool {
102 if self.smoothing.is_none() {
103 return false;
104 }
105 if (self.scroll_target - self.scroll_pos).abs() < 0.01 {
106 return false
107 }
108 if self.scroll_pos > self.scroll_target { self.scroll_pos = self.scroll_pos + (self.smoothing.unwrap() * self.scroll_delta).min(-1.);
110 if self.scroll_pos <= self.scroll_target { self.scroll_pos = self.scroll_target;
112 self.update_shader_scroll_pos(cx);
113 return false;
114 }
115 }
116 else { self.scroll_pos = self.scroll_pos + (self.smoothing.unwrap() * self.scroll_delta).max(1.);
118 if self.scroll_pos > self.scroll_target { self.scroll_pos = self.scroll_target;
120 self.update_shader_scroll_pos(cx);
121 return false;
122 }
123 }
124 self.update_shader_scroll_pos(cx);
125 true
126 }
127
128 pub fn get_scroll_pos(&self) -> f64 {
129 return self.scroll_pos;
130 }
131
132 pub fn set_scroll_pos_no_action(&mut self, cx: &mut Cx, scroll_pos: f64) -> bool {
133 let scroll_pos = scroll_pos.min(self.view_total - self.view_visible).max(0.);
134 if self.scroll_pos != scroll_pos {
135 self.scroll_pos = scroll_pos;
136 self.scroll_target = scroll_pos;
137 self.update_shader_scroll_pos(cx);
138 return true
139 };
140 return false
141 }
142 pub fn set_scroll_pos(&mut self, cx: &mut Cx, scroll_pos: f64) -> bool {
143 let scroll_pos = scroll_pos.min(self.view_total - self.view_visible).max(0.);
144 if self.scroll_pos != scroll_pos {
145 self.scroll_pos = scroll_pos;
146 self.scroll_target = scroll_pos;
147 self.update_shader_scroll_pos(cx);
148 self.next_frame = cx.new_next_frame();
149 return true
150 };
151 return false
152 }
153
154
155 pub fn set_scroll_pos_no_clip(&mut self, cx: &mut Cx, scroll_pos: f64) -> bool {
156 if self.scroll_pos != scroll_pos {
157 self.scroll_pos = scroll_pos;
158 self.scroll_target = scroll_pos;
159 self.update_shader_scroll_pos(cx);
160 self.next_frame = cx.new_next_frame();
161 return true
162 };
163 return false
164 }
165
166 pub fn get_scroll_target(&mut self) -> f64 {
167 return self.scroll_target
168 }
169
170 pub fn set_scroll_view_total(&mut self, _cx: &mut Cx, view_total: f64) {
171 self.view_total = view_total;
172 }
173
174 pub fn get_scroll_view_total(&self) -> f64 {
175 return self.view_total;
176 }
177
178 pub fn get_scroll_view_visible(&self) -> f64 {
179 return self.view_visible;
180 }
181
182
183 pub fn set_scroll_target(&mut self, cx: &mut Cx, scroll_pos_target: f64) -> bool {
184 let new_target = scroll_pos_target.min(self.view_total - self.view_visible).max(0.);
187 if self.scroll_target != new_target {
188 self.scroll_target = new_target;
189 self.scroll_delta = new_target - self.scroll_pos;
190 self.next_frame = cx.new_next_frame();
191 return true
192 };
193 return false
194 }
195
196 pub fn scroll_into_view(&mut self, cx: &mut Cx, pos: f64, size: f64, smooth: bool) {
197 if pos < self.scroll_pos { let scroll_to = pos;
199 if !smooth || self.smoothing.is_none() {
200 self.set_scroll_pos(cx, scroll_to);
201 }
202 else {
203 self.set_scroll_target(cx, scroll_to);
204 }
205 }
206 else if pos + size > self.scroll_pos + self.view_visible { let scroll_to = (pos + size) - self.view_visible;
208 if pos + size > self.view_total { self.view_total = pos + size;
210 }
211 if !smooth || self.smoothing.is_none() {
212 self.set_scroll_pos(cx, scroll_to);
213 }
214 else {
215 self.set_scroll_target(cx, scroll_to);
216 }
217 }
218 }
219
220 pub fn handle_scroll_event(&mut self, cx: &mut Cx, event: &Event, scroll_area: Area, dispatch_action: &mut dyn FnMut(&mut Cx, ScrollBarAction)) {
221 if let Event::Scroll(e) = event {
222 if scroll_area.get_rect(cx).contains(e.abs) {
223 if !match self.axis {
224 Axis::Horizontal => e.handled_x.get(),
225 Axis::Vertical => e.handled_y.get()
226 } {
227 let scroll = match self.axis {
228 Axis::Horizontal => if self.use_vertical_finger_scroll {e.scroll.y}else {e.scroll.x},
229 Axis::Vertical => e.scroll.y
230 };
231 if !self.smoothing.is_none() && e.is_mouse {
232 let scroll_pos_target = self.get_scroll_target();
233 if self.set_scroll_target(cx, scroll_pos_target + scroll) {
234 match self.axis {
235 Axis::Horizontal => e.handled_x.set(true),
236 Axis::Vertical => e.handled_y.set(true)
237 }
238 };
239 self.move_towards_scroll_target(cx); return dispatch_action(cx, self.make_scroll_action());
241 }
242 else {
243 let scroll_pos = self.get_scroll_pos();
244 if self.set_scroll_pos(cx, scroll_pos + scroll) {
245 match self.axis {
246 Axis::Horizontal => e.handled_x.set(true),
247 Axis::Vertical => e.handled_y.set(true)
248 }
249 }
250 return dispatch_action(cx, self.make_scroll_action());
251 }
252 }
253 }
254 }
255 }
256 pub fn is_area_captured(&self, cx:&Cx)->bool{
257 cx.fingers.is_area_captured(self.draw_bar.area())
258 }
259
260 pub fn handle_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, ScrollBarAction)) {
261 if self.visible {
262 self.animator_handle_event(cx, event);
263 if self.next_frame.is_event(event).is_some() {
264 if self.move_towards_scroll_target(cx) {
265 self.next_frame = cx.new_next_frame();
266 }
267 return dispatch_action(cx, self.make_scroll_action());
268 }
269
270 match event.hits(cx, self.draw_bar.area()) {
271 Hit::FingerDown(fe) => {
272 self.animator_play(cx, id!(hover.pressed));
273 let rel = fe.abs - fe.rect.pos;
274 let rel = match self.axis {
275 Axis::Horizontal => rel.x,
276 Axis::Vertical => rel.y
277 };
278 let (norm_scroll, norm_handle) = self.get_normalized_scroll_pos();
279 let bar_start = norm_scroll * self.scroll_size;
280 let bar_size = norm_handle * self.scroll_size;
281 if rel < bar_start || rel > bar_start + bar_size { self.drag_point = Some(bar_size * 0.5);
283 if self.set_scroll_pos_from_finger(rel - self.drag_point.unwrap()){
284 dispatch_action(cx, self.make_scroll_action());
285 }
286 }
287 else { self.drag_point = Some(rel - bar_start); }
290 },
291 Hit::FingerHoverIn(_) => {
292 self.animator_play(cx, id!(hover.on));
293 },
294 Hit::FingerHoverOut(_) => {
295 self.animator_play(cx, id!(hover.off));
296 },
297 Hit::FingerUp(fe) => {
298 self.drag_point = None;
299 if fe.is_over && fe.device.has_hovers() {
300 self.animator_play(cx, id!(hover.on));
301 }
302 else {
303 self.animator_play(cx, id!(hover.off));
304 }
305 return;
306 },
307 Hit::FingerMove(fe) => {
308 let rel = fe.abs - fe.rect.pos;
309 if self.drag_point.is_none() {
311 }
314 else {
315 match self.axis {
316 Axis::Horizontal => {
317 if self.set_scroll_pos_from_finger(rel.x - self.drag_point.unwrap()){
318 dispatch_action(cx, self.make_scroll_action());
319 }
320 },
321 Axis::Vertical => {
322 if self.set_scroll_pos_from_finger(rel.y - self.drag_point.unwrap()){
323 dispatch_action(cx, self.make_scroll_action());
324 }
325 }
326 }
327 }
328 },
329 _ => ()
330 };
331 }
332 }
333
334 pub fn draw_scroll_bar(&mut self, cx: &mut Cx2d, axis: Axis, view_rect: Rect, view_total: DVec2) -> f64 {
335
336 self.axis = axis;
337
338 match self.axis {
339 Axis::Horizontal => {
340 self.visible = view_total.x > view_rect.size.x + 0.1;
341 self.scroll_size = if view_total.y > view_rect.size.y + 0.1 {
342 view_rect.size.x - self.bar_size
343 }
344 else {
345 view_rect.size.x
346 } -self.bar_side_margin * 2.;
347 self.view_total = view_total.x;
348 self.view_visible = view_rect.size.x;
349 self.scroll_pos = self.scroll_pos.min(self.view_total - self.view_visible).max(0.);
350
351 if self.visible {
352 let (norm_scroll, norm_handle) = self.get_normalized_scroll_pos();
353 self.draw_bar.is_vertical = 0.0;
354 self.draw_bar.norm_scroll = norm_scroll as f32;
355 self.draw_bar.norm_handle = norm_handle as f32;
356 let scroll = cx.turtle().scroll();
357 self.draw_bar.draw_rel(
358 cx,
359 Rect {
360 pos: dvec2(self.bar_side_margin, view_rect.size.y - self.bar_size) + scroll,
361 size: dvec2(self.scroll_size, self.bar_size),
362 }
363 );
364 }
365 },
366 Axis::Vertical => {
367 self.visible = view_total.y > view_rect.size.y + 0.1;
369 self.scroll_size = if view_total.x > view_rect.size.x + 0.1 {
370 view_rect.size.y - self.bar_size
371 }
372 else {
373 view_rect.size.y
374 } -self.bar_side_margin * 2.;
375 self.view_total = view_total.y;
376 self.view_visible = view_rect.size.y;
377 self.scroll_pos = self.scroll_pos.min(self.view_total - self.view_visible).max(0.);
378 if self.visible {
379 let (norm_scroll, norm_handle) = self.get_normalized_scroll_pos();
380 self.draw_bar.is_vertical = 1.0;
381 self.draw_bar.norm_scroll = norm_scroll as f32;
382 self.draw_bar.norm_handle = norm_handle as f32;
383 let scroll = cx.turtle().scroll();
384 self.draw_bar.draw_rel(
385 cx,
386 Rect {
387 pos: dvec2(view_rect.size.x - self.bar_size, self.bar_side_margin) + scroll,
388 size: dvec2(self.bar_size, self.scroll_size)
389 }
390 );
391 }
392 }
393 }
394
395
396 let clamped_pos = self.scroll_pos.min(self.view_total - self.view_visible).max(0.);
398 if clamped_pos != self.scroll_pos {
399 self.scroll_pos = clamped_pos;
400 self.scroll_target = clamped_pos;
401 self.next_frame = cx.new_next_frame();
403 }
404
405 self.scroll_pos
406 }
407}