1use crate::adapt::{AdaptEvents, AdaptWidget};
9use crate::{AccessLabel, Page, Row, Stack};
10use kas::messages::{Select, SetIndex};
11use kas::prelude::*;
12use kas::theme::FrameStyle;
13
14#[impl_self]
15mod Tab {
16 #[widget]
24 #[layout(frame!(self.label).with_style(FrameStyle::Tab))]
25 pub struct Tab {
26 core: widget_core!(),
27 #[widget]
28 label: AccessLabel,
29 }
30
31 impl Self {
32 #[inline]
34 pub fn new(label: impl Into<AccessString>) -> Self {
35 Tab {
36 core: Default::default(),
37 label: AccessLabel::new(label),
38 }
39 }
40
41 pub fn as_str(&self) -> &str {
43 self.label.as_str()
44 }
45 }
46
47 impl Tile for Self {
48 fn navigable(&self) -> bool {
49 true
50 }
51
52 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
53 cx.set_label(self.label.id());
54 Role::Tab
55 }
56 }
57
58 impl Events for Self {
59 const REDRAW_ON_MOUSE_OVER: bool = true;
60
61 type Data = ();
62
63 fn probe(&self, _: Coord) -> Id {
64 self.id()
65 }
66
67 fn handle_event(&mut self, cx: &mut EventCx, _: &(), event: Event) -> IsUsed {
68 event.on_click(cx, self.id(), |cx| cx.push(Select))
69 }
70
71 fn handle_messages(&mut self, cx: &mut EventCx, _: &()) {
72 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
73 cx.push(Select);
74 cx.depress_with_key(&self, code);
75 }
76 }
77 }
78
79 impl<T: Into<AccessString>> From<T> for Tab {
80 fn from(label: T) -> Self {
81 Tab::new(label)
82 }
83 }
84}
85
86pub type BoxTabStack<Data> = TabStack<Box<dyn Widget<Data = Data>>>;
90
91#[impl_self]
92mod TabStack {
93 #[impl_default(Self::new())]
113 #[widget]
114 #[layout(list![self.stack, self.tabs].with_direction(self.direction))]
115 pub struct TabStack<A> {
116 core: widget_core!(),
117 direction: Direction,
118 #[widget(&())]
119 tabs: AdaptEvents<Row<Vec<Tab>>>, #[widget]
121 stack: Stack<A>,
122 on_change: Option<Box<dyn Fn(&mut EventCx, &A, usize, &str)>>,
123 }
124
125 impl Self {
126 pub fn new() -> Self {
130 Self {
131 core: Default::default(),
132 direction: Direction::Up,
133 stack: Stack::new(),
134 tabs: Row::new(vec![]).map_message(|index, Select| SetIndex(index)),
135 on_change: None,
136 }
137 }
138
139 pub fn set_direction(&mut self, cx: &mut ConfigCx, direction: Direction) {
143 if direction == self.direction {
144 return;
145 }
146
147 self.direction = direction;
148 cx.resize();
149 }
150
151 #[inline]
155 #[must_use]
156 pub fn with(mut self, f: impl Fn(&mut EventCx, &A, usize, &str) + 'static) -> Self {
157 debug_assert!(self.on_change.is_none());
158 self.on_change = Some(Box::new(f));
159 self
160 }
161
162 #[inline]
166 #[must_use]
167 pub fn with_msg<M>(self, f: impl Fn(usize, &str) -> M + 'static) -> Self
168 where
169 M: std::fmt::Debug + 'static,
170 {
171 self.with(move |cx, _, index, title| cx.push(f(index, title)))
172 }
173 }
174
175 impl Tile for Self {
176 fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
177 let reverse = reverse ^ !self.direction.is_reversed();
178 kas::util::nav_next(reverse, from, self.child_indices())
179 }
180 }
181
182 impl Events for Self {
183 type Data = A;
184
185 fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
186 if let Some(SetIndex(index)) = cx.try_pop() {
187 self.set_active(cx, data, index);
188 if let Some(ref f) = self.on_change {
189 let title = self.tabs.inner[index].as_str();
190 f(cx, data, index, title);
191 }
192 }
193 }
194 }
195}
196
197impl<A> TabStack<A> {
198 pub fn set_size_limit(&mut self, limit: usize) {
206 self.stack.set_size_limit(limit);
207 }
208
209 pub fn with_size_limit(mut self, limit: usize) -> Self {
217 self.stack.set_size_limit(limit);
218 self
219 }
220
221 #[inline]
223 pub fn active(&self) -> usize {
224 self.stack.active()
225 }
226
227 #[inline]
232 pub fn with_active(mut self, active: usize) -> Self {
233 self.stack = self.stack.with_active(active);
234 self
235 }
236
237 pub fn set_active(&mut self, cx: &mut ConfigCx, data: &A, index: usize) {
239 self.stack.set_active(cx, data, index);
240 }
241
242 pub fn get_active(&self) -> Option<&Page<A>> {
244 self.stack.get_active()
245 }
246
247 pub fn is_empty(&self) -> bool {
249 self.stack.is_empty()
250 }
251
252 pub fn len(&self) -> usize {
254 self.stack.len()
255 }
256
257 pub fn clear(&mut self) {
261 self.stack.clear();
262 self.tabs.inner.clear();
263 }
264
265 pub fn get(&self, index: usize) -> Option<&Page<A>> {
267 self.stack.get(index)
268 }
269
270 pub fn get_mut(&mut self, index: usize) -> Option<&mut Page<A>> {
272 self.stack.get_mut(index)
273 }
274
275 pub fn get_tab(&self, index: usize) -> Option<&Tab> {
277 self.tabs.inner.get(index)
278 }
279
280 pub fn get_tab_mut(&mut self, index: usize) -> Option<&mut Tab> {
282 self.tabs.inner.get_mut(index)
283 }
284
285 pub fn push(&mut self, cx: &mut ConfigCx, data: &A, tab: Tab, page: Page<A>) -> usize {
292 let ti = self.tabs.inner.push(cx, &(), tab);
293 let si = self.stack.push(cx, data, page);
294 debug_assert_eq!(ti, si);
295 si
296 }
297
298 pub fn pop(&mut self, cx: &mut ConfigCx) -> Option<(Tab, Page<A>)> {
303 let tab = self.tabs.inner.pop(cx);
304 let w = self.stack.pop(cx);
305 debug_assert_eq!(tab.is_some(), w.is_some());
306 tab.zip(w)
307 }
308
309 pub fn insert(&mut self, cx: &mut ConfigCx, data: &A, index: usize, tab: Tab, page: Page<A>) {
315 self.tabs.inner.insert(cx, &(), index, tab);
316 self.stack.insert(cx, data, index, page);
317 }
318
319 pub fn remove(&mut self, cx: &mut ConfigCx, index: usize) -> (Tab, Page<A>) {
326 let tab = self.tabs.inner.remove(cx, index);
327 let stack = self.stack.remove(cx, index);
328 (tab, stack)
329 }
330
331 pub fn replace(&mut self, cx: &mut ConfigCx, data: &A, index: usize, page: Page<A>) -> Page<A> {
337 self.stack.replace(cx, data, index, page)
338 }
339
340 pub fn extend<T: IntoIterator<Item = (Tab, Page<A>)>>(
345 &mut self,
346 cx: &mut ConfigCx,
347 data: &A,
348 iter: T,
349 ) {
350 let iter = iter.into_iter();
351 for (tab, w) in iter {
355 self.tabs.inner.push(cx, &(), tab);
356 self.stack.push(cx, data, w);
357 }
358 }
359}
360
361impl<A, T, I> From<I> for TabStack<A>
362where
363 Tab: From<T>,
364 I: IntoIterator<Item = (T, Page<A>)>,
365{
366 #[inline]
367 fn from(iter: I) -> Self {
368 let iter = iter.into_iter();
369 let min_len = iter.size_hint().0;
370 let mut stack = Vec::with_capacity(min_len);
371 let mut tabs = Vec::with_capacity(min_len);
372 for (tab, w) in iter {
373 stack.push(w);
374 tabs.push(Tab::from(tab));
375 }
376 Self {
377 stack: Stack::from(stack),
378 tabs: Row::new(tabs).map_message(|index, Select| SetIndex(index)),
379 ..Default::default()
380 }
381 }
382}