1use crate::{ScrollBar, ScrollBarMode, ScrollBarMsg};
9use kas::event::{CursorIcon, Scroll, components::ScrollComponent};
10use kas::prelude::*;
11use std::fmt::Debug;
12
13#[impl_self]
14mod ClipRegion {
15 #[derive(Debug, Default)]
31 #[widget]
32 pub struct ClipRegion<W: Widget> {
33 core: widget_core!(),
34 min_child_size: Size,
35 offset: Offset,
36 frame_size: Size,
37 #[widget]
38 inner: W,
39 }
40
41 impl Self {
42 #[inline]
44 pub fn new(inner: W) -> Self {
45 ClipRegion {
46 core: Default::default(),
47 min_child_size: Size::ZERO,
48 offset: Default::default(),
49 frame_size: Default::default(),
50 inner,
51 }
52 }
53
54 #[inline]
56 pub fn inner(&self) -> &W {
57 &self.inner
58 }
59
60 #[inline]
62 pub fn inner_mut(&mut self) -> &mut W {
63 &mut self.inner
64 }
65 }
66
67 impl Layout for Self {
68 fn size_rules(&mut self, cx: &mut SizeCx, mut axis: AxisInfo) -> SizeRules {
69 let dir = axis.as_direction();
70 axis.map_other(|x| {
71 (x - self.frame_size.extract(dir)).max(self.min_child_size.extract(dir))
72 });
73
74 let mut rules = self.inner.size_rules(cx, axis);
75 self.min_child_size.set_component(axis, rules.min_size());
76 rules.reduce_min_to(cx.min_scroll_size(axis, None));
77
78 let frame = kas::layout::FrameRules::ZERO;
80 let (rules, offset, size) = frame.surround(rules);
81 self.offset.set_component(axis, offset);
82 self.frame_size.set_component(axis, size);
83 rules
84 }
85
86 fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
87 self.core.set_rect(rect);
88 let child_size = (rect.size - self.frame_size).max(self.min_child_size);
89 let child_rect = Rect::new(rect.pos, child_size);
90 self.inner.set_rect(cx, child_rect, hints);
91 }
92 }
93
94 impl Viewport for Self {
95 #[inline]
96 fn content_size(&self) -> Size {
97 self.min_child_size
98 }
99
100 fn draw_with_offset(&self, mut draw: DrawCx, rect: Rect, offset: Offset) {
101 draw.with_clip_region(rect, offset, |mut draw| {
103 self.inner.draw(draw.re());
104 });
105 }
106 }
107
108 impl Events for Self {
109 type Data = W::Data;
110
111 fn probe(&self, coord: Coord) -> Id {
112 self.inner.try_probe(coord).unwrap_or_else(|| self.id())
113 }
114 }
115}
116
117#[impl_self]
118mod ScrollRegion {
119 #[derive(Debug, Default)]
150 #[widget]
151 pub struct ScrollRegion<W: Viewport + Widget> {
152 core: widget_core!(),
153 scroll: ScrollComponent,
154 mode: ScrollBarMode,
155 show_bars: (bool, bool), hints: AlignHints,
157 #[widget(&())]
158 horiz_bar: ScrollBar<kas::dir::Right>,
159 #[widget(&())]
160 vert_bar: ScrollBar<kas::dir::Down>,
161 #[widget]
162 inner: W,
163 }
164
165 impl<Inner: Widget> ScrollRegion<ClipRegion<Inner>> {
166 #[inline]
173 pub fn new_clip(inner: Inner) -> Self {
174 Self::new_viewport(ClipRegion::new(inner))
175 }
176 }
177
178 impl Self {
179 #[inline]
183 pub fn new_viewport(inner: W) -> Self {
184 ScrollRegion {
185 core: Default::default(),
186 scroll: Default::default(),
187 mode: ScrollBarMode::Auto,
188 show_bars: (false, false),
189 hints: Default::default(),
190 horiz_bar: ScrollBar::new(),
191 vert_bar: ScrollBar::new(),
192 inner,
193 }
194 }
195
196 #[inline]
198 pub fn with_fixed_bars(mut self, horiz: bool, vert: bool) -> Self
199 where
200 Self: Sized,
201 {
202 self.mode = ScrollBarMode::Fixed(horiz, vert);
203 self.horiz_bar.set_invisible(false);
204 self.vert_bar.set_invisible(false);
205 self.show_bars = (horiz, vert);
206 self
207 }
208
209 #[inline]
214 pub fn with_invisible_bars(mut self, horiz: bool, vert: bool) -> Self
215 where
216 Self: Sized,
217 {
218 self.mode = ScrollBarMode::Invisible(horiz, vert);
219 self.horiz_bar.set_invisible(true);
220 self.vert_bar.set_invisible(true);
221 self.show_bars = (horiz, vert);
222 self
223 }
224
225 #[inline]
227 pub fn scroll_bar_mode(&self) -> ScrollBarMode {
228 self.mode
229 }
230
231 pub fn set_scroll_bar_mode(&mut self, cx: &mut ConfigCx, mode: ScrollBarMode) {
233 if mode != self.mode {
234 self.mode = mode;
235 let (invis_horiz, invis_vert) = match mode {
236 ScrollBarMode::Auto => (false, false),
237 ScrollBarMode::Fixed(horiz, vert) => {
238 self.show_bars = (horiz, vert);
239 (false, false)
240 }
241 ScrollBarMode::Invisible(horiz, vert) => {
242 self.show_bars = (horiz, vert);
243 (horiz, vert)
244 }
245 };
246 self.horiz_bar.set_invisible(invis_horiz);
247 self.vert_bar.set_invisible(invis_vert);
248 cx.resize();
249 }
250 }
251
252 #[inline]
254 pub fn inner(&self) -> &W {
255 &self.inner
256 }
257
258 #[inline]
260 pub fn inner_mut(&mut self) -> &mut W {
261 &mut self.inner
262 }
263 }
264
265 impl Layout for Self {
266 fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules {
267 let mut rules = self.inner.size_rules(cx, axis);
268 let vert_rules = self.vert_bar.size_rules(cx, axis);
269 let horiz_rules = self.horiz_bar.size_rules(cx, axis);
270 let (use_horiz, use_vert) = match self.mode {
271 ScrollBarMode::Fixed(horiz, vert) => (horiz, vert),
272 ScrollBarMode::Auto => (true, true),
273 ScrollBarMode::Invisible(_, _) => (false, false),
274 };
275 if axis.is_horizontal() && use_horiz {
276 rules.append(vert_rules);
277 } else if axis.is_vertical() && use_vert {
278 rules.append(horiz_rules);
279 }
280 rules
281 }
282
283 fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
284 self.core.set_rect(rect);
285 self.hints = hints;
286 let pos = rect.pos;
287 let mut child_size = rect.size;
288
289 let bar_width = cx.scroll_bar_width();
290 let content_size = self.inner.content_size();
291 if self.mode == ScrollBarMode::Auto {
292 let max_offset = content_size - child_size;
293 self.show_bars.0 = max_offset.0 > 0;
294 self.show_bars.1 = max_offset.1 > 0;
295 }
296 if self.show_bars.0 && !self.horiz_bar.is_invisible() {
297 child_size.1 -= bar_width;
298 }
299 if self.show_bars.1 && !self.vert_bar.is_invisible() {
300 child_size.0 -= bar_width;
301 }
302
303 let child_rect = Rect::new(pos, child_size);
304 self.inner.set_rect(cx, child_rect, hints);
305
306 let _ = self.scroll.set_sizes(child_size, content_size);
307 let offset = self.scroll.offset();
308 let max_scroll_offset = self.scroll.max_offset();
309 self.inner.set_offset(cx, child_rect, offset);
310
311 if self.show_bars.0 {
312 let pos = Coord(pos.0, rect.pos2().1 - bar_width);
313 let size = Size::new(child_size.0, bar_width);
314 self.horiz_bar
315 .set_rect(cx, Rect { pos, size }, AlignHints::NONE);
316 self.horiz_bar
317 .set_limits(cx, max_scroll_offset.0, rect.size.0);
318 self.horiz_bar.set_value(cx, offset.0);
319 } else {
320 self.horiz_bar.set_rect(cx, Rect::ZERO, AlignHints::NONE);
321 }
322
323 if self.show_bars.1 {
324 let pos = Coord(rect.pos2().0 - bar_width, pos.1);
325 let size = Size::new(bar_width, self.rect().size.1);
326 self.vert_bar
327 .set_rect(cx, Rect { pos, size }, AlignHints::NONE);
328 self.vert_bar
329 .set_limits(cx, max_scroll_offset.1, rect.size.1);
330 self.vert_bar.set_value(cx, offset.1);
331 } else {
332 self.vert_bar.set_rect(cx, Rect::ZERO, AlignHints::NONE);
333 }
334 }
335
336 fn draw(&self, mut draw: DrawCx) {
337 let viewport = self.inner.rect();
338 self.inner
339 .draw_with_offset(draw.re(), viewport, self.scroll.offset());
340 if self.show_bars == (false, false) {
341 return;
342 }
343
344 let ev_state = draw.ev_state();
347 if matches!(self.mode, ScrollBarMode::Invisible(_, _))
348 && (self.horiz_bar.currently_visible(ev_state)
349 || self.vert_bar.currently_visible(ev_state))
350 {
351 draw.with_pass(|mut draw| {
352 if self.show_bars.0 {
353 self.horiz_bar.draw(draw.re());
354 }
355 if self.show_bars.1 {
356 self.vert_bar.draw(draw.re());
357 }
358 });
359 } else {
360 if self.show_bars.0 {
361 self.horiz_bar.draw(draw.re());
362 }
363 if self.show_bars.1 {
364 self.vert_bar.draw(draw.re());
365 }
366 }
367 }
368 }
369
370 impl Tile for Self {
371 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
372 Role::ScrollRegion {
373 offset: self.scroll.offset(),
374 max_offset: self.scroll.max_offset(),
375 }
376 }
377
378 #[inline]
379 fn translation(&self, index: usize) -> Offset {
380 if index == widget_index![self.inner] {
381 self.scroll.offset()
382 } else {
383 Offset::ZERO
384 }
385 }
386 }
387
388 impl Events for Self {
389 type Data = W::Data;
390
391 fn probe(&self, coord: Coord) -> Id {
392 if let Some(id) = self
393 .vert_bar
394 .try_probe(coord)
395 .or_else(|| self.horiz_bar.try_probe(coord))
396 {
397 return id;
398 }
399 if self.scroll.is_kinetic_scrolling() {
400 return self.id();
401 }
402
403 self.inner
404 .try_probe_with_offset(coord, self.scroll.offset())
405 .unwrap_or_else(|| self.id())
406 }
407
408 fn mouse_over_icon(&self) -> Option<CursorIcon> {
409 self.scroll
410 .is_kinetic_scrolling()
411 .then_some(CursorIcon::AllScroll)
412 }
413
414 fn configure(&mut self, cx: &mut ConfigCx) {
415 cx.register_nav_fallback(self.id());
416 }
417
418 fn handle_event(&mut self, cx: &mut EventCx, data: &Self::Data, event: Event) -> IsUsed {
419 let initial_offset = self.scroll.offset();
420 let is_used = self
421 .scroll
422 .scroll_by_event(cx, event, self.id(), self.inner.rect());
423
424 let offset = self.scroll.offset();
425 if offset != initial_offset {
426 self.horiz_bar.set_value(cx, offset.0);
427 self.vert_bar.set_value(cx, offset.1);
428 self.inner
429 .update_offset(cx, data, self.inner.rect(), offset);
430 }
431
432 is_used
433 }
434
435 fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) {
436 let index = cx.last_child();
437 let offset = if index == Some(widget_index![self.horiz_bar])
438 && let Some(ScrollBarMsg(x)) = cx.try_pop()
439 {
440 Offset(x, self.scroll.offset().1)
441 } else if index == Some(widget_index![self.vert_bar])
442 && let Some(ScrollBarMsg(y)) = cx.try_pop()
443 {
444 Offset(self.scroll.offset().0, y)
445 } else if let Some(kas::messages::SetScrollOffset(offset)) = cx.try_pop() {
446 self.horiz_bar.set_value(cx, offset.0);
447 self.vert_bar.set_value(cx, offset.1);
448 offset
449 } else {
450 return;
451 };
452
453 let action = self.scroll.set_offset(offset);
454 cx.action_moved(action);
455 self.inner
456 .update_offset(cx, data, self.inner.rect(), offset);
457 }
458
459 fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> Option<ActionResize> {
460 let _ = self.size_rules(&mut cx.size_cx(), AxisInfo::new(false, None));
461 let width = self.rect().size.0;
462 let _ = self.size_rules(&mut cx.size_cx(), AxisInfo::new(true, Some(width)));
463 self.set_rect(&mut cx.size_cx(), self.rect(), self.hints);
464 None
465 }
466
467 fn handle_scroll(&mut self, cx: &mut EventCx, data: &Self::Data, scroll: Scroll) {
468 self.scroll.scroll(cx, self.id(), self.rect(), scroll);
469
470 let offset = self.scroll.offset();
471 self.horiz_bar.set_value(cx, offset.0);
472 self.vert_bar.set_value(cx, offset.1);
473 self.inner
474 .update_offset(cx, data, self.inner.rect(), offset);
475 }
476 }
477}