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 fn probe(&self, _: Coord) -> Id {
58 self.id()
59 }
60 }
61
62 impl Events for Self {
63 const REDRAW_ON_MOUSE_OVER: bool = true;
64
65 type Data = ();
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 EventState, direction: Direction) {
143 if direction == self.direction {
144 return;
145 }
146
147 self.direction = direction;
148 cx.resize(self);
150 }
151
152 #[inline]
156 #[must_use]
157 pub fn with(mut self, f: impl Fn(&mut EventCx, &A, usize, &str) + 'static) -> Self {
158 debug_assert!(self.on_change.is_none());
159 self.on_change = Some(Box::new(f));
160 self
161 }
162
163 #[inline]
167 #[must_use]
168 pub fn with_msg<M>(self, f: impl Fn(usize, &str) -> M + 'static) -> Self
169 where
170 M: std::fmt::Debug + 'static,
171 {
172 self.with(move |cx, _, index, title| cx.push(f(index, title)))
173 }
174 }
175
176 impl Tile for Self {
177 fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
178 let reverse = reverse ^ !self.direction.is_reversed();
179 kas::util::nav_next(reverse, from, self.child_indices())
180 }
181 }
182
183 impl Events for Self {
184 type Data = A;
185
186 fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
187 if let Some(SetIndex(index)) = cx.try_pop() {
188 self.set_active(&mut cx.config_cx(), data, index);
189 if let Some(ref f) = self.on_change {
190 let title = self.tabs.inner[index].as_str();
191 f(cx, data, index, title);
192 }
193 }
194 }
195 }
196}
197
198impl<A> TabStack<A> {
199 pub fn set_size_limit(&mut self, limit: usize) {
207 self.stack.set_size_limit(limit);
208 }
209
210 pub fn with_size_limit(mut self, limit: usize) -> Self {
218 self.stack.set_size_limit(limit);
219 self
220 }
221
222 #[inline]
224 pub fn active(&self) -> usize {
225 self.stack.active()
226 }
227
228 #[inline]
233 pub fn with_active(mut self, active: usize) -> Self {
234 self.stack = self.stack.with_active(active);
235 self
236 }
237
238 pub fn set_active(&mut self, cx: &mut ConfigCx, data: &A, index: usize) {
240 self.stack.set_active(cx, data, index);
241 }
242
243 pub fn get_active(&self) -> Option<&Page<A>> {
245 self.stack.get_active()
246 }
247
248 pub fn is_empty(&self) -> bool {
250 self.stack.is_empty()
251 }
252
253 pub fn len(&self) -> usize {
255 self.stack.len()
256 }
257
258 pub fn clear(&mut self) {
262 self.stack.clear();
263 self.tabs.inner.clear();
264 }
265
266 pub fn get(&self, index: usize) -> Option<&Page<A>> {
268 self.stack.get(index)
269 }
270
271 pub fn get_mut(&mut self, index: usize) -> Option<&mut Page<A>> {
273 self.stack.get_mut(index)
274 }
275
276 pub fn get_tab(&self, index: usize) -> Option<&Tab> {
278 self.tabs.inner.get(index)
279 }
280
281 pub fn get_tab_mut(&mut self, index: usize) -> Option<&mut Tab> {
283 self.tabs.inner.get_mut(index)
284 }
285
286 pub fn push(&mut self, cx: &mut ConfigCx, data: &A, tab: Tab, page: Page<A>) -> usize {
293 let ti = self.tabs.inner.push(cx, &(), tab);
294 let si = self.stack.push(cx, data, page);
295 debug_assert_eq!(ti, si);
296 si
297 }
298
299 pub fn pop(&mut self, cx: &mut EventState) -> Option<(Tab, Page<A>)> {
304 let tab = self.tabs.inner.pop(cx);
305 let w = self.stack.pop(cx);
306 debug_assert_eq!(tab.is_some(), w.is_some());
307 tab.zip(w)
308 }
309
310 pub fn insert(&mut self, cx: &mut ConfigCx, data: &A, index: usize, tab: Tab, page: Page<A>) {
316 self.tabs.inner.insert(cx, &(), index, tab);
317 self.stack.insert(cx, data, index, page);
318 }
319
320 pub fn remove(&mut self, cx: &mut EventState, index: usize) -> (Tab, Page<A>) {
327 let tab = self.tabs.inner.remove(cx, index);
328 let stack = self.stack.remove(cx, index);
329 (tab, stack)
330 }
331
332 pub fn replace(&mut self, cx: &mut ConfigCx, data: &A, index: usize, page: Page<A>) -> Page<A> {
338 self.stack.replace(cx, data, index, page)
339 }
340
341 pub fn extend<T: IntoIterator<Item = (Tab, Page<A>)>>(
346 &mut self,
347 cx: &mut ConfigCx,
348 data: &A,
349 iter: T,
350 ) {
351 let iter = iter.into_iter();
352 for (tab, w) in iter {
356 self.tabs.inner.push(cx, &(), tab);
357 self.stack.push(cx, data, w);
358 }
359 }
360}
361
362impl<A, T, I> From<I> for TabStack<A>
363where
364 Tab: From<T>,
365 I: IntoIterator<Item = (T, Page<A>)>,
366{
367 #[inline]
368 fn from(iter: I) -> Self {
369 let iter = iter.into_iter();
370 let min_len = iter.size_hint().0;
371 let mut stack = Vec::with_capacity(min_len);
372 let mut tabs = Vec::with_capacity(min_len);
373 for (tab, w) in iter {
374 stack.push(w);
375 tabs.push(Tab::from(tab));
376 }
377 Self {
378 stack: Stack::from(stack),
379 tabs: Row::new(tabs).map_message(|index, Select| SetIndex(index)),
380 ..Default::default()
381 }
382 }
383}