1use super::*;
9use kas::event::components::ScrollComponent;
10use kas::event::{CursorIcon, FocusSource, NavAdvance, Scroll, TimerHandle};
11use kas::layout::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::fmt::Debug;
19use std::ops::Range;
20use std::time::Instant;
21
22const TIMER_UPDATE_WIDGETS: TimerHandle = TimerHandle::new(1, true);
23
24#[impl_self]
25mod ListItem {
26 #[widget]
37 #[layout(frame!(self.inner).with_style(kas::theme::FrameStyle::NavFocus))]
38 struct ListItem<K, I, V: Driver<K, I>> {
39 core: widget_core!(),
40 index: usize,
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 ListItem {
52 core: Default::default(),
53 index: 0,
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::OptionListItem {
66 index: Some(self.index),
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: DataClerk<usize>, V: Driver<C::Key, C::Item>> {
94 token: Option<C::Token>,
95 item: ListItem<C::Key, C::Item, V>,
96}
97
98impl<C: DataClerk<usize>, V: Driver<C::Key, C::Item>> WidgetData<C, V> {
99 fn key(&self) -> Option<&C::Key> {
100 self.token.as_ref().map(Borrow::borrow)
101 }
102}
103
104#[impl_self]
105mod ListView {
106 #[widget]
129 pub struct ListView<C: DataClerk<usize>, V, D = Direction>
130 where
131 V: Driver<C::Key, C::Item>,
132 D: Directional,
133 {
134 core: widget_core!(),
135 frame_offset: Offset,
136 frame_size: Size,
137 clerk: C,
138 driver: V,
139 widgets: Vec<WidgetData<C, V>>,
140 alloc_len: u32,
141 data_len: u32,
142 token_update: Update,
143 rect_update: bool,
144 len_is_known: bool,
145 cur_len: u32,
147 first_data: u32,
149 direction: D,
150 align_hints: AlignHints,
151 ideal_visible: i32,
152 child_size_min: i32,
153 child_size_ideal: i32,
154 child_inter_margin: i32,
155 skip: i32,
156 child_size: Size,
157 scroll: ScrollComponent,
158 virtual_offset: i32,
160 sel_mode: SelectionMode,
161 sel_style: SelectionStyle,
162 selection: LinearSet<C::Key>,
164 press_target: Option<(usize, C::Key)>,
165 }
166
167 impl Self
168 where
169 D: Default,
170 {
171 pub fn new(clerk: C, driver: V) -> Self {
173 Self::new_dir(clerk, driver, D::default())
174 }
175 }
176 impl<C: DataClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Left> {
177 pub fn left(clerk: C, driver: V) -> Self {
179 Self::new(clerk, driver)
180 }
181 }
182 impl<C: DataClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Right> {
183 pub fn right(clerk: C, driver: V) -> Self {
185 Self::new(clerk, driver)
186 }
187 }
188 impl<C: DataClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Up> {
189 pub fn up(clerk: C, driver: V) -> Self {
191 Self::new(clerk, driver)
192 }
193 }
194 impl<C: DataClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Down> {
195 pub fn down(clerk: C, driver: V) -> Self {
197 Self::new(clerk, driver)
198 }
199 }
200 impl<C: DataClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, Direction> {
201 pub fn set_direction(&mut self, cx: &mut EventState, direction: Direction) {
203 if direction != self.direction {
204 self.direction = direction;
205 cx.action(self, Action::SET_RECT);
206 }
207 }
208 }
209
210 impl Self {
211 pub fn new_dir(clerk: C, driver: V, direction: D) -> Self {
213 ListView {
214 core: Default::default(),
215 frame_offset: Default::default(),
216 frame_size: Default::default(),
217 clerk,
218 driver,
219 widgets: Default::default(),
220 alloc_len: 0,
221 data_len: 0,
222 token_update: Update::None,
223 rect_update: false,
224 len_is_known: false,
225 cur_len: 0,
226 first_data: 0,
227 direction,
228 align_hints: Default::default(),
229 ideal_visible: 5,
230 child_size_min: 0,
231 child_size_ideal: 0,
232 child_inter_margin: 0,
233 skip: 1,
234 child_size: Size::ZERO,
235 scroll: Default::default(),
236 virtual_offset: 0,
237 sel_mode: SelectionMode::None,
238 sel_style: SelectionStyle::Highlight,
239 selection: Default::default(),
240 press_target: None,
241 }
242 }
243
244 pub fn clerk(&self) -> &C {
246 &self.clerk
247 }
248
249 pub fn clerk_mut(&mut self) -> &mut C {
255 &mut self.clerk
256 }
257
258 pub fn view_range(&self) -> Range<usize> {
263 let start: usize = self.first_data.cast();
264 let end = start + usize::conv(self.cur_len);
265 start..end
266 }
267
268 pub fn selection_mode(&self) -> SelectionMode {
270 self.sel_mode
271 }
272 pub fn set_selection_mode(&mut self, cx: &mut EventState, mode: SelectionMode) {
282 self.sel_mode = mode;
283 match mode {
284 SelectionMode::None if !self.selection.is_empty() => {
285 self.selection.clear();
286 self.update_selected_items();
287 cx.redraw(self);
288 }
289 SelectionMode::Single if self.selection.len() > 1 => {
290 if let Some(first) = self.selection.iter().next().cloned() {
291 self.selection.retain(|item| *item == first);
292 }
293 self.update_selected_items();
294 cx.redraw(self);
295 }
296 _ => (),
297 }
298 }
299 #[must_use]
303 pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
304 debug_assert!(self.selection.is_empty());
305 self.sel_mode = mode;
306 self
307 }
308
309 pub fn selection_style(&self) -> SelectionStyle {
311 self.sel_style
312 }
313 pub fn set_selection_style(&mut self, cx: &mut EventState, style: SelectionStyle) {
318 if style.is_external() != self.sel_style.is_external() {
319 cx.resize(&self);
320 };
321 self.sel_style = style;
322 }
323 #[must_use]
327 pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
328 self.sel_style = style;
329 self
330 }
331
332 pub fn selected_iter(&'_ self) -> impl Iterator<Item = &'_ C::Key> + '_ {
337 self.selection.iter()
338 }
339
340 pub fn is_selected(&self, key: &C::Key) -> bool {
342 self.selection.contains(key)
343 }
344
345 pub fn clear_selected(&mut self, cx: &mut EventState) {
347 if !self.selection.is_empty() {
348 self.selection.clear();
349 self.update_selected_items();
350 cx.redraw(self);
351 }
352 }
353
354 pub fn select(&mut self, cx: &mut EventState, key: C::Key) -> bool {
364 match self.sel_mode {
365 SelectionMode::None => return false,
366 SelectionMode::Single => self.selection.clear(),
367 _ => (),
368 }
369 let r = self.selection.insert(key);
370 if r {
371 self.update_selected_items();
372 cx.redraw(self);
373 }
374 r
375 }
376
377 pub fn deselect(&mut self, cx: &mut EventState, key: &C::Key) -> bool {
382 let r = self.selection.remove(key);
383 if r {
384 self.update_selected_items();
385 cx.redraw(self);
386 }
387 r
388 }
389
390 pub fn deselect_unavailable(&mut self, cx: &mut EventState) {
396 let len = self.selection.len();
397 self.selection
398 .retain(|key| self.widgets.iter().any(|widget| widget.key() == Some(key)));
399 self.update_selected_items();
400 if len != self.selection.len() {
401 cx.redraw(self);
402 }
403 }
404
405 fn update_selected_items(&mut self) {
407 let unselected = match self.sel_mode {
408 SelectionMode::None | SelectionMode::Single => None,
409 SelectionMode::Multiple => Some(false),
410 };
411 for w in &mut self.widgets {
412 if let Some(key) = w.key() {
413 if self.selection.contains(key) {
414 w.item.selected = Some(true);
415 } else {
416 w.item.selected = unselected;
417 }
418 }
419 }
420 }
421
422 pub fn direction(&self) -> Direction {
424 self.direction.as_direction()
425 }
426
427 #[must_use]
432 pub fn with_num_visible(mut self, number: i32) -> Self {
433 self.ideal_visible = number;
434 self
435 }
436
437 #[inline]
438 fn virtual_offset(&self) -> Offset {
439 match self.direction.is_vertical() {
440 false => Offset(self.virtual_offset, 0),
441 true => Offset(0, self.virtual_offset),
442 }
443 }
444
445 fn position_solver(&self) -> PositionSolver {
446 let cur_len: usize = self.cur_len.cast();
447 let mut first_data: usize = self.first_data.cast();
448 let mut skip = Offset::ZERO;
449 skip.set_component(self.direction, self.skip);
450
451 let mut pos_start = self.rect().pos + self.frame_offset + self.virtual_offset();
452 if self.direction.is_reversed() && self.len_is_known {
453 let data_len: usize = self.data_len.cast();
454 first_data = (data_len - first_data).saturating_sub(cur_len);
455 pos_start += skip * i32::conv(data_len.saturating_sub(1));
456 skip = skip * -1;
457 }
458
459 PositionSolver {
460 pos_start,
461 skip,
462 size: self.child_size,
463 first_data,
464 cur_len,
465 }
466 }
467
468 fn post_scroll(&mut self, cx: &mut ConfigCx, data: &C::Data) {
470 let offset = self.scroll_offset().extract(self.direction);
471 let first_data = usize::conv(u64::conv(offset) / u64::conv(self.skip));
472
473 let alloc_len = self.alloc_len.cast();
474 let lbound = first_data + 2 * alloc_len;
475 let data_len = self.clerk.len(data, lbound);
476 self.len_is_known = data_len.is_known();
477 let data_len = data_len.len();
478 if data_len != usize::conv(self.data_len) {
479 self.data_len = data_len.cast();
480 self.update_content_size(cx);
481 }
482 let cur_len: usize = data_len.min(alloc_len);
483
484 let first_data = first_data.min(data_len - cur_len);
485
486 let old_start = self.first_data.cast();
487 let old_end = old_start + usize::conv(self.cur_len);
488 let (mut start, mut end) = (first_data, first_data + cur_len);
489
490 let virtual_offset = -(offset & 0x7FF0_0000);
491 if virtual_offset != self.virtual_offset {
492 self.virtual_offset = virtual_offset;
493 self.rect_update = true;
494 } else if self.rect_update || self.token_update != Update::None {
495 } else if start >= old_start {
497 start = start.max(old_end);
498 } else if end <= old_end {
499 end = end.min(old_start);
500 }
501
502 debug_assert!(cur_len <= self.widgets.len());
503 self.cur_len = cur_len.cast();
504 self.first_data = first_data.cast();
505
506 if start < end {
507 self.map_view_widgets(cx, data, start..end);
508 }
509 }
510
511 fn map_view_widgets(&mut self, cx: &mut ConfigCx, data: &C::Data, range: Range<usize>) {
515 let time = Instant::now();
516
517 self.clerk
518 .prepare_range(cx, self.id(), self.view_range(), data, range.clone());
519
520 let id = self.id();
521
522 let solver = self.position_solver();
523 for i in range.clone() {
524 let w = &mut self.widgets[i % solver.cur_len];
525
526 let force = self.token_update != Update::None;
527 let changes = self.clerk.update_token(data, i, force, &mut w.token);
528 let Some(token) = w.token.as_ref() else {
529 continue;
530 };
531
532 let mut rect_update = self.rect_update;
533 if changes.key() || self.token_update == Update::Configure {
534 w.item.index = i;
535 self.driver.set_key(&mut w.item.inner, token.borrow());
538
539 let item = self.clerk.item(data, token);
540 let id = token.borrow().make_id(&id);
541 cx.configure(w.item.as_node(item), id);
542
543 solve_size_rules(
544 &mut w.item,
545 cx.size_cx(),
546 Some(self.child_size.0),
547 Some(self.child_size.1),
548 );
549 rect_update = true;
550 } else if changes.item() {
551 let item = self.clerk.item(data, token);
552 cx.update(w.item.as_node(item));
553 }
554
555 if rect_update {
556 w.item.set_rect(cx, solver.rect(i), self.align_hints);
557 }
558 }
559
560 self.token_update = Update::None;
561 self.rect_update = false;
562
563 let dur = (Instant::now() - time).as_micros();
564 log::debug!(
565 target: "kas_perf::view::list_view",
566 "map_view_widgets: {} view widgets in: {dur}μs",
567 range.len(),
568 );
569 }
570
571 fn handle_clerk_update(
573 &mut self,
574 cx: &mut ConfigCx,
575 data: &C::Data,
576 changes: DataChanges<usize>,
577 ) {
578 let start: usize = self.first_data.cast();
579 let end = start + usize::conv(self.cur_len);
580 let range = match changes {
581 DataChanges::None | DataChanges::NoPreparedItems => 0..0,
582 DataChanges::Range(range) => start.max(range.start)..end.min(range.end),
583 DataChanges::Any => start..end,
584 };
585
586 let lbound = self.first_data + 2 * self.alloc_len;
587 let data_len = self.clerk.len(data, lbound.cast());
588 self.len_is_known = data_len.is_known();
589 let data_len = data_len.len().cast();
590 if data_len != self.data_len {
591 self.data_len = data_len;
592
593 if self.update_content_size(cx) {
594 cx.resize(&self);
597 return;
598 }
599
600 if self.cur_len != data_len.min(self.alloc_len.cast()) {
601 return self.post_scroll(cx, data);
602 }
603 }
604
605 if !range.is_empty() {
606 self.token_update = self.token_update.max(Update::Token);
607 self.map_view_widgets(cx, data, range);
608 }
609 }
610
611 fn update_content_size(&mut self, cx: &mut ConfigCx) -> bool {
613 let data_len: i32 = self.data_len.cast();
614 let view_size = self.rect().size - self.frame_size;
615 let mut content_size = view_size;
616 content_size.set_component(
617 self.direction,
618 (self.skip * data_len - self.child_inter_margin).max(0),
619 );
620 let action = self.scroll.set_sizes(view_size, content_size);
621 cx.action(self, action);
622 !action.is_empty()
623 }
624 }
625
626 impl Scrollable for Self {
627 fn content_size(&self) -> Size {
628 let data_len: i32 = self.data_len.cast();
629 let m = self.child_inter_margin;
630 let step = self.child_size_ideal + m;
631 let mut content_size = Size::ZERO;
632 content_size.set_component(self.direction, (step * data_len - m).max(0));
633 content_size
634 }
635
636 #[inline]
637 fn max_scroll_offset(&self) -> Offset {
638 self.scroll.max_offset()
639 }
640
641 #[inline]
642 fn scroll_offset(&self) -> Offset {
643 self.scroll.offset()
644 }
645
646 fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset {
647 let action = self.scroll.set_offset(offset);
648 if !action.is_empty() {
649 cx.action(&self, action);
650 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
651 }
652 self.scroll.offset()
653 }
654 }
655
656 impl Layout for Self {
657 fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
658 let inner_margin = if self.sel_style.is_external() {
660 sizer.inner_margins().extract(axis)
661 } else {
662 (0, 0)
663 };
664 let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));
665
666 let other = axis.other().map(|mut size| {
667 let other_axis = axis.flipped();
669 size -= self.frame_size.extract(other_axis);
670 if self.direction.is_horizontal() == other_axis.is_horizontal() {
671 size = (size / self.ideal_visible)
672 .clamp(self.child_size_min, self.child_size_ideal);
673 }
674 size
675 });
676 axis = AxisInfo::new(axis.is_vertical(), other);
677
678 let mut rules = SizeRules::EMPTY;
679 for w in self.widgets.iter_mut() {
680 if w.token.is_some() {
681 let child_rules = w.item.size_rules(sizer.re(), axis);
682 rules = rules.max(child_rules);
683 }
684 }
685 if axis.is_vertical() == self.direction.is_vertical() {
686 self.child_size_min = rules.min_size().max(1);
687 self.child_size_ideal = rules.ideal_size().max(sizer.min_element_size());
688 let m = rules.margins();
689 self.child_inter_margin =
690 m.0.max(m.1).max(inner_margin.0).max(inner_margin.1).cast();
691 rules.multiply_with_margin(2, self.ideal_visible);
692 rules.set_stretch(rules.stretch().max(Stretch::High));
693 } else {
694 rules.set_stretch(rules.stretch().max(Stretch::Low));
695 }
696 let (rules, offset, size) = frame.surround(rules);
697 self.frame_offset.set_component(axis, offset);
698 self.frame_size.set_component(axis, size);
699 rules
700 }
701
702 fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
703 widget_set_rect!(rect);
704 self.align_hints = hints;
705
706 let mut child_size = rect.size - self.frame_size;
707 let (size, skip);
708 if self.direction.is_horizontal() {
709 child_size.0 = (child_size.0 / self.ideal_visible)
710 .clamp(self.child_size_min, self.child_size_ideal);
711 size = rect.size.0;
712 skip = child_size.0 + self.child_inter_margin;
713 } else {
714 child_size.1 = (child_size.1 / self.ideal_visible)
715 .clamp(self.child_size_min, self.child_size_ideal);
716 size = rect.size.1;
717 skip = child_size.1 + self.child_inter_margin;
718 }
719
720 self.child_size = child_size;
721 self.skip = skip;
722 self.update_content_size(cx);
723
724 if skip == 0 {
725 self.skip = 1; self.alloc_len = 0;
727 return;
728 }
729 let req_widgets = usize::conv((size + skip - 1) / skip + 1);
730 self.alloc_len = req_widgets.cast();
731
732 let avail_widgets = self.widgets.len();
733 if avail_widgets < req_widgets {
734 log::debug!(
735 "set_rect: allocating widgets (old len = {avail_widgets}, new = {req_widgets})",
736 );
737 self.widgets.reserve(req_widgets - avail_widgets);
738 let key = C::Key::default();
739 for _ in avail_widgets..req_widgets {
740 let item = ListItem::new(self.driver.make(&key));
741 self.widgets.push(WidgetData { token: None, item });
742 }
743 }
744
745 let solver = self.position_solver();
750 for i in 0..solver.cur_len {
751 let i = solver.first_data + i;
752 let w = &mut self.widgets[i % solver.cur_len];
753 if w.token.is_some() {
754 w.item.set_rect(cx, solver.rect(i), self.align_hints);
755 }
756 }
757
758 self.rect_update = true;
759 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
760 }
761
762 fn draw(&self, mut draw: DrawCx) {
763 let offset = self.scroll_offset() + self.virtual_offset();
765 draw.with_clip_region(self.rect(), offset, |mut draw| {
766 for child in &self.widgets[..self.cur_len.cast()] {
767 if let Some(key) = child.key() {
768 if self.selection.contains(key) {
769 draw.selection(child.item.rect(), self.sel_style);
770 }
771 child.item.draw(draw.re());
772 }
773 }
774 });
775 }
776 }
777
778 impl Tile for Self {
779 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
780 cx.set_scroll_offset(self.scroll_offset(), self.max_scroll_offset());
781 Role::OptionList {
782 len: self.len_is_known.then(|| self.data_len.cast()),
783 direction: self.direction.as_direction(),
784 }
785 }
786
787 #[inline]
788 fn child_indices(&self) -> ChildIndices {
789 (0..self.cur_len.cast()).into()
790 }
791 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
792 self.widgets
793 .get(index)
794 .filter(|w| w.token.is_some())
795 .map(|w| w.item.as_tile())
796 }
797 fn find_child_index(&self, id: &Id) -> Option<usize> {
798 let key = C::Key::reconstruct_key(self.id_ref(), id);
799 if key.is_some() {
800 let num = self.cur_len.cast();
801 for (i, w) in self.widgets[..num].iter().enumerate() {
802 if key.as_ref() == w.key() {
803 return Some(i);
804 }
805 }
806 }
807 None
808 }
809
810 #[inline]
811 fn translation(&self, _: usize) -> Offset {
812 self.scroll_offset() + self.virtual_offset()
813 }
814
815 fn probe(&self, coord: Coord) -> Id {
816 if self.scroll.is_kinetic_scrolling() {
817 return self.id();
818 }
819
820 let coord = coord + self.translation(0);
821 for child in &self.widgets[..self.cur_len.cast()] {
822 if child.token.is_some()
823 && let Some(id) = child.item.try_probe(coord)
824 {
825 return id;
826 }
827 }
828 self.id()
829 }
830 }
831
832 impl Events for Self {
833 fn mouse_over_icon(&self) -> Option<CursorIcon> {
834 self.scroll
835 .is_kinetic_scrolling()
836 .then_some(CursorIcon::AllScroll)
837 }
838
839 #[inline]
840 fn make_child_id(&mut self, _: usize) -> Id {
841 unimplemented!()
843 }
844
845 fn configure(&mut self, cx: &mut ConfigCx) {
846 cx.register_nav_fallback(self.id());
847 }
848
849 fn configure_recurse(&mut self, cx: &mut ConfigCx, data: &Self::Data) {
850 if self.widgets.is_empty() {
851 self.skip = 1; let len = self.ideal_visible.cast();
856 let key = C::Key::default();
857 self.widgets.resize_with(len, || WidgetData {
858 token: None,
859 item: ListItem::new(self.driver.make(&key)),
860 });
861 self.alloc_len = len.cast();
862 } else {
863 for w in &mut self.widgets {
865 w.token = None;
866 }
867 }
868
869 let alloc_len = self.alloc_len.cast();
870 let first_data: usize = self.first_data.cast();
871 let lbound = first_data + 2 * alloc_len;
872 let data_len = self.clerk.len(data, lbound);
873 self.len_is_known = data_len.is_known();
874 let data_len = data_len.len();
875 self.data_len = data_len.cast();
876 let cur_len = data_len.min(alloc_len);
877 debug_assert!(cur_len <= self.widgets.len());
878 self.cur_len = cur_len.cast();
879
880 self.token_update = Update::Configure;
881 self.map_view_widgets(cx, data, first_data..(first_data + cur_len));
882 }
883
884 fn update(&mut self, cx: &mut ConfigCx, data: &C::Data) {
885 let changes = self.clerk.update(cx, self.id(), self.view_range(), data);
886 if changes != DataChanges::None {
887 self.handle_clerk_update(cx, data, changes);
888 }
889 }
890
891 fn update_recurse(&mut self, _: &mut ConfigCx, _: &Self::Data) {}
892
893 fn handle_event(&mut self, cx: &mut EventCx, data: &C::Data, event: Event) -> IsUsed {
894 let mut is_used = match event {
895 Event::Command(cmd, _) => {
896 let last = usize::conv(self.data_len).wrapping_sub(1);
897 if last == usize::MAX {
898 return Unused;
899 }
900
901 let solver = self.position_solver();
902 let cur = match cx.nav_focus().and_then(|id| self.find_child_index(id)) {
903 Some(index) => solver.child_to_data(index),
904 None => return Unused,
905 };
906 let is_vert = self.direction.is_vertical();
907 let len = solver.cur_len;
908
909 use Command as C;
910 let data_index = match cmd {
911 C::Home | C::DocHome => Some(0),
912 C::End | C::DocEnd => Some(last),
913 C::Left | C::WordLeft if !is_vert && cur > 0 => Some(cur - 1),
914 C::Up if is_vert && cur > 0 => Some(cur - 1),
915 C::Right | C::WordRight if !is_vert && cur < last => Some(cur + 1),
916 C::Down if is_vert && cur < last => Some(cur + 1),
917 C::PageUp if cur > 0 => Some(cur.saturating_sub(len / 2)),
918 C::PageDown if cur < last => Some((cur + len / 2).min(last)),
919 _ => None,
921 };
922 return if let Some(i_data) = data_index {
923 let rect = solver.rect(i_data) - self.virtual_offset();
925 let act = self.scroll.focus_rect(cx, rect, self.rect());
926 if !act.is_empty() {
927 cx.action(&self, act);
928 self.post_scroll(&mut cx.config_cx(), data);
929 }
930 let index = i_data % usize::conv(self.cur_len);
931 let w = &self.widgets[index];
932 if w.token.is_some() {
933 cx.next_nav_focus(w.item.id(), false, FocusSource::Key);
934 }
935 Used
936 } else {
937 Unused
938 };
939 }
940 Event::PressStart(ref press)
941 if press.is_primary() && cx.config().event().mouse_nav_focus() =>
942 {
943 if let Some(index) = cx.last_child() {
944 self.press_target = self.widgets[index].key().map(|k| (index, k.clone()));
945 }
946 if let Some((index, ref key)) = self.press_target {
947 let w = &mut self.widgets[index];
948 if w.key() == Some(key) {
949 cx.next_nav_focus(w.item.id(), false, FocusSource::Pointer);
950 }
951 }
952
953 press.grab_click(self.id()).complete(cx)
956 }
957 Event::PressEnd { ref press, success } if press.is_primary() => {
958 if let Some((index, ref key)) = self.press_target {
959 let w = &mut self.widgets[index];
960 if success
961 && !matches!(self.sel_mode, SelectionMode::None)
962 && !self.scroll.is_kinetic_scrolling()
963 && w.key() == Some(key)
964 && w.item.rect().contains(press.coord + self.translation(0))
965 {
966 cx.push(kas::messages::Select);
967 }
968 }
969 Used
970 }
971 Event::Timer(TIMER_UPDATE_WIDGETS) => {
972 self.post_scroll(&mut cx.config_cx(), data);
973 Used
974 }
975 _ => Unused, };
977
978 let offset = self.scroll.offset();
979 is_used |= self
980 .scroll
981 .scroll_by_event(cx, event, self.id(), self.rect());
982 if offset != self.scroll.offset() {
983 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
986 }
987 is_used
988 }
989
990 fn handle_messages(&mut self, cx: &mut EventCx, data: &C::Data) {
991 if let Some(kas::messages::SetScrollOffset(offset)) = cx.try_pop() {
992 self.set_scroll_offset(cx, offset);
993 return;
994 }
995
996 let mut opt_key = None;
997 if let Some(index) = cx.last_child() {
998 if let Some(token) = self.widgets.get_mut(index).and_then(|w| w.token.as_mut()) {
1000 opt_key = Some(Borrow::<C::Key>::borrow(token).clone());
1001 } else {
1002 return; };
1004 }
1005
1006 if let Some(kas::messages::Select) = cx.try_pop() {
1007 let key = match opt_key {
1008 Some(key) => key,
1009 None => match self.press_target.as_ref() {
1010 Some((_, k)) => k.clone(),
1011 None => return,
1012 },
1013 };
1014 opt_key = None;
1015
1016 match self.sel_mode {
1017 SelectionMode::None => (),
1018 SelectionMode::Single => {
1019 cx.redraw(&self);
1020 self.selection.clear();
1021 self.selection.insert(key.clone());
1022 self.update_selected_items();
1023 cx.push(SelectionMsg::Select(key));
1024 }
1025 SelectionMode::Multiple => {
1026 cx.redraw(&self);
1027 if self.selection.remove(&key) {
1028 cx.push(SelectionMsg::Deselect(key.clone()));
1029 } else {
1030 self.selection.insert(key.clone());
1031 cx.push(SelectionMsg::Select(key));
1032 }
1033 self.update_selected_items();
1034 }
1035 }
1036 }
1037
1038 let changes =
1039 self.clerk
1040 .handle_messages(cx, self.id(), self.view_range(), data, opt_key);
1041 if changes != DataChanges::None {
1042 self.handle_clerk_update(&mut cx.config_cx(), data, changes);
1043 }
1044 }
1045
1046 fn handle_scroll(&mut self, cx: &mut EventCx, data: &C::Data, scroll: Scroll) {
1047 self.scroll
1048 .scroll(cx, self.id(), self.rect(), scroll - self.virtual_offset());
1049 self.post_scroll(&mut cx.config_cx(), data);
1050 }
1051 }
1052
1053 impl Widget for Self {
1055 type Data = C::Data;
1056
1057 fn child_node<'n>(&'n mut self, data: &'n C::Data, index: usize) -> Option<Node<'n>> {
1058 if let Some(w) = self.widgets.get_mut(index)
1059 && let Some(ref token) = w.token
1060 {
1061 let item = self.clerk.item(data, token);
1062 return Some(w.item.as_node(item));
1063 }
1064
1065 None
1066 }
1067
1068 fn _nav_next(
1070 &mut self,
1071 cx: &mut ConfigCx,
1072 data: &C::Data,
1073 focus: Option<&Id>,
1074 advance: NavAdvance,
1075 ) -> Option<Id> {
1076 if cx.is_disabled(self.id_ref()) || self.cur_len == 0 {
1077 return None;
1078 }
1079
1080 let mut child = focus.and_then(|id| self.find_child_index(id));
1081
1082 if let Some(index) = child {
1083 let mut opt_id = None;
1084 let out = &mut opt_id;
1085 if let Some(mut node) = self.as_node(data).get_child(index) {
1086 *out = node._nav_next(cx, focus, advance);
1087 }
1088 if let Some(id) = opt_id {
1089 return Some(id);
1090 }
1091 }
1092
1093 let reverse = match advance {
1094 NavAdvance::None => return None,
1095 NavAdvance::Forward(_) => false,
1096 NavAdvance::Reverse(_) => true,
1097 };
1098
1099 let mut starting_child = child;
1100 loop {
1101 let solver = self.position_solver();
1102 let last_data = usize::conv(self.data_len).wrapping_sub(1);
1103 let data_index = if let Some(index) = child {
1104 let data = solver.child_to_data(index);
1105 if !reverse && data < last_data {
1106 data + 1
1107 } else if reverse && data > 0 {
1108 data - 1
1109 } else {
1110 return None;
1111 }
1112 } else if !reverse {
1113 0
1114 } else {
1115 last_data
1116 };
1117
1118 let rect = solver.rect(data_index) - self.virtual_offset();
1119 let act = self.scroll.self_focus_rect(rect, self.rect());
1120 if !act.is_empty() {
1121 cx.action(&self, act);
1122 self.post_scroll(cx, data);
1123 }
1124
1125 let index = data_index % usize::conv(self.cur_len);
1126
1127 let mut opt_id = None;
1128 let out = &mut opt_id;
1129 if let Some(mut node) = self.as_node(data).get_child(index) {
1130 *out = node._nav_next(cx, focus, advance);
1131 }
1132 if let Some(id) = opt_id {
1133 return Some(id);
1134 }
1135
1136 child = Some(index);
1137 if starting_child == child {
1138 return None;
1139 } else if starting_child.is_none() {
1140 starting_child = child;
1141 }
1142 }
1143 }
1144 }
1145}
1146
1147#[derive(Debug)]
1148struct PositionSolver {
1149 pos_start: Coord,
1150 skip: Offset,
1151 size: Size,
1152 first_data: usize,
1153 cur_len: usize,
1154}
1155
1156impl PositionSolver {
1157 fn child_to_data(&self, index: usize) -> usize {
1159 let mut data = (self.first_data / self.cur_len) * self.cur_len + index;
1160 if data < self.first_data {
1161 data += self.cur_len;
1162 }
1163 data
1164 }
1165
1166 fn rect(&self, i: usize) -> Rect {
1168 let pos = self.pos_start + self.skip * i32::conv(i);
1169 Rect::new(pos, self.size)
1170 }
1171}