1use kas::layout::solve_size_rules;
9use kas::prelude::*;
10use std::ops::{Index, IndexMut};
11
12#[impl_self]
13mod Page {
14 #[widget]
16 #[layout(self.inner)]
17 pub struct Page<A> {
18 core: widget_core!(),
19 #[widget]
20 pub inner: Box<dyn Widget<Data = A>>,
21 }
22
23 impl Tile for Self {
24 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
25 Role::TabPage
26 }
27 }
28
29 impl Events for Self {
30 type Data = A;
31 }
32
33 impl Self {
34 pub fn new(widget: impl Widget<Data = A> + 'static) -> Self {
36 Page::new_boxed(Box::new(widget))
37 }
38
39 #[inline]
41 pub fn new_boxed(inner: Box<dyn Widget<Data = A>>) -> Self {
42 Page {
43 core: Default::default(),
44 inner,
45 }
46 }
47 }
48}
49
50#[impl_self]
51mod Stack {
52 #[widget]
66 pub struct Stack<A> {
67 core: widget_core!(),
68 align_hints: AlignHints,
69 widgets: Vec<(Page<A>, usize)>,
71 active: usize,
72 size_limit: usize,
73 next: usize,
74 }
75
76 impl Default for Self {
77 fn default() -> Self {
78 Stack {
79 core: Default::default(),
80 align_hints: AlignHints::NONE,
81 widgets: Vec::new(),
82 active: 0,
83 size_limit: usize::MAX,
84 next: 0,
85 }
86 }
87 }
88
89 impl Layout for Self {
90 fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules {
91 let (mut min, mut ideal) = (0, 0);
92 let mut m = (0, 0);
93 let mut stretch = Stretch::None;
94
95 for (i, entry) in self.widgets.iter_mut().enumerate() {
96 if entry.1 != usize::MAX {
97 let rules = entry.0.size_rules(cx, axis);
98 ideal = ideal.max(rules.ideal_size());
99 m = (m.0.max(rules.margins().0), m.1.max(rules.margins().1));
100 stretch = stretch.max(rules.stretch());
101 if i == self.active {
102 min = rules.min_size();
103 }
104 }
105 }
106
107 SizeRules::new(min, ideal, stretch).with_margins(m)
108 }
109
110 fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
111 self.core.set_rect(rect);
112 self.align_hints = hints;
113
114 for entry in self.widgets.iter_mut() {
115 if entry.1 != usize::MAX {
116 entry.0.set_rect(cx, rect, hints);
117 }
118 }
119 }
120
121 fn draw(&self, mut draw: DrawCx) {
122 if let Some(entry) = self.widgets.get(self.active) {
123 debug_assert!(entry.1 != usize::MAX);
124 entry.0.draw(draw.re());
125 }
126 }
127 }
128
129 impl Tile for Self {
130 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
131 Role::None
132 }
133
134 #[inline]
135 fn child_indices(&self) -> ChildIndices {
136 if self.active < self.widgets.len() {
137 ChildIndices::one(self.active)
138 } else {
139 ChildIndices::none()
140 }
141 }
142 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
143 self.widgets
144 .get(index)
145 .filter(|w| w.1 != usize::MAX)
146 .map(|w| w.0.as_tile())
147 }
148
149 fn find_child_index(&self, id: &Id) -> Option<usize> {
150 let key = id.next_key_after(self.id_ref())?;
154 for (i, w) in self.widgets.iter().enumerate() {
155 if w.1 == key {
156 return Some(i);
157 }
158 }
159 None
160 }
161
162 fn nav_next(&self, _: bool, from: Option<usize>) -> Option<usize> {
163 let active = match from {
164 None => self.active,
165 Some(active) if active != self.active => self.active,
166 _ => return None,
167 };
168 if let Some(entry) = self.widgets.get(active) {
169 debug_assert!(entry.1 != usize::MAX);
170 return Some(active);
171 }
172 None
173 }
174 }
175
176 impl Events for Self {
177 fn make_child_id(&mut self, index: usize) -> Id {
178 let Some((child, key)) = self.widgets.get(index) else {
179 return Id::default();
180 };
181 let id = child.id_ref();
182 if id.is_valid()
183 && let Some(k) = id.next_key_after(self.id_ref())
184 && (*key == k || self.widgets.iter().all(|entry| k != entry.1))
185 {
186 let id = id.clone();
187 self.widgets[index].1 = k;
188 return id;
189 }
190
191 loop {
192 let key = self.next;
193 self.next += 1;
194 if self.widgets.iter().any(|entry| entry.1 == key) {
195 continue;
196 }
197
198 self.widgets[index].1 = key;
199 return self.id_ref().make_child(key);
200 }
201 }
202
203 fn probe(&self, coord: Coord) -> Id {
204 if let Some(entry) = self.widgets.get(self.active) {
205 debug_assert!(entry.1 != usize::MAX);
206 if let Some(id) = entry.0.try_probe(coord) {
207 return id;
208 }
209 }
210 self.id()
211 }
212
213 fn configure(&mut self, _: &mut ConfigCx) {
214 for entry in self.widgets.iter_mut() {
215 entry.1 = usize::MAX;
216 }
217 }
218
219 #[inline]
220 fn recurse_indices(&self) -> ChildIndices {
221 let end = self.widgets.len().min(self.size_limit.max(self.active + 1));
222 let start = end.saturating_sub(self.size_limit).min(self.active);
223 ChildIndices::range(start..end)
224 }
225
226 fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
227 if let Some(kas::messages::SetIndex(index)) = cx.try_pop() {
228 self.set_active(cx, data, index);
229 }
230 }
231 }
232
233 impl Widget for Self {
234 type Data = A;
235
236 fn child_node<'n>(&'n mut self, data: &'n A, index: usize) -> Option<Node<'n>> {
237 self.widgets
238 .get_mut(index)
239 .filter(|w| w.1 != usize::MAX)
240 .map(|w| w.0.as_node(data))
241 }
242 }
243
244 impl Index<usize> for Self {
245 type Output = Page<A>;
246
247 fn index(&self, index: usize) -> &Self::Output {
248 &self.widgets[index].0
249 }
250 }
251
252 impl IndexMut<usize> for Self {
253 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
254 &mut self.widgets[index].0
255 }
256 }
257}
258
259impl<A> Stack<A> {
260 pub fn new() -> Self {
264 Stack::default()
265 }
266
267 pub fn set_size_limit(&mut self, limit: usize) {
277 self.size_limit = limit;
278 }
279
280 pub fn with_size_limit(mut self, limit: usize) -> Self {
288 self.size_limit = limit;
289 self
290 }
291
292 #[inline]
294 pub fn active(&self) -> usize {
295 self.active
296 }
297
298 #[inline]
303 pub fn with_active(mut self, active: usize) -> Self {
304 debug_assert_eq!(
305 self.widgets
306 .get(self.active)
307 .map(|e| e.1)
308 .unwrap_or_default(),
309 usize::MAX
310 );
311 self.active = active;
312 self
313 }
314
315 pub fn set_active(&mut self, cx: &mut ConfigCx, data: &A, index: usize) {
321 let old_index = self.active;
322 if old_index == index {
323 return;
324 }
325 self.active = index;
326
327 let rect = self.rect();
328 if index < self.widgets.len() {
329 let id = (self.widgets[index].1 == usize::MAX).then_some(self.make_child_id(index));
330 let entry = &mut self.widgets[index];
331 let node = entry.0.as_node(data);
332 if let Some(id) = id {
333 cx.configure(node, id);
334 debug_assert!(entry.1 != usize::MAX);
335 } else {
336 cx.update(node);
337 }
338
339 let Size(w, _h) = rect.size;
340 solve_size_rules(&mut entry.0, &mut cx.size_cx(), Some(w), None);
344
345 entry.0.set_rect(&mut cx.size_cx(), rect, self.align_hints);
346 cx.region_moved();
347 } else {
348 if old_index < self.widgets.len() {
349 cx.region_moved();
350 }
351 }
352 }
353
354 pub fn get_active(&self) -> Option<&Page<A>> {
356 if self.active < self.widgets.len() {
357 Some(&self.widgets[self.active].0)
358 } else {
359 None
360 }
361 }
362
363 pub fn is_empty(&self) -> bool {
365 self.widgets.is_empty()
366 }
367
368 pub fn len(&self) -> usize {
370 self.widgets.len()
371 }
372
373 pub fn clear(&mut self) {
377 self.widgets.clear();
378 }
379
380 pub fn get(&self, index: usize) -> Option<&Page<A>> {
382 self.widgets.get(index).map(|e| &e.0)
383 }
384
385 pub fn get_mut(&mut self, index: usize) -> Option<&mut Page<A>> {
387 self.widgets.get_mut(index).map(|e| &mut e.0)
388 }
389
390 fn configure_and_size(&mut self, cx: &mut ConfigCx, data: &A, index: usize) {
392 let Size(w, h) = self.rect().size;
393 let id = self.make_child_id(index);
394 if let Some(entry) = self.widgets.get_mut(index) {
395 cx.configure(entry.0.as_node(data), id);
396 solve_size_rules(&mut entry.0, &mut cx.size_cx(), Some(w), Some(h));
397 debug_assert!(entry.1 != usize::MAX);
398 }
399 }
400
401 pub fn push(&mut self, cx: &mut ConfigCx, data: &A, page: Page<A>) -> usize {
408 let index = self.widgets.len();
409 if index == self.active {
410 self.active = usize::MAX;
411 }
412 self.widgets.push((page, usize::MAX));
413
414 if index < self.size_limit {
415 self.configure_and_size(cx, data, index);
416 }
417 index
418 }
419
420 pub fn pop(&mut self, cx: &mut EventState) -> Option<Page<A>> {
425 let result = self.widgets.pop().map(|w| w.0);
426 if result.is_some() {
427 if self.active > 0 && self.active == self.widgets.len() {
428 cx.region_moved();
429 }
430 }
431 result
432 }
433
434 pub fn insert(&mut self, cx: &mut ConfigCx, data: &A, index: usize, page: Page<A>) {
440 if self.active >= index {
441 self.active = self.active.saturating_add(1);
442 }
443
444 self.widgets.insert(index, (page, usize::MAX));
445
446 if index < self.size_limit {
447 self.configure_and_size(cx, data, index);
448 }
449 }
450
451 pub fn remove(&mut self, cx: &mut EventState, index: usize) -> Page<A> {
458 let w = self.widgets.remove(index);
459
460 if self.active == index {
461 self.active = usize::MAX;
462 cx.region_moved();
463 }
464
465 for entry in self.widgets[index..].iter_mut() {
466 entry.1 -= 1;
467 }
468 w.0
469 }
470
471 pub fn replace(
477 &mut self,
478 cx: &mut ConfigCx,
479 data: &A,
480 index: usize,
481 mut page: Page<A>,
482 ) -> Page<A> {
483 let entry = &mut self.widgets[index];
484 std::mem::swap(&mut page, &mut entry.0);
485 entry.1 = usize::MAX;
486
487 if index < self.size_limit || index == self.active {
488 self.configure_and_size(cx, data, index);
489 }
490
491 if index == self.active {
492 cx.resize();
493 }
494
495 page
496 }
497
498 pub fn extend<T: IntoIterator<Item = Page<A>>>(
503 &mut self,
504 cx: &mut ConfigCx,
505 data: &A,
506 iter: T,
507 ) {
508 let old_len = self.widgets.len();
509 let iter = iter.into_iter();
510 if let Some(ub) = iter.size_hint().1 {
511 self.widgets.reserve(ub);
512 }
513 for w in iter {
514 let index = self.widgets.len();
515 self.widgets.push((w, usize::MAX));
516 if index < self.size_limit {
517 self.configure_and_size(cx, data, index);
518 }
519 }
520
521 if (old_len..self.widgets.len()).contains(&self.active) {
522 self.active = usize::MAX;
523 }
524 }
525
526 pub fn resize_with<F: Fn(usize) -> Page<A>>(
531 &mut self,
532 cx: &mut ConfigCx,
533 data: &A,
534 len: usize,
535 f: F,
536 ) {
537 let old_len = self.widgets.len();
538
539 if len < old_len {
540 loop {
541 let _ = self.widgets.pop().unwrap();
542 if len == self.widgets.len() {
543 break;
544 }
545 }
546 }
547
548 if len > old_len {
549 self.widgets.reserve(len - old_len);
550 for index in old_len..len {
551 self.widgets.push((f(index), usize::MAX));
552 if index < self.size_limit {
553 self.configure_and_size(cx, data, index);
554 }
555 }
556
557 if (old_len..len).contains(&self.active) {
558 self.active = usize::MAX;
559 }
560 }
561 }
562}
563
564impl<A, I> From<I> for Stack<A>
565where
566 I: IntoIterator<Item = Page<A>>,
567{
568 #[inline]
569 fn from(iter: I) -> Self {
570 Self {
571 widgets: iter.into_iter().map(|w| (w, usize::MAX)).collect(),
572 ..Default::default()
573 }
574 }
575}