1use crate::{DataKey, Driver, ListData, SelectionMode, SelectionMsg};
9use kas::event::components::ScrollComponent;
10use kas::event::{Command, FocusSource, Scroll};
11use kas::layout::solve_size_rules;
12use kas::prelude::*;
13use kas::theme::SelectionStyle;
14use kas::NavAdvance;
15#[allow(unused)] use kas_widgets::ScrollBars;
17use linear_map::set::LinearSet;
18use std::borrow::Borrow;
19use std::fmt::Debug;
20use std::time::Instant;
21
22#[derive(Clone, Debug, Default)]
23struct WidgetData<K, W> {
24 key: Option<K>,
25 widget: W,
26}
27
28impl_scope! {
29 #[derive(Clone, Debug)]
48 #[widget]
49 pub struct ListView<A: ListData, V, D = Direction>
50 where
51 V: Driver<A::Item, A>,
52 D: Directional,
53 {
54 core: widget_core!(),
55 frame_offset: Offset,
56 frame_size: Size,
57 driver: V,
58 widgets: Vec<WidgetData<A::Key, V::Widget>>,
59 data_len: u32,
60 cur_len: u32,
62 first_data: u32,
64 direction: D,
65 align_hints: AlignHints,
66 ideal_visible: i32,
67 child_size_min: i32,
68 child_size_ideal: i32,
69 child_inter_margin: i32,
70 skip: i32,
71 child_size: Size,
72 scroll: ScrollComponent,
73 sel_mode: SelectionMode,
74 sel_style: SelectionStyle,
75 selection: LinearSet<A::Key>,
77 press_target: Option<(usize, A::Key)>,
78 }
79
80 impl Self
81 where
82 D: Default,
83 {
84 pub fn new(driver: V) -> Self {
86 Self::new_dir(driver, D::default())
87 }
88 }
89 impl<A: ListData, V: Driver<A::Item, A>> ListView<A, V, kas::dir::Left> {
90 pub fn left(driver: V) -> Self {
92 Self::new(driver)
93 }
94 }
95 impl<A: ListData, V: Driver<A::Item, A>> ListView<A, V, kas::dir::Right> {
96 pub fn right(driver: V) -> Self {
98 Self::new(driver)
99 }
100 }
101 impl<A: ListData, V: Driver<A::Item, A>> ListView<A, V, kas::dir::Up> {
102 pub fn up(driver: V) -> Self {
104 Self::new(driver)
105 }
106 }
107 impl<A: ListData, V: Driver<A::Item, A>> ListView<A, V, kas::dir::Down> {
108 pub fn down(driver: V) -> Self {
110 Self::new(driver)
111 }
112 }
113 impl<A: ListData, V: Driver<A::Item, A>> ListView<A, V, Direction> {
114 pub fn set_direction(&mut self, direction: Direction) -> Action {
116 if direction == self.direction {
117 return Action::empty();
118 }
119
120 self.direction = direction;
121 Action::SET_RECT
122 }
123 }
124
125 impl Self {
126 pub fn new_dir(driver: V, direction: D) -> Self {
128 ListView {
129 core: Default::default(),
130 frame_offset: Default::default(),
131 frame_size: Default::default(),
132 driver,
133 widgets: Default::default(),
134 data_len: 0,
135 cur_len: 0,
136 first_data: 0,
137 direction,
138 align_hints: Default::default(),
139 ideal_visible: 5,
140 child_size_min: 0,
141 child_size_ideal: 0,
142 child_inter_margin: 0,
143 skip: 1,
144 child_size: Size::ZERO,
145 scroll: Default::default(),
146 sel_mode: SelectionMode::None,
147 sel_style: SelectionStyle::Highlight,
148 selection: Default::default(),
149 press_target: None,
150 }
151 }
152
153 pub fn selection_mode(&self) -> SelectionMode {
155 self.sel_mode
156 }
157 pub fn set_selection_mode(&mut self, mode: SelectionMode) -> Action {
172 self.sel_mode = mode;
173 match mode {
174 SelectionMode::None if !self.selection.is_empty() => {
175 self.selection.clear();
176 Action::REDRAW
177 }
178 SelectionMode::Single if self.selection.len() > 1 => {
179 if let Some(first) = self.selection.iter().next().cloned() {
180 self.selection.retain(|item| *item == first);
181 }
182 Action::REDRAW
183 }
184 _ => Action::empty(),
185 }
186 }
187 #[must_use]
191 pub fn with_selection_mode(mut self, mode: SelectionMode) -> Self {
192 let _ = self.set_selection_mode(mode);
193 self
194 }
195
196 pub fn selection_style(&self) -> SelectionStyle {
198 self.sel_style
199 }
200 pub fn set_selection_style(&mut self, style: SelectionStyle) -> Action {
205 let action = if style.is_external() != self.sel_style.is_external() {
206 Action::RESIZE
207 } else {
208 Action::empty()
209 };
210 self.sel_style = style;
211 action
212 }
213 #[must_use]
217 pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
218 self.sel_style = style;
219 self
220 }
221
222 pub fn selected_iter(&'_ self) -> impl Iterator<Item = &'_ A::Key> + '_ {
227 self.selection.iter()
228 }
229
230 pub fn is_selected(&self, key: &A::Key) -> bool {
232 self.selection.contains(key)
233 }
234
235 pub fn clear_selected(&mut self) -> Action {
237 if self.selection.is_empty() {
238 Action::empty()
239 } else {
240 self.selection.clear();
241 Action::REDRAW
242 }
243 }
244
245 pub fn select(&mut self, key: A::Key) -> Action {
255 match self.sel_mode {
256 SelectionMode::None => return Action::empty(),
257 SelectionMode::Single => self.selection.clear(),
258 _ => (),
259 }
260 match self.selection.insert(key) {
261 true => Action::REDRAW,
262 false => Action::empty(),
263 }
264 }
265
266 pub fn deselect(&mut self, key: &A::Key) -> Action {
271 match self.selection.remove(key) {
272 true => Action::REDRAW,
273 false => Action::empty(),
274 }
275 }
276
277 pub fn direction(&self) -> Direction {
279 self.direction.as_direction()
280 }
281
282 #[must_use]
287 pub fn with_num_visible(mut self, number: i32) -> Self {
288 self.ideal_visible = number;
289 self
290 }
291
292 fn position_solver(&self) -> PositionSolver {
293 let data_len: usize = self.data_len.cast();
294 let cur_len: usize = self.cur_len.cast();
295 let mut first_data: usize = self.first_data.cast();
296 let mut skip = Offset::ZERO;
297 skip.set_component(self.direction, self.skip);
298
299 let mut pos_start = self.core.rect.pos + self.frame_offset;
300 if self.direction.is_reversed() {
301 first_data = (data_len - first_data).saturating_sub(cur_len);
302 pos_start += skip * i32::conv(data_len.saturating_sub(1));
303 skip = skip * -1;
304 }
305
306 PositionSolver {
307 pos_start,
308 skip,
309 size: self.child_size,
310 first_data,
311 cur_len,
312 }
313 }
314
315 fn update_widgets(&mut self, cx: &mut ConfigCx, data: &A) {
316 let time = Instant::now();
317
318 let offset = u64::conv(self.scroll_offset().extract(self.direction));
319 let mut first_data = usize::conv(offset / u64::conv(self.skip));
320
321 let data_len: usize = self.data_len.cast();
322 let cur_len: usize = self.widgets.len().min(data_len);
323 first_data = first_data.min(data_len - cur_len);
324 self.cur_len = cur_len.cast();
325 debug_assert!(self.num_children() <= self.widgets.len());
326 self.first_data = first_data.cast();
327
328 let solver = self.position_solver();
329 let keys = data.iter_from(solver.first_data, solver.cur_len);
330
331 let mut count = 0;
332 for (i, key) in keys.enumerate() {
333 count += 1;
334 let i = solver.first_data + i;
335 let id = key.make_id(self.id_ref());
336 let w = &mut self.widgets[i % solver.cur_len];
337 if w.key.as_ref() != Some(&key) {
338 self.driver.set_key(&mut w.widget, &key);
339
340 if let Some(item) = data.borrow(&key) {
341 cx.configure(w.widget.as_node(item.borrow()), id);
342
343 solve_size_rules(
344 &mut w.widget,
345 cx.size_cx(),
346 Some(self.child_size.0),
347 Some(self.child_size.1),
348 );
349 w.key = Some(key);
350 } else {
351 w.key = None; }
353 } else if let Some(item) = data.borrow(&key) {
354 cx.update(w.widget.as_node(item.borrow()));
355 }
356 w.widget.set_rect(cx, solver.rect(i), self.align_hints);
357 }
358
359 if count < solver.cur_len {
360 log::warn!(
361 "{}: data.iter_from({}, {}) yielded insufficient items (possibly incorrect data.len())", self.identify(),
362 solver.first_data,
363 solver.cur_len,
364 );
365 }
366
367 let dur = (Instant::now() - time).as_micros();
368 log::trace!(target: "kas_perf::view::list_view", "update_widgets: {dur}μs");
369 }
370
371 fn update_content_size(&mut self, cx: &mut ConfigCx) {
372 let data_len: i32 = self.data_len.cast();
373 let view_size = self.rect().size - self.frame_size;
374 let mut content_size = view_size;
375 content_size.set_component(
376 self.direction,
377 (self.skip * data_len - self.child_inter_margin).max(0),
378 );
379 let action = self.scroll.set_sizes(view_size, content_size);
380 cx.action(self, action);
381 }
382 }
383
384 impl Scrollable for Self {
385 fn scroll_axes(&self, size: Size) -> (bool, bool) {
386 let data_len: i32 = self.data_len.cast();
389 let inner_size = (size - self.frame_size).extract(self.direction());
390 let child_size = (inner_size / self.ideal_visible)
391 .min(self.child_size_ideal)
392 .max(self.child_size_min);
393 let m = self.child_inter_margin;
394 let step = child_size + m;
395 let content_size = (step * data_len - m).max(0);
396 if self.direction.is_horizontal() {
397 (content_size > inner_size, false)
398 } else {
399 (false, content_size > inner_size)
400 }
401 }
402
403 #[inline]
404 fn max_scroll_offset(&self) -> Offset {
405 self.scroll.max_offset()
406 }
407
408 #[inline]
409 fn scroll_offset(&self) -> Offset {
410 self.scroll.offset()
411 }
412
413 #[inline]
414 fn set_scroll_offset(&mut self, cx: &mut EventCx, offset: Offset) -> Offset {
415 let act = self.scroll.set_offset(offset);
416 cx.action(&self, act);
417 cx.request_update(self.id(), false);
418 self.scroll.offset()
419 }
420 }
421
422 impl Layout for Self {
423 #[inline]
424 fn num_children(&self) -> usize {
425 self.cur_len.cast()
426 }
427 fn get_child(&self, index: usize) -> Option<&dyn Layout> {
428 self.widgets.get(index).map(|w| w.widget.as_layout())
429 }
430 fn find_child_index(&self, id: &Id) -> Option<usize> {
431 let key = A::Key::reconstruct_key(self.id_ref(), id);
432 if key.is_some() {
433 self.widgets
434 .iter()
435 .enumerate()
436 .filter_map(|(i, w)| (key == w.key).then_some(i))
437 .next()
438 } else {
439 None
440 }
441 }
442
443 fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
444 let inner_margin = if self.sel_style.is_external() {
446 sizer.inner_margins().extract(axis)
447 } else {
448 (0, 0)
449 };
450 let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));
451
452 let other = axis.other().map(|mut size| {
453 let other_axis = axis.flipped();
455 size -= self.frame_size.extract(other_axis);
456 if self.direction.is_horizontal() == other_axis.is_horizontal() {
457 size = (size / self.ideal_visible)
458 .min(self.child_size_ideal)
459 .max(self.child_size_min);
460 }
461 size
462 });
463 axis = AxisInfo::new(axis.is_vertical(), other);
464
465 self.child_size_min = i32::MAX;
466 let mut rules = SizeRules::EMPTY;
467 for w in self.widgets.iter_mut() {
468 if w.key.is_some() {
469 let child_rules = w.widget.size_rules(sizer.re(), axis);
470 if axis.is_vertical() == self.direction.is_vertical() {
471 self.child_size_min = self.child_size_min.min(child_rules.min_size());
472 }
473 rules = rules.max(child_rules);
474 }
475 }
476 if self.child_size_min == i32::MAX {
477 self.child_size_min = 0;
478 }
479
480 if axis.is_vertical() == self.direction.is_vertical() {
481 self.child_size_ideal = rules.ideal_size();
482 let m = rules.margins();
483 self.child_inter_margin =
484 m.0.max(m.1).max(inner_margin.0).max(inner_margin.1).cast();
485 rules.multiply_with_margin(2, self.ideal_visible);
486 rules.set_stretch(rules.stretch().max(Stretch::High));
487 } else {
488 rules.set_stretch(rules.stretch().max(Stretch::Low));
489 }
490 let (rules, offset, size) = frame.surround(rules);
491 self.frame_offset.set_component(axis, offset);
492 self.frame_size.set_component(axis, size);
493 rules
494 }
495
496 fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
497 self.core.rect = rect;
498 self.align_hints = hints;
499
500 self.cur_len = 0; cx.request_update(self.id(), false);
503
504 let mut child_size = rect.size - self.frame_size;
505 let (size, skip);
506 if self.direction.is_horizontal() {
507 child_size.0 = (child_size.0 / self.ideal_visible)
508 .min(self.child_size_ideal)
509 .max(self.child_size_min);
510 size = rect.size.0;
511 skip = child_size.0 + self.child_inter_margin;
512 } else {
513 child_size.1 = (child_size.1 / self.ideal_visible)
514 .min(self.child_size_ideal)
515 .max(self.child_size_min);
516 size = rect.size.1;
517 skip = child_size.1 + self.child_inter_margin;
518 }
519
520 self.child_size = child_size;
521 self.skip = skip;
522 self.update_content_size(cx);
523
524 if skip == 0 {
525 self.skip = 1; return;
527 }
528 let req_widgets = usize::conv((size + skip - 1) / skip + 1);
529
530 let avail_widgets = self.widgets.len();
531 if avail_widgets < req_widgets {
532 log::debug!(
533 "set_rect: allocating widgets (old len = {}, new = {})",
534 avail_widgets,
535 req_widgets
536 );
537 self.widgets.reserve(req_widgets - avail_widgets);
538 let key = A::Key::default();
539 for _ in avail_widgets..req_widgets {
540 let widget = self.driver.make(&key);
541 self.widgets.push(WidgetData { key: None, widget });
542 }
543 }
544 if req_widgets + 64 <= avail_widgets {
545 self.widgets.truncate(req_widgets);
547 }
548 debug_assert!(self.widgets.len() >= req_widgets);
549 }
550
551 #[inline]
552 fn translation(&self) -> Offset {
553 self.scroll_offset()
554 }
555
556 fn find_id(&mut self, coord: Coord) -> Option<Id> {
557 if !self.rect().contains(coord) {
558 return None;
559 }
560
561 let coord = coord + self.scroll.offset();
562 for child in &mut self.widgets[..self.cur_len.cast()] {
563 if child.key.is_some() {
564 if let Some(id) = child.widget.find_id(coord) {
565 return Some(id);
566 }
567 }
568 }
569 Some(self.id())
570 }
571
572 fn draw(&mut self, mut draw: DrawCx) {
573 let offset = self.scroll_offset();
574 draw.with_clip_region(self.core.rect, offset, |mut draw| {
575 for child in &mut self.widgets[..self.cur_len.cast()] {
576 if let Some(ref key) = child.key {
577 if self.selection.contains(key) {
578 draw.selection(child.widget.rect(), self.sel_style);
579 }
580 }
581 draw.recurse(&mut child.widget);
582 }
583 });
584 }
585 }
586
587 impl Events for Self {
588 #[inline]
589 fn make_child_id(&mut self, _: usize) -> Id {
590 unimplemented!()
592 }
593
594 fn configure(&mut self, cx: &mut ConfigCx) {
595 if self.widgets.is_empty() {
596 self.skip = 1; let len = self.ideal_visible.cast();
601 let key = A::Key::default();
602 self.widgets.resize_with(len, || {
603 WidgetData {
604 key: None,
605 widget: self.driver.make(&key),
606 }
607 });
608 }
609
610 cx.register_nav_fallback(self.id());
611 }
612
613 fn configure_recurse(&mut self, _: &mut ConfigCx, _: &Self::Data) {}
614
615 fn update(&mut self, cx: &mut ConfigCx, data: &A) {
616 self.selection.retain(|key| data.contains_key(key));
617
618 let data_len = data.len().cast();
619 if data_len != self.data_len {
620 self.data_len = data_len;
621 cx.resize(&self);
625 }
626
627 self.update_widgets(cx, data);
628 self.update_content_size(cx);
629 }
630
631 fn update_recurse(&mut self, _: &mut ConfigCx, _: &Self::Data) {}
632
633 fn handle_event(&mut self, cx: &mut EventCx, data: &A, event: Event) -> IsUsed {
634 let is_used = match event {
635 Event::Command(cmd, _) => {
636 let last = data.len().wrapping_sub(1);
637 if last == usize::MAX {
638 return Unused;
639 }
640
641 let solver = self.position_solver();
642 let cur = match cx.nav_focus().and_then(|id| self.find_child_index(id)) {
643 Some(index) => solver.child_to_data(index),
644 None => return Unused,
645 };
646 let is_vert = self.direction.is_vertical();
647 let len = solver.cur_len;
648
649 use Command as C;
650 let data_index = match cmd {
651 C::Home | C::DocHome => Some(0),
652 C::End | C::DocEnd => Some(last),
653 C::Left | C::WordLeft if !is_vert && cur > 0 => Some(cur - 1),
654 C::Up if is_vert && cur > 0 => Some(cur - 1),
655 C::Right | C::WordRight if !is_vert && cur < last => Some(cur + 1),
656 C::Down if is_vert && cur < last => Some(cur + 1),
657 C::PageUp if cur > 0 => Some(cur.saturating_sub(len / 2)),
658 C::PageDown if cur < last => Some((cur + len / 2).min(last)),
659 _ => None,
661 };
662 return if let Some(i_data) = data_index {
663 let act = self.scroll.focus_rect(cx, solver.rect(i_data), self.core.rect);
665 if !act.is_empty() {
666 cx.action(&self, act);
667 self.update_widgets(&mut cx.config_cx(), data);
668 }
669 let index = i_data % usize::conv(self.cur_len);
670 cx.next_nav_focus(self.widgets[index].widget.id(), false, FocusSource::Key);
671 Used
672 } else {
673 Unused
674 };
675 }
676 Event::PressStart { ref press } if
677 press.is_primary() && cx.config().event().mouse_nav_focus() =>
678 {
679 if let Some(index) = cx.last_child() {
680 self.press_target = self.widgets[index].key.clone().map(|k| (index, k));
681 }
682 if let Some((index, ref key)) = self.press_target {
683 let w = &mut self.widgets[index];
684 if w.key.as_ref().map(|k| k == key).unwrap_or(false) {
685 cx.next_nav_focus(w.widget.id(), false, FocusSource::Pointer);
686 }
687 }
688
689 press.grab(self.id()).with_cx(cx)
692 }
693 Event::PressEnd { ref press, success } if press.is_primary() => {
694 if let Some((index, ref key)) = self.press_target {
695 let w = &mut self.widgets[index];
696 if success
697 && !matches!(self.sel_mode, SelectionMode::None)
698 && !self.scroll.is_gliding()
699 && w.key.as_ref().map(|k| k == key).unwrap_or(false)
700 && w.widget.rect().contains(press.coord + self.scroll.offset())
701 {
702 cx.push(kas::messages::Select);
703 }
704 }
705 Used
706 }
707 _ => Unused, };
709
710 let (moved, used_by_sber) = self
711 .scroll
712 .scroll_by_event(cx, event, self.id(), self.core.rect);
713 if moved {
714 self.update_widgets(&mut cx.config_cx(), data);
715 }
716 is_used | used_by_sber
717 }
718
719 fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
720 let key: A::Key;
721 if let Some(index) = cx.last_child() {
722 let w = &mut self.widgets[index];
723 key = match w.key.as_ref() {
724 Some(k) => k.clone(),
725 None => return,
726 };
727
728 self.driver.on_messages(cx, &mut w.widget, data, &key);
729 } else {
730 key = match self.press_target.as_ref() {
732 Some((_, k)) => k.clone(),
733 None => return,
734 };
735 }
736
737 if let Some(kas::messages::Select) = cx.try_pop() {
738 match self.sel_mode {
739 SelectionMode::None => (),
740 SelectionMode::Single => {
741 cx.redraw(&self);
742 self.selection.clear();
743 self.selection.insert(key.clone());
744 cx.push(SelectionMsg::Select(key));
745 }
746 SelectionMode::Multiple => {
747 cx.redraw(&self);
748 if self.selection.remove(&key) {
749 cx.push(SelectionMsg::Deselect(key.clone()));
750 } else {
751 self.selection.insert(key.clone());
752 cx.push(SelectionMsg::Select(key));
753 }
754 }
755 }
756 }
757 }
758
759 fn handle_scroll(&mut self, cx: &mut EventCx, data: &A, scroll: Scroll) {
760 let act = self.scroll.scroll(cx, self.rect(), scroll);
761 self.update_widgets(&mut cx.config_cx(), data);
762 cx.action(self, act);
763 }
764 }
765
766 impl Widget for Self {
768 type Data = A;
769
770 fn for_child_node(
771 &mut self,
772 data: &A,
773 index: usize,
774 closure: Box<dyn FnOnce(Node<'_>) + '_>,
775 ) {
776 if let Some(w) = self.widgets.get_mut(index) {
777 if let Some(ref key) = w.key {
778 if let Some(item) = data.borrow(key) {
779 closure(w.widget.as_node(item.borrow()));
780 }
781 }
782 }
783 }
784
785 fn _configure(&mut self, cx: &mut ConfigCx, data: &A, id: Id) {
786 self.core.id = id;
787 #[cfg(debug_assertions)]
788 self.core.status.configure(&self.core.id);
789
790 self.configure(cx);
791 self.update(cx, data);
792 }
793
794 fn _update(&mut self, cx: &mut ConfigCx, data: &A) {
795 #[cfg(debug_assertions)]
796 self.core.status.update(&self.core.id);
797
798 self.update(cx, data);
799 }
800
801 fn _send(
802 &mut self,
803 cx: &mut EventCx,
804 data: &A,
805 id: Id,
806 event: Event,
807 ) -> IsUsed {
808 kas::impls::_send(self, cx, data, id, event)
809 }
810
811 fn _replay(&mut self, cx: &mut EventCx, data: &A, id: Id) {
812 kas::impls::_replay(self, cx, data, id);
813 }
814
815 fn _nav_next(
817 &mut self,
818 cx: &mut ConfigCx,
819 data: &A,
820 focus: Option<&Id>,
821 advance: NavAdvance,
822 ) -> Option<Id> {
823 if cx.is_disabled(self.id_ref()) || self.cur_len == 0 {
824 return None;
825 }
826
827 let mut child = focus.and_then(|id| self.find_child_index(id));
828
829 if let Some(index) = child {
830 if let Some(Some(id)) = self.as_node(data)
831 .for_child(index, |mut w| w._nav_next(cx, focus, advance))
832 {
833 return Some(id);
834 }
835 }
836
837 let reverse = match advance {
838 NavAdvance::None => return None,
839 NavAdvance::Forward(_) => false,
840 NavAdvance::Reverse(_) => true,
841 };
842
843 let mut starting_child = child;
844 loop {
845 let solver = self.position_solver();
846 let last_data = data.len() - 1;
847 let data_index = if let Some(index) = child {
848 let data = solver.child_to_data(index);
849 if !reverse && data < last_data {
850 data + 1
851 } else if reverse && data > 0 {
852 data - 1
853 } else {
854 return None;
855 }
856 } else if !reverse {
857 0
858 } else {
859 last_data
860 };
861
862 let act = self.scroll.self_focus_rect(solver.rect(data_index), self.core.rect);
863 if !act.is_empty() {
864 cx.action(&self, act);
865 self.update_widgets(cx, data);
866 }
867
868 let index = data_index % usize::conv(self.cur_len);
869 if let Some(Some(id)) = self.as_node(data)
870 .for_child(index, |mut w| w._nav_next(cx, focus, advance))
871 {
872 return Some(id);
873 }
874
875 child = Some(index);
876 if starting_child == child {
877 return None;
878 } else if starting_child.is_none() {
879 starting_child = child;
880 }
881 }
882 }
883 }
884}
885
886#[derive(Debug)]
887struct PositionSolver {
888 pos_start: Coord,
889 skip: Offset,
890 size: Size,
891 first_data: usize,
892 cur_len: usize,
893}
894
895impl PositionSolver {
896 fn child_to_data(&self, index: usize) -> usize {
898 let mut data = (self.first_data / self.cur_len) * self.cur_len + index;
899 if data < self.first_data {
900 data += self.cur_len;
901 }
902 data
903 }
904
905 fn rect(&self, i: usize) -> Rect {
907 let pos = self.pos_start + self.skip * i32::conv(i);
908 Rect::new(pos, self.size)
909 }
910}