freya_components/scrollviews/
scrollbar.rs

1use freya_core::prelude::*;
2use torin::{
3    prelude::{
4        Alignment,
5        Direction,
6        Position,
7    },
8    size::Size,
9};
10
11use crate::{
12    get_theme,
13    scrollviews::{
14        ScrollThumb,
15        shared::Axis,
16    },
17    theming::component_themes::ScrollBarThemePartial,
18};
19
20#[derive(Clone, Copy, PartialEq, Debug)]
21enum ScrollBarState {
22    Idle,
23    Hovering,
24}
25
26#[derive(Clone, PartialEq)]
27pub struct ScrollBar {
28    pub(crate) theme: Option<ScrollBarThemePartial>,
29    pub clicking_scrollbar: State<Option<(Axis, f64)>>,
30    pub axis: Axis,
31    pub offset: f32,
32    pub size: Size,
33    pub thumb: ScrollThumb,
34}
35
36impl ComponentOwned for ScrollBar {
37    fn render(self) -> impl IntoElement {
38        let scrollbar_theme = get_theme!(&self.theme, scrollbar);
39
40        let mut state = use_state(|| ScrollBarState::Idle);
41
42        let (cross_size, cross_offset, opacity) = match *state.read() {
43            _ if self.clicking_scrollbar.read().is_some() => (16., 0., 160),
44            ScrollBarState::Idle => (12., 3., 0),
45            ScrollBarState::Hovering => (16., 0., 160),
46        };
47
48        let (
49            width,
50            height,
51            offset_x,
52            offset_y,
53            inner_offset_x,
54            inner_offset_y,
55            inner_width,
56            inner_height,
57        ) = match self.axis {
58            Axis::X => (
59                self.size.clone(),
60                Size::px(16.),
61                0.,
62                -16.,
63                self.offset,
64                cross_offset,
65                self.size.clone(),
66                Size::px(cross_size),
67            ),
68            Axis::Y => (
69                Size::px(16.),
70                self.size.clone(),
71                -16.,
72                0.,
73                cross_offset,
74                self.offset,
75                Size::px(cross_size),
76                self.size.clone(),
77            ),
78        };
79
80        let on_pointer_enter = move |_| {
81            state.set(ScrollBarState::Hovering);
82        };
83        let on_pointer_leave = move |_| state.set(ScrollBarState::Idle);
84
85        rect()
86            .position(Position::new_absolute())
87            .width(width)
88            .height(height)
89            .offset_x(offset_x)
90            .offset_y(offset_y)
91            .layer(999)
92            .child(
93                rect()
94                    .width(Size::fill())
95                    .height(Size::fill())
96                    .direction(if self.axis == Axis::Y {
97                        Direction::vertical()
98                    } else {
99                        Direction::horizontal()
100                    })
101                    .cross_align(Alignment::end())
102                    .background(scrollbar_theme.background.with_a(opacity))
103                    .on_pointer_enter(on_pointer_enter)
104                    .on_pointer_leave(on_pointer_leave)
105                    .child(
106                        rect()
107                            .width(inner_width)
108                            .height(inner_height)
109                            .offset_x(inner_offset_x)
110                            .offset_y(inner_offset_y)
111                            .child(self.thumb),
112                    ),
113            )
114    }
115}