1use super::*;
9use kas::event::components::ScrollComponent;
10use kas::event::{CursorIcon, FocusSource, NavAdvance, Scroll, TimerHandle};
11use kas::layout::{GridCellInfo, solve_size_rules};
12use kas::prelude::*;
13use kas::theme::SelectionStyle;
14#[allow(unused)] use kas_widgets::ScrollBars;
16use linear_map::set::LinearSet;
17use std::borrow::Borrow;
18use std::ops::Range;
19use std::time::Instant;
20
21const TIMER_UPDATE_WIDGETS: TimerHandle = TimerHandle::new(1, true);
22
23#[impl_self]
24mod GridCell {
25 #[widget]
36 #[layout(frame!(self.inner).with_style(kas::theme::FrameStyle::NavFocus))]
37 struct GridCell<K, I, V: Driver<K, I>> {
38 core: widget_core!(),
39 index: GridIndex,
40 selected: Option<bool>,
41 #[widget]
43 inner: V::Widget,
44 }
45
46 impl Self {
47 #[inline]
49 fn new(inner: V::Widget) -> Self {
50 GridCell {
51 core: Default::default(),
52 index: GridIndex::default(),
53 selected: None,
54 inner,
55 }
56 }
57 }
58
59 impl Tile for Self {
60 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
61 if let Some(label) = V::label(&self.inner) {
62 cx.set_label(label);
63 }
64 Role::GridCell {
65 info: Some(GridCellInfo::new(self.index.col, self.index.row)),
66 selected: self.selected,
67 }
68 }
69
70 fn navigable(&self) -> bool {
71 V::navigable(&self.inner)
72 }
73 }
74
75 impl Events for Self {
76 type Data = I;
77
78 fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
79 match event {
80 Event::Command(cmd, code) if cmd.is_activate() => {
81 cx.depress_with_key(&self, code);
82 cx.push(kas::messages::Select);
83 Used
84 }
85 _ => Unused,
86 }
87 }
88 }
89}
90
91#[autoimpl(Debug ignore self.item where C::Token: trait)]
92struct WidgetData<C: DataClerk<GridIndex>, V: Driver<C::Key, C::Item>> {
93 token: Option<C::Token>,
94 item: GridCell<C::Key, C::Item, V>,
95}
96
97impl<C: DataClerk<GridIndex>, V: Driver<C::Key, C::Item>> WidgetData<C, V> {
98 fn key(&self) -> Option<&C::Key> {
99 self.token.as_ref().map(Borrow::borrow)
100 }
101}
102
103#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
105pub struct GridIndex {
106 pub col: u32,
107 pub row: u32,
108}
109
110impl GridIndex {
111 pub const ZERO: GridIndex = GridIndex { col: 0, row: 0 };
113
114 pub const fn splat(x: u32) -> Self {
116 GridIndex { col: x, row: x }
117 }
118}
119
120impl std::ops::Add for GridIndex {
121 type Output = Self;
122 fn add(self, rhs: Self) -> Self {
123 GridIndex {
124 col: self.col + rhs.col,
125 row: self.row + rhs.row,
126 }
127 }
128}
129
130impl crate::DataKey for GridIndex {
131 fn make_id(&self, parent: &Id) -> Id {
132 parent
133 .make_child(self.col.cast())
134 .make_child(self.row.cast())
135 }
136
137 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
138 let mut iter = child.iter_keys_after(parent);
139 let col = iter.next().map(|i| i.cast())?;
140 let row = iter.next().map(|i| i.cast())?;
141 Some(GridIndex { col, row })
142 }
143}
144
145#[impl_self]
146mod GridView {
147 #[widget]
170 pub struct GridView<C: DataClerk<GridIndex>, V: Driver<C::Key, C::Item>> {
171 core: widget_core!(),
172 frame_offset: Offset,
173 frame_size: Size,
174 clerk: C,
175 driver: V,
176 widgets: Vec<WidgetData<C, V>>,
177 align_hints: AlignHints,
178 ideal_len: GridIndex,
179 alloc_len: GridIndex,
180 data_len: GridIndex,
181 token_update: Update,
182 rect_update: bool,
183 len_is_known: bool,
184 cur_len: GridIndex,
185 first_data: GridIndex,
186 child_size_min: Size,
187 child_size_ideal: Size,
188 child_inter_margin: Size,
189 child_size: Size,
190 scroll: ScrollComponent,
191 virtual_offset: Offset,
193 sel_mode: SelectionMode,
194 sel_style: SelectionStyle,
195 selection: LinearSet<C::Key>,
197 press_target: Option<(usize, C::Key)>,
198 }
199
200 impl Self {
201 pub fn new(clerk: C, driver: V) -> Self {
203 GridView {
204 core: Default::default(),
205 frame_offset: Default::default(),
206 frame_size: Default::default(),
207 clerk,
208 driver,
209 widgets: Default::default(),
210 align_hints: Default::default(),
211 ideal_len: GridIndex { col: 3, row: 5 },
212 alloc_len: GridIndex::ZERO,
213 data_len: GridIndex::ZERO,
214 token_update: Update::None,
215 rect_update: false,
216 len_is_known: false,
217 cur_len: GridIndex::ZERO,
218 first_data: GridIndex::ZERO,
219 child_size_min: Size::ZERO,
220 child_size_ideal: Size::ZERO,
221 child_inter_margin: Size::ZERO,
222 child_size: Size::ZERO,
223 scroll: Default::default(),
224 virtual_offset: Offset::ZERO,
225 sel_mode: SelectionMode::None,
226 sel_style: SelectionStyle::Highlight,
227 selection: Default::default(),
228 press_target: None,
229 }
230 }
231
232 pub fn clerk(&self) -> &C {
234 &self.clerk
235 }
236
237 pub fn clerk_mut(&mut self) -> &mut C {
243 &mut self.clerk
244 }
245
246 pub fn view_range(&self) -> Range<GridIndex> {
251 self.first_data..self.first_data + self.cur_len
252 }
253
254 pub fn selection_mode(&self) -> SelectionMode {
256 self.sel_mode
257 }
258 pub fn set_selection_mode(&mut self, cx: &mut EventState, mode: SelectionMode) {
268 self.sel_mode = mode;
269 match mode {
270 SelectionMode::None if !self.selection.is_empty() => {
271 self.selection.clear();
272 cx.redraw(self);
273 }
274 SelectionMode::Single if self.selection.len() > 1 => {
275 if let Some(first) = self.selection.iter().next().cloned() {
276 self.selection.retain(|item| *item == first);
277 }
278 cx.redraw(self);
279 }
280 _ => (),
281 }
282 }
283 #[must_use]
287 pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
288 debug_assert!(self.selection.is_empty());
289 self.sel_mode = mode;
290 self
291 }
292
293 pub fn selection_style(&self) -> SelectionStyle {
295 self.sel_style
296 }
297 pub fn set_selection_style(&mut self, cx: &mut EventState, style: SelectionStyle) {
302 if style.is_external() != self.sel_style.is_external() {
303 cx.resize(&self);
304 };
305 self.sel_style = style;
306 }
307 #[must_use]
311 pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
312 self.sel_style = style;
313 self
314 }
315
316 pub fn selected_iter(&'_ self) -> impl Iterator<Item = &'_ C::Key> + '_ {
321 self.selection.iter()
322 }
323
324 pub fn is_selected(&self, key: &C::Key) -> bool {
326 self.selection.contains(key)
327 }
328
329 pub fn clear_selected(&mut self, cx: &mut EventState) {
331 if !self.selection.is_empty() {
332 self.selection.clear();
333 cx.redraw(self);
334 }
335 }
336
337 pub fn select(&mut self, cx: &mut EventState, key: C::Key) -> bool {
347 match self.sel_mode {
348 SelectionMode::None => return false,
349 SelectionMode::Single => self.selection.clear(),
350 _ => (),
351 }
352 let r = self.selection.insert(key);
353 if r {
354 cx.redraw(self);
355 }
356 r
357 }
358
359 pub fn deselect(&mut self, cx: &mut EventState, key: &C::Key) -> bool {
364 let r = self.selection.remove(key);
365 if r {
366 cx.redraw(self);
367 }
368 r
369 }
370
371 #[must_use]
376 pub fn with_num_visible(mut self, cols: u32, rows: u32) -> Self {
377 self.ideal_len = GridIndex {
378 col: cols,
379 row: rows,
380 };
381 self
382 }
383
384 #[inline]
386 fn cur_end(&self) -> usize {
387 usize::conv(self.cur_len.col) * usize::conv(self.cur_len.row)
388 }
389
390 fn position_solver(&self) -> PositionSolver {
391 PositionSolver {
392 pos_start: self.rect().pos + self.frame_offset + self.virtual_offset,
393 skip: self.child_size + self.child_inter_margin,
394 size: self.child_size,
395 first_data: self.first_data,
396 cur_len: self.cur_len,
397 }
398 }
399
400 fn post_scroll(&mut self, cx: &mut ConfigCx, data: &C::Data) {
404 let offset = self.scroll_offset();
405 let skip = (self.child_size + self.child_inter_margin).max(Size(1, 1));
406 let first_col = u32::conv(u64::conv(offset.0) / u64::conv(skip.0));
407 let first_row = u32::conv(u64::conv(offset.1) / u64::conv(skip.1));
408
409 let lbound = GridIndex {
410 col: first_col + 2 * self.alloc_len.col,
411 row: first_row + 2 * self.alloc_len.row,
412 };
413
414 let data_len = self.clerk.len(data, lbound);
415 self.len_is_known = data_len.is_known();
416 let data_len = data_len.len();
417 if data_len != self.data_len {
418 self.data_len = data_len;
419 self.update_content_size(cx);
420 self.token_update = Update::Token;
421 }
422
423 let col_len = data_len.col.min(self.alloc_len.col);
424 let row_len = data_len.row.min(self.alloc_len.row);
425
426 let first_data = GridIndex {
427 col: first_col.min(data_len.col - col_len),
428 row: first_row.min(data_len.row - row_len),
429 };
430 let cur_len = GridIndex {
431 col: col_len.cast(),
432 row: row_len.cast(),
433 };
434 let (mut start, mut end) = (first_data, first_data + cur_len);
435 let (old_start, old_end) = (self.first_data, self.first_data + self.cur_len);
436
437 let virtual_offset = -Offset(offset.0 & 0x7FF0_0000, offset.1 & 0x7FF0_0000);
438 if virtual_offset != self.virtual_offset {
439 self.virtual_offset = virtual_offset;
440 self.rect_update = true;
441 } else if self.rect_update || self.token_update != Update::None {
442 } else if start == old_start && cur_len == self.cur_len {
444 return;
445 } else if start.col == old_start.col && end.col == old_end.col {
446 if start.row >= old_start.row {
447 start.row = start.row.max(old_end.row);
448 } else if end.row <= old_end.row {
449 end.row = end.row.min(old_start.row);
450 }
451 if start.row >= end.row {
452 return;
453 }
454 } else if start.row == old_start.row && end.row == old_end.row {
455 if start.col >= old_start.col {
456 start.col = start.col.max(old_end.col);
457 } else if end.col <= old_end.col {
458 end.col = end.col.min(old_start.col);
459 }
460 if start.col >= end.col {
461 return;
462 }
463 }
464
465 self.cur_len = cur_len;
466 debug_assert!(self.cur_end() <= self.widgets.len());
467 self.first_data = first_data;
468
469 self.map_view_widgets(cx, data, start..end);
470 }
471
472 fn map_view_widgets(
476 &mut self,
477 cx: &mut ConfigCx,
478 data: &C::Data,
479 Range { start, end }: Range<GridIndex>,
480 ) {
481 let time = Instant::now();
482
483 self.clerk
484 .prepare_range(cx, self.id(), self.view_range(), data, start..end);
485
486 let id = self.id();
487
488 let solver = self.position_solver();
489 for row in start.row..end.row {
490 for col in start.col..end.col {
491 let cell = GridIndex { col, row };
492 let i = solver.data_to_child(cell);
493 let w = &mut self.widgets[i];
494
495 let force = self.token_update != Update::None;
496 let changes = self.clerk.update_token(data, cell, force, &mut w.token);
497 let Some(token) = w.token.as_ref() else {
498 continue;
499 };
500
501 let mut rect_update = self.rect_update;
502 if changes.key() || self.token_update == Update::Configure {
503 w.item.index = cell;
504 self.driver.set_key(&mut w.item.inner, token.borrow());
507
508 let item = self.clerk.item(data, token);
509 let id = token.borrow().make_id(&id);
510 cx.configure(w.item.as_node(item), id);
511
512 solve_size_rules(
513 &mut w.item,
514 cx.size_cx(),
515 Some(self.child_size.0),
516 Some(self.child_size.1),
517 );
518 rect_update = true;
519 } else if changes.item() {
520 let item = self.clerk.item(data, token);
521 cx.update(w.item.as_node(item));
522 }
523
524 if rect_update {
525 w.item.set_rect(cx, solver.rect(cell), self.align_hints);
526 }
527 }
528 }
529
530 self.token_update = Update::None;
531 self.rect_update = false;
532
533 let dur = (Instant::now() - time).as_micros();
534 log::debug!(
535 target: "kas_perf::view::grid_view",
536 "map_view_widgets {}×{} widgets in {dur}μs",
537 end.col - start.col,
538 end.row - start.row,
539 );
540 }
541
542 fn handle_clerk_update(
544 &mut self,
545 cx: &mut ConfigCx,
546 data: &C::Data,
547 changes: DataChanges<GridIndex>,
548 ) {
549 let start = self.first_data;
550 let end = self.first_data + self.cur_len;
551 let range = match changes {
552 DataChanges::None | DataChanges::NoPreparedItems => start..start,
553 DataChanges::Range(range) => {
554 let start = GridIndex {
555 col: start.col.max(range.start.col),
556 row: start.row.max(range.start.row),
557 };
558 let end = GridIndex {
559 col: end.col.min(range.end.col),
560 row: end.row.min(range.end.row),
561 };
562 start..end
563 }
564 DataChanges::Any => start..end,
565 };
566
567 let lbound = GridIndex {
568 col: self.first_data.col + 2 * self.alloc_len.col,
569 row: self.first_data.row + 2 * self.alloc_len.row,
570 };
571 let data_len = self.clerk.len(data, lbound);
572 self.len_is_known = data_len.is_known();
573 let data_len = data_len.len();
574 if data_len != self.data_len {
575 self.data_len = data_len;
576 self.token_update = Update::Token;
577
578 if self.update_content_size(cx) {
579 cx.resize(&self);
582 return;
583 }
584
585 if self.cur_len.col != data_len.col.min(self.alloc_len.col)
586 || self.cur_len.row != data_len.row.min(self.alloc_len.row)
587 {
588 return self.post_scroll(cx, data);
590 }
591 }
592
593 if range.start.col < range.end.col && range.start.row < range.end.row {
594 self.token_update = self.token_update.max(Update::Token);
595 return self.map_view_widgets(cx, data, start..end);
596 }
597 }
598
599 fn update_content_size(&mut self, cx: &mut ConfigCx) -> bool {
601 let view_size = self.rect().size - self.frame_size;
602 let content_size = self.content_size();
603 let action = self.scroll.set_sizes(view_size, content_size);
604 cx.action(self, action);
605 !action.is_empty()
606 }
607 }
608
609 impl Scrollable for Self {
610 fn content_size(&self) -> Size {
611 let data_len = self.data_len;
612 let m = self.child_inter_margin;
613 let step = self.child_size + m;
614 Size(
615 step.0 * i32::conv(data_len.col) - m.0,
616 step.1 * i32::conv(data_len.row) - m.1,
617 )
618 .max(Size::ZERO)
619 }
620
621 #[inline]
622 fn max_scroll_offset(&self) -> Offset {
623 self.scroll.max_offset()
624 }
625
626 #[inline]
627 fn scroll_offset(&self) -> Offset {
628 self.scroll.offset()
629 }
630
631 fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset {
632 let action = self.scroll.set_offset(offset);
633 if !action.is_empty() {
634 cx.action(&self, action);
635 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
636 }
637 self.scroll.offset()
638 }
639 }
640
641 impl Layout for Self {
642 fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
643 let inner_margin = if self.sel_style.is_external() {
645 sizer.inner_margins().extract(axis)
646 } else {
647 (0, 0)
648 };
649 let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));
650
651 let other = axis.other().map(|mut size| {
652 let other_axis = axis.flipped();
654 size -= self.frame_size.extract(other_axis);
655 let (cols, rows) = (self.ideal_len.col.cast(), self.ideal_len.row.cast());
656 let div = Size(cols, rows).extract(other_axis);
657 (size / div)
658 .min(self.child_size_ideal.extract(other_axis))
659 .max(self.child_size_min.extract(other_axis))
660 });
661 axis = AxisInfo::new(axis.is_vertical(), other);
662
663 let mut rules = SizeRules::EMPTY;
664 for w in self.widgets.iter_mut() {
665 if w.token.is_some() {
666 let child_rules = w.item.size_rules(sizer.re(), axis);
667 rules = rules.max(child_rules);
668 }
669 }
670 self.child_size_min
671 .set_component(axis, rules.min_size().max(1));
672 self.child_size_ideal
673 .set_component(axis, rules.ideal_size().max(sizer.min_element_size()));
674
675 let m = rules.margins();
676 self.child_inter_margin.set_component(
677 axis,
678 m.0.max(m.1).max(inner_margin.0).max(inner_margin.1).cast(),
679 );
680
681 let ideal_len = match axis.is_vertical() {
682 false => self.ideal_len.col,
683 true => self.ideal_len.row,
684 };
685 rules.multiply_with_margin(2, ideal_len.cast());
686 rules.set_stretch(rules.stretch().max(Stretch::High));
687
688 let (rules, offset, size) = frame.surround(rules);
689 self.frame_offset.set_component(axis, offset);
690 self.frame_size.set_component(axis, size);
691 rules
692 }
693
694 fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
695 widget_set_rect!(rect);
696 self.align_hints = hints;
697
698 let avail = rect.size - self.frame_size;
699 let (cols, rows): (i32, i32) = (self.ideal_len.col.cast(), self.ideal_len.row.cast());
700 let child_size = Size(avail.0 / cols, avail.1 / rows)
701 .clamp(self.child_size_min, self.child_size_ideal);
702 self.child_size = child_size;
703 self.update_content_size(cx);
704
705 let skip = self.child_size + self.child_inter_margin;
706 if skip.0 == 0 || skip.1 == 0 {
707 self.alloc_len = GridIndex::ZERO;
708 return;
709 }
710 let vis_len = (rect.size + skip - Size::splat(1)).cwise_div(skip) + Size::splat(1);
711 let req_widgets = usize::conv(vis_len.0) * usize::conv(vis_len.1);
712
713 self.alloc_len = GridIndex {
714 col: vis_len.0.cast(),
715 row: vis_len.1.cast(),
716 };
717
718 let avail_widgets = self.widgets.len();
719 if avail_widgets < req_widgets {
720 log::debug!(
721 "set_rect: allocating widgets (old len = {avail_widgets}, new = {req_widgets})",
722 );
723 self.widgets.resize_with(req_widgets, || WidgetData {
724 token: None,
725 item: GridCell::new(self.driver.make(&C::Key::default())),
726 });
727 }
728
729 let solver = self.position_solver();
734 for row in solver.first_data.row..solver.first_data.row + solver.cur_len.row {
735 for col in solver.first_data.col..solver.first_data.col + solver.cur_len.col {
736 let cell = GridIndex { col, row };
737 let i = solver.data_to_child(cell);
738 let w = &mut self.widgets[i];
739 if w.token.is_some() {
740 w.item.set_rect(cx, solver.rect(cell), self.align_hints);
741 }
742 }
743 }
744
745 self.rect_update = true;
747 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
748 }
749
750 fn draw(&self, mut draw: DrawCx) {
751 let offset = self.scroll_offset() + self.virtual_offset;
753 let rect = self.rect() + offset;
754 let num = self.cur_end();
755 draw.with_clip_region(self.rect(), offset, |mut draw| {
756 for child in &self.widgets[..num] {
757 if let Some(key) = child.key() {
758 if rect.intersection(&child.item.rect()).is_some() {
761 if self.selection.contains(key) {
762 draw.selection(child.item.rect(), self.sel_style);
763 }
764 child.item.draw(draw.re());
765 }
766 }
767 }
768 });
769 }
770 }
771
772 impl Tile for Self {
773 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
774 cx.set_scroll_offset(self.scroll_offset(), self.max_scroll_offset());
775 Role::Grid {
776 columns: self.len_is_known.then(|| self.data_len.col.cast()),
777 rows: self.len_is_known.then(|| self.data_len.row.cast()),
778 }
779 }
780
781 #[inline]
782 fn child_indices(&self) -> ChildIndices {
783 (0..self.cur_end()).into()
784 }
785 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
786 self.widgets
787 .get(index)
788 .filter(|w| w.token.is_some())
789 .map(|w| w.item.as_tile())
790 }
791 fn find_child_index(&self, id: &Id) -> Option<usize> {
792 let key = C::Key::reconstruct_key(self.id_ref(), id);
793 if key.is_some() {
794 let num = self.cur_end();
795 for (i, w) in self.widgets[..num].iter().enumerate() {
796 if key.as_ref() == w.key() {
797 return Some(i);
798 }
799 }
800 }
801 None
802 }
803
804 #[inline]
805 fn translation(&self, _: usize) -> Offset {
806 self.scroll_offset() + self.virtual_offset
807 }
808
809 fn probe(&self, coord: Coord) -> Id {
810 if self.scroll.is_kinetic_scrolling() {
811 return self.id();
812 }
813
814 let num = self.cur_end();
815 let coord = coord + self.translation(0);
816 for child in &self.widgets[..num] {
817 if child.token.is_some()
818 && let Some(id) = child.item.try_probe(coord)
819 {
820 return id;
821 }
822 }
823 self.id()
824 }
825 }
826
827 impl Events for Self {
828 fn mouse_over_icon(&self) -> Option<CursorIcon> {
829 self.scroll
830 .is_kinetic_scrolling()
831 .then_some(CursorIcon::AllScroll)
832 }
833
834 #[inline]
835 fn make_child_id(&mut self, _: usize) -> Id {
836 unimplemented!()
838 }
839
840 fn configure(&mut self, cx: &mut ConfigCx) {
841 cx.register_nav_fallback(self.id());
842 }
843
844 fn configure_recurse(&mut self, cx: &mut ConfigCx, data: &Self::Data) {
845 if self.widgets.is_empty() {
846 self.child_size = Size::splat(1); let len = self.ideal_len.col * self.ideal_len.row;
851 self.widgets.resize_with(len.cast(), || WidgetData {
852 token: None,
853 item: GridCell::new(self.driver.make(&C::Key::default())),
854 });
855 self.alloc_len = self.ideal_len;
856 } else {
857 for w in &mut self.widgets {
859 w.token = None;
860 }
861 }
862
863 let lbound = GridIndex {
864 col: self.first_data.col + 2 * self.alloc_len.col,
865 row: self.first_data.row + 2 * self.alloc_len.row,
866 };
867 let data_len = self.clerk.len(data, lbound);
868 self.len_is_known = data_len.is_known();
869 let data_len = data_len.len();
870 self.data_len = data_len;
871
872 let col_len = data_len.col.min(self.alloc_len.col);
873 let row_len = data_len.row.min(self.alloc_len.row);
874
875 let cur_len = GridIndex {
876 col: col_len.cast(),
877 row: row_len.cast(),
878 };
879 self.cur_len = cur_len;
880 debug_assert!(self.cur_end() <= self.widgets.len());
881
882 self.token_update = Update::Configure;
883 let end = self.first_data + cur_len;
884 self.map_view_widgets(cx, data, self.first_data..end);
885 }
886
887 fn update(&mut self, cx: &mut ConfigCx, data: &C::Data) {
888 let changes = self.clerk.update(cx, self.id(), self.view_range(), data);
889 if changes != DataChanges::None {
890 self.handle_clerk_update(cx, data, changes);
891 }
892 }
893
894 fn update_recurse(&mut self, _: &mut ConfigCx, _: &Self::Data) {}
895
896 fn handle_event(&mut self, cx: &mut EventCx, data: &C::Data, event: Event) -> IsUsed {
897 let mut is_used = match event {
898 Event::Command(cmd, _) => {
899 let len = self.data_len;
900 if len == GridIndex::ZERO {
901 return Unused;
902 }
903 let (last_col, last_row) = (len.col.wrapping_sub(1), len.row.wrapping_sub(1));
904
905 let row_len = self.cur_len.row;
906 let mut solver = self.position_solver();
907 let cell = match cx.nav_focus().and_then(|id| self.find_child_index(id)) {
908 Some(index) => solver.child_to_data(index),
909 None => return Unused,
910 };
911 let (ci, ri) = (cell.col, cell.row);
912
913 use Command as C;
914 let data_index = match cmd {
915 C::DocHome => Some((0, 0)),
916 C::DocEnd => Some((last_col, last_row)),
917 C::Home => Some((0, ri)),
918 C::End => Some((last_col, ri)),
919 C::Left | C::WordLeft if ci > 0 => Some((ci - 1, ri)),
920 C::Up if ri > 0 => Some((ci, ri - 1)),
921 C::Right | C::WordRight if ci < last_col => Some((ci + 1, ri)),
922 C::Down if ri < last_row => Some((ci, ri + 1)),
923 C::PageUp if ri > 0 => Some((ci, ri.saturating_sub(row_len / 2))),
924 C::PageDown if ri < last_row => {
925 Some((ci, (ri + row_len / 2).min(last_row)))
926 }
927 _ => None,
929 };
930 return if let Some((col, row)) = data_index {
931 let cell = GridIndex { col, row };
932 let rect = solver.rect(cell) - self.virtual_offset;
934 let action = self.scroll.focus_rect(cx, rect, self.rect());
935 if !action.is_empty() {
936 cx.action(&self, action);
937 self.post_scroll(&mut cx.config_cx(), data);
938 solver = self.position_solver();
939 }
940
941 let index = solver.data_to_child(cell);
942 let w = &self.widgets[index];
943
944 if w.token.is_some() {
945 cx.next_nav_focus(w.item.id(), false, FocusSource::Key);
946 }
947 Used
948 } else {
949 Unused
950 };
951 }
952 Event::PressStart(ref press)
953 if press.is_primary() && cx.config().event().mouse_nav_focus() =>
954 {
955 if let Some(index) = cx.last_child() {
956 self.press_target = self.widgets[index].key().map(|k| (index, k.clone()));
957 }
958 if let Some((index, ref key)) = self.press_target {
959 let w = &mut self.widgets[index];
960 if w.key() == Some(key) {
961 cx.next_nav_focus(w.item.id(), false, FocusSource::Pointer);
962 }
963 }
964
965 press.grab_click(self.id()).complete(cx)
968 }
969 Event::PressEnd { ref press, success } if press.is_primary() => {
970 if let Some((index, ref key)) = self.press_target {
971 let w = &mut self.widgets[index];
972 if success
973 && !matches!(self.sel_mode, SelectionMode::None)
974 && !self.scroll.is_kinetic_scrolling()
975 && w.key() == Some(key)
976 && w.item.rect().contains(press.coord + self.translation(0))
977 {
978 cx.push(kas::messages::Select);
979 }
980 }
981 Used
982 }
983 Event::Timer(TIMER_UPDATE_WIDGETS) => {
984 self.post_scroll(&mut cx.config_cx(), data);
985 Used
986 }
987 _ => Unused, };
989
990 let offset = self.scroll.offset();
991 is_used |= self
992 .scroll
993 .scroll_by_event(cx, event, self.id(), self.rect());
994 if offset != self.scroll.offset() {
995 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
998 }
999 is_used
1000 }
1001
1002 fn handle_messages(&mut self, cx: &mut EventCx, data: &C::Data) {
1003 if let Some(kas::messages::SetScrollOffset(offset)) = cx.try_pop() {
1004 self.set_scroll_offset(cx, offset);
1005 return;
1006 }
1007
1008 let mut opt_key = None;
1009 if let Some(index) = cx.last_child() {
1010 if let Some(token) = self.widgets.get_mut(index).and_then(|w| w.token.as_mut()) {
1012 opt_key = Some(Borrow::<C::Key>::borrow(token).clone());
1013 } else {
1014 return; };
1016 }
1017
1018 if let Some(kas::messages::Select) = cx.try_pop() {
1019 let key = match opt_key {
1020 Some(key) => key,
1021 None => match self.press_target.as_ref() {
1022 Some((_, k)) => k.clone(),
1023 None => return,
1024 },
1025 };
1026 opt_key = None;
1027
1028 match self.sel_mode {
1029 SelectionMode::None => (),
1030 SelectionMode::Single => {
1031 cx.redraw(&self);
1032 self.selection.clear();
1033 self.selection.insert(key.clone());
1034 cx.push(SelectionMsg::Select(key));
1035 }
1036 SelectionMode::Multiple => {
1037 cx.redraw(&self);
1038 if self.selection.remove(&key) {
1039 cx.push(SelectionMsg::Deselect(key));
1040 } else {
1041 self.selection.insert(key.clone());
1042 cx.push(SelectionMsg::Select(key));
1043 }
1044 }
1045 }
1046 }
1047
1048 let changes =
1049 self.clerk
1050 .handle_messages(cx, self.id(), self.view_range(), data, opt_key);
1051 if changes != DataChanges::None {
1052 self.handle_clerk_update(&mut cx.config_cx(), data, changes);
1053 }
1054 }
1055
1056 fn handle_scroll(&mut self, cx: &mut EventCx, data: &C::Data, scroll: Scroll) {
1057 self.scroll
1058 .scroll(cx, self.id(), self.rect(), scroll - self.virtual_offset);
1059 self.post_scroll(&mut cx.config_cx(), data);
1060 }
1061 }
1062
1063 impl Widget for Self {
1065 type Data = C::Data;
1066
1067 fn child_node<'n>(&'n mut self, data: &'n C::Data, index: usize) -> Option<Node<'n>> {
1068 if let Some(w) = self.widgets.get_mut(index)
1069 && let Some(ref token) = w.token
1070 {
1071 let item = self.clerk.item(data, token);
1072 return Some(w.item.as_node(item));
1073 }
1074
1075 None
1076 }
1077
1078 fn _nav_next(
1080 &mut self,
1081 cx: &mut ConfigCx,
1082 data: &C::Data,
1083 focus: Option<&Id>,
1084 advance: NavAdvance,
1085 ) -> Option<Id> {
1086 if cx.is_disabled(self.id_ref()) || self.cur_len == GridIndex::ZERO {
1087 return None;
1088 }
1089
1090 let mut child = focus.and_then(|id| self.find_child_index(id));
1091
1092 if let Some(index) = child {
1093 let mut opt_id = None;
1094 let out = &mut opt_id;
1095 if let Some(mut node) = self.as_node(data).get_child(index) {
1096 *out = node._nav_next(cx, focus, advance);
1097 }
1098 if let Some(id) = opt_id {
1099 return Some(id);
1100 }
1101 }
1102
1103 let reverse = match advance {
1104 NavAdvance::None => return None,
1105 NavAdvance::Forward(_) => false,
1106 NavAdvance::Reverse(_) => true,
1107 };
1108
1109 let mut starting_child = child;
1110 loop {
1111 let mut solver = self.position_solver();
1112 let mut cell;
1113 if let Some(index) = child {
1114 cell = solver.child_to_data(index);
1115 if !reverse {
1116 if cell.col + 1 < self.data_len.col {
1117 cell.col += 1;
1118 } else if cell.row + 1 < self.data_len.row {
1119 cell = GridIndex {
1120 col: 0,
1121 row: cell.row + 1,
1122 };
1123 } else {
1124 return None;
1125 }
1126 } else {
1127 if cell.col > 0 {
1128 cell.col -= 1;
1129 } else if cell.row > 0 {
1130 cell = GridIndex {
1131 col: self.data_len.col - 1,
1132 row: cell.row - 1,
1133 };
1134 } else {
1135 return None;
1136 }
1137 }
1138 } else if !reverse {
1139 cell = GridIndex::ZERO;
1140 } else {
1141 cell = GridIndex {
1142 col: self.data_len.col - 1,
1143 row: self.data_len.row - 1,
1144 };
1145 }
1146
1147 let rect = solver.rect(cell) - self.virtual_offset;
1148 let action = self.scroll.self_focus_rect(rect, self.rect());
1149 if !action.is_empty() {
1150 cx.action(&self, action);
1151 self.post_scroll(cx, data);
1152 solver = self.position_solver();
1153 }
1154
1155 let index = solver.data_to_child(cell);
1156
1157 let mut opt_id = None;
1158 let out = &mut opt_id;
1159 if let Some(mut node) = self.as_node(data).get_child(index) {
1160 *out = node._nav_next(cx, focus, advance);
1161 }
1162 if let Some(id) = opt_id {
1163 return Some(id);
1164 }
1165
1166 child = Some(index);
1167 if starting_child == child {
1168 return None;
1169 } else if starting_child.is_none() {
1170 starting_child = child;
1171 }
1172 }
1173 }
1174 }
1175}
1176
1177#[derive(Debug)]
1178struct PositionSolver {
1179 pos_start: Coord,
1180 skip: Size,
1181 size: Size,
1182 first_data: GridIndex,
1183 cur_len: GridIndex,
1184}
1185
1186impl PositionSolver {
1187 fn data_to_child(&self, cell: GridIndex) -> usize {
1189 let col_len: usize = self.cur_len.col.cast();
1190 let row_len: usize = self.cur_len.row.cast();
1191 (cell.col as usize % col_len) + (cell.row as usize % row_len) * col_len
1192 }
1193
1194 fn child_to_data(&self, index: usize) -> GridIndex {
1196 let col_len = self.cur_len.col;
1197 let row_len = self.cur_len.row;
1198 let ci: u32 = (index % usize::conv(col_len)).cast();
1199 let ri: u32 = (index / usize::conv(col_len)).cast();
1200 let mut col = (self.first_data.col / col_len) * col_len + ci;
1201 let mut row = (self.first_data.row / row_len) * row_len + ri;
1202 if col < self.first_data.col {
1203 col += col_len;
1204 }
1205 if row < self.first_data.row {
1206 row += row_len;
1207 }
1208 GridIndex { col, row }
1209 }
1210
1211 fn rect(&self, GridIndex { col, row }: GridIndex) -> Rect {
1213 let pos = self.pos_start + self.skip.cwise_mul(Size(col.cast(), row.cast()));
1214 Rect::new(pos, self.size)
1215 }
1216}