vizia_core/views/
tabview.rs
1use std::ops::Deref;
2
3use crate::{icons::ICON_PLUS, prelude::*};
4
5pub enum TabEvent {
6 SetSelected(usize),
7}
8
9#[derive(Lens)]
10pub struct TabView {
11 selected_index: usize,
12 is_vertical: bool,
13
14 #[lens(ignore)]
15 on_select: Option<Box<dyn Fn(&mut EventContext, usize)>>,
16}
17
18impl TabView {
19 pub fn new<L, T, F>(cx: &mut Context, lens: L, content: F) -> Handle<Self>
20 where
21 L: Lens<Target: std::ops::Deref<Target = [T]>>,
22 T: Clone + 'static,
23 F: 'static + Clone + Fn(&mut Context, Index<L, T>) -> TabPair,
24 {
25 Self { selected_index: 0, is_vertical: false, on_select: None }
26 .build(cx, move |cx| {
27 let content2 = content.clone();
28 ScrollView::new(cx, move |cx| {
30 Binding::new(cx, lens.map(|list| list.len()), move |cx, list_length| {
32 let list_length = list_length.get(cx);
33 for index in 0..list_length {
34 let l = lens.idx(index);
35 let builder = (content2)(cx, l).header;
36 TabHeader::new(cx, index, builder)
37 .bind(TabView::selected_index, move |handle, selected_index| {
38 let selected_index = selected_index.get(handle.cx);
39 handle.checked(selected_index == index);
40 })
41 .toggle_class("vertical", TabView::is_vertical);
42 }
43 })
44 })
48 .class("tabview-header")
49 .z_index(1)
50 .toggle_class("vertical", TabView::is_vertical);
51
52 Divider::new(cx).toggle_class("vertical", TabView::is_vertical);
53
54 VStack::new(cx, |cx| {
56 Binding::new(cx, TabView::selected_index, move |cx, selected| {
57 let selected = selected.get(cx);
58 let l = lens.idx(selected);
59 ((content)(cx, l).content)(cx);
60 });
61 })
62 .overflow(Overflow::Hidden)
63 .class("tabview-content-wrapper");
64 })
65 .toggle_class("vertical", TabView::is_vertical)
66 }
67}
68
69impl View for TabView {
70 fn element(&self) -> Option<&'static str> {
71 Some("tabview")
72 }
73
74 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
75 event.map(|tab_event, meta| match tab_event {
76 TabEvent::SetSelected(index) => {
77 self.selected_index = *index;
78 if let Some(callback) = &self.on_select {
79 (callback)(cx, self.selected_index);
80 }
81 meta.consume();
82 }
83 });
84 }
85}
86
87impl Handle<'_, TabView> {
88 pub fn vertical(self) -> Self {
89 self.modify(|tabview: &mut TabView| tabview.is_vertical = true)
90 }
91
92 pub fn on_select(self, callback: impl Fn(&mut EventContext, usize) + 'static) -> Self {
93 self.modify(|tabview: &mut TabView| tabview.on_select = Some(Box::new(callback)))
94 }
95
96 pub fn with_selected<U: Into<usize>>(mut self, selected: impl Res<U>) -> Self {
97 let entity = self.entity();
98 selected.set_or_bind(self.context(), entity, |cx, selected| {
99 let index = selected.get(cx).into();
100 cx.emit(TabEvent::SetSelected(index));
101 });
102
103 self
104 }
105}
106
107pub struct TabPair {
108 pub header: Box<dyn Fn(&mut Context)>,
109 pub content: Box<dyn Fn(&mut Context)>,
110}
111
112impl TabPair {
113 pub fn new<H, C>(header: H, content: C) -> Self
114 where
115 H: 'static + Fn(&mut Context),
116 C: 'static + Fn(&mut Context),
117 {
118 Self { header: Box::new(header), content: Box::new(content) }
119 }
120}
121
122pub struct TabHeader {
123 index: usize,
124}
125
126impl TabHeader {
127 pub fn new<F>(cx: &mut Context, index: usize, content: F) -> Handle<Self>
128 where
129 F: 'static + Fn(&mut Context),
130 {
131 Self { index }.build(cx, |cx| (content)(cx))
132 }
133}
134
135impl View for TabHeader {
136 fn element(&self) -> Option<&'static str> {
137 Some("tabheader")
138 }
139
140 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
141 event.map(|window_event, _meta| match window_event {
142 WindowEvent::PressDown { mouse: _ } => {
143 cx.emit(TabEvent::SetSelected(self.index));
144 }
145
146 _ => {}
147 });
148 }
149}
150
151pub struct TabBar {}
152
153impl TabBar {
154 pub fn new<L: Lens, T: 'static>(
155 cx: &mut Context,
156 list: L,
157 item_content: impl 'static + Fn(&mut Context, usize, MapRef<L, T>),
158 ) -> Handle<Self>
159 where
160 L::Target: Deref<Target = [T]> + Data,
161 {
162 Self {}
163 .build(cx, |cx| {
164 List::new(cx, list, item_content)
165 .selectable(Selectable::Single)
166 .layout_type(LayoutType::Row);
167 Button::new(cx, |cx| Svg::new(cx, ICON_PLUS).size(Stretch(1.0)))
168 .variant(ButtonVariant::Text)
169 .padding(Pixels(0.0))
170 .size(Pixels(16.0));
171 })
172 .alignment(Alignment::Left)
173 .layout_type(LayoutType::Row)
174 }
175}
176
177impl View for TabBar {
178 fn element(&self) -> Option<&'static str> {
179 Some("tabbar")
180 }
181}