tuix_widgets/scroll_container.rs
1use tuix_core::layout::GeometryChanged;
2
3use crate::common::*;
4
5// #[derive(Debug, Copy, Clone, PartialEq)]
6// pub enum ScrollEvent {
7// Scroll(f32, f32, f32),
8// }
9
10
11#[derive(Debug, Default, Clone, Copy, PartialEq)]
12pub struct Scroll {
13 pub scroll_pos: f32,
14 pub scroll_size: f32,
15 pub overflow: f32,
16}
17
18// #[derive(Debug, Default, Lens, Clone, Copy)]
19// pub struct ScrollData {
20// scroll: Scroll,
21// }
22
23// impl Model for ScrollData {
24// fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
25// if let Some(scroll_event) = event.message.downcast() {
26// match scroll_event {
27
28// ScrollEvent::Scroll(pos, size, overflow) => {
29// self.scroll.scroll_pos = *pos;
30// self.scroll.scroll_size = *size;
31// self.scroll.overflow = *overflow;
32// entity.emit(state, BindEvent::Update);
33// event.consume();
34// }
35// }
36// }
37// }
38// }
39
40pub struct ScrollContainerH {
41 container: Entity,
42 horizontal_scroll: Entity,
43 //vertical_scroll: Entity,
44 //scrolly: f32,
45 //scrollh: f32,
46
47 pub scroll: Scroll,
48
49 pressedx: f32,
50 pressedy: f32,
51 moving: bool,
52 position: f32,
53
54 vertical_scroll_animation: Animation,
55 vertical_container_animation: Animation,
56
57 scrollbar: bool,
58 scroll_wheel: bool,
59
60 on_scroll: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
61}
62
63impl ScrollContainerH {
64 pub fn new() -> Self {
65 ScrollContainerH {
66 container: Entity::null(),
67 horizontal_scroll: Entity::null(),
68 //vertical_scroll: Entity::null(),
69 //scrolly: 0.0,
70 //scrollh: 0.0,
71
72 scroll: Scroll::default(),
73
74 pressedx: 0.0,
75 pressedy: 0.0,
76 moving: false,
77 position: 0.0,
78
79 vertical_scroll_animation: Animation::default(),
80 vertical_container_animation: Animation::default(),
81
82 scrollbar: true,
83 scroll_wheel: true,
84
85 on_scroll: None,
86 }
87 }
88
89 // TODO
90 pub fn disable_scrollbar(mut self) -> Self {
91 self.scrollbar = false;
92
93 self
94 }
95
96 pub fn disable_scroll_wheel(mut self) -> Self {
97 self.scroll_wheel = false;
98
99 self
100 }
101
102 pub fn on_scroll<F>(mut self, callback: F) -> Self
103 where F: 'static + Fn(&mut Self, &mut State, Entity)
104 {
105 self.on_scroll = Some(Box::new(callback));
106
107 self
108 }
109}
110
111impl Widget for ScrollContainerH {
112 type Ret = Entity;
113 type Data = Scroll;
114 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
115 entity
116 .set_layout_type(state, LayoutType::Column)
117 .set_min_width(state, Pixels(0.0));
118
119 self.container = Element::new().build(state, entity, |builder| {
120 builder
121 //.set_position_type(PositionType::SelfDirected)
122 .set_width(Auto)
123 .set_height(Stretch(1.0))
124 // .set_left(Units::Percentage(0.0))
125 // .set_align_self(AlignSelf::FlexStart)
126 //.set_background_color(Color::rgb(200, 70, 70))
127 .class("container")
128 //.set_hoverable(false)
129 });
130
131 state.style.clip_widget.insert(self.container, entity);
132
133 //if self.scrollbar {
134 self.horizontal_scroll = Element::new().build(state, entity, |builder| {
135 builder
136 //.set_position_type(PositionType::SelfDirected)
137 .set_min_width(Pixels(0.0))
138 // .set_left(Units::Percentage(0.0))
139 //.set_height(Units::Pixels(10.0))
140 // .set_width(Units::Percentage(0.0))
141 // .set_align_self(AlignSelf::FlexStart)
142 //.set_background_color(Color::rgb(70, 70, 200))
143 //.set_right(Units::Pixels(0.0))
144 .class("scrollbar")
145
146 //
147 });
148 //}
149
150 entity.set_disabled(state, true);
151
152 entity.set_element(state, "scroll_containerh");
153
154 let vertical_scroll_animation = AnimationState::new()
155 .with_duration(std::time::Duration::from_millis(100))
156 .with_keyframe((0.0, Units::Percentage(0.0)))
157 .with_keyframe((1.0, Units::Percentage(20.0)));
158
159 self.vertical_scroll_animation =
160 state.style.left.insert_animation(vertical_scroll_animation);
161
162 let vertical_container_animation = AnimationState::new()
163 .with_duration(std::time::Duration::from_millis(100))
164 .with_keyframe((0.0, Units::Percentage(0.0)))
165 .with_keyframe((1.0, Units::Percentage(-20.0)));
166
167 self.vertical_container_animation = state
168 .style
169 .left
170 .insert_animation(vertical_container_animation);
171
172 self.container
173 }
174
175 fn on_update(&mut self, state: &mut State, _entity: Entity, data: &Self::Data) {
176 //self.scroll.scroll_pos = data.scroll_pos;
177 //self.scroll.scroll_size = data.scroll_size;
178
179 self.scroll = *data;
180
181 // let overflow = 1.0
182 // - (state.data.get_width(self.container)
183 // / state.data.get_width(entity));
184 // let overflow2 = 1.0
185 // - (state.data.get_width(entity)
186 // / state.data.get_width(self.container));
187
188 let overflow2 = 1.0 - (1.0 / (1.0 - self.scroll.overflow));
189
190 self.container
191 .set_left(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
192 self.horizontal_scroll
193 .set_left(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
194 }
195
196
197 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
198 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
199 match window_event {
200 WindowEvent::GeometryChanged(geometry_changed) => {
201 if event.target == self.container || event.target == entity {
202 if geometry_changed.contains(GeometryChanged::WIDTH_CHANGED) || geometry_changed.contains(GeometryChanged::HEIGHT_CHANGED) {
203 self.scroll.scroll_size =
204 state.data.get_width(entity) / state.data.get_width(self.container);
205
206 if self.scroll.scroll_size >= 1.0 {
207 self.scroll.scroll_size = 1.0;
208 entity.set_disabled(state, true);
209 }
210
211 if self.scroll.scroll_size < 1.0 {
212 entity.set_disabled(state, false);
213 }
214
215 if !state.style.left.is_animating(self.horizontal_scroll) {
216 let dist = state.data.get_posx(self.horizontal_scroll)
217 - state.data.get_posx(entity);
218 let space = state.data.get_width(entity)
219 - (self.scroll.scroll_size * state.data.get_width(entity));
220 self.scroll.scroll_pos = dist / space;
221 }
222
223 if self.scroll.scroll_pos.is_nan() {
224 self.scroll.scroll_pos = 0.0;
225 }
226
227 if self.scroll.scroll_pos < 0.0 {
228 self.scroll.scroll_pos = 0.0;
229 }
230
231 if self.scroll.scroll_pos >= 1.0 {
232 self.scroll.scroll_pos = 1.0;
233 }
234
235 // Setting it this way avoid calling Restyle automatically
236 state
237 .style
238 .width
239 .insert(self.horizontal_scroll, Units::Percentage(self.scroll.scroll_size * 100.0))
240 .expect("");
241
242 self.scroll.overflow = 1.0
243 - (state.data.get_width(self.container)
244 / state.data.get_width(entity));
245 let overflow2 = 1.0 - (1.0 / (1.0 - self.scroll.overflow));
246 // let overflow2 = 1.0
247 // - (state.data.get_width(entity)
248 // / state.data.get_width(self.container));
249
250 state
251 .style
252 .left
253 .insert(self.container, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0))
254 .expect("");
255
256 state.style.left.insert(
257 self.horizontal_scroll,
258 Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0),
259 ).expect("");
260
261 state.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()).origin(entity));
262
263
264 if let Some(callback) = self.on_scroll.take() {
265 (callback)(self, state, entity);
266
267 self.on_scroll = Some(callback);
268 }
269
270 // state.insert_event(
271 // Event::new(ScrollEvent::Scroll(self.scroll.scroll_pos, self.scroll.scroll_size, overflow)).target(entity).origin(entity),
272 // );
273 }
274 }
275 }
276
277 WindowEvent::MouseScroll(x, _) => {
278 if self.scroll_wheel {
279 self.scroll.overflow = state.data.get_width(entity)
280 - state.data.get_width(self.horizontal_scroll);
281
282 if self.scroll.overflow == 0.0 {
283 return;
284 }
285
286 // Need better names for these
287 self.scroll.overflow = 1.0
288 - (state.data.get_width(self.container) / state.data.get_width(entity));
289 let overflow2 = 1.0
290 - (state.data.get_width(entity) / state.data.get_width(self.container));
291
292 self.scroll.scroll_pos += (30.0 * -*x) / (state.data.get_width(entity) * self.scroll.overflow);
293
294 if self.scroll.scroll_pos < 0.0 {
295 self.scroll.scroll_pos = 0.0;
296 }
297
298 if self.scroll.scroll_pos > 1.0 {
299 self.scroll.scroll_pos = 1.0;
300 }
301
302 let _current_scroll_top = state
303 .style
304 .left
305 .get(self.horizontal_scroll)
306 .cloned()
307 .unwrap_or_default();
308 let _current_container_top = state
309 .style
310 .left
311 .get(self.container)
312 .cloned()
313 .unwrap_or_default();
314
315 self.container
316 .set_left(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
317 self.horizontal_scroll
318 .set_left(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
319
320
321 if let Some(callback) = self.on_scroll.take() {
322 (callback)(self, state, entity);
323
324 self.on_scroll = Some(callback);
325 }
326
327 // state.insert_event(
328 // Event::new(ScrollEvent::Scroll(self.scroll.scroll_pos, self.scroll.scroll_size, overflow))
329 // .target(entity).origin(entity),
330 // );
331
332 // Capture the event to stop it triggering twice
333 //event.consume();
334 }
335 }
336
337 WindowEvent::WindowResize(_, _) => {
338 // let scroll = state
339 // .style
340 // .scroll
341 // .get(self.container)
342 // .cloned()
343 // .unwrap_or_default();
344
345 // event_manager.insert_event(
346 // Event::new(StyleEvent::Restyle).target(state.root),
347 // );
348 }
349
350 WindowEvent::MouseDown(button) => match button {
351 MouseButton::Left => {
352 if state.hovered == self.horizontal_scroll {
353 //println!("Clicked on scrollbar");
354 self.pressedx = state.mouse.cursorx;
355 self.pressedy = state.mouse.cursory;
356 self.moving = true;
357 // let scroll = state
358 // .style
359 // .scroll
360 // .get(self.entity)
361 // .cloned()
362 // .unwrap_or_default();
363 //self.position = state.data.get_posy(self.vertical_scroll);
364 self.position = self.scroll.scroll_pos;
365 state.capture(entity);
366 }
367 }
368 _ => {}
369 },
370
371 WindowEvent::MouseUp(button) => match button {
372 MouseButton::Left => {
373 self.moving = false;
374 state.release(entity);
375 }
376
377 _ => {}
378 },
379
380 WindowEvent::MouseMove(x, _) => {
381 if self.moving {
382 let dist_x = *x - self.pressedx;
383 let scroll_bar_overflow = state.data.get_width(entity)
384 - state.data.get_width(self.horizontal_scroll);
385
386 if scroll_bar_overflow == 0.0 {
387 return;
388 }
389
390 let ratio = dist_x / scroll_bar_overflow;
391 let r = self.position + ratio;
392
393 // let mut scrollh = state.data.get_height(entity) / state.data.get_height(self.container);
394 // if scrollh > 1.0 {
395 // scrollh = 1.0;
396 // }
397
398 self.scroll.scroll_pos = r;
399
400 if self.scroll.scroll_pos < 0.0 {
401 self.scroll.scroll_pos = 0.0;
402 }
403
404 if self.scroll.scroll_pos > 1.0 {
405 self.scroll.scroll_pos = 1.0;
406 }
407
408 // let scroll = state
409 // .style
410 // .scroll
411 // .get(self.entity)
412 // .cloned()
413 // .unwrap_or_default();
414 //self.vertical_scroll
415 // .set_top(state, Units::Pixels(self.position + dist_y));
416
417 self.scroll.overflow = 1.0
418 - (state.data.get_width(self.container) / state.data.get_width(entity));
419 let overflow2 = 1.0
420 - (state.data.get_width(entity) / state.data.get_width(self.container));
421
422 self.container
423 .set_left(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
424 self.horizontal_scroll
425 .set_left(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
426
427
428 if let Some(callback) = self.on_scroll.take() {
429 (callback)(self, state, entity);
430
431 self.on_scroll = Some(callback);
432 }
433
434 // state.insert_event(
435 // Event::new(ScrollEvent::Scroll(self.scroll.scroll_pos, self.scroll.scroll_size, overflow))
436 // .target(entity).origin(entity),
437 // );
438
439 state.insert_event(Event::new(WindowEvent::Restyle));
440 state
441 .insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
442 state.insert_event(Event::new(WindowEvent::Redraw));
443 //println!("overflow: {}, dist: {}, ratio: {}, scrolly: {}", overflow, dist_y, r, self.scroll.scroll_pos);
444 }
445 }
446
447 _ => {}
448 }
449 }
450 }
451}
452
453pub struct ScrollContainer {
454 container: Entity,
455 vertical_scroll: Entity,
456 pub scroll: Scroll,
457
458 scrollbar: bool,
459
460 pressedx: f32,
461 pressedy: f32,
462 moving: bool,
463 position: f32,
464
465 vertical_scroll_animation: Animation,
466 vertical_container_animation: Animation,
467
468 on_scroll: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
469}
470
471impl ScrollContainer {
472 /// Create a new ScrollContainer widget
473 pub fn new() -> Self {
474 ScrollContainer {
475 container: Entity::null(),
476 vertical_scroll: Entity::null(),
477 scroll: Scroll::default(),
478
479 scrollbar: true,
480
481 pressedx: 0.0,
482 pressedy: 0.0,
483 moving: false,
484 position: 0.0,
485
486 vertical_scroll_animation: Animation::default(),
487 vertical_container_animation: Animation::default(),
488
489 on_scroll: None,
490 }
491 }
492
493 pub fn disable_scrollbar(mut self) -> Self {
494 self.scrollbar = false;
495
496 self
497 }
498
499 pub fn on_scroll<F>(mut self, callback: F) -> Self
500 where F: 'static + Fn(&mut Self, &mut State, Entity)
501 {
502 self.on_scroll = Some(Box::new(callback));
503
504 self
505 }
506}
507
508impl Widget for ScrollContainer {
509 type Ret = Entity;
510 type Data = Scroll;
511 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
512 entity
513 .set_layout_type(state, LayoutType::Row)
514 .set_min_height(state, Pixels(0.0));
515
516 self.container = Element::new().build(state, entity, |builder| {
517 builder
518 //.set_position_type(PositionType::SelfDirected)
519 .set_height(Auto)
520 .set_width(Stretch(1.0))
521 //.set_top(Units::Percentage(0.0))
522 //.set_flex_grow(1.0)
523 //.set_align_self(AlignSelf::FlexStart)
524 .class("container")
525 });
526
527 state.style.clip_widget.insert(self.container, entity);
528
529 self.vertical_scroll = Element::new().build(state, entity, |builder| {
530 builder
531 //.set_position_type(PositionType::SelfDirected)
532 .set_min_height(Pixels(0.0))
533 //.set_top(Units::Percentage(0.0))
534 // .set_width(Units::Pixels(10.0))
535 //.set_height(Units::Percentage(0.0))
536 //.set_align_self(AlignSelf::FlexStart)
537 //.set_background_color(Color::rgb(70, 200, 70))
538 //.set_right(Units::Pixels(0.0))
539 .class("scrollbar")
540
541 //
542 });
543
544 entity.set_disabled(state, true);
545 // self.vertical_scroll =
546 // Scrollbar::new(self.container, Direction::Vertical).build(state, entity, |builder| {
547 // builder
548 // .set_width(Units::Pixels(10.0))
549 // .set_height(Units::Percentage(1.0))
550 // .set_background_color(Color::rgb(50, 50, 100))
551 // });
552
553 entity.set_element(state, "scroll_container");
554
555 let vertical_scroll_animation = AnimationState::new()
556 .with_duration(std::time::Duration::from_millis(100))
557 .with_keyframe((0.0, Units::Percentage(0.0)))
558 .with_keyframe((1.0, Units::Percentage(20.0)));
559
560 self.vertical_scroll_animation =
561 state.style.top.insert_animation(vertical_scroll_animation);
562
563 let vertical_container_animation = AnimationState::new()
564 .with_duration(std::time::Duration::from_millis(100))
565 .with_keyframe((0.0, Units::Percentage(0.0)))
566 .with_keyframe((1.0, Units::Percentage(-20.0)));
567
568 self.vertical_container_animation = state
569 .style
570 .top
571 .insert_animation(vertical_container_animation);
572
573 self.container
574 }
575
576 fn on_update(&mut self, state: &mut State, entity: Entity, data: &Self::Data) {
577 self.scroll = *data;
578
579
580 let overflow2 = 1.0
581 - (state.data.get_height(entity)
582 / state.data.get_height(self.container));
583
584 self.container
585 .set_top(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
586 self.vertical_scroll
587 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
588 }
589
590 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
591 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
592 match window_event {
593 WindowEvent::GeometryChanged(geometry_changed) => {
594 if event.target == self.container || event.target == entity {
595 if geometry_changed.contains(GeometryChanged::WIDTH_CHANGED) || geometry_changed.contains(GeometryChanged::HEIGHT_CHANGED) {
596 self.scroll.scroll_size = state.data.get_height(entity)
597 / state.data.get_height(self.container);
598
599 if self.scroll.scroll_size >= 1.0 {
600 self.scroll.scroll_size = 1.0;
601 entity.set_disabled(state, true);
602 }
603
604 if self.scroll.scroll_size < 1.0 {
605 entity.set_disabled(state, false);
606 }
607
608 if !state.style.top.is_animating(self.vertical_scroll) {
609 let dist = state.data.get_posy(self.vertical_scroll)
610 - state.data.get_posy(entity);
611 let space = state.data.get_height(entity)
612 - (self.scroll.scroll_size * state.data.get_height(entity));
613 self.scroll.scroll_pos = dist / space;
614 }
615
616 if self.scroll.scroll_pos.is_nan() {
617 self.scroll.scroll_pos = 0.0;
618 }
619
620 if self.scroll.scroll_pos < 0.0 {
621 self.scroll.scroll_pos = 0.0;
622 }
623
624 if self.scroll.scroll_pos >= 1.0 {
625 self.scroll.scroll_pos = 1.0;
626 }
627
628 // Setting it this way avoid calling Restyle automatically
629 state
630 .style
631 .height
632 .insert(self.vertical_scroll, Units::Percentage(self.scroll.scroll_size * 100.0))
633 .expect("");
634
635 self.scroll.overflow = 1.0
636 - (state.data.get_height(self.container)
637 / state.data.get_height(entity));
638 let overflow2 = 1.0
639 - (state.data.get_height(entity)
640 / state.data.get_height(self.container));
641
642 state
643 .style
644 .top
645 .insert(self.container, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0)).expect("Failed to set top position of container");
646
647 state.style.top.insert(
648 self.vertical_scroll,
649 Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0),
650 ).expect("Failed to set top position of vertical scroll bar.");
651
652 state.insert_event(
653 Event::new(WindowEvent::Relayout)
654 .target(Entity::root())
655 .origin(entity),
656 );
657
658 if let Some(callback) = self.on_scroll.take() {
659 (callback)(self, state, entity);
660
661 self.on_scroll = Some(callback);
662 }
663
664 // state.insert_event(
665 // Event::new(ScrollEvent::Scroll(self.scroll.scroll_pos, self.scroll.scroll_size, overflow)).target(entity).origin(entity),
666 // );
667 }
668 }
669 }
670
671 WindowEvent::MouseScroll(_, y) => {
672
673 self.scroll.overflow =
674 state.data.get_height(entity) - state.data.get_height(self.vertical_scroll);
675
676 if self.scroll.overflow == 0.0 {
677 return;
678 }
679
680 // Need better names for these
681 self.scroll.overflow = 1.0
682 - (state.data.get_height(self.container) / state.data.get_height(entity));
683 let overflow2 = 1.0
684 - (state.data.get_height(entity) / state.data.get_height(self.container));
685
686 // TODO - Need a way to configure this
687 self.scroll.scroll_pos += (30.0 * *y) / (state.data.get_height(entity) * self.scroll.overflow);
688
689 if self.scroll.scroll_pos < 0.0 {
690 self.scroll.scroll_pos = 0.0;
691 }
692
693 if self.scroll.scroll_pos > 1.0 {
694 self.scroll.scroll_pos = 1.0;
695 }
696
697 self.container
698 .set_top(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
699 self.vertical_scroll
700 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
701
702 if let Some(callback) = self.on_scroll.take() {
703 (callback)(self, state, entity);
704
705 self.on_scroll = Some(callback);
706 }
707
708 // state.insert_event(
709 // Event::new(ScrollEvent::Scroll(self.scroll.scroll_pos, self.scroll.scroll_size, overflow)).target(entity).origin(entity),
710 // );
711
712 //event.consume();
713 }
714
715 WindowEvent::WindowResize(_, _) => {
716 // let scroll = state
717 // .style
718 // .scroll
719 // .get(self.container)
720 // .cloned()
721 // .unwrap_or_default();
722
723 // event_manager.insert_event(
724 // Event::new(StyleEvent::Restyle).target(state.root),
725 // );
726 }
727
728 WindowEvent::MouseDown(button) => match button {
729 MouseButton::Left => {
730 if state.hovered == self.vertical_scroll {
731 //println!("Clicked on scrollbar");
732 self.pressedx = state.mouse.cursorx;
733 self.pressedy = state.mouse.cursory;
734 self.moving = true;
735 // let scroll = state
736 // .style
737 // .scroll
738 // .get(self.entity)
739 // .cloned()
740 // .unwrap_or_default();
741 //self.position = state.data.get_posy(self.vertical_scroll);
742 self.position = self.scroll.scroll_pos;
743 state.capture(entity);
744 }
745 }
746 _ => {}
747 },
748
749 WindowEvent::MouseUp(button) => match button {
750 MouseButton::Left => {
751 self.moving = false;
752 state.release(entity);
753 }
754
755 _ => {}
756 },
757
758 WindowEvent::MouseMove(_, y) => {
759 if self.moving {
760 let dist_y = *y - self.pressedy;
761 let scroll_bar_overflow = state.data.get_height(entity)
762 - state.data.get_height(self.vertical_scroll);
763
764 if scroll_bar_overflow == 0.0 {
765 return;
766 }
767
768 let ratio = dist_y / scroll_bar_overflow;
769 let r = self.position + ratio;
770
771 // let mut scrollh = state.data.get_height(entity) / state.data.get_height(self.container);
772 // if scrollh > 1.0 {
773 // scrollh = 1.0;
774 // }
775
776 self.scroll.scroll_pos = r;
777
778 if self.scroll.scroll_pos < 0.0 {
779 self.scroll.scroll_pos = 0.0;
780 }
781
782 if self.scroll.scroll_pos > 1.0 {
783 self.scroll.scroll_pos = 1.0;
784 }
785
786 // let scroll = state
787 // .style
788 // .scroll
789 // .get(self.entity)
790 // .cloned()
791 // .unwrap_or_default();
792 //self.vertical_scroll
793 // .set_top(state, Units::Pixels(self.position + dist_y));
794
795 self.scroll.overflow = 1.0
796 - (state.data.get_height(self.container)
797 / state.data.get_height(entity));
798 let overflow2 = 1.0
799 - (state.data.get_height(entity)
800 / state.data.get_height(self.container));
801
802 self.container
803 .set_top(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
804 self.vertical_scroll
805 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
806
807 if let Some(callback) = self.on_scroll.take() {
808 (callback)(self, state, entity);
809
810 self.on_scroll = Some(callback);
811 }
812
813 // state.insert_event(
814 // Event::new(ScrollEvent::Scroll(self.scroll.scroll_pos, self.scroll.scroll_size, overflow))
815 // .target(entity).origin(entity),
816 // );
817
818 state.insert_event(Event::new(WindowEvent::Restyle));
819 state
820 .insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
821 state.insert_event(Event::new(WindowEvent::Redraw));
822 //println!("overflow: {}, dist: {}, ratio: {}, scrolly: {}", overflow, dist_y, r, self.scroll.scroll_pos);
823 }
824 }
825
826 _ => {}
827 }
828 }
829 }
830}
831
832//
833/*
834pub struct ScrollContainerHV {
835 container: Entity,
836 horizontal_scroll: Entity,
837 vertical_scroll: Entity,
838 scrollx: f32,
839 scrolly: f32,
840
841 pressedx: f32,
842 pressedy: f32,
843 moving: bool,
844 position: f32,
845 //vertical_scroll_animation: usize,
846 //vertical_container_animation: usize,
847}
848
849impl ScrollContainerHV {
850 pub fn new() -> Self {
851 ScrollContainerHV {
852 container: Entity::null(),
853 horizontal_scroll: Entity::null(),
854 vertical_scroll: Entity::null(),
855 scrollx: 0.0,
856 scrolly: 0.0,
857
858 pressedx: 0.0,
859 pressedy: 0.0,
860 moving: false,
861 position: 0.0,
862 //vertical_scroll_animation: std::usize::MAX,
863 //vertical_container_animation: std::usize::MAX,
864 }
865 }
866}
867
868impl Widget for ScrollContainerHV {
869 type Ret = Entity;
870 type Data = ();
871 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
872 entity.set_layout_type(state, LayoutType::Row);
873
874 let row = Row::new().build(state, entity, |builder| builder);
875
876 let column = Column::new().build(state, row, |builder| builder);
877
878 self.container = Element::new().build(state, column, |builder| {
879 builder
880 .set_top(Units::Percentage(0.0))
881 //.set_align_self(AlignSelf::FlexStart)
882 .class("container")
883 });
884
885 state.style.clip_widget.insert(self.container, entity);
886
887 //println!("Container: {}", self.container);
888
889 self.vertical_scroll = Element::new().build(state, row, |builder| {
890 builder
891 //.set_position(Position::Absolute)
892 .set_top(Units::Percentage(0.0))
893 .set_width(Units::Pixels(10.0))
894 .set_height(Units::Percentage(100.0))
895 //.set_align_self(AlignSelf::FlexStart)
896 //.set_background_color(Color::rgb(70, 200, 70))
897 //.set_right(Units::Pixels(0.0))
898 //.class("scrollbar")
899
900 //
901 });
902
903 self.vertical_scroll = Element::new().build(state, row, |builder| {
904 builder
905 //.set_position(Position::Absolute)
906 .set_left(Units::Percentage(0.0))
907 .set_height(Units::Pixels(10.0))
908 .set_width(Units::Percentage(100.0))
909 //.set_align_self(AlignSelf::FlexStart)
910 //.set_background_color(Color::rgb(20, 70, 200))
911 //.set_right(Units::Pixels(0.0))
912 //.class("scrollbar")
913
914 //
915 });
916
917 //self.vertical_scroll.set_disabled(state, true);
918
919 // self.vertical_scroll =
920 // Scrollbar::new(self.container, Direction::Vertical).build(state, entity, |builder| {
921 // builder
922 // .set_width(Units::Pixels(10.0))
923 // .set_height(Units::Percentage(1.0))
924 // .set_background_color(Color::rgb(50, 50, 100))
925 // });
926
927 entity.set_element(state, "scroll_containerhv");
928
929 self.container
930 }
931
932 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
933 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
934 match window_event {
935 WindowEvent::Relayout => {
936 // // To prevent recursive loop when layout event is triggered inside here
937 if event.origin != entity
938 && event.origin != self.container
939 && event.origin != self.vertical_scroll
940 {
941 let mut scrollv =
942 state.data.get_height(entity) / state.data.get_height(self.container);
943
944 if scrollv >= 1.0 {
945 scrollv = 1.0;
946 self.vertical_scroll.set_disabled(state, true);
947 }
948
949 if scrollv < 1.0 {
950 self.vertical_scroll.set_disabled(state, false);
951 }
952
953 let mut scrollh =
954 state.data.get_width(entity) / state.data.get_width(self.container);
955
956 if scrollh >= 1.0 {
957 scrollh = 1.0;
958 self.horizontal_scroll.set_disabled(state, true);
959 }
960
961 if scrollh < 1.0 {
962 self.horizontal_scroll.set_disabled(state, false);
963 }
964
965 // BUG: fast scrolling causes smaller scroll because the animation hasn't finished when this function is called again
966 // One way to fix this might be to check whether the value is currently being animated before setting here
967 // Possibly not the best solution but it works
968 // if !state.style.top.is_animating(self.vertical_scroll) {
969 // let dist = state.data.get_posy(self.vertical_scroll)
970 // - state.data.get_posy(entity);
971 // let space = state.data.get_height(entity)
972 // - (scrollh * state.data.get_height(entity));
973 // self.scroll.scroll_pos = dist / space;
974 // }
975
976 if self.scroll.scroll_pos.is_nan() {
977 self.scroll.scroll_pos = 0.0;
978 }
979
980 if self.scroll.scroll_pos < 0.0 {
981 self.scroll.scroll_pos = 0.0;
982 }
983
984 if self.scroll.scroll_pos >= 1.0 {
985 self.scroll.scroll_pos = 1.0;
986 }
987
988 // Setting it this way avoid calling Restyle automatically
989 state
990 .style
991 .height
992 .insert(self.vertical_scroll, Units::Percentage(scrollv * 100.0));
993
994 let overflow = 1.0
995 - (state.data.get_height(self.container)
996 / state.data.get_height(entity));
997 let overflow2 = 1.0
998 - (state.data.get_height(entity)
999 / state.data.get_height(self.container));
1000
1001 state
1002 .style
1003 .top
1004 .insert(self.container, Units::Percentage(self.scroll.scroll_pos * overflow * 100.0));
1005
1006 state.style.top.insert(
1007 self.vertical_scroll,
1008 Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0),
1009 );
1010
1011 if self.scrollx.is_nan() {
1012 self.scrollx = 0.0;
1013 }
1014
1015 if self.scrollx < 0.0 {
1016 self.scrollx = 0.0;
1017 }
1018
1019 if self.scrollx >= 1.0 {
1020 self.scrollx = 1.0;
1021 }
1022
1023 // Setting it this way avoid calling Restyle automatically
1024 state
1025 .style
1026 .width
1027 .insert(self.vertical_scroll, Units::Percentage(scrollh * 100.0));
1028
1029 let overflow = 1.0
1030 - (state.data.get_width(self.container) / state.data.get_width(entity));
1031 let overflow2 = 1.0
1032 - (state.data.get_width(entity) / state.data.get_width(self.container));
1033
1034 state
1035 .style
1036 .left
1037 .insert(self.container, Units::Percentage(self.scrollx * overflow * 100.0));
1038
1039 state.style.top.insert(
1040 self.vertical_scroll,
1041 Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0),
1042 );
1043
1044 // Relayout and Redraw wont get called automatically so need to manually trigger them
1045 state.insert_event(Event::new(WindowEvent::Relayout).origin(entity));
1046 //state.insert_event(Event::new(WindowEvent::Redraw));
1047 //return true;
1048 }
1049 }
1050
1051 /*
1052 WindowEvent::MouseScroll(_, y) => {
1053 println!("Mouse Scroll Event");
1054 // Forward mouse scroll event to the scrollbar
1055 // state.insert_event(
1056 // Event::new(WindowEvent::MouseScroll(*x, *y))
1057 // .target(self.vertical_scroll)
1058 // .propagate(Propagation::None),
1059 // );
1060
1061 //if event.target == entity {
1062
1063 println!("Height: {}", state.data.get_height(entity));
1064
1065 let overflow = state.data.get_height(entity)
1066 - state.data.get_height(self.vertical_scroll);
1067
1068 if overflow == 0.0 {
1069 return false;
1070 }
1071
1072 // Need better names for these
1073 let overflow = 1.0
1074 - (state.data.get_height(self.container)
1075 / state.data.get_height(entity));
1076 let overflow2 = 1.0
1077 - (state.data.get_height(entity)
1078 / state.data.get_height(self.container));
1079
1080 self.scroll.scroll_pos += (40.0 * *y) / (state.data.get_height(entity) * overflow);
1081
1082 if self.scroll.scroll_pos < 0.0 {
1083 self.scroll.scroll_pos = 0.0;
1084 }
1085
1086 if self.scroll.scroll_pos > 1.0 {
1087 self.scroll.scroll_pos = 1.0;
1088 }
1089
1090 //println!("Scroll: {}", self.scroll.scroll_pos);
1091
1092 // let mut scrollh = state.data.get_height(entity) / state.data.get_height(self.container);
1093 // if scrollh > 1.0 {
1094 // scrollh = 1.0;
1095 // }
1096
1097 let current_scroll_top = state
1098 .style
1099 .top
1100 .get(self.vertical_scroll)
1101 .cloned()
1102 .unwrap_or_default();
1103 let current_container_top = state
1104 .style
1105 .top
1106 .get(self.container)
1107 .cloned()
1108 .unwrap_or_default();
1109
1110 self.container
1111 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow));
1112 self.vertical_scroll
1113 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2));
1114
1115 /*
1116 if let Some(animation) = state.style.top.get_animation_mut(self.vertical_scroll_animation) {
1117 *animation.keyframes.first_mut().unwrap() = (0.0, current_scroll_top);
1118 *animation.keyframes.last_mut().unwrap() = (1.0, Units::Percentage(self.scroll.scroll_pos * overflow2));
1119 }
1120
1121 state.style.top.play_animation(self.vertical_scroll, self.vertical_scroll_animation);
1122
1123
1124
1125
1126
1127 if let Some(animation) = state.style.top.get_animation_mut(self.vertical_container_animation) {
1128 *animation.keyframes.first_mut().unwrap() = (0.0, current_container_top);
1129 *animation.keyframes.last_mut().unwrap() = (1.0, Units::Percentage(self.scroll.scroll_pos * overflow));
1130 }
1131
1132 state.style.top.play_animation(self.container, self.vertical_container_animation);
1133 */
1134
1135 //println!("A: {:?} B: {:?}", current_container_top, self.scroll.scroll_pos * overflow);
1136
1137 //state.insert_event(Event::new(WindowEvent::Relayout).target(Entity::null()));
1138 //state.insert_event(Event::new(WindowEvent::Redraw));
1139 //}
1140
1141 // Capture the event to stop it triggering twice
1142 return true;
1143 }
1144 */
1145 WindowEvent::MouseDown(button) => match button {
1146 MouseButton::Left => {
1147 if state.hovered == self.vertical_scroll {
1148 //println!("Clicked on scrollbar");
1149 self.pressedx = state.mouse.cursorx;
1150 self.pressedy = state.mouse.cursory;
1151 self.moving = true;
1152 // let scroll = state
1153 // .style
1154 // .scroll
1155 // .get(self.entity)
1156 // .cloned()
1157 // .unwrap_or_default();
1158 //self.position = state.data.get_posy(self.vertical_scroll);
1159 self.position = self.scroll.scroll_pos;
1160 state.capture(entity);
1161 }
1162
1163 if state.hovered == self.horizontal_scroll {
1164 //println!("Clicked on scrollbar");
1165 self.pressedx = state.mouse.cursorx;
1166 self.pressedy = state.mouse.cursory;
1167 self.moving = true;
1168 // let scroll = state
1169 // .style
1170 // .scroll
1171 // .get(self.entity)
1172 // .cloned()
1173 // .unwrap_or_default();
1174 //self.position = state.data.get_posy(self.vertical_scroll);
1175 self.position = self.scrollx;
1176 state.capture(entity);
1177 }
1178 }
1179 _ => {}
1180 },
1181
1182 WindowEvent::MouseUp(button) => match button {
1183 MouseButton::Left => {
1184 self.moving = false;
1185 //println!("Scroll release");
1186 state.release(entity);
1187 }
1188
1189 _ => {}
1190 },
1191
1192 WindowEvent::MouseMove(x, y) => {
1193 if self.moving && state.captured == self.vertical_scroll {
1194 let dist_y = *y - self.pressedy;
1195 let overflow = state.data.get_height(entity)
1196 - state.data.get_height(self.vertical_scroll);
1197
1198 if overflow == 0.0 {
1199 return;
1200 }
1201
1202 let ratio = dist_y / overflow;
1203 let r = self.position + ratio;
1204
1205 // let mut scrollh = state.data.get_height(entity) / state.data.get_height(self.container);
1206 // if scrollh > 1.0 {
1207 // scrollh = 1.0;
1208 // }
1209
1210 self.scroll.scroll_pos = r;
1211
1212 if self.scroll.scroll_pos < 0.0 {
1213 self.scroll.scroll_pos = 0.0;
1214 }
1215
1216 if self.scroll.scroll_pos > 1.0 {
1217 self.scroll.scroll_pos = 1.0;
1218 }
1219
1220 // let scroll = state
1221 // .style
1222 // .scroll
1223 // .get(self.entity)
1224 // .cloned()
1225 // .unwrap_or_default();
1226 //self.vertical_scroll
1227 // .set_top(state, Units::Pixels(self.position + dist_y));
1228
1229 let overflow = 1.0
1230 - (state.data.get_height(self.container)
1231 / state.data.get_height(entity));
1232 let overflow2 = 1.0
1233 - (state.data.get_height(entity)
1234 / state.data.get_height(self.container));
1235
1236 self.container
1237 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow * 100.0));
1238 self.vertical_scroll
1239 .set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
1240
1241 state.insert_event(Event::new(WindowEvent::Restyle));
1242 state
1243 .insert_event(Event::new(WindowEvent::Relayout).target(Entity::null()));
1244 state.insert_event(Event::new(WindowEvent::Redraw));
1245 //println!("overflow: {}, dist: {}, ratio: {}, scrolly: {}", overflow, dist_y, r, self.scroll.scroll_pos);
1246 }
1247
1248 if self.moving && state.captured == self.horizontal_scroll {
1249 let dist_x = *x - self.pressedx;
1250 let overflow = state.data.get_width(entity)
1251 - state.data.get_width(self.vertical_scroll);
1252
1253 if overflow == 0.0 {
1254 return;
1255 }
1256
1257 let ratio = dist_x / overflow;
1258 let r = self.position + ratio;
1259
1260 // let mut scrollh = state.data.get_height(entity) / state.data.get_height(self.container);
1261 // if scrollh > 1.0 {
1262 // scrollh = 1.0;
1263 // }
1264
1265 self.scrollx = r;
1266
1267 if self.scrollx < 0.0 {
1268 self.scrollx = 0.0;
1269 }
1270
1271 if self.scrollx > 1.0 {
1272 self.scrollx = 1.0;
1273 }
1274
1275 // let scroll = state
1276 // .style
1277 // .scroll
1278 // .get(self.entity)
1279 // .cloned()
1280 // .unwrap_or_default();
1281 //self.vertical_scroll
1282 // .set_top(state, Units::Pixels(self.position + dist_y));
1283
1284 let overflow = 1.0
1285 - (state.data.get_width(self.container) / state.data.get_width(entity));
1286 let overflow2 = 1.0
1287 - (state.data.get_width(entity) / state.data.get_width(self.container));
1288
1289 self.container
1290 .set_left(state, Units::Percentage(self.scrollx * overflow * 100.0));
1291 self.vertical_scroll
1292 .set_left(state, Units::Percentage(self.scrollx * overflow2 * 100.0));
1293
1294 state.insert_event(Event::new(WindowEvent::Restyle));
1295 state
1296 .insert_event(Event::new(WindowEvent::Relayout).target(Entity::null()));
1297 state.insert_event(Event::new(WindowEvent::Redraw));
1298 //println!("overflow: {}, dist: {}, ratio: {}, scrolly: {}", overflow, dist_y, r, self.scroll.scroll_pos);
1299 }
1300 }
1301
1302 _ => {}
1303 }
1304 }
1305 }
1306}
1307*/