1use crate::clerk::{Changes, Key, TokenClerk};
9use crate::{Driver, SelectionMode, SelectionMsg, Update};
10use kas::event::components::{ClickInput, ClickInputAction};
11use kas::event::{FocusSource, Scroll, TimerHandle};
12use kas::layout::{GridCellInfo, solve_size_rules};
13use kas::prelude::*;
14use kas::theme::SelectionStyle;
15#[allow(unused)] use kas_widgets::ScrollRegion;
17use linear_map::set::LinearSet;
18use std::borrow::Borrow;
19use std::ops::Range;
20use std::time::Instant;
21
22const TIMER_UPDATE_WIDGETS: TimerHandle = TimerHandle::new(1, true);
23
24#[impl_self]
25mod GridCell {
26 #[widget]
37 #[layout(frame!(self.inner).with_style(kas::theme::FrameStyle::NavFocus))]
38 struct GridCell<K, I, V: Driver<K, I>> {
39 core: widget_core!(),
40 index: GridIndex,
41 selected: Option<bool>,
42 #[widget]
44 inner: V::Widget,
45 }
46
47 impl Self {
48 #[inline]
50 fn new(inner: V::Widget) -> Self {
51 GridCell {
52 core: Default::default(),
53 index: GridIndex::default(),
54 selected: None,
55 inner,
56 }
57 }
58 }
59
60 impl Tile for Self {
61 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
62 if let Some(label) = V::label(&self.inner) {
63 cx.set_label(label);
64 }
65 Role::GridCell {
66 info: Some(GridCellInfo::new(self.index.col, self.index.row)),
67 selected: self.selected,
68 }
69 }
70
71 fn navigable(&self) -> bool {
72 V::navigable(&self.inner)
73 }
74 }
75
76 impl Events for Self {
77 type Data = I;
78
79 fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
80 match event {
81 Event::Command(cmd, code) if cmd.is_activate() => {
82 cx.depress_with_key(&self, code);
83 cx.push(kas::messages::Select);
84 Used
85 }
86 _ => Unused,
87 }
88 }
89 }
90}
91
92#[autoimpl(Debug ignore self.item where C::Token: trait)]
93struct WidgetData<C: TokenClerk<GridIndex>, V: Driver<C::Key, C::Item>> {
94 token: Option<C::Token>,
95 is_mock: bool,
96 item: GridCell<C::Key, C::Item, V>,
97}
98
99impl<C: TokenClerk<GridIndex>, V: Driver<C::Key, C::Item>> WidgetData<C, V> {
100 fn key(&self) -> Option<&C::Key> {
101 self.token.as_ref().map(Borrow::borrow)
102 }
103}
104
105#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
107pub struct GridIndex {
108 pub col: u32,
109 pub row: u32,
110}
111
112impl GridIndex {
113 pub const ZERO: GridIndex = GridIndex { col: 0, row: 0 };
115
116 pub const fn splat(x: u32) -> Self {
118 GridIndex { col: x, row: x }
119 }
120}
121
122impl GridIndex {
123 pub fn min(self, rhs: Self) -> Self {
125 GridIndex {
126 col: self.col.min(rhs.col),
127 row: self.row.min(rhs.row),
128 }
129 }
130}
131
132impl std::ops::Add for GridIndex {
133 type Output = Self;
134 fn add(self, rhs: Self) -> Self {
135 GridIndex {
136 col: self.col + rhs.col,
137 row: self.row + rhs.row,
138 }
139 }
140}
141
142impl Key for GridIndex {
143 fn make_id(&self, parent: &Id) -> Id {
144 parent
145 .make_child(self.col.cast())
146 .make_child(self.row.cast())
147 }
148
149 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
150 let mut iter = child.iter_keys_after(parent);
151 let col = iter.next().map(|i| i.cast())?;
152 let row = iter.next().map(|i| i.cast())?;
153 Some(GridIndex { col, row })
154 }
155}
156
157#[derive(Debug)]
158struct FocusGridCell(GridIndex);
159
160#[impl_self]
161mod GridView {
162 #[widget]
186 pub struct GridView<C: TokenClerk<GridIndex>, V: Driver<C::Key, C::Item>> {
187 core: widget_core!(),
188 frame_offset: Offset,
189 frame_size: Size,
190 clerk: C,
191 driver: V,
192 widgets: Vec<WidgetData<C, V>>,
193 align_hints: AlignHints,
194 ideal_len: GridIndex,
195 alloc_len: GridIndex,
196 data_len: GridIndex,
197 token_update: Update,
198 rect_update: bool,
199 immediate_scroll_update: bool,
200 len_is_known: bool,
201 cur_len: GridIndex,
202 first_data: GridIndex,
203 last_focus: GridIndex,
205 child_inter_margin: Size,
206 child_size: Size,
207 offset: Offset,
209 virtual_offset: Offset,
210 sel_mode: SelectionMode,
211 sel_style: SelectionStyle,
212 selection: LinearSet<C::Key>,
214 click: ClickInput,
215 press_target: Option<(usize, C::Key)>,
216 }
217
218 impl Default for Self
219 where
220 C: Default,
221 V: Default,
222 {
223 fn default() -> Self {
224 Self::new(C::default(), V::default())
225 }
226 }
227 impl Self {
228 pub fn new(clerk: C, driver: V) -> Self {
230 GridView {
231 core: Default::default(),
232 frame_offset: Default::default(),
233 frame_size: Default::default(),
234 clerk,
235 driver,
236 widgets: Default::default(),
237 align_hints: Default::default(),
238 ideal_len: GridIndex { col: 3, row: 5 },
239 alloc_len: GridIndex::ZERO,
240 data_len: GridIndex::ZERO,
241 token_update: Update::None,
242 rect_update: false,
243 immediate_scroll_update: false,
244 len_is_known: false,
245 cur_len: GridIndex::ZERO,
246 first_data: GridIndex::ZERO,
247 last_focus: GridIndex::ZERO,
248 child_inter_margin: Size::ZERO,
249 child_size: Size::ZERO,
250 offset: Offset::ZERO,
251 virtual_offset: Offset::ZERO,
252 sel_mode: SelectionMode::None,
253 sel_style: SelectionStyle::Highlight,
254 selection: Default::default(),
255 click: Default::default(),
256 press_target: None,
257 }
258 }
259
260 pub fn clerk(&self) -> &C {
262 &self.clerk
263 }
264
265 pub fn clerk_mut(&mut self) -> &mut C {
270 &mut self.clerk
271 }
272
273 pub fn view_range(&self) -> Range<GridIndex> {
278 self.first_data..self.first_data + self.cur_len
279 }
280
281 pub fn selection_mode(&self) -> SelectionMode {
283 self.sel_mode
284 }
285 pub fn set_selection_mode(&mut self, cx: &mut EventState, mode: SelectionMode) {
295 self.sel_mode = mode;
296 match mode {
297 SelectionMode::None if !self.selection.is_empty() => {
298 self.selection.clear();
299 cx.redraw(self);
300 }
301 SelectionMode::Single if self.selection.len() > 1 => {
302 if let Some(first) = self.selection.iter().next().cloned() {
303 self.selection.retain(|item| *item == first);
304 }
305 cx.redraw(self);
306 }
307 _ => (),
308 }
309 }
310 #[must_use]
314 pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
315 debug_assert!(self.selection.is_empty());
316 self.sel_mode = mode;
317 self
318 }
319
320 pub fn selection_style(&self) -> SelectionStyle {
322 self.sel_style
323 }
324 pub fn set_selection_style(&mut self, cx: &mut ConfigCx, style: SelectionStyle) {
329 if style.is_external() != self.sel_style.is_external() {
330 cx.resize();
331 };
332 self.sel_style = style;
333 }
334 #[must_use]
338 pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
339 self.sel_style = style;
340 self
341 }
342
343 pub fn selected_iter(&'_ self) -> impl Iterator<Item = &'_ C::Key> + '_ {
348 self.selection.iter()
349 }
350
351 pub fn is_selected(&self, key: &C::Key) -> bool {
353 self.selection.contains(key)
354 }
355
356 pub fn clear_selected(&mut self, cx: &mut EventState) {
358 if !self.selection.is_empty() {
359 self.selection.clear();
360 cx.redraw(self);
361 }
362 }
363
364 pub fn select(&mut self, cx: &mut EventState, key: C::Key) -> bool {
374 match self.sel_mode {
375 SelectionMode::None => return false,
376 SelectionMode::Single => self.selection.clear(),
377 _ => (),
378 }
379 let r = self.selection.insert(key);
380 if r {
381 cx.redraw(self);
382 }
383 r
384 }
385
386 pub fn deselect(&mut self, cx: &mut EventState, key: &C::Key) -> bool {
391 let r = self.selection.remove(key);
392 if r {
393 cx.redraw(self);
394 }
395 r
396 }
397
398 #[must_use]
403 pub fn with_num_visible(mut self, cols: u32, rows: u32) -> Self {
404 self.ideal_len = GridIndex {
405 col: cols,
406 row: rows,
407 };
408 self
409 }
410
411 #[inline]
413 fn cur_end(&self) -> usize {
414 usize::conv(self.cur_len.col) * usize::conv(self.cur_len.row)
415 }
416
417 fn position_solver(&self) -> PositionSolver {
418 PositionSolver {
419 pos_start: self.rect().pos + self.frame_offset + self.virtual_offset,
420 skip: self.child_size + self.child_inter_margin,
421 size: self.child_size,
422 first_data: self.first_data,
423 cur_len: self.cur_len,
424 }
425 }
426
427 #[inline]
431 fn post_scroll(&mut self, cx: &mut ConfigCx, data: &C::Data) {
432 self.handle_update(cx, data, Changes::None, false);
433 }
434
435 fn handle_update(
437 &mut self,
438 cx: &mut ConfigCx,
439 data: &C::Data,
440 changes: Changes<GridIndex>,
441 force_update: bool,
442 ) {
443 if matches!(changes, Changes::Range(_) | Changes::Any) {
445 self.token_update = self.token_update.max(Update::Token);
446 }
447
448 let offset = self.offset;
449 let skip = (self.child_size + self.child_inter_margin).max(Size(1, 1));
450 let first_col = u32::conv(u64::conv(offset.0) / u64::conv(skip.0));
451 let first_row = u32::conv(u64::conv(offset.1) / u64::conv(skip.1));
452
453 let lbound = GridIndex {
454 col: first_col + 2 * self.alloc_len.col,
455 row: first_row + 2 * self.alloc_len.row,
456 };
457
458 let data_len;
459 if !self.len_is_known || changes != Changes::None {
460 let result = self.clerk.len(data, lbound);
461 self.len_is_known = result.is_known();
462 data_len = result.len();
463 if data_len != self.data_len {
464 self.data_len = data_len;
465 cx.resize();
466 self.token_update = Update::Token;
467 }
468 } else {
469 data_len = self.data_len;
470 }
471 let cur_len = data_len.min(self.alloc_len);
472
473 let first_data = GridIndex {
474 col: first_col.min(data_len.col - cur_len.col),
475 row: first_row.min(data_len.row - cur_len.row),
476 };
477 let (mut start, mut end) = (first_data, first_data + cur_len);
478 let (old_start, old_end) = (self.first_data, self.first_data + self.cur_len);
479
480 let virtual_offset = -Offset(offset.0 & 0x7FF0_0000, offset.1 & 0x7FF0_0000);
481 if virtual_offset != self.virtual_offset {
482 self.virtual_offset = virtual_offset;
483 self.rect_update = true;
484 } else if force_update || self.rect_update || self.token_update != Update::None {
485 } else if start == old_start && cur_len == self.cur_len {
487 return;
488 } else if start.col == old_start.col && end.col == old_end.col {
489 if start.row >= old_start.row {
490 start.row = start.row.max(old_end.row);
491 } else if end.row <= old_end.row {
492 end.row = end.row.min(old_start.row);
493 }
494 if start.row >= end.row {
495 return;
496 }
497 } else if start.row == old_start.row && end.row == old_end.row {
498 if start.col >= old_start.col {
499 start.col = start.col.max(old_end.col);
500 } else if end.col <= old_end.col {
501 end.col = end.col.min(old_start.col);
502 }
503 if start.col >= end.col {
504 return;
505 }
506 }
507
508 self.cur_len = cur_len;
509 debug_assert!(self.cur_end() <= self.widgets.len());
510 self.first_data = first_data;
511
512 self.map_view_widgets(cx, data, start..end, force_update);
513 }
514
515 fn map_view_widgets(
519 &mut self,
520 cx: &mut ConfigCx,
521 data: &C::Data,
522 Range { start, end }: Range<GridIndex>,
523 force_update: bool,
524 ) {
525 let time = Instant::now();
526
527 self.clerk
528 .prepare_range(cx, self.id(), self.view_range(), data, start..end);
529
530 let id = self.id();
531
532 let solver = self.position_solver();
533 for row in start.row..end.row {
534 for col in start.col..end.col {
535 let cell = GridIndex { col, row };
536 let i = solver.data_to_child(cell);
537 let w = &mut self.widgets[i];
538
539 let force = self.token_update != Update::None;
540 let changes = self.clerk.update_token(data, cell, force, &mut w.token);
541 w.is_mock = false;
542 let Some(token) = w.token.as_ref() else {
543 continue;
544 };
545
546 let mut rect_update = self.rect_update;
547 if changes.key() || self.token_update == Update::Configure {
548 w.item.index = cell;
549 self.driver.set_key(&mut w.item.inner, token.borrow());
552
553 let item = self.clerk.item(data, token);
554 let id = token.borrow().make_id(&id);
555 cx.configure(w.item.as_node(item), id);
556
557 solve_size_rules(
558 &mut w.item,
559 &mut cx.size_cx(),
560 Some(self.child_size.0),
561 Some(self.child_size.1),
562 );
563 rect_update = true;
564 } else if force_update || changes.item() {
565 let item = self.clerk.item(data, token);
566 cx.update(w.item.as_node(item));
567 }
568
569 if rect_update {
570 w.item
571 .set_rect(&mut cx.size_cx(), solver.rect(cell), self.align_hints);
572 }
573 }
574 }
575
576 self.token_update = Update::None;
577 self.rect_update = false;
578
579 let dur = (Instant::now() - time).as_micros();
580 log::debug!(
581 target: "kas_perf::view::grid_view",
582 "map_view_widgets {}×{} widgets in {dur}μs",
583 end.col - start.col,
584 end.row - start.row,
585 );
586 }
587 }
588
589 impl Layout for Self {
590 fn size_rules(&mut self, cx: &mut SizeCx, mut axis: AxisInfo) -> SizeRules {
591 let inner_margin = if self.sel_style.is_external() {
593 cx.inner_margins().extract(axis)
594 } else {
595 (0, 0)
596 };
597 let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));
598
599 let other = axis
600 .other()
601 .map(|_| self.child_size.extract(axis.flipped()));
602 axis = AxisInfo::new(axis.is_vertical(), other);
603
604 let mut rules = SizeRules::EMPTY;
605 for w in self.widgets.iter_mut() {
606 if w.token.is_some() || w.is_mock {
607 let child_rules = w.item.size_rules(cx, axis);
608 rules = rules.max(child_rules);
609 }
610 }
611
612 let size = rules.min_size().max(1);
614 self.child_size.set_component(axis, size);
615
616 let m = rules.margins();
617 let inter_margin = m.0.max(m.1).max(inner_margin.0).max(inner_margin.1);
618 let stretch = rules.stretch();
619 let m = rules.margins();
620 self.child_inter_margin
621 .set_component(axis, inter_margin.cast());
622 let inter_margin: i32 = inter_margin.cast();
623
624 let min_len = 2;
625 let ideal_len = i32::conv(match axis.is_vertical() {
626 false => self.ideal_len.col,
627 true => self.ideal_len.row,
628 });
629 let min = min_len * size + (min_len - 1) * inter_margin;
630 let ideal = ideal_len * size + (ideal_len - 1) * inter_margin;
631 let rules = SizeRules::new(min, ideal, stretch.max(Stretch::High)).with_margins(m);
632
633 let (rules, offset, size) = frame.surround(rules);
634 self.frame_offset.set_component(axis, offset);
635 self.frame_size.set_component(axis, size);
636 rules
637 }
638
639 fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
640 self.core.set_rect(rect);
641 self.align_hints = hints;
642
643 let skip = self.child_size + self.child_inter_margin;
644 if skip.0 == 0 || skip.1 == 0 {
645 self.alloc_len = GridIndex::ZERO;
646 return;
647 }
648 let vis_len = (rect.size + skip - Size::splat(1)).cwise_div(skip) + Size::splat(1);
649 let req_widgets = usize::conv(vis_len.0) * usize::conv(vis_len.1);
650
651 self.alloc_len = GridIndex {
652 col: vis_len.0.cast(),
653 row: vis_len.1.cast(),
654 };
655
656 let avail_widgets = self.widgets.len();
657 if avail_widgets < req_widgets {
658 log::debug!(
659 "set_rect: allocating widgets (old len = {avail_widgets}, new = {req_widgets})",
660 );
661 self.widgets.resize_with(req_widgets, || WidgetData {
662 token: None,
663 is_mock: false,
664 item: GridCell::new(self.driver.make(&C::Key::default())),
665 });
666 }
667
668 let solver = self.position_solver();
673 for row in solver.first_data.row..solver.first_data.row + solver.cur_len.row {
674 for col in solver.first_data.col..solver.first_data.col + solver.cur_len.col {
675 let cell = GridIndex { col, row };
676 let i = solver.data_to_child(cell);
677 let w = &mut self.widgets[i];
678 if w.token.is_some() {
679 w.item.set_rect(cx, solver.rect(cell), self.align_hints);
680 }
681 }
682 }
683
684 self.rect_update = true;
686 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
687 }
688 }
689
690 impl Viewport for Self {
691 fn content_size(&self) -> Size {
692 let m = self.child_inter_margin;
693 let step = self.child_size + m;
694 let data_len = self.data_len;
695 Size(
696 step.0 * i32::conv(data_len.col) - m.0,
697 step.1 * i32::conv(data_len.row) - m.1,
698 )
699 .max(Size::ZERO)
700 }
701
702 fn set_offset(&mut self, _: &mut SizeCx, _: Rect, offset: Offset) {
703 self.offset = offset;
706 }
707
708 fn update_offset(&mut self, cx: &mut ConfigCx, data: &Self::Data, _: Rect, offset: Offset) {
709 self.offset = offset;
710 if self.immediate_scroll_update {
711 self.immediate_scroll_update = false;
712 self.post_scroll(cx, data);
713 } else {
714 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
717 }
718 }
719
720 fn draw_with_offset(&self, mut draw: DrawCx, viewport: Rect, offset: Offset) {
721 let offset = offset + self.virtual_offset;
723 let rect = viewport + offset;
724 let num = self.cur_end();
725 draw.with_clip_region(viewport, offset, |mut draw| {
726 for child in &self.widgets[..num] {
727 if let Some(key) = child.key() {
728 if rect.intersection(&child.item.rect()).is_some() {
731 if self.selection.contains(key) {
732 draw.selection(child.item.rect(), self.sel_style);
733 }
734 child.item.draw(draw.re());
735 }
736 }
737 }
738 });
739 }
740 }
741
742 impl Tile for Self {
743 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
744 Role::Grid {
745 columns: self.len_is_known.then(|| self.data_len.col.cast()),
746 rows: self.len_is_known.then(|| self.data_len.row.cast()),
747 }
748 }
749
750 #[inline]
751 fn child_indices(&self) -> ChildIndices {
752 ChildIndices::range(0..self.cur_end())
753 }
754 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
755 self.widgets
756 .get(index)
757 .filter(|w| w.token.is_some())
758 .map(|w| w.item.as_tile())
759 }
760 fn find_child_index(&self, id: &Id) -> Option<usize> {
761 let key = C::Key::reconstruct_key(self.id_ref(), id);
762 if key.is_some() {
763 let num = self.cur_end();
764 for (i, w) in self.widgets[..num].iter().enumerate() {
765 if key.as_ref() == w.key() {
766 return Some(i);
767 }
768 }
769 }
770 None
771 }
772
773 fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
774 if self.data_len.col == 0 || self.data_len.row == 0 {
775 return None;
776 }
777
778 let solver = self.position_solver();
779 let cell = if V::TAB_NAVIGABLE {
780 let first_data = self.first_data;
781 let skip = self.child_size + self.child_inter_margin;
782 let stride = self.rect().size.cwise_div(skip);
783 let last_visible = GridIndex {
784 col: (first_data.col + u32::conv(stride.0)).min(self.data_len.col - 1),
785 row: (first_data.row + u32::conv(stride.1)).min(self.data_len.row - 1),
786 };
787 if let Some(index) = from {
788 let cell = solver.child_to_data(index);
789 if !reverse {
790 if cell.col + 1 < last_visible.col {
791 GridIndex {
792 col: cell.col + 1,
793 row: cell.row,
794 }
795 } else if cell.row + 1 < last_visible.row {
796 GridIndex {
797 col: 0,
798 row: cell.row + 1,
799 }
800 } else {
801 return None;
802 }
803 } else {
804 if cell.col > first_data.col {
805 GridIndex {
806 col: cell.col - 1,
807 row: cell.row,
808 }
809 } else if cell.row > first_data.row {
810 GridIndex {
811 col: self.data_len.col - 1,
812 row: cell.row - 1,
813 }
814 } else {
815 return None;
816 }
817 }
818 } else if !reverse {
819 first_data
820 } else {
821 last_visible
822 }
823 } else {
824 if from.is_some() {
825 return None;
826 } else {
827 self.last_focus
828 }
829 };
830
831 let index = solver.data_to_child(cell);
832 self.get_child(index).is_some().then_some(index)
833 }
834
835 #[inline]
836 fn translation(&self, _: usize) -> Offset {
837 self.virtual_offset
838 }
839 }
840
841 impl Events for Self {
842 #[inline]
843 fn make_child_id(&mut self, _: usize) -> Id {
844 unimplemented!()
846 }
847
848 fn probe(&self, coord: Coord) -> Id {
849 let num = self.cur_end();
850 let coord = coord + self.translation(0);
851 for child in &self.widgets[..num] {
852 if child.token.is_some()
853 && let Some(id) = child.item.try_probe(coord)
854 {
855 return id;
856 }
857 }
858 self.id()
859 }
860
861 fn configure(&mut self, cx: &mut ConfigCx) {
862 cx.register_nav_fallback(self.id());
863
864 if self.widgets.is_empty() {
865 self.child_size = Size::splat(1); let len = self.ideal_len.col * self.ideal_len.row;
869 self.widgets.resize_with(len.cast(), || WidgetData {
870 token: None,
871 is_mock: false,
872 item: GridCell::new(self.driver.make(&C::Key::default())),
873 });
874 self.alloc_len = self.ideal_len;
875 } else {
876 for w in &mut self.widgets {
878 w.token = None;
879 }
880 }
881
882 self.token_update = Update::Configure;
883 }
885
886 fn update(&mut self, cx: &mut ConfigCx, data: &C::Data) {
887 let changes = self.clerk.update(cx, self.id(), self.view_range(), data);
888 if self.token_update != Update::None || changes != Changes::None {
889 self.handle_update(cx, data, changes, true);
890 } else {
891 let end = self.cur_end();
892 for w in &mut self.widgets[..end] {
893 if let Some(ref token) = w.token {
894 let item = self.clerk.item(data, token);
895 cx.update(w.item.as_node(item));
896 }
897 }
898 }
899
900 let id = self.id();
901 if self.cur_len == GridIndex::ZERO
902 && let Some(w) = self.widgets.get_mut(0)
903 && w.token.is_none()
904 && !w.is_mock
905 && let Some(item) = self.clerk.mock_item(data)
906 {
907 cx.configure(w.item.as_node(&item), id);
909 w.is_mock = true;
910 }
911 }
912
913 #[inline]
914 fn recurse_indices(&self) -> ChildIndices {
915 ChildIndices::none()
916 }
917
918 fn child_nav_focus(&mut self, cx: &mut EventCx, _: Id) {
919 if let Some(index) = cx.last_child()
920 && self.get_child(index).is_some()
921 {
922 let solver = self.position_solver();
923 self.last_focus = solver.child_to_data(index);
924 }
925 }
926
927 fn handle_event(&mut self, cx: &mut EventCx, data: &C::Data, event: Event) -> IsUsed {
928 match event {
929 Event::Command(cmd, _) => {
930 let len = self.data_len;
931 if len == GridIndex::ZERO {
932 return Unused;
933 }
934 let (last_col, last_row) = (len.col.wrapping_sub(1), len.row.wrapping_sub(1));
935
936 let row_len = self.cur_len.row;
937 let solver = self.position_solver();
938 let cell = match cx.nav_focus().and_then(|id| self.find_child_index(id)) {
939 Some(index) => solver.child_to_data(index),
940 None => return Unused,
941 };
942 let (ci, ri) = (cell.col, cell.row);
943
944 use Command as C;
945 let data_index = match cmd {
946 C::DocHome => Some((0, 0)),
947 C::DocEnd => Some((last_col, last_row)),
948 C::Home => Some((0, ri)),
949 C::End => Some((last_col, ri)),
950 C::Left | C::WordLeft if ci > 0 => Some((ci - 1, ri)),
951 C::Up if ri > 0 => Some((ci, ri - 1)),
952 C::Right | C::WordRight if ci < last_col => Some((ci + 1, ri)),
953 C::Down if ri < last_row => Some((ci, ri + 1)),
954 C::PageUp if ri > 0 => Some((ci, ri.saturating_sub(row_len / 2))),
955 C::PageDown if ri < last_row => {
956 Some((ci, (ri + row_len / 2).min(last_row)))
957 }
958 _ => None,
960 };
961 if let Some((col, row)) = data_index {
962 let cell = GridIndex { col, row };
963 let rect = solver.rect(cell) - self.virtual_offset;
965 cx.set_scroll(Scroll::Rect(rect));
966
967 let index = solver.data_to_child(cell);
968 let w = &self.widgets[index];
969 if w.item.index == cell && w.token.is_some() {
970 cx.next_nav_focus(w.item.id(), false, FocusSource::Key);
971 } else {
972 self.immediate_scroll_update = true;
973 cx.send(self.id(), FocusGridCell(cell));
974 }
975 Used
976 } else {
977 Unused
978 }
979 }
980 Event::Timer(TIMER_UPDATE_WIDGETS) => {
981 self.post_scroll(cx, data);
982 Used
983 }
984 event => match self.click.handle(cx, self.id(), event) {
985 ClickInputAction::Used => Used,
986 ClickInputAction::Unused => Unused,
987 ClickInputAction::ClickStart { .. } => {
988 if let Some(index) = cx.last_child() {
989 self.press_target =
990 self.widgets[index].key().map(|k| (index, k.clone()));
991 }
992 Used
993 }
994 ClickInputAction::ClickEnd { coord, success } => {
995 if let Some((index, ref key)) = self.press_target {
996 let w = &mut self.widgets[index];
997 if success
998 && !matches!(self.sel_mode, SelectionMode::None)
999 && w.key() == Some(key)
1000 && w.item.rect().contains(coord + self.translation(0))
1001 {
1002 cx.push(kas::messages::Select);
1003 }
1004 }
1005 Used
1006 }
1007 },
1008 }
1009 }
1010
1011 fn handle_messages(&mut self, cx: &mut EventCx, data: &C::Data) {
1012 if let Some(FocusGridCell(cell)) = cx.try_pop() {
1013 let solver = self.position_solver();
1014 let index = solver.data_to_child(cell);
1015 let w = &self.widgets[index];
1016 if w.item.index == cell && w.token.is_some() {
1017 cx.next_nav_focus(w.item.id(), false, FocusSource::Key);
1018 } else {
1019 log::error!("GridView failed to set focus: data item {cell:?} not in view");
1020 }
1021 }
1022
1023 let mut opt_key = None;
1024 if let Some(index) = cx.last_child() {
1025 if let Some(token) = self.widgets.get_mut(index).and_then(|w| w.token.as_mut()) {
1027 opt_key = Some(Borrow::<C::Key>::borrow(token).clone());
1028 } else {
1029 return; };
1031 }
1032
1033 if let Some(kas::messages::Select) = cx.try_pop() {
1034 let key = match opt_key {
1035 Some(key) => key,
1036 None => match self.press_target.as_ref() {
1037 Some((_, k)) => k.clone(),
1038 None => return,
1039 },
1040 };
1041 opt_key = None;
1042
1043 match self.sel_mode {
1044 SelectionMode::None => (),
1045 SelectionMode::Single => {
1046 cx.redraw();
1047 self.selection.clear();
1048 self.selection.insert(key.clone());
1049 cx.push(SelectionMsg::Select(key));
1050 }
1051 SelectionMode::Multiple => {
1052 cx.redraw();
1053 if self.selection.remove(&key) {
1054 cx.push(SelectionMsg::Deselect(key));
1055 } else {
1056 self.selection.insert(key.clone());
1057 cx.push(SelectionMsg::Select(key));
1058 }
1059 }
1060 }
1061 }
1062
1063 let changes =
1064 self.clerk
1065 .handle_messages(cx, self.id(), self.view_range(), data, opt_key);
1066 if changes != Changes::None {
1067 self.handle_update(cx, data, changes, false);
1068 }
1069 }
1070
1071 fn handle_scroll(&mut self, cx: &mut EventCx, _: &C::Data, scroll: Scroll) {
1072 cx.set_scroll(scroll - self.virtual_offset);
1073 }
1074 }
1075
1076 impl Widget for Self {
1078 type Data = C::Data;
1079
1080 fn child_node<'n>(&'n mut self, data: &'n C::Data, index: usize) -> Option<Node<'n>> {
1081 if let Some(w) = self.widgets.get_mut(index)
1082 && let Some(ref token) = w.token
1083 {
1084 let item = self.clerk.item(data, token);
1085 return Some(w.item.as_node(item));
1086 }
1087
1088 None
1089 }
1090 }
1091}
1092
1093#[derive(Debug)]
1094struct PositionSolver {
1095 pos_start: Coord,
1096 skip: Size,
1097 size: Size,
1098 first_data: GridIndex,
1099 cur_len: GridIndex,
1100}
1101
1102impl PositionSolver {
1103 fn data_to_child(&self, cell: GridIndex) -> usize {
1105 let col_len: usize = self.cur_len.col.cast();
1106 let row_len: usize = self.cur_len.row.cast();
1107 (cell.col as usize % col_len) + (cell.row as usize % row_len) * col_len
1108 }
1109
1110 fn child_to_data(&self, index: usize) -> GridIndex {
1112 let col_len = self.cur_len.col;
1113 let row_len = self.cur_len.row;
1114 let ci: u32 = (index % usize::conv(col_len)).cast();
1115 let ri: u32 = (index / usize::conv(col_len)).cast();
1116 let mut col = (self.first_data.col / col_len) * col_len + ci;
1117 let mut row = (self.first_data.row / row_len) * row_len + ri;
1118 if col < self.first_data.col {
1119 col += col_len;
1120 }
1121 if row < self.first_data.row {
1122 row += row_len;
1123 }
1124 GridIndex { col, row }
1125 }
1126
1127 fn rect(&self, GridIndex { col, row }: GridIndex) -> Rect {
1129 let pos = self.pos_start + self.skip.cwise_mul(Size(col.cast(), row.cast()));
1130 Rect::new(pos, self.size)
1131 }
1132}