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 widget_base(&self) -> Option<&WidgetBase> {
33 Some(&self.base)
34 }
35 fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
36 Some(&mut self.base)
37 }
38 fn h_anchor(&self) -> HAnchor {
39 self.base.h_anchor
40 }
41 fn v_anchor(&self) -> VAnchor {
42 self.base.v_anchor
43 }
44 fn min_size(&self) -> Size {
45 self.base.min_size
46 }
47 fn max_size(&self) -> Size {
48 self.base.max_size
49 }
50
51 fn hit_test(&self, local_pos: Point) -> bool {
52 if self.v.dragging || self.h.dragging || self.middle_dragging {
53 return true;
54 }
55 let b = self.bounds();
56 local_pos.x >= 0.0
57 && local_pos.x <= b.width
58 && local_pos.y >= 0.0
59 && local_pos.y <= b.height
60 }
61
62 fn claims_pointer_exclusively(&self, local_pos: Point) -> bool {
63 if self.v.dragging || self.h.dragging || self.middle_dragging {
64 return true;
65 }
66 let (vw, vh) = self.viewport();
67 if self.v.enabled && self.v.content > vh && self.pos_in_v_hover(local_pos) {
68 return true;
69 }
70 if self.h.enabled && self.h.content > vw && self.pos_in_h_hover(local_pos) {
71 return true;
72 }
73 false
74 }
75
76 fn layout(&mut self, available: Size) -> Size {
77 if let Some(c) = &self.offset_cell {
79 self.v.offset = c.get();
80 }
81 if let Some(c) = &self.h_offset_cell {
82 self.h.offset = c.get();
83 }
84 if let Some(c) = &self.visibility_cell {
85 self.bar_visibility = c.get();
86 } else if !self.visibility_explicit {
87 self.bar_visibility = current_scroll_visibility();
88 }
89 if let Some(c) = &self.style_cell {
90 self.style = c.get();
91 } else if !self.style_explicit {
92 self.style = current_scroll_style();
95 }
96
97 self.bounds = Rect::new(0.0, 0.0, available.width, available.height);
98
99 let (vw_guess, _vh_guess) = self.viewport();
103 let child_in_w = if self.h.enabled {
104 f64::MAX / 2.0
105 } else {
106 vw_guess
107 };
108 let child_in_h = f64::MAX / 2.0;
109
110 if let Some(child) = self.children.first_mut() {
111 let natural = child.layout(Size::new(child_in_w, child_in_h));
112 self.v.content = natural.height;
113 self.h.content = if self.h.enabled {
114 natural.width
115 } else {
116 vw_guess
117 };
118 }
119
120 let (vw, vh) = self.viewport();
123
124 if self.stick_to_bottom && self.was_at_bottom {
125 self.v.offset = self.v.max_scroll(vh);
126 }
127 self.clamp_offsets();
128 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
129
130 if let Some(c) = &self.offset_cell {
132 c.set(self.v.offset);
133 }
134 if let Some(c) = &self.max_scroll_cell {
135 c.set(self.v.max_scroll(vh));
136 }
137 if let Some(c) = &self.h_offset_cell {
138 c.set(self.h.offset);
139 }
140 if let Some(c) = &self.h_max_scroll_cell {
141 c.set(self.h.max_scroll(vw));
142 }
143 if let Some(c) = &self.viewport_cell {
144 c.set(Rect::new(self.h.offset, self.v.offset, vw, vh));
151 }
152
153 if let Some(child) = self.children.first_mut() {
155 let child_y = vh - self.v.content + self.v.offset;
156 let child_x = -self.h.offset;
157 child.set_bounds(Rect::new(
158 child_x.round(),
159 child_y.round(),
160 if self.h.enabled { self.h.content } else { vw },
161 self.v.content,
162 ));
163 }
164
165 available
166 }
167
168 fn paint(&mut self, _ctx: &mut dyn DrawCtx) {}
169
170 fn clip_children_rect(&self) -> Option<(f64, f64, f64, f64)> {
171 let (vw, vh) = self.viewport();
174 Some((0.0, self.bounds.height - vh, vw, vh))
175 }
176
177 fn paint_overlay(&mut self, ctx: &mut dyn DrawCtx) {
178 self.painted_style_epoch.set(current_scroll_style_epoch());
179
180 if self.style.fade_strength > 0.001 && self.style.fade_size > 0.5 {
185 self.paint_fade(ctx);
186 }
187
188 let (_, vh) = self.viewport();
190 let v_geom = self.v_scrollbar_geometry();
191 if let Some(bar) = self
192 .v
193 .prepare_paint(vh, self.style, self.bar_visibility, v_geom)
194 {
195 paint_prepared_scrollbar(ctx, bar);
196 }
197
198 let (vw, _) = self.viewport();
200 let h_geom = self.h_scrollbar_geometry();
201 if let Some(bar) = self
202 .h
203 .prepare_paint(vw, self.style, self.bar_visibility, h_geom)
204 {
205 paint_prepared_scrollbar(ctx, bar);
206 }
207 }
208
209 fn on_event(&mut self, event: &Event) -> EventResult {
210 match event {
211 Event::MouseWheel {
213 delta_y, delta_x, ..
214 } => {
215 let mut consumed = false;
216 if self.v.enabled {
217 self.v.offset = self.v.offset + delta_y * 40.0;
218 consumed = true;
219 }
220 if self.h.enabled {
221 self.h.offset = self.h.offset + delta_x * 40.0;
222 consumed = true;
223 }
224 self.clamp_offsets();
225 let (_, vh) = self.viewport();
226 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
227 if let Some(c) = &self.offset_cell {
228 c.set(self.v.offset);
229 }
230 if let Some(c) = &self.h_offset_cell {
231 c.set(self.h.offset);
232 }
233 if consumed {
234 crate::animation::request_draw();
235 EventResult::Consumed
236 } else {
237 EventResult::Ignored
238 }
239 }
240
241 Event::MouseMove { pos } => {
243 if self.middle_dragging {
244 let world = crate::widget::current_mouse_world().unwrap_or(*pos);
245 let dx = world.x - self.middle_start_world.x;
246 let dy = world.y - self.middle_start_world.y;
247 if self.h.enabled {
248 self.h.offset = self.middle_start_h_offset - dx;
249 }
250 if self.v.enabled {
251 self.v.offset = self.middle_start_v_offset + dy;
252 }
253 self.clamp_offsets();
254 let (_, vh) = self.viewport();
255 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
256 self.publish_offsets();
257 crate::animation::request_draw();
258 return EventResult::Consumed;
259 }
260
261 let (vw, vh) = self.viewport();
262 let v_scroll = self.v.enabled && self.v.content > vh;
263 let h_scroll = self.h.enabled && self.h.content > vw;
264 let v_hover_changed =
265 self.v
266 .update_hover(*pos, vh, self.style, self.v_scrollbar_geometry());
267 let h_hover_changed =
268 self.h
269 .update_hover(*pos, vw, self.style, self.h_scrollbar_geometry());
270 if (v_scroll && v_hover_changed) || (h_scroll && h_hover_changed) {
271 crate::animation::request_draw();
272 }
273
274 if self.v.dragging {
275 if self
276 .v
277 .drag_to(*pos, vh, self.style, self.v_scrollbar_geometry())
278 {
279 self.was_at_bottom = (self.v.max_scroll(vh) - self.v.offset).abs() < 0.5;
280 if let Some(c) = &self.offset_cell {
281 c.set(self.v.offset);
282 }
283 crate::animation::request_draw();
284 }
285 return EventResult::Consumed;
286 }
287 if self.h.dragging {
288 if self
289 .h
290 .drag_to(*pos, vw, self.style, self.h_scrollbar_geometry())
291 {
292 if let Some(c) = &self.h_offset_cell {
293 c.set(self.h.offset);
294 }
295 crate::animation::request_draw();
296 }
297 return EventResult::Consumed;
298 }
299 EventResult::Ignored
300 }
301
302 Event::MouseDown {
304 pos,
305 button: MouseButton::Middle,
306 ..
307 } => {
308 let (vw, vh) = self.viewport();
309 if (self.v.enabled && self.v.content > vh)
310 || (self.h.enabled && self.h.content > vw)
311 {
312 self.middle_dragging = true;
313 self.middle_start_world = crate::widget::current_mouse_world().unwrap_or(*pos);
314 self.middle_start_v_offset = self.v.offset;
315 self.middle_start_h_offset = self.h.offset;
316 crate::animation::request_draw();
317 return EventResult::Consumed;
318 }
319 EventResult::Ignored
320 }
321
322 Event::MouseDown {
323 pos,
324 button: MouseButton::Left,
325 ..
326 } => {
327 let (vw, vh) = self.viewport();
328 let v_scroll = self.v.enabled && self.v.content > vh;
329 let h_scroll = self.h.enabled && self.h.content > vw;
330
331 if v_scroll && self.pos_in_v_hover(*pos) {
332 if self
333 .v
334 .begin_drag(*pos, vh, self.style, self.v_scrollbar_geometry())
335 {
336 } else if self
339 .v
340 .page_at(*pos, vh, self.style, self.v_scrollbar_geometry())
341 {
342 if let Some(c) = &self.offset_cell {
343 c.set(self.v.offset);
344 }
345 crate::animation::request_draw();
347 }
348 return EventResult::Consumed;
349 }
350 if h_scroll && self.pos_in_h_hover(*pos) {
351 if self
352 .h
353 .begin_drag(*pos, vw, self.style, self.h_scrollbar_geometry())
354 {
355 } else if self
357 .h
358 .page_at(*pos, vw, self.style, self.h_scrollbar_geometry())
359 {
360 if let Some(c) = &self.h_offset_cell {
361 c.set(self.h.offset);
362 }
363 crate::animation::request_draw();
364 }
365 return EventResult::Consumed;
366 }
367 EventResult::Ignored
368 }
369
370 Event::MouseUp { button, .. } => {
372 let was = self.v.dragging
373 || self.h.dragging
374 || (*button == MouseButton::Middle && self.middle_dragging);
375 self.v.dragging = false;
376 self.h.dragging = false;
377 if *button == MouseButton::Middle {
378 self.middle_dragging = false;
379 }
380 if was {
381 crate::animation::request_draw();
382 EventResult::Consumed
383 } else {
384 EventResult::Ignored
385 }
386 }
387
388 _ => EventResult::Ignored,
389 }
390 }
391
392 fn properties(&self) -> Vec<(&'static str, String)> {
396 let (vw, vh) = self.viewport();
397 vec![
398 ("v_enabled", self.v.enabled.to_string()),
399 ("h_enabled", self.h.enabled.to_string()),
400 ("bar_visibility", format!("{:?}", self.bar_visibility)),
401 ("v_offset", format!("{:.1}", self.v.offset)),
402 ("h_offset", format!("{:.1}", self.h.offset)),
403 ("max_scroll", format!("{:.1}", self.v.max_scroll(vh))),
404 ("h_max_scroll", format!("{:.1}", self.h.max_scroll(vw))),
405 ("v_content", format!("{:.1}", self.v.content)),
406 ("h_content", format!("{:.1}", self.h.content)),
407 ]
408 }
409}
410
411impl ScrollView {
412 fn paint_fade(&self, ctx: &mut dyn DrawCtx) {
417 let v = ctx.visuals();
418 let c = self.fade_color.unwrap_or(v.window_fill);
425 let (vw, vh) = self.viewport();
426 let strength = self.style.fade_strength.clamp(0.0, 1.0) as f32;
427 let size = self.style.fade_size.max(0.0);
428 let max_a = strength;
429
430 if self.v.enabled {
432 if self.v.offset > 0.5 {
433 Self::fill_v_gradient(
435 ctx,
436 c,
437 max_a,
438 0.0,
439 self.bounds.height - size,
440 vw,
441 size,
442 false,
443 );
444 }
445 if (self.v.max_scroll(vh) - self.v.offset) > 0.5 {
446 let y_bottom = self.bounds.height - vh;
448 Self::fill_v_gradient(ctx, c, max_a, 0.0, y_bottom, vw, size, true);
449 }
450 }
451 if self.h.enabled {
452 if self.h.offset > 0.5 {
453 Self::fill_h_gradient(ctx, c, max_a, 0.0, self.bounds.height - vh, size, vh, true);
455 }
456 if (self.h.max_scroll(vw) - self.h.offset) > 0.5 {
457 Self::fill_h_gradient(
459 ctx,
460 c,
461 max_a,
462 vw - size,
463 self.bounds.height - vh,
464 size,
465 vh,
466 false,
467 );
468 }
469 }
470 }
471
472 fn fill_v_gradient(
478 ctx: &mut dyn DrawCtx,
479 c: Color,
480 max_alpha: f32,
481 x: f64,
482 y: f64,
483 w: f64,
484 h: f64,
485 opaque_at_bottom: bool,
486 ) {
487 const STEPS: usize = 64;
488 let strip_h = h / STEPS as f64;
489 for i in 0..STEPS {
490 let t = (i as f32 + 0.5) / STEPS as f32;
492 let a = if opaque_at_bottom { 1.0 - t } else { t };
493 ctx.set_fill_color(Color::rgba(c.r, c.g, c.b, a * max_alpha));
494 ctx.begin_path();
495 ctx.rect(x, y + i as f64 * strip_h, w, strip_h + 0.5);
496 ctx.fill();
497 }
498 }
499
500 fn fill_h_gradient(
506 ctx: &mut dyn DrawCtx,
507 c: Color,
508 max_alpha: f32,
509 x: f64,
510 y: f64,
511 w: f64,
512 h: f64,
513 opaque_at_left: bool,
514 ) {
515 const STEPS: usize = 64;
516 let strip_w = w / STEPS as f64;
517 for i in 0..STEPS {
518 let t = (i as f32 + 0.5) / STEPS as f32;
519 let a = if opaque_at_left { 1.0 - t } else { t };
520 ctx.set_fill_color(Color::rgba(c.r, c.g, c.b, a * max_alpha));
521 ctx.begin_path();
522 ctx.rect(x + i as f64 * strip_w, y, strip_w + 0.5, h);
523 ctx.fill();
524 }
525 }
526}