minifb_ui/ui/
scrollarea.rs1pub struct ScrollArea {
2 pub pos_x: usize,
3 pub pos_y: usize,
4 pub width: usize,
5 pub height: usize,
6
7 pub content_height: usize,
8 scroll_offset: f32,
9 pub scroll_speed: f32,
10
11 pub scrollbar_width: usize,
12 pub scrollbar_color: crate::color::Color,
13 pub scrollbar_track_color: crate::color::Color,
14 pub scrollbar_radius: usize,
15
16 dragging: bool,
17 drag_start_y: f32,
18 drag_start_offset: f32,
19 lmb_was_down: bool,
20}
21
22impl Default for ScrollArea {
23 fn default() -> Self {
24 Self {
25 pos_x: 0,
26 pos_y: 0,
27 width: 200,
28 height: 300,
29 content_height: 300,
30 scroll_offset: 0.0,
31 scroll_speed: 3.0,
32 scrollbar_width: 6,
33 scrollbar_color: crate::color::Color::rgba(200, 200, 210, 120),
34 scrollbar_track_color: crate::color::Color::rgba(60, 60, 80, 40),
35 scrollbar_radius: 3,
36 dragging: false,
37 drag_start_y: 0.0,
38 drag_start_offset: 0.0,
39 lmb_was_down: false,
40 }
41 }
42}
43
44impl ScrollArea {
45 pub fn position(mut self, x: usize, y: usize) -> Self {
46 self.pos_x = x;
47 self.pos_y = y;
48 self
49 }
50
51 pub fn size(mut self, width: usize, height: usize) -> Self {
52 self.width = width;
53 self.height = height;
54 self
55 }
56
57 pub fn content_height(mut self, h: usize) -> Self {
58 self.content_height = h;
59 self
60 }
61
62 pub fn scroll_speed(mut self, speed: f32) -> Self {
63 self.scroll_speed = speed;
64 self
65 }
66
67 pub fn scrollbar_width(mut self, w: usize) -> Self {
68 self.scrollbar_width = w;
69 self
70 }
71
72 pub fn scrollbar_color(mut self, color: crate::color::Color) -> Self {
73 self.scrollbar_color = color;
74 self
75 }
76
77 pub fn scrollbar_track_color(mut self, color: crate::color::Color) -> Self {
78 self.scrollbar_track_color = color;
79 self
80 }
81
82 pub fn scrollbar_radius(mut self, r: usize) -> Self {
83 self.scrollbar_radius = r;
84 self
85 }
86
87 pub fn set_content_height(&mut self, h: usize) {
89 self.content_height = h;
90 }
91
92 pub fn offset(&self) -> f32 {
94 self.scroll_offset
95 }
96
97 pub fn set_offset(&mut self, offset: f32) {
99 self.scroll_offset = offset.max(0.0);
100 self.clamp_offset();
101 }
102
103 pub fn can_scroll(&self) -> bool {
105 self.content_height > self.height
106 }
107
108 fn max_offset(&self) -> f32 {
109 (self.content_height as f32 - self.height as f32).max(0.0)
110 }
111
112 fn clamp_offset(&mut self) {
113 self.scroll_offset = self.scroll_offset.clamp(0.0, self.max_offset());
114 }
115
116 pub fn begin_draw(&mut self, window: &mut crate::window::Window) {
119 let mouse = window.get_mouse_state();
120 let mx = mouse.pos_x;
121 let my = mouse.pos_y;
122 let lmb = mouse.lmb_clicked;
123
124 let in_bounds = mx >= self.pos_x as f32
125 && mx < (self.pos_x + self.width) as f32
126 && my >= self.pos_y as f32
127 && my < (self.pos_y + self.height) as f32;
128
129 if in_bounds {
131 if let Some(scroll) = window.window.get_scroll_wheel() {
132 self.scroll_offset += scroll.1 * self.scroll_speed;
133 self.clamp_offset();
134 }
135 }
136
137 if self.can_scroll() {
139 let sb_x = self.pos_x + self.width - self.scrollbar_width - 2;
140 let thumb_frac = self.height as f32 / self.content_height as f32;
141 let thumb_h = (thumb_frac * self.height as f32).max(20.0);
142 let track_range = self.height as f32 - thumb_h;
143 let thumb_y = self.pos_y as f32 + (self.scroll_offset / self.max_offset()) * track_range;
144
145 let lmb_just = lmb && !self.lmb_was_down;
146
147 if lmb_just {
148 let on_thumb = mx >= sb_x as f32
149 && mx < (sb_x + self.scrollbar_width) as f32
150 && my >= thumb_y
151 && my < thumb_y + thumb_h;
152 if on_thumb {
153 self.dragging = true;
154 self.drag_start_y = my;
155 self.drag_start_offset = self.scroll_offset;
156 }
157 }
158
159 if self.dragging {
160 if lmb {
161 let delta = my - self.drag_start_y;
162 let ratio = delta / track_range;
163 self.scroll_offset = self.drag_start_offset + ratio * self.max_offset();
164 self.clamp_offset();
165 } else {
166 self.dragging = false;
167 }
168 }
169 }
170
171 self.lmb_was_down = lmb;
172
173 window.push_clip(self.pos_x, self.pos_y, self.width, self.height);
175 }
176
177 pub fn end_draw(&self, window: &mut crate::window::Window) {
179 window.pop_clip();
180
181 if self.can_scroll() {
183 let sb_x = self.pos_x + self.width - self.scrollbar_width - 2;
184 let thumb_frac = self.height as f32 / self.content_height as f32;
185 let thumb_h = (thumb_frac * self.height as f32).max(20.0) as usize;
186 let track_range = self.height as f32 - thumb_h as f32;
187 let thumb_y = self.pos_y + ((self.scroll_offset / self.max_offset()) * track_range) as usize;
188
189 window.draw_rect_f(
191 sb_x, self.pos_y, self.scrollbar_width, self.height,
192 self.scrollbar_radius, &self.scrollbar_track_color, 0,
193 );
194
195 window.draw_rect_f(
197 sb_x, thumb_y, self.scrollbar_width, thumb_h,
198 self.scrollbar_radius, &self.scrollbar_color, 0,
199 );
200 }
201 }
202}