agg_gui/widgets/scroll_view/
widget_impl.rs1use super::*;
2
3impl Widget for ScrollView {
4 fn type_name(&self) -> &'static str {
5 "ScrollView"
6 }
7 fn bounds(&self) -> Rect {
8 self.bounds
9 }
10 fn set_bounds(&mut self, b: Rect) {
11 self.bounds = b;
12 }
13 fn children(&self) -> &[Box<dyn Widget>] {
14 &self.children
15 }
16 fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
17 &mut self.children
18 }
19
20 fn needs_draw(&self) -> bool {
21 if !self.is_visible() {
22 return false;
23 }
24 self.scrollbar_animation_active()
25 || self.painted_style_epoch.get() != current_scroll_style_epoch()
26 || self.children().iter().any(|c| c.needs_draw())
27 }
28
29 fn margin(&self) -> Insets {
30 self.base.margin
31 }
32 fn h_anchor(&self) -> HAnchor {
33 self.base.h_anchor
34 }
35 fn v_anchor(&self) -> VAnchor {
36 self.base.v_anchor
37 }
38 fn min_size(&self) -> Size {
39 self.base.min_size
40 }
41 fn max_size(&self) -> Size {
42 self.base.max_size
43 }
44
45 fn hit_test(&self, local_pos: Point) -> bool {
46 if self.v.dragging || self.h.dragging || self.middle_dragging {
47 return true;
48 }
49 let b = self.bounds();
50 local_pos.x >= 0.0
51 && local_pos.x <= b.width
52 && local_pos.y >= 0.0
53 && local_pos.y <= b.height
54 }
55
56 fn claims_pointer_exclusively(&self, local_pos: Point) -> bool {
57 if self.v.dragging || self.h.dragging || self.middle_dragging {
58 return true;
59 }
60 let (vw, vh) = self.viewport();
61 if self.v.enabled && self.v.content > vh && self.pos_in_v_hover(local_pos) {
62 return true;
63 }
64 if self.h.enabled && self.h.content > vw && self.pos_in_h_hover(local_pos) {
65 return true;
66 }
67 false
68 }
69
70 fn layout(&mut self, available: Size) -> Size {
71 if let Some(c) = &self.offset_cell {
73 self.v.offset = c.get();
74 }
75 if let Some(c) = &self.h_offset_cell {
76 self.h.offset = c.get();
77 }
78 if let Some(c) = &self.visibility_cell {
79 self.bar_visibility = c.get();
80 } else if !self.visibility_explicit {
81 self.bar_visibility = current_scroll_visibility();
82 }
83 if let Some(c) = &self.style_cell {
84 self.style = c.get();
85 } else if !self.style_explicit {
86 self.style = current_scroll_style();
89 }
90
91 self.bounds = Rect::new(0.0, 0.0, available.width, available.height);
92
93 let (vw_guess, _vh_guess) = self.viewport();
97 let child_in_w = if self.h.enabled {
98 f64::MAX / 2.0
99 } else {
100 vw_guess
101 };
102 let child_in_h = f64::MAX / 2.0;
103
104 if let Some(child) = self.children.first_mut() {
105 let natural = child.layout(Size::new(child_in_w, child_in_h));
106 self.v.content = natural.height;
107 self.h.content = if self.h.enabled {
108 natural.width
109 } else {
110 vw_guess
111 };
112 }
113
114 let (vw, vh) = self.viewport();
117
118 if self.stick_to_bottom && self.was_at_bottom {
119 self.v.offset = self.v.max_scroll(vh);
120 }
121 self.clamp_offsets();
122 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
123
124 if let Some(c) = &self.offset_cell {
126 c.set(self.v.offset);
127 }
128 if let Some(c) = &self.max_scroll_cell {
129 c.set(self.v.max_scroll(vh));
130 }
131 if let Some(c) = &self.h_offset_cell {
132 c.set(self.h.offset);
133 }
134 if let Some(c) = &self.h_max_scroll_cell {
135 c.set(self.h.max_scroll(vw));
136 }
137 if let Some(c) = &self.viewport_cell {
138 c.set(Rect::new(self.h.offset, self.v.offset, vw, vh));
145 }
146
147 if let Some(child) = self.children.first_mut() {
149 let child_y = vh - self.v.content + self.v.offset;
150 let child_x = -self.h.offset;
151 child.set_bounds(Rect::new(
152 child_x.round(),
153 child_y.round(),
154 if self.h.enabled { self.h.content } else { vw },
155 self.v.content,
156 ));
157 }
158
159 available
160 }
161
162 fn paint(&mut self, _ctx: &mut dyn DrawCtx) {}
163
164 fn clip_children_rect(&self) -> Option<(f64, f64, f64, f64)> {
165 let (vw, vh) = self.viewport();
168 Some((0.0, self.bounds.height - vh, vw, vh))
169 }
170
171 fn paint_overlay(&mut self, ctx: &mut dyn DrawCtx) {
172 self.painted_style_epoch.set(current_scroll_style_epoch());
173
174 if self.style.fade_strength > 0.001 && self.style.fade_size > 0.5 {
179 self.paint_fade(ctx);
180 }
181
182 let (_, vh) = self.viewport();
184 let v_geom = self.v_scrollbar_geometry();
185 if let Some(bar) = self
186 .v
187 .prepare_paint(vh, self.style, self.bar_visibility, v_geom)
188 {
189 paint_prepared_scrollbar(ctx, bar);
190 }
191
192 let (vw, _) = self.viewport();
194 let h_geom = self.h_scrollbar_geometry();
195 if let Some(bar) = self
196 .h
197 .prepare_paint(vw, self.style, self.bar_visibility, h_geom)
198 {
199 paint_prepared_scrollbar(ctx, bar);
200 }
201 }
202
203 fn on_event(&mut self, event: &Event) -> EventResult {
204 match event {
205 Event::MouseWheel {
207 delta_y, delta_x, ..
208 } => {
209 let mut consumed = false;
210 if self.v.enabled {
211 self.v.offset = self.v.offset + delta_y * 40.0;
212 consumed = true;
213 }
214 if self.h.enabled {
215 self.h.offset = self.h.offset + delta_x * 40.0;
216 consumed = true;
217 }
218 self.clamp_offsets();
219 let (_, vh) = self.viewport();
220 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
221 if let Some(c) = &self.offset_cell {
222 c.set(self.v.offset);
223 }
224 if let Some(c) = &self.h_offset_cell {
225 c.set(self.h.offset);
226 }
227 if consumed {
228 crate::animation::request_draw();
229 EventResult::Consumed
230 } else {
231 EventResult::Ignored
232 }
233 }
234
235 Event::MouseMove { pos } => {
237 if self.middle_dragging {
238 let world = crate::widget::current_mouse_world().unwrap_or(*pos);
239 let dx = world.x - self.middle_start_world.x;
240 let dy = world.y - self.middle_start_world.y;
241 if self.h.enabled {
242 self.h.offset = self.middle_start_h_offset - dx;
243 }
244 if self.v.enabled {
245 self.v.offset = self.middle_start_v_offset + dy;
246 }
247 self.clamp_offsets();
248 let (_, vh) = self.viewport();
249 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
250 self.publish_offsets();
251 crate::animation::request_draw();
252 return EventResult::Consumed;
253 }
254
255 let (vw, vh) = self.viewport();
256 let v_scroll = self.v.enabled && self.v.content > vh;
257 let h_scroll = self.h.enabled && self.h.content > vw;
258 let v_hover_changed =
259 self.v
260 .update_hover(*pos, vh, self.style, self.v_scrollbar_geometry());
261 let h_hover_changed =
262 self.h
263 .update_hover(*pos, vw, self.style, self.h_scrollbar_geometry());
264 if (v_scroll && v_hover_changed) || (h_scroll && h_hover_changed) {
265 crate::animation::request_draw();
266 }
267
268 if self.v.dragging {
269 if self
270 .v
271 .drag_to(*pos, vh, self.style, self.v_scrollbar_geometry())
272 {
273 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
274 if let Some(c) = &self.offset_cell {
275 c.set(self.v.offset);
276 }
277 crate::animation::request_draw();
278 }
279 return EventResult::Consumed;
280 }
281 if self.h.dragging {
282 if self
283 .h
284 .drag_to(*pos, vw, self.style, self.h_scrollbar_geometry())
285 {
286 if let Some(c) = &self.h_offset_cell {
287 c.set(self.h.offset);
288 }
289 crate::animation::request_draw();
290 }
291 return EventResult::Consumed;
292 }
293 EventResult::Ignored
294 }
295
296 Event::MouseDown {
298 pos,
299 button: MouseButton::Middle,
300 ..
301 } => {
302 let (vw, vh) = self.viewport();
303 if (self.v.enabled && self.v.content > vh)
304 || (self.h.enabled && self.h.content > vw)
305 {
306 self.middle_dragging = true;
307 self.middle_start_world = crate::widget::current_mouse_world().unwrap_or(*pos);
308 self.middle_start_v_offset = self.v.offset;
309 self.middle_start_h_offset = self.h.offset;
310 crate::animation::request_draw();
311 return EventResult::Consumed;
312 }
313 EventResult::Ignored
314 }
315
316 Event::MouseDown {
317 pos,
318 button: MouseButton::Left,
319 ..
320 } => {
321 let (vw, vh) = self.viewport();
322 let v_scroll = self.v.enabled && self.v.content > vh;
323 let h_scroll = self.h.enabled && self.h.content > vw;
324
325 if v_scroll && self.pos_in_v_hover(*pos) {
326 if self
327 .v
328 .begin_drag(*pos, vh, self.style, self.v_scrollbar_geometry())
329 {
330 } else if self
333 .v
334 .page_at(*pos, vh, self.style, self.v_scrollbar_geometry())
335 {
336 if let Some(c) = &self.offset_cell {
337 c.set(self.v.offset);
338 }
339 crate::animation::request_draw();
341 }
342 return EventResult::Consumed;
343 }
344 if h_scroll && self.pos_in_h_hover(*pos) {
345 if self
346 .h
347 .begin_drag(*pos, vw, self.style, self.h_scrollbar_geometry())
348 {
349 } else if self
351 .h
352 .page_at(*pos, vw, self.style, self.h_scrollbar_geometry())
353 {
354 if let Some(c) = &self.h_offset_cell {
355 c.set(self.h.offset);
356 }
357 crate::animation::request_draw();
358 }
359 return EventResult::Consumed;
360 }
361 EventResult::Ignored
362 }
363
364 Event::MouseUp { button, .. } => {
366 let was = self.v.dragging
367 || self.h.dragging
368 || (*button == MouseButton::Middle && self.middle_dragging);
369 self.v.dragging = false;
370 self.h.dragging = false;
371 if *button == MouseButton::Middle {
372 self.middle_dragging = false;
373 }
374 if was {
375 crate::animation::request_draw();
376 EventResult::Consumed
377 } else {
378 EventResult::Ignored
379 }
380 }
381
382 _ => EventResult::Ignored,
383 }
384 }
385
386 fn properties(&self) -> Vec<(&'static str, String)> {
390 let (vw, vh) = self.viewport();
391 vec![
392 ("v_enabled", self.v.enabled.to_string()),
393 ("h_enabled", self.h.enabled.to_string()),
394 ("bar_visibility", format!("{:?}", self.bar_visibility)),
395 ("v_offset", format!("{:.1}", self.v.offset)),
396 ("h_offset", format!("{:.1}", self.h.offset)),
397 ("max_scroll", format!("{:.1}", self.v.max_scroll(vh))),
398 ("h_max_scroll", format!("{:.1}", self.h.max_scroll(vw))),
399 ("v_content", format!("{:.1}", self.v.content)),
400 ("h_content", format!("{:.1}", self.h.content)),
401 ]
402 }
403}
404
405impl ScrollView {
406 fn paint_fade(&self, ctx: &mut dyn DrawCtx) {
411 let v = ctx.visuals();
412 let c = v.window_fill;
413 let (vw, vh) = self.viewport();
414 let strength = self.style.fade_strength.clamp(0.0, 1.0) as f32;
415 let size = self.style.fade_size.max(0.0);
416 let max_a = strength;
417
418 if self.v.enabled {
420 if self.v.offset > 0.5 {
421 Self::fill_v_gradient(
423 ctx,
424 c,
425 max_a,
426 0.0,
427 self.bounds.height - size,
428 vw,
429 size,
430 false,
431 );
432 }
433 if (self.v.max_scroll(vh) - self.v.offset) > 0.5 {
434 let y_bottom = self.bounds.height - vh;
436 Self::fill_v_gradient(ctx, c, max_a, 0.0, y_bottom, vw, size, true);
437 }
438 }
439 if self.h.enabled {
440 if self.h.offset > 0.5 {
441 Self::fill_h_gradient(ctx, c, max_a, 0.0, self.bounds.height - vh, size, vh, true);
443 }
444 if (self.h.max_scroll(vw) - self.h.offset) > 0.5 {
445 Self::fill_h_gradient(
447 ctx,
448 c,
449 max_a,
450 vw - size,
451 self.bounds.height - vh,
452 size,
453 vh,
454 false,
455 );
456 }
457 }
458 }
459
460 fn fill_v_gradient(
466 ctx: &mut dyn DrawCtx,
467 c: Color,
468 max_alpha: f32,
469 x: f64,
470 y: f64,
471 w: f64,
472 h: f64,
473 opaque_at_bottom: bool,
474 ) {
475 const STEPS: usize = 64;
476 let strip_h = h / STEPS as f64;
477 for i in 0..STEPS {
478 let t = (i as f32 + 0.5) / STEPS as f32;
480 let a = if opaque_at_bottom { 1.0 - t } else { t };
481 ctx.set_fill_color(Color::rgba(c.r, c.g, c.b, a * max_alpha));
482 ctx.begin_path();
483 ctx.rect(x, y + i as f64 * strip_h, w, strip_h + 0.5);
484 ctx.fill();
485 }
486 }
487
488 fn fill_h_gradient(
494 ctx: &mut dyn DrawCtx,
495 c: Color,
496 max_alpha: f32,
497 x: f64,
498 y: f64,
499 w: f64,
500 h: f64,
501 opaque_at_left: bool,
502 ) {
503 const STEPS: usize = 64;
504 let strip_w = w / STEPS as f64;
505 for i in 0..STEPS {
506 let t = (i as f32 + 0.5) / STEPS as f32;
507 let a = if opaque_at_left { 1.0 - t } else { t };
508 ctx.set_fill_color(Color::rgba(c.r, c.g, c.b, a * max_alpha));
509 ctx.begin_path();
510 ctx.rect(x + i as f64 * strip_w, y, strip_w + 0.5, h);
511 ctx.fill();
512 }
513 }
514}