1use crate::makepad_draw::*;
2
3live_design!{
4 link widgets;
5 use link::theme::*;
6 use makepad_draw::shader::std::*;
7
8 DrawScrollBar= {{DrawScrollBar}} {}
9
10 pub ScrollBarBase= {{ScrollBar}} {}
11
12 pub ScrollBar = <ScrollBarBase> {
13 bar_size: 10.0,
14 bar_side_margin: 3.0
15 min_handle_size: 30.0
16
17 draw_bg: {
18 instance drag: 0.0
19 instance hover: 0.0
20
21 uniform size: 6.0
22 uniform border_size: (THEME_BEVELING)
23 uniform border_radius: 1.5
24
25 uniform color: (THEME_COLOR_OUTSET)
26 uniform color_hover: (THEME_COLOR_OUTSET_HOVER)
27 uniform color_drag: (THEME_COLOR_OUTSET_DRAG)
28
29 uniform border_color: (THEME_COLOR_U_HIDDEN)
30 uniform border_color_hover: (THEME_COLOR_U_HIDDEN)
31 uniform border_color_drag: (THEME_COLOR_U_HIDDEN)
32
33 fn pixel(self) -> vec4 {
34 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
35 if self.is_vertical > 0.5 {
36 sdf.box(
37 1.,
38 self.rect_size.y * self.norm_scroll,
39 self.size,
40 self.rect_size.y * self.norm_handle,
41 self.border_radius
42 );
43 }
44 else {
45 sdf.box(
46 self.rect_size.x * self.norm_scroll,
47 1.,
48 self.rect_size.x * self.norm_handle,
49 self.size,
50 self.border_radius
51 );
52 }
53
54 sdf.fill_keep(mix(
55 self.color,
56 mix(
57 self.color_hover,
58 self.color_drag,
59 self.drag
60 ),
61 self.hover
62 ));
63
64 sdf.stroke(mix(
65 self.border_color,
66 mix(
67 self.border_color_hover,
68 self.border_color_drag,
69 self.drag
70 ),
71 self.hover
72 ), self.border_size);
73
74 return sdf.result
75 }
76 }
77
78 animator: {
79 hover = {
80 default: off
81 off = {
82 from: {all: Forward {duration: 0.1}}
83 apply: {
84 draw_bg: {drag: 0.0, hover: 0.0}
85 }
86 }
87
88 on = {
89 cursor: Default,
90 from: {
91 all: Forward {duration: 0.1}
92 drag: Forward {duration: 0.01}
93 }
94 apply: {
95 draw_bg: {
96 drag: 0.0,
97 hover: [{time: 0.0, value: 1.0}],
98 }
99 }
100 }
101
102 drag = {
103 cursor: Default,
104 from: {all: Snap}
105 apply: {
106 draw_bg: {
107 drag: 1.0,
108 hover: 1.0,
109 }
110 }
111 }
112 }
113 }
114 }
115
116 pub ScrollBarTabs = <ScrollBar> {
117 draw_bg: {
118 instance drag: 0.0
119 instance hover: 0.0
120
121 uniform size: 6.0
122 uniform border_size: 1.0
123 uniform border_radius: 1.5
124
125 uniform color: (THEME_COLOR_U_HIDDEN)
126 uniform color_hover: (THEME_COLOR_OUTSET_HOVER)
127 uniform color_drag: (THEME_COLOR_OUTSET_DRAG)
128
129 uniform border_color: (THEME_COLOR_U_HIDDEN)
130 uniform border_color_hover: (THEME_COLOR_U_HIDDEN)
131 uniform border_color_drag: (THEME_COLOR_U_HIDDEN)
132
133 fn pixel(self) -> vec4 {
134 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
135 if self.is_vertical > 0.5 {
136 sdf.box(
137 1.,
138 self.rect_size.y * self.norm_scroll,
139 self.size,
140 self.rect_size.y * self.norm_handle,
141 self.border_radius
142 );
143 }
144 else {
145 sdf.box(
146 self.rect_size.x * self.norm_scroll,
147 1.,
148 self.rect_size.x * self.norm_handle,
149 self.size,
150 self.border_radius
151 );
152 }
153
154 sdf.fill_keep(mix(
155 self.color,
156 mix(
157 self.color_hover,
158 self.color_drag,
159 self.drag
160 ),
161 self.hover
162 ));
163
164 sdf.stroke(mix(
165 self.border_color,
166 mix(
167 self.border_color_hover,
168 self.border_color_drag,
169 self.drag
170 ),
171 self.hover
172 ), self.border_size);
173
174 return sdf.result
175 }
176 }
177 }
178
179
180}
181
182#[derive(Copy, Clone, Debug, Live, LiveHook)]
183#[live_ignore]
184pub enum ScrollAxis {
185 #[pick] Horizontal,
186 Vertical
187}
188#[derive(Live, LiveHook, LiveRegister)]
189pub struct ScrollBar {
190 #[live] draw_bg: DrawScrollBar,
191 #[live] pub bar_size: f64,
192 #[live] pub min_handle_size: f64, #[live] bar_side_margin: f64,
194 #[live(ScrollAxis::Horizontal)] pub axis: ScrollAxis,
195
196 #[live] use_vertical_finger_scroll: bool,
197 #[live] smoothing: Option<f64>,
198
199 #[animator] animator: Animator,
200
201 #[rust] next_frame: NextFrame,
202 #[rust(false)] visible: bool,
203 #[rust] view_total: f64, #[rust] view_visible: f64, #[rust] scroll_size: f64, #[rust] scroll_pos: f64, #[rust] scroll_target: f64,
209 #[rust] scroll_delta: f64,
210 #[rust] drag_point: Option<f64>, }
212
213#[derive(Live, LiveHook, LiveRegister)]
214#[repr(C)]
215pub struct DrawScrollBar {
216 #[deref] draw_super: DrawQuad,
217 #[live] is_vertical: f32,
218 #[live] norm_handle: f32,
219 #[live] norm_scroll: f32
220}
221
222#[derive(Clone, PartialEq, Debug)]
223pub enum ScrollBarAction {
224 None,
225 Scroll {scroll_pos: f64, view_total: f64, view_visible: f64},
226 ScrollDone
227}
228
229impl ScrollBar {
230 pub fn get_normalized_scroll_pos(&self) -> (f64, f64) {
237 let vy = self.view_visible / self.view_total;
239 if !self.visible {
240 return (0.0, 0.0);
241 }
242 let norm_handle = vy.max(self.min_handle_size / self.scroll_size);
243 let norm_scroll = (1. - norm_handle) * ((self.scroll_pos / self.view_total) / (1. - vy));
244 return (norm_scroll, norm_handle)
245 }
246
247 pub fn set_scroll_pos_from_finger(&mut self,finger: f64) -> bool {
249 let vy = self.view_visible / self.view_total;
250 let norm_handle = vy.max(self.min_handle_size / self.scroll_size);
251
252 let new_scroll_pos = (
253 (self.view_total * (1. - vy) * (finger / self.scroll_size)) / (1. - norm_handle)
254 ).max(0.).min(self.view_total - self.view_visible);
255 let changed = self.scroll_pos != new_scroll_pos;
258 self.scroll_pos = new_scroll_pos;
259 self.scroll_target = new_scroll_pos;
260 changed
261 }
262
263 pub fn update_shader_scroll_pos(&mut self, cx: &mut Cx) {
265 let (norm_scroll, _) = self.get_normalized_scroll_pos();
266 self.draw_bg.apply_over(cx, live!{
267 norm_scroll: (norm_scroll)
268 });
269 }
271
272 pub fn make_scroll_action(&mut self) -> ScrollBarAction {
274 ScrollBarAction::Scroll {
275 scroll_pos: self.scroll_pos,
276 view_total: self.view_total,
277 view_visible: self.view_visible
278 }
279 }
280
281 pub fn move_towards_scroll_target(&mut self, cx: &mut Cx) -> bool {
282 if self.smoothing.is_none() {
283 return false;
284 }
285 if (self.scroll_target - self.scroll_pos).abs() < 0.01 {
286 return false
287 }
288 if self.scroll_pos > self.scroll_target { self.scroll_pos = self.scroll_pos + (self.smoothing.unwrap() * self.scroll_delta).min(-1.);
290 if self.scroll_pos <= self.scroll_target { self.scroll_pos = self.scroll_target;
292 self.update_shader_scroll_pos(cx);
293 return false;
294 }
295 }
296 else { self.scroll_pos = self.scroll_pos + (self.smoothing.unwrap() * self.scroll_delta).max(1.);
298 if self.scroll_pos > self.scroll_target { self.scroll_pos = self.scroll_target;
300 self.update_shader_scroll_pos(cx);
301 return false;
302 }
303 }
304 self.update_shader_scroll_pos(cx);
305 true
306 }
307
308 pub fn get_scroll_pos(&self) -> f64 {
309 return self.scroll_pos;
310 }
311
312 pub fn set_scroll_pos_no_action(&mut self, cx: &mut Cx, scroll_pos: f64) -> bool {
313 let scroll_pos = scroll_pos.min(self.view_total - self.view_visible).max(0.);
314 if self.scroll_pos != scroll_pos {
315 self.scroll_pos = scroll_pos;
316 self.scroll_target = scroll_pos;
317 self.update_shader_scroll_pos(cx);
318 return true
319 };
320 return false
321 }
322 pub fn set_scroll_pos(&mut self, cx: &mut Cx, scroll_pos: f64) -> bool {
323 let scroll_pos = scroll_pos.min(self.view_total - self.view_visible).max(0.);
324 if self.scroll_pos != scroll_pos {
325 self.scroll_pos = scroll_pos;
326 self.scroll_target = scroll_pos;
327 self.update_shader_scroll_pos(cx);
328 self.next_frame = cx.new_next_frame();
329 return true
330 };
331 return false
332 }
333
334
335 pub fn set_scroll_pos_no_clip(&mut self, cx: &mut Cx, scroll_pos: f64) -> bool {
336 if self.scroll_pos != scroll_pos {
337 self.scroll_pos = scroll_pos;
338 self.scroll_target = scroll_pos;
339 self.update_shader_scroll_pos(cx);
340 self.next_frame = cx.new_next_frame();
341 return true
342 };
343 return false
344 }
345
346 pub fn get_scroll_target(&mut self) -> f64 {
347 return self.scroll_target
348 }
349
350 pub fn set_scroll_view_total(&mut self, _cx: &mut Cx, view_total: f64) {
351 self.view_total = view_total;
352 }
353
354 pub fn get_scroll_view_total(&self) -> f64 {
355 return self.view_total;
356 }
357
358 pub fn get_scroll_view_visible(&self) -> f64 {
359 return self.view_visible;
360 }
361
362
363 pub fn set_scroll_target(&mut self, cx: &mut Cx, scroll_pos_target: f64) -> bool {
364 let new_target = scroll_pos_target.min(self.view_total - self.view_visible).max(0.);
367 if self.scroll_target != new_target {
368 self.scroll_target = new_target;
369 self.scroll_delta = new_target - self.scroll_pos;
370 self.next_frame = cx.new_next_frame();
371 return true
372 };
373 return false
374 }
375
376 pub fn scroll_into_view(&mut self, cx: &mut Cx, pos: f64, size: f64, smooth: bool) {
377 if pos < self.scroll_pos { let scroll_to = pos;
379 if !smooth || self.smoothing.is_none() {
380 self.set_scroll_pos(cx, scroll_to);
381 }
382 else {
383 self.set_scroll_target(cx, scroll_to);
384 }
385 }
386 else if pos + size > self.scroll_pos + self.view_visible { let scroll_to = (pos + size) - self.view_visible;
388 if pos + size > self.view_total { self.view_total = pos + size;
390 }
391 if !smooth || self.smoothing.is_none() {
392 self.set_scroll_pos(cx, scroll_to);
393 }
394 else {
395 self.set_scroll_target(cx, scroll_to);
396 }
397 }
398 }
399
400 pub fn handle_scroll_event(&mut self, cx: &mut Cx, event: &Event, scroll_area: Area, dispatch_action: &mut dyn FnMut(&mut Cx, ScrollBarAction)) {
401 if let Event::Scroll(e) = event {
402 if scroll_area.rect(cx).contains(e.abs) {
403 if !match self.axis {
404 ScrollAxis::Horizontal => e.handled_x.get(),
405 ScrollAxis::Vertical => e.handled_y.get()
406 } {
407 let scroll = match self.axis {
408 ScrollAxis::Horizontal => if self.use_vertical_finger_scroll {e.scroll.y}else {e.scroll.x},
409 ScrollAxis::Vertical => e.scroll.y
410 };
411 if !self.smoothing.is_none() && e.is_mouse {
412 let scroll_pos_target = self.get_scroll_target();
413 if self.set_scroll_target(cx, scroll_pos_target + scroll) {
414 match self.axis {
415 ScrollAxis::Horizontal => e.handled_x.set(true),
416 ScrollAxis::Vertical => e.handled_y.set(true)
417 }
418 };
419 self.move_towards_scroll_target(cx); return dispatch_action(cx, self.make_scroll_action());
421 }
422 else {
423 let scroll_pos = self.get_scroll_pos();
424 if self.set_scroll_pos(cx, scroll_pos + scroll) {
425 match self.axis {
426 ScrollAxis::Horizontal => e.handled_x.set(true),
427 ScrollAxis::Vertical => e.handled_y.set(true)
428 }
429 }
430 return dispatch_action(cx, self.make_scroll_action());
431 }
432 }
433 }
434 }
435 }
436 pub fn is_area_captured(&self, cx:&Cx)->bool{
437 cx.fingers.is_area_captured(self.draw_bg.area())
438 }
439
440 pub fn handle_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, ScrollBarAction)) {
441 if self.visible {
442 self.animator_handle_event(cx, event);
443 if self.next_frame.is_event(event).is_some() {
444 if self.move_towards_scroll_target(cx) {
445 self.next_frame = cx.new_next_frame();
446 }
447 return dispatch_action(cx, self.make_scroll_action());
448 }
449
450 match event.hits(cx, self.draw_bg.area()) {
451 Hit::FingerDown(fe) if fe.is_primary_hit() => {
452 self.animator_play(cx, id!(hover.drag));
453 let rel = fe.abs - fe.rect.pos;
454 let rel = match self.axis {
455 ScrollAxis::Horizontal => rel.x,
456 ScrollAxis::Vertical => rel.y
457 };
458 let (norm_scroll, norm_handle) = self.get_normalized_scroll_pos();
459 let bar_start = norm_scroll * self.scroll_size;
460 let bar_size = norm_handle * self.scroll_size;
461 if rel < bar_start || rel > bar_start + bar_size { self.drag_point = Some(bar_size * 0.5);
463 if self.set_scroll_pos_from_finger(rel - self.drag_point.unwrap()){
464 dispatch_action(cx, self.make_scroll_action());
465 }
466 }
467 else { self.drag_point = Some(rel - bar_start); }
470 },
471 Hit::FingerHoverIn(_) => {
472 self.animator_play(cx, id!(hover.on));
473 },
474 Hit::FingerHoverOut(_) => {
475 self.animator_play(cx, id!(hover.off));
476 },
477 Hit::FingerUp(fe) if fe.is_primary_hit() => {
478 self.drag_point = None;
479 if fe.is_over && fe.device.has_hovers() {
480 self.animator_play(cx, id!(hover.on));
481 }
482 else {
483 self.animator_play(cx, id!(hover.off));
484 }
485 return;
486 },
487 Hit::FingerMove(fe) => {
488 let rel = fe.abs - fe.rect.pos;
489 if self.drag_point.is_none() {
491 }
494 else {
495 match self.axis {
496 ScrollAxis::Horizontal => {
497 if self.set_scroll_pos_from_finger(rel.x - self.drag_point.unwrap()){
498 dispatch_action(cx, self.make_scroll_action());
499 }
500 },
501 ScrollAxis::Vertical => {
502 if self.set_scroll_pos_from_finger(rel.y - self.drag_point.unwrap()){
503 dispatch_action(cx, self.make_scroll_action());
504 }
505 }
506 }
507 }
508 },
509 _ => ()
510 };
511 }
512 }
513
514 pub fn draw_scroll_bar(&mut self, cx: &mut Cx2d, axis: ScrollAxis, view_rect: Rect, view_total: DVec2) -> f64 {
515
516 self.axis = axis;
517
518 match self.axis {
519 ScrollAxis::Horizontal => {
520 self.visible = view_total.x > view_rect.size.x + 0.1;
521 self.scroll_size = if view_total.y > view_rect.size.y + 0.1 {
522 view_rect.size.x - self.bar_size
523 }
524 else {
525 view_rect.size.x
526 } -self.bar_side_margin * 2.;
527 self.view_total = view_total.x;
528 self.view_visible = view_rect.size.x;
529 self.scroll_pos = self.scroll_pos.min(self.view_total - self.view_visible).max(0.);
530
531 if self.visible {
532 let (norm_scroll, norm_handle) = self.get_normalized_scroll_pos();
533 self.draw_bg.is_vertical = 0.0;
534 self.draw_bg.norm_scroll = norm_scroll as f32;
535 self.draw_bg.norm_handle = norm_handle as f32;
536 let scroll = cx.turtle().scroll();
537 self.draw_bg.draw_rel(
538 cx,
539 Rect {
540 pos: dvec2(self.bar_side_margin, view_rect.size.y - self.bar_size) + scroll,
541 size: dvec2(self.scroll_size, self.bar_size),
542 }
543 );
544 }
545 },
546 ScrollAxis::Vertical => {
547 self.visible = view_total.y > view_rect.size.y + 0.1;
549 self.scroll_size = if view_total.x > view_rect.size.x + 0.1 {
550 view_rect.size.y - self.bar_size
551 }
552 else {
553 view_rect.size.y
554 } -self.bar_side_margin * 2.;
555 self.view_total = view_total.y;
556 self.view_visible = view_rect.size.y;
557 self.scroll_pos = self.scroll_pos.min(self.view_total - self.view_visible).max(0.);
558 if self.visible {
559 let (norm_scroll, norm_handle) = self.get_normalized_scroll_pos();
560 self.draw_bg.is_vertical = 1.0;
561 self.draw_bg.norm_scroll = norm_scroll as f32;
562 self.draw_bg.norm_handle = norm_handle as f32;
563 let scroll = cx.turtle().scroll();
564 self.draw_bg.draw_rel(
565 cx,
566 Rect {
567 pos: dvec2(view_rect.size.x - self.bar_size, self.bar_side_margin) + scroll,
568 size: dvec2(self.bar_size, self.scroll_size)
569 }
570 );
571 }
572 }
573 }
574
575
576 let clamped_pos = self.scroll_pos.min(self.view_total - self.view_visible).max(0.);
578 if clamped_pos != self.scroll_pos {
579 self.scroll_pos = clamped_pos;
580 self.scroll_target = clamped_pos;
581 self.next_frame = cx.new_next_frame();
583 }
584
585 self.scroll_pos
586 }
587}