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::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::fmt::Debug;
20use std::ops::Range;
21use std::time::Instant;
22
23const TIMER_UPDATE_WIDGETS: TimerHandle = TimerHandle::new(1, true);
24
25#[impl_self]
26mod ListItem {
27 #[widget]
38 #[layout(frame!(self.inner).with_style(kas::theme::FrameStyle::NavFocus))]
39 struct ListItem<K, I, V: Driver<K, I>> {
40 core: widget_core!(),
41 index: usize,
42 selected: Option<bool>,
43 #[widget]
45 inner: V::Widget,
46 }
47
48 impl Self {
49 #[inline]
51 fn new(inner: V::Widget) -> Self {
52 ListItem {
53 core: Default::default(),
54 index: 0,
55 selected: None,
56 inner,
57 }
58 }
59 }
60
61 impl Tile for Self {
62 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
63 if let Some(label) = V::label(&self.inner) {
64 cx.set_label(label);
65 }
66 Role::OptionListItem {
67 index: Some(self.index),
68 selected: self.selected,
69 }
70 }
71
72 fn navigable(&self) -> bool {
73 V::navigable(&self.inner)
74 }
75 }
76
77 impl Events for Self {
78 type Data = I;
79
80 fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
81 match event {
82 Event::Command(cmd, code) if cmd.is_activate() => {
83 cx.depress_with_key(&self, code);
84 cx.push(kas::messages::Select);
85 Used
86 }
87 _ => Unused,
88 }
89 }
90 }
91}
92
93#[autoimpl(Debug ignore self.item where C::Token: trait)]
94struct WidgetData<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>> {
95 token: Option<C::Token>,
96 is_mock: bool,
97 item: ListItem<C::Key, C::Item, V>,
98}
99
100impl<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>> WidgetData<C, V> {
101 fn key(&self) -> Option<&C::Key> {
102 self.token.as_ref().map(Borrow::borrow)
103 }
104}
105
106#[derive(Debug)]
107struct FocusIndex(usize);
108
109#[impl_self]
110mod ListView {
111 #[widget]
135 pub struct ListView<C: TokenClerk<usize>, V, D = Direction>
136 where
137 V: Driver<C::Key, C::Item>,
138 D: Directional,
139 {
140 core: widget_core!(),
141 frame_offset: Offset,
142 frame_size: Size,
143 clerk: C,
144 driver: V,
145 widgets: Vec<WidgetData<C, V>>,
146 data_len: u32,
147 token_update: Update,
148 rect_update: bool,
149 immediate_scroll_update: bool,
150 len_is_known: bool,
151 cur_len: u32,
153 first_data: u32,
155 last_focus: u32,
157 direction: D,
158 align_hints: AlignHints,
159 ideal_visible: i32,
160 child_inter_margin: i32,
161 skip: i32,
162 child_size: Size,
163 offset: Offset,
165 virtual_offset: i32,
166 sel_mode: SelectionMode,
167 sel_style: SelectionStyle,
168 selection: LinearSet<C::Key>,
170 click: ClickInput,
171 press_target: Option<(usize, C::Key)>,
172 }
173
174 impl Default for Self
175 where
176 C: Default,
177 V: Default,
178 D: Default,
179 {
180 fn default() -> Self {
181 Self::new(C::default(), V::default())
182 }
183 }
184 impl Self
185 where
186 D: Default,
187 {
188 pub fn new(clerk: C, driver: V) -> Self {
190 Self::new_dir(clerk, driver, D::default())
191 }
192 }
193 impl<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Left> {
194 pub fn left(clerk: C, driver: V) -> Self {
196 Self::new(clerk, driver)
197 }
198 }
199 impl<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Right> {
200 pub fn right(clerk: C, driver: V) -> Self {
202 Self::new(clerk, driver)
203 }
204 }
205 impl<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Up> {
206 pub fn up(clerk: C, driver: V) -> Self {
208 Self::new(clerk, driver)
209 }
210 }
211 impl<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>> ListView<C, V, kas::dir::Down> {
212 pub fn down(clerk: C, driver: V) -> Self {
214 Self::new(clerk, driver)
215 }
216 }
217 impl<C: TokenClerk<usize>, V: Driver<C::Key, C::Item>, D: Directional + Eq> ListView<C, V, D> {
218 pub fn set_direction(&mut self, cx: &mut ConfigCx, direction: D) {
220 if direction == self.direction {
221 return;
222 }
223
224 self.direction = direction;
225 cx.resize();
226 }
227 }
228
229 impl Self {
230 pub fn new_dir(clerk: C, driver: V, direction: D) -> Self {
232 ListView {
233 core: Default::default(),
234 frame_offset: Default::default(),
235 frame_size: Default::default(),
236 clerk,
237 driver,
238 widgets: Default::default(),
239 data_len: 0,
240 token_update: Update::None,
241 rect_update: false,
242 immediate_scroll_update: false,
243 len_is_known: false,
244 cur_len: 0,
245 first_data: 0,
246 last_focus: 0,
247 direction,
248 align_hints: Default::default(),
249 ideal_visible: 5,
250 child_inter_margin: 0,
251 skip: 1,
252 child_size: Size::ZERO,
253 offset: Offset::ZERO,
254 virtual_offset: 0,
255 sel_mode: SelectionMode::None,
256 sel_style: SelectionStyle::Highlight,
257 selection: Default::default(),
258 click: Default::default(),
259 press_target: None,
260 }
261 }
262
263 pub fn clerk(&self) -> &C {
265 &self.clerk
266 }
267
268 pub fn clerk_mut(&mut self) -> &mut C {
273 &mut self.clerk
274 }
275
276 pub fn view_range(&self) -> Range<usize> {
281 let start: usize = self.first_data.cast();
282 let end = start + usize::conv(self.cur_len);
283 start..end
284 }
285
286 pub fn selection_mode(&self) -> SelectionMode {
288 self.sel_mode
289 }
290 pub fn set_selection_mode(&mut self, cx: &mut EventState, mode: SelectionMode) {
300 self.sel_mode = mode;
301 match mode {
302 SelectionMode::None if !self.selection.is_empty() => {
303 self.selection.clear();
304 self.update_selected_items();
305 cx.redraw(self);
306 }
307 SelectionMode::Single if self.selection.len() > 1 => {
308 if let Some(first) = self.selection.iter().next().cloned() {
309 self.selection.retain(|item| *item == first);
310 }
311 self.update_selected_items();
312 cx.redraw(self);
313 }
314 _ => (),
315 }
316 }
317 #[must_use]
321 pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
322 debug_assert!(self.selection.is_empty());
323 self.sel_mode = mode;
324 self
325 }
326
327 pub fn selection_style(&self) -> SelectionStyle {
329 self.sel_style
330 }
331 pub fn set_selection_style(&mut self, cx: &mut ConfigCx, style: SelectionStyle) {
336 if style.is_external() != self.sel_style.is_external() {
337 cx.resize();
338 };
339 self.sel_style = style;
340 }
341 #[must_use]
345 pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
346 self.sel_style = style;
347 self
348 }
349
350 pub fn selected_iter(&'_ self) -> impl Iterator<Item = &'_ C::Key> + '_ {
355 self.selection.iter()
356 }
357
358 pub fn is_selected(&self, key: &C::Key) -> bool {
360 self.selection.contains(key)
361 }
362
363 pub fn clear_selected(&mut self, cx: &mut EventState) {
365 if !self.selection.is_empty() {
366 self.selection.clear();
367 self.update_selected_items();
368 cx.redraw(self);
369 }
370 }
371
372 pub fn select(&mut self, cx: &mut EventState, key: C::Key) -> bool {
382 match self.sel_mode {
383 SelectionMode::None => return false,
384 SelectionMode::Single => self.selection.clear(),
385 _ => (),
386 }
387 let r = self.selection.insert(key);
388 if r {
389 self.update_selected_items();
390 cx.redraw(self);
391 }
392 r
393 }
394
395 pub fn deselect(&mut self, cx: &mut EventState, key: &C::Key) -> bool {
400 let r = self.selection.remove(key);
401 if r {
402 self.update_selected_items();
403 cx.redraw(self);
404 }
405 r
406 }
407
408 pub fn deselect_unavailable(&mut self, cx: &mut EventState) {
414 let len = self.selection.len();
415 self.selection
416 .retain(|key| self.widgets.iter().any(|widget| widget.key() == Some(key)));
417 self.update_selected_items();
418 if len != self.selection.len() {
419 cx.redraw(self);
420 }
421 }
422
423 fn update_selected_items(&mut self) {
425 let unselected = match self.sel_mode {
426 SelectionMode::None | SelectionMode::Single => None,
427 SelectionMode::Multiple => Some(false),
428 };
429 for w in &mut self.widgets {
430 if let Some(key) = w.key() {
431 if self.selection.contains(key) {
432 w.item.selected = Some(true);
433 } else {
434 w.item.selected = unselected;
435 }
436 }
437 }
438 }
439
440 pub fn direction(&self) -> Direction {
442 self.direction.as_direction()
443 }
444
445 #[must_use]
450 pub fn with_num_visible(mut self, number: i32) -> Self {
451 self.ideal_visible = number;
452 self
453 }
454
455 #[inline]
456 fn virtual_offset(&self) -> Offset {
457 match self.direction.is_vertical() {
458 false => Offset(self.virtual_offset, 0),
459 true => Offset(0, self.virtual_offset),
460 }
461 }
462
463 fn position_solver(&self) -> PositionSolver {
464 let cur_len: usize = self.cur_len.cast();
465 let mut first_data: usize = self.first_data.cast();
466 let mut skip = Offset::ZERO;
467 skip.set_component(self.direction, self.skip);
468
469 let mut pos_start = self.rect().pos + self.frame_offset + self.virtual_offset();
470 if self.direction.is_reversed() && self.len_is_known {
471 let data_len: usize = self.data_len.cast();
472 first_data = (data_len - first_data).saturating_sub(cur_len);
473 pos_start += skip * i32::conv(data_len.saturating_sub(1));
474 skip = skip * -1;
475 }
476
477 PositionSolver {
478 pos_start,
479 skip,
480 size: self.child_size,
481 first_data,
482 cur_len,
483 }
484 }
485
486 #[inline]
488 fn post_scroll(&mut self, cx: &mut ConfigCx, data: &C::Data) {
489 self.handle_update(cx, data, Changes::None, false);
490 }
491
492 fn handle_update(
494 &mut self,
495 cx: &mut ConfigCx,
496 data: &C::Data,
497 changes: Changes<usize>,
498 force_update: bool,
499 ) {
500 if matches!(changes, Changes::Range(_) | Changes::Any) {
502 self.token_update = self.token_update.max(Update::Token);
503 }
504
505 let offset = self.offset.extract(self.direction);
506 let first_data = usize::conv(u64::conv(offset) / u64::conv(self.skip));
507
508 let alloc_len = self.widgets.len();
509 let data_len;
510 if !self.len_is_known || changes != Changes::None {
511 let lbound = first_data + 2 * alloc_len;
512 let result = self.clerk.len(data, lbound);
513 self.len_is_known = result.is_known();
514 data_len = result.len();
515 if data_len != usize::conv(self.data_len) {
516 self.data_len = data_len.cast();
517 cx.resize();
518 }
519 } else {
520 data_len = self.data_len.cast();
521 }
522 let cur_len = data_len.min(alloc_len);
523 let first_data = first_data.min(data_len - cur_len);
524
525 let old_start = self.first_data.cast();
526 let old_end = old_start + usize::conv(self.cur_len);
527 let (mut start, mut end) = (first_data, first_data + cur_len);
528
529 let virtual_offset = -(offset & 0x7FF0_0000);
530 if virtual_offset != self.virtual_offset {
531 self.virtual_offset = virtual_offset;
532 self.rect_update = true;
533 } else if force_update || self.rect_update || self.token_update != Update::None {
534 } else if start >= old_start {
536 start = start.max(old_end);
537 } else if end <= old_end {
538 end = end.min(old_start);
539 }
540
541 debug_assert!(cur_len <= self.widgets.len());
542 self.cur_len = cur_len.cast();
543 self.first_data = first_data.cast();
544
545 if start < end {
546 self.map_view_widgets(cx, data, start..end, force_update);
547 }
548 }
549
550 fn map_view_widgets(
554 &mut self,
555 cx: &mut ConfigCx,
556 data: &C::Data,
557 range: Range<usize>,
558 force_update: bool,
559 ) {
560 let time = Instant::now();
561
562 self.clerk
563 .prepare_range(cx, self.id(), self.view_range(), data, range.clone());
564
565 let id = self.id();
566
567 let solver = self.position_solver();
568 for i in range.clone() {
569 let w = &mut self.widgets[i % solver.cur_len];
570
571 let force = self.token_update != Update::None;
572 let changes = self.clerk.update_token(data, i, force, &mut w.token);
573 w.is_mock = false;
574 let Some(token) = w.token.as_ref() else {
575 continue;
576 };
577
578 let mut rect_update = self.rect_update;
579 if changes.key() || self.token_update == Update::Configure {
580 w.item.index = i;
581 self.driver.set_key(&mut w.item.inner, token.borrow());
584
585 let item = self.clerk.item(data, token);
586 let id = token.borrow().make_id(&id);
587 cx.configure(w.item.as_node(item), id);
588
589 solve_size_rules(
590 &mut w.item,
591 &mut cx.size_cx(),
592 Some(self.child_size.0),
593 Some(self.child_size.1),
594 );
595 rect_update = true;
596 } else if force_update || changes.item() {
597 let item = self.clerk.item(data, token);
598 cx.update(w.item.as_node(item));
599 }
600
601 if rect_update {
602 w.item
603 .set_rect(&mut cx.size_cx(), solver.rect(i), self.align_hints);
604 }
605 }
606
607 self.token_update = Update::None;
608 self.rect_update = false;
609
610 let dur = (Instant::now() - time).as_micros();
611 log::debug!(
612 target: "kas_perf::view::list_view",
613 "map_view_widgets: {} view widgets in: {dur}μs",
614 range.len(),
615 );
616 }
617 }
618
619 impl Layout for Self {
620 fn size_rules(&mut self, cx: &mut SizeCx, mut axis: AxisInfo) -> SizeRules {
621 let inner_margin = if self.sel_style.is_external() {
623 cx.inner_margins().extract(axis)
624 } else {
625 (0, 0)
626 };
627 let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));
628
629 let other = axis.other().map(|size| {
630 if self.direction.is_horizontal() == axis.is_vertical() {
631 self.child_size.extract(axis.flipped())
632 } else {
633 size - self.frame_size.extract(axis.flipped())
634 }
635 });
636 axis = AxisInfo::new(axis.is_vertical(), other);
637
638 let mut rules = SizeRules::EMPTY;
639 for w in self.widgets.iter_mut() {
640 if w.token.is_some() || w.is_mock {
641 let child_rules = w.item.size_rules(cx, axis);
642 rules = rules.max(child_rules);
643 }
644 }
645 if axis.is_vertical() == self.direction.is_vertical() {
646 let size = rules.min_size().max(1);
648 self.child_size.set_component(axis, size);
649 let m = rules.margins();
650 let inter_margin = m.0.max(m.1).max(inner_margin.0).max(inner_margin.1);
651 self.child_inter_margin = inter_margin.cast();
652 let inter_margin: i32 = inter_margin.cast();
653 let stretch = rules.stretch();
654
655 let (min_len, ideal_len) = (2, self.ideal_visible);
656 let min = min_len * size + (min_len - 1) * inter_margin;
657 let ideal = ideal_len * size + (ideal_len - 1) * inter_margin;
658
659 rules = SizeRules::new(min, ideal, stretch.max(Stretch::High)).with_margins(m);
660 } else {
661 rules.set_stretch(rules.stretch().max(Stretch::Low));
662 }
663 let (rules, offset, size) = frame.surround(rules);
664 self.frame_offset.set_component(axis, offset);
665 self.frame_size.set_component(axis, size);
666 rules
667 }
668
669 fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
670 self.core.set_rect(rect);
671 self.align_hints = hints;
672
673 let skip = if self.direction.is_horizontal() {
674 self.child_size.1 = rect.size.1 - self.frame_size.1;
675 self.child_size.0 + self.child_inter_margin
676 } else {
677 self.child_size.0 = rect.size.0 - self.frame_size.0;
678 self.child_size.1 + self.child_inter_margin
679 };
680
681 let req_widgets = if skip == 0 {
682 self.skip = 1; 0
684 } else {
685 self.skip = skip;
686 let size = rect.size.extract(self.direction);
687 usize::conv(size).div_ceil(usize::conv(skip)) + 1
688 };
689
690 let avail_widgets = self.widgets.len();
691 if avail_widgets < req_widgets {
692 log::debug!(
693 "set_rect: allocating widgets (old len = {avail_widgets}, new = {req_widgets})",
694 );
695 self.widgets.reserve(req_widgets - avail_widgets);
696 let key = C::Key::default();
697 for _ in avail_widgets..req_widgets {
698 let item = ListItem::new(self.driver.make(&key));
699 self.widgets.push(WidgetData {
700 token: None,
701 is_mock: false,
702 item,
703 });
704 }
705 }
706
707 let solver = self.position_solver();
712 for i in 0..solver.cur_len {
713 let i = solver.first_data + i;
714 let w = &mut self.widgets[i % solver.cur_len];
715 if w.token.is_some() {
716 w.item.set_rect(cx, solver.rect(i), self.align_hints);
717 }
718 }
719
720 self.rect_update = true;
721 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
722 }
723 }
724
725 impl Viewport for Self {
726 fn content_size(&self) -> Size {
727 let data_len: i32 = self.data_len.cast();
728 let m = self.child_inter_margin;
729 let step = self.child_size.extract(self.direction) + m;
730 let mut content_size = Size::ZERO;
731 content_size.set_component(self.direction, (step * data_len - m).max(0));
732 content_size
733 }
734
735 fn set_offset(&mut self, _: &mut SizeCx, _: Rect, offset: Offset) {
736 self.offset = offset;
739 }
740
741 fn update_offset(&mut self, cx: &mut ConfigCx, data: &Self::Data, _: Rect, offset: Offset) {
742 self.offset = offset;
743 if self.immediate_scroll_update {
744 self.immediate_scroll_update = false;
745 self.post_scroll(cx, data);
746 } else {
747 cx.request_frame_timer(self.id(), TIMER_UPDATE_WIDGETS);
750 }
751 }
752
753 fn draw_with_offset(&self, mut draw: DrawCx, viewport: Rect, offset: Offset) {
754 draw.with_clip_region(viewport, offset + self.virtual_offset(), |mut draw| {
756 for child in &self.widgets[..self.cur_len.cast()] {
757 if let Some(key) = child.key() {
758 if self.selection.contains(key) {
759 draw.selection(child.item.rect(), self.sel_style);
760 }
761 child.item.draw(draw.re());
762 }
763 }
764 });
765 }
766 }
767
768 impl Tile for Self {
769 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
770 Role::OptionList {
771 len: self.len_is_known.then(|| self.data_len.cast()),
772 direction: self.direction.as_direction(),
773 }
774 }
775
776 #[inline]
777 fn child_indices(&self) -> ChildIndices {
778 ChildIndices::range(0..self.cur_len.cast())
779 }
780 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
781 self.widgets
782 .get(index)
783 .filter(|w| w.token.is_some())
784 .map(|w| w.item.as_tile())
785 }
786 fn find_child_index(&self, id: &Id) -> Option<usize> {
787 let key = C::Key::reconstruct_key(self.id_ref(), id);
788 if key.is_some() {
789 let num = self.cur_len.cast();
790 for (i, w) in self.widgets[..num].iter().enumerate() {
791 if key.as_ref() == w.key() {
792 return Some(i);
793 }
794 }
795 }
796 None
797 }
798
799 fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
800 if self.data_len == 0 {
801 return None;
802 }
803
804 let solver = self.position_solver();
805 let data_index = if V::TAB_NAVIGABLE {
806 let first_data: usize = self.first_data.cast();
807 let last_data = usize::conv(self.data_len) - 1;
808 let size: usize = self.rect().size.extract(self.direction).cast();
809 let last_visible = (first_data + size / usize::conv(self.skip)).min(last_data);
810 if let Some(index) = from {
811 let data = solver.child_to_data(index);
812 if !reverse && data < last_visible {
813 data + 1
814 } else if reverse && data > first_data {
815 data - 1
816 } else {
817 return None;
818 }
819 } else if !reverse {
820 first_data
821 } else {
822 last_visible
823 }
824 } else {
825 if from.is_some() {
826 return None;
827 } else {
828 self.last_focus.cast()
829 }
830 };
831
832 let index = data_index % usize::conv(self.cur_len);
833 self.get_child(index).is_some().then_some(index)
834 }
835
836 #[inline]
837 fn translation(&self, _: usize) -> Offset {
838 self.virtual_offset()
839 }
840 }
841
842 impl Events for Self {
843 #[inline]
844 fn make_child_id(&mut self, _: usize) -> Id {
845 unimplemented!()
847 }
848
849 fn probe(&self, coord: Coord) -> Id {
850 let coord = coord + self.translation(0);
851 for child in &self.widgets[..self.cur_len.cast()] {
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.skip = 1; let len = self.ideal_visible.cast();
868 let key = C::Key::default();
869 self.widgets.resize_with(len, || WidgetData {
870 token: None,
871 is_mock: false,
872 item: ListItem::new(self.driver.make(&key)),
873 });
874 } else {
875 for w in &mut self.widgets {
877 w.token = None;
878 }
879 }
880 self.token_update = Update::Configure;
881 }
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 self.token_update != Update::None || changes != Changes::None {
887 self.handle_update(cx, data, changes, true);
888 } else {
889 for w in &mut self.widgets[..self.cur_len.cast()] {
890 if let Some(ref token) = w.token {
891 let item = self.clerk.item(data, token);
892 cx.update(w.item.as_node(item));
893 }
894 }
895 }
896
897 let id = self.id();
898 if self.cur_len == 0
899 && let Some(w) = self.widgets.get_mut(0)
900 && w.token.is_none()
901 && !w.is_mock
902 && let Some(item) = self.clerk.mock_item(data)
903 {
904 cx.configure(w.item.as_node(&item), id);
906 w.is_mock = true;
907 }
908 }
909
910 #[inline]
911 fn recurse_indices(&self) -> ChildIndices {
912 ChildIndices::none()
913 }
914
915 fn child_nav_focus(&mut self, cx: &mut EventCx, _: Id) {
916 if let Some(index) = cx.last_child()
917 && self.get_child(index).is_some()
918 {
919 let solver = self.position_solver();
920 self.last_focus = solver.child_to_data(index).cast();
921 }
922 }
923
924 fn handle_event(&mut self, cx: &mut EventCx, data: &C::Data, event: Event) -> IsUsed {
925 match event {
926 Event::Command(cmd, _) => {
927 let last = usize::conv(self.data_len).wrapping_sub(1);
928 if last == usize::MAX {
929 return Unused;
930 }
931
932 let solver = self.position_solver();
933 let cur = match cx.nav_focus().and_then(|id| self.find_child_index(id)) {
934 Some(index) => solver.child_to_data(index),
935 None => return Unused,
936 };
937 let is_vert = self.direction.is_vertical();
938 let len = solver.cur_len;
939
940 use Command as C;
941 let data_index = match cmd {
942 C::Home | C::DocHome => Some(0),
943 C::End | C::DocEnd => Some(last),
944 C::Left | C::WordLeft if !is_vert && cur > 0 => Some(cur - 1),
945 C::Up if is_vert && cur > 0 => Some(cur - 1),
946 C::Right | C::WordRight if !is_vert && cur < last => Some(cur + 1),
947 C::Down if is_vert && cur < last => Some(cur + 1),
948 C::PageUp if cur > 0 => Some(cur.saturating_sub(len / 2)),
949 C::PageDown if cur < last => Some((cur + len / 2).min(last)),
950 _ => None,
952 };
953 if let Some(i_data) = data_index {
954 let rect = solver.rect(i_data) - self.virtual_offset();
956 cx.set_scroll(Scroll::Rect(rect));
957 let index = i_data % usize::conv(self.cur_len);
958 let w = &self.widgets[index];
959 if w.item.index == i_data && w.token.is_some() {
960 cx.next_nav_focus(w.item.id(), false, FocusSource::Key);
961 } else {
962 self.immediate_scroll_update = true;
963 cx.send(self.id(), FocusIndex(i_data));
964 }
965 Used
966 } else {
967 Unused
968 }
969 }
970 Event::Timer(TIMER_UPDATE_WIDGETS) => {
971 self.post_scroll(cx, data);
972 Used
973 }
974 event => match self.click.handle(cx, self.id(), event) {
975 ClickInputAction::Used => Used,
976 ClickInputAction::Unused => Unused,
977 ClickInputAction::ClickStart { .. } => {
978 if let Some(index) = cx.last_child() {
979 self.press_target =
980 self.widgets[index].key().map(|k| (index, k.clone()));
981 }
982 Used
983 }
984 ClickInputAction::ClickEnd { coord, success } => {
985 if let Some((index, ref key)) = self.press_target {
986 let w = &mut self.widgets[index];
987 if success
988 && !matches!(self.sel_mode, SelectionMode::None)
989 && w.key() == Some(key)
990 && w.item.rect().contains(coord + self.translation(0))
991 {
992 cx.push(kas::messages::Select);
993 }
994 }
995 Used
996 }
997 },
998 }
999 }
1000
1001 fn handle_messages(&mut self, cx: &mut EventCx, data: &C::Data) {
1002 if let Some(FocusIndex(i_data)) = cx.try_pop() {
1003 let index = i_data % usize::conv(self.cur_len);
1004 let w = &self.widgets[index];
1005 if w.item.index == i_data && w.token.is_some() {
1006 cx.next_nav_focus(w.item.id(), false, FocusSource::Key);
1007 } else {
1008 log::error!("ListView failed to set focus: data item {i_data:?} not in view");
1009 }
1010 }
1011
1012 let mut opt_key = None;
1013 if let Some(index) = cx.last_child() {
1014 if let Some(token) = self.widgets.get_mut(index).and_then(|w| w.token.as_mut()) {
1016 opt_key = Some(Borrow::<C::Key>::borrow(token).clone());
1017 } else {
1018 return; };
1020 }
1021
1022 if let Some(kas::messages::Select) = cx.try_pop() {
1023 let key = match opt_key {
1024 Some(key) => key,
1025 None => match self.press_target.as_ref() {
1026 Some((_, k)) => k.clone(),
1027 None => return,
1028 },
1029 };
1030 opt_key = None;
1031
1032 match self.sel_mode {
1033 SelectionMode::None => (),
1034 SelectionMode::Single => {
1035 cx.redraw();
1036 self.selection.clear();
1037 self.selection.insert(key.clone());
1038 self.update_selected_items();
1039 cx.push(SelectionMsg::Select(key));
1040 }
1041 SelectionMode::Multiple => {
1042 cx.redraw();
1043 if self.selection.remove(&key) {
1044 cx.push(SelectionMsg::Deselect(key.clone()));
1045 } else {
1046 self.selection.insert(key.clone());
1047 cx.push(SelectionMsg::Select(key));
1048 }
1049 self.update_selected_items();
1050 }
1051 }
1052 }
1053
1054 let changes =
1055 self.clerk
1056 .handle_messages(cx, self.id(), self.view_range(), data, opt_key);
1057 if changes != Changes::None {
1058 self.handle_update(cx, data, changes, false);
1059 }
1060 }
1061
1062 fn handle_scroll(&mut self, cx: &mut EventCx, _: &C::Data, scroll: Scroll) {
1063 cx.set_scroll(scroll - self.virtual_offset());
1064 }
1065 }
1066
1067 impl Widget for Self {
1069 type Data = C::Data;
1070
1071 fn child_node<'n>(&'n mut self, data: &'n C::Data, index: usize) -> Option<Node<'n>> {
1072 if let Some(w) = self.widgets.get_mut(index)
1073 && let Some(ref token) = w.token
1074 {
1075 let item = self.clerk.item(data, token);
1076 return Some(w.item.as_node(item));
1077 }
1078
1079 None
1080 }
1081 }
1082}
1083
1084#[derive(Debug)]
1085struct PositionSolver {
1086 pos_start: Coord,
1087 skip: Offset,
1088 size: Size,
1089 first_data: usize,
1090 cur_len: usize,
1091}
1092
1093impl PositionSolver {
1094 fn child_to_data(&self, index: usize) -> usize {
1096 let mut data = (self.first_data / self.cur_len) * self.cur_len + index;
1097 if data < self.first_data {
1098 data += self.cur_len;
1099 }
1100 data
1101 }
1102
1103 fn rect(&self, i: usize) -> Rect {
1105 let pos = self.pos_start + self.skip * i32::conv(i);
1106 Rect::new(pos, self.size)
1107 }
1108}