1use kas::layout::solve_size_rules;
9use kas::prelude::*;
10use std::collections::hash_map::{Entry, HashMap};
11use std::fmt::Debug;
12use std::ops::{Index, IndexMut};
13
14#[impl_self]
15mod Page {
16 #[widget]
18 #[layout(self.inner)]
19 pub struct Page<A> {
20 core: widget_core!(),
21 #[widget]
22 pub inner: Box<dyn Widget<Data = A>>,
23 }
24
25 impl Tile for Self {
26 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
27 Role::TabPage
28 }
29 }
30
31 impl Events for Self {
32 type Data = A;
33 }
34
35 impl Self {
36 pub fn new(widget: impl Widget<Data = A> + 'static) -> Self {
38 Page::new_boxed(Box::new(widget))
39 }
40
41 #[inline]
43 pub fn new_boxed(inner: Box<dyn Widget<Data = A>>) -> Self {
44 Page {
45 core: Default::default(),
46 inner,
47 }
48 }
49 }
50}
51
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
53enum State {
54 #[default]
55 None,
56 Configured,
57 Sized,
58}
59impl State {
60 fn is_configured(self) -> bool {
61 self != State::None
62 }
63}
64
65#[impl_self]
66mod Stack {
67 #[widget]
81 pub struct Stack<A> {
82 core: widget_core!(),
83 align_hints: AlignHints,
84 widgets: Vec<(Page<A>, State)>,
85 active: usize,
86 size_limit: usize,
87 next: usize,
88 id_map: HashMap<usize, usize>, }
90
91 impl Default for Self {
92 fn default() -> Self {
93 Stack {
94 core: Default::default(),
95 align_hints: AlignHints::NONE,
96 widgets: Vec::new(),
97 active: 0,
98 size_limit: usize::MAX,
99 next: 0,
100 id_map: HashMap::new(),
101 }
102 }
103 }
104
105 impl Layout for Self {
106 fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
107 let mut rules = SizeRules::EMPTY;
108 for (index, entry) in self.widgets.iter_mut().enumerate() {
109 if index < self.size_limit || index == self.active {
110 if entry.1.is_configured() {
111 rules = rules.max(entry.0.size_rules(sizer.re(), axis));
112 entry.1 = State::Sized;
113 } else {
114 entry.1 = State::None;
115 }
116 } else {
117 entry.1 = entry.1.min(State::Configured);
119 }
120 }
121 rules
122 }
123
124 fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
125 widget_set_rect!(rect);
126 self.align_hints = hints;
127 if let Some(entry) = self.widgets.get_mut(self.active) {
128 debug_assert_eq!(entry.1, State::Sized);
129 entry.0.set_rect(cx, rect, hints);
130 }
131 }
132
133 fn draw(&self, mut draw: DrawCx) {
134 if let Some(entry) = self.widgets.get(self.active) {
135 debug_assert_eq!(entry.1, State::Sized);
136 entry.0.draw(draw.re());
137 }
138 }
139 }
140
141 impl Tile for Self {
142 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
143 Role::None
144 }
145
146 #[inline]
147 fn child_indices(&self) -> ChildIndices {
148 let mut end = self.active;
149 if self.active < self.widgets.len() {
150 end += 1;
151 }
152 (self.active..end).into()
153 }
154 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
155 self.widgets.get(index).map(|(w, _)| w.as_tile())
156 }
157
158 fn find_child_index(&self, id: &Id) -> Option<usize> {
159 id.next_key_after(self.id_ref())
161 .and_then(|k| self.id_map.get(&k).cloned())
162 .filter(|index| {
163 self.widgets
164 .get(*index)
165 .map(|(_, state)| *state >= State::Configured)
166 .unwrap_or(false)
167 })
168 }
169
170 fn nav_next(&self, _: bool, from: Option<usize>) -> Option<usize> {
171 let active = match from {
172 None => self.active,
173 Some(active) if active != self.active => self.active,
174 _ => return None,
175 };
176 if let Some(entry) = self.widgets.get(active) {
177 debug_assert_eq!(entry.1, State::Sized);
178 return Some(active);
179 }
180 None
181 }
182
183 fn probe(&self, coord: Coord) -> Id {
184 if let Some(entry) = self.widgets.get(self.active) {
185 debug_assert_eq!(entry.1, State::Sized);
186 if let Some(id) = entry.0.try_probe(coord) {
187 return id;
188 }
189 }
190 self.id()
191 }
192 }
193
194 impl Events for Self {
195 fn make_child_id(&mut self, index: usize) -> Id {
196 if let Some((child, state)) = self.widgets.get(index) {
197 if state.is_configured()
199 && child.id_ref().is_valid()
200 && self.id_ref().is_ancestor_of(child.id_ref())
201 {
202 if let Some(key) = child.id_ref().next_key_after(self.id_ref()) {
203 if let Entry::Vacant(entry) = self.id_map.entry(key) {
204 entry.insert(index);
205 return child.id();
206 }
207 }
208 }
209 }
210
211 loop {
212 let key = self.next;
213 self.next += 1;
214 if let Entry::Vacant(entry) = self.id_map.entry(key) {
215 entry.insert(index);
216 return self.id_ref().make_child(key);
217 }
218 }
219 }
220
221 fn configure(&mut self, _: &mut ConfigCx) {
222 self.id_map.clear();
224 }
225
226 fn configure_recurse(&mut self, cx: &mut ConfigCx, data: &Self::Data) {
227 for index in 0..self.widgets.len() {
228 if index < self.size_limit || index == self.active {
229 let id = self.make_child_id(index);
230 let entry = &mut self.widgets[index];
231 cx.configure(entry.0.as_node(data), id);
232 if entry.1 == State::None {
233 entry.1 = State::Configured;
234 }
235 } else {
236 self.widgets[index].1 = State::None;
238 }
239 }
240 }
241
242 fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
243 if let Some(kas::messages::SetIndex(index)) = cx.try_pop() {
244 self.set_active(&mut cx.config_cx(), data, index);
245 }
246 }
247 }
248
249 impl Widget for Self {
250 type Data = A;
251
252 fn child_node<'n>(&'n mut self, data: &'n A, index: usize) -> Option<Node<'n>> {
253 self.widgets.get_mut(index).map(|(w, _)| w.as_node(data))
254 }
255 }
256
257 impl Index<usize> for Self {
258 type Output = Page<A>;
259
260 fn index(&self, index: usize) -> &Self::Output {
261 &self.widgets[index].0
262 }
263 }
264
265 impl IndexMut<usize> for Self {
266 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
267 &mut self.widgets[index].0
268 }
269 }
270}
271
272impl<A> Stack<A> {
273 pub fn new() -> Self {
277 Stack::default()
278 }
279
280 pub fn set_size_limit(&mut self, limit: usize) {
288 self.size_limit = limit;
289 }
290
291 pub fn with_size_limit(mut self, limit: usize) -> Self {
299 self.size_limit = limit;
300 self
301 }
302
303 #[inline]
305 pub fn active(&self) -> usize {
306 self.active
307 }
308
309 #[inline]
314 pub fn with_active(mut self, active: usize) -> Self {
315 debug_assert_eq!(
316 self.widgets
317 .get(self.active)
318 .map(|e| e.1)
319 .unwrap_or_default(),
320 State::None
321 );
322 self.active = active;
323 self
324 }
325
326 pub fn set_active(&mut self, cx: &mut ConfigCx, data: &A, index: usize) {
328 let old_index = self.active;
329 if old_index == index {
330 return;
331 }
332 self.active = index;
333
334 let rect = self.rect();
335 let id = self.make_child_id(index);
336 if let Some(entry) = self.widgets.get_mut(index) {
337 let node = entry.0.as_node(data);
338
339 if entry.1 == State::None {
340 cx.configure(node, id);
341 entry.1 = State::Configured;
342 } else {
343 cx.update(node);
344 }
345
346 if entry.1 == State::Configured {
347 let Size(w, _h) = rect.size;
348 solve_size_rules(&mut entry.0, cx.size_cx(), Some(w), None);
352 entry.1 = State::Sized;
353 }
354
355 debug_assert_eq!(entry.1, State::Sized);
356 entry.0.set_rect(cx, rect, self.align_hints);
357 cx.region_moved();
358 } else {
359 if old_index < self.widgets.len() {
360 cx.region_moved();
361 }
362 }
363 }
364
365 pub fn get_active(&self) -> Option<&Page<A>> {
367 if self.active < self.widgets.len() {
368 Some(&self.widgets[self.active].0)
369 } else {
370 None
371 }
372 }
373
374 pub fn is_empty(&self) -> bool {
376 self.widgets.is_empty()
377 }
378
379 pub fn len(&self) -> usize {
381 self.widgets.len()
382 }
383
384 pub fn clear(&mut self) {
388 self.widgets.clear();
389 }
390
391 pub fn get(&self, index: usize) -> Option<&Page<A>> {
393 self.widgets.get(index).map(|e| &e.0)
394 }
395
396 pub fn get_mut(&mut self, index: usize) -> Option<&mut Page<A>> {
398 self.widgets.get_mut(index).map(|e| &mut e.0)
399 }
400
401 fn configure_and_size(&mut self, cx: &mut ConfigCx, data: &A, index: usize) {
403 let Size(w, h) = self.rect().size;
404 let id = self.make_child_id(index);
405 if let Some(entry) = self.widgets.get_mut(index) {
406 cx.configure(entry.0.as_node(data), id);
407 solve_size_rules(&mut entry.0, cx.size_cx(), Some(w), Some(h));
408 entry.1 = State::Sized;
409 }
410 }
411
412 pub fn push(&mut self, cx: &mut ConfigCx, data: &A, page: Page<A>) -> usize {
419 let index = self.widgets.len();
420 if index == self.active {
421 self.active = usize::MAX;
422 }
423 self.widgets.push((page, State::None));
424
425 if index < self.size_limit {
426 self.configure_and_size(cx, data, index);
427 }
428 index
429 }
430
431 pub fn pop(&mut self, cx: &mut EventState) -> Option<Page<A>> {
436 let result = self.widgets.pop().map(|(w, _)| w);
437 if let Some(w) = result.as_ref() {
438 if self.active > 0 && self.active == self.widgets.len() {
439 cx.region_moved();
440 }
441
442 if w.id_ref().is_valid() {
443 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
444 self.id_map.remove(&key);
445 }
446 }
447 }
448 result
449 }
450
451 pub fn insert(&mut self, cx: &mut ConfigCx, data: &A, index: usize, page: Page<A>) {
457 if self.active >= index {
458 self.active = self.active.saturating_add(1);
459 }
460
461 self.widgets.insert(index, (page, State::None));
462
463 for v in self.id_map.values_mut() {
464 if *v >= index {
465 *v += 1;
466 }
467 }
468
469 if index < self.size_limit {
470 self.configure_and_size(cx, data, index);
471 }
472 }
473
474 pub fn remove(&mut self, cx: &mut EventState, index: usize) -> Page<A> {
481 let (w, _) = self.widgets.remove(index);
482 if w.id_ref().is_valid() {
483 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
484 self.id_map.remove(&key);
485 }
486 }
487
488 if self.active == index {
489 self.active = usize::MAX;
490 cx.region_moved();
491 }
492
493 for v in self.id_map.values_mut() {
494 if *v > index {
495 *v -= 1;
496 }
497 }
498 w
499 }
500
501 pub fn replace(
507 &mut self,
508 cx: &mut ConfigCx,
509 data: &A,
510 index: usize,
511 mut page: Page<A>,
512 ) -> Page<A> {
513 let entry = &mut self.widgets[index];
514 std::mem::swap(&mut page, &mut entry.0);
515 entry.1 = State::None;
516
517 if page.id_ref().is_valid() {
518 if let Some(key) = page.id_ref().next_key_after(self.id_ref()) {
519 self.id_map.remove(&key);
520 }
521 }
522
523 if index < self.size_limit || index == self.active {
524 self.configure_and_size(cx, data, index);
525 }
526
527 if index == self.active {
528 cx.resize(self);
529 }
530
531 page
532 }
533
534 pub fn extend<T: IntoIterator<Item = Page<A>>>(
539 &mut self,
540 cx: &mut ConfigCx,
541 data: &A,
542 iter: T,
543 ) {
544 let old_len = self.widgets.len();
545 let iter = iter.into_iter();
546 if let Some(ub) = iter.size_hint().1 {
547 self.widgets.reserve(ub);
548 }
549 for w in iter {
550 let index = self.widgets.len();
551 self.widgets.push((w, State::None));
552 if index < self.size_limit {
553 self.configure_and_size(cx, data, index);
554 }
555 }
556
557 if (old_len..self.widgets.len()).contains(&self.active) {
558 self.active = usize::MAX;
559 }
560 }
561
562 pub fn resize_with<F: Fn(usize) -> Page<A>>(
567 &mut self,
568 cx: &mut ConfigCx,
569 data: &A,
570 len: usize,
571 f: F,
572 ) {
573 let old_len = self.widgets.len();
574
575 if len < old_len {
576 loop {
577 let (w, _) = self.widgets.pop().unwrap();
578 if w.id_ref().is_valid() {
579 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
580 self.id_map.remove(&key);
581 }
582 }
583 if len == self.widgets.len() {
584 return;
585 }
586 }
587 }
588
589 if len > old_len {
590 self.widgets.reserve(len - old_len);
591 for index in old_len..len {
592 self.widgets.push((f(index), State::None));
593 if index < self.size_limit {
594 self.configure_and_size(cx, data, index);
595 }
596 }
597
598 if (old_len..len).contains(&self.active) {
599 self.active = usize::MAX;
600 }
601 }
602 }
603}
604
605impl<A, I> From<I> for Stack<A>
606where
607 I: IntoIterator<Item = Page<A>>,
608{
609 #[inline]
610 fn from(iter: I) -> Self {
611 Self {
612 widgets: iter.into_iter().map(|w| (w, State::None)).collect(),
613 ..Default::default()
614 }
615 }
616}