zaplib_components/
tabcontrol.rs

1use zaplib::*;
2
3use crate::background::*;
4use crate::scrollview::*;
5use crate::tab::*;
6
7#[derive(Default)]
8pub struct TabControl {
9    pub tabs_view: ScrollView,
10    pub tabs: Vec<Tab>,
11    pub drag_tab_view: View,
12    pub drag_tab: Tab,
13    pub page_view: View,
14    //pub tab_fill_color: ColorId,
15    pub tab_fill: Background,
16
17    pub _dragging_tab: Option<(PointerMoveEvent, usize)>,
18    pub _tab_id_alloc: usize,
19    pub _tab_now_selected: Option<usize>,
20    pub _tab_last_selected: Option<usize>,
21    pub _focussed: bool,
22}
23
24#[derive(Clone, PartialEq)]
25pub enum TabControlEvent {
26    None,
27    TabDragMove { pe: PointerMoveEvent, tab_id: usize },
28    TabDragEnd { pe: PointerUpEvent, tab_id: usize },
29    TabSelect { tab_id: usize },
30    TabClose { tab_id: usize },
31}
32
33const COLOR_BG_NORMAL: Vec4 = Vec4::all(0.);
34
35impl TabControl {
36    pub fn new() -> Self {
37        Self {
38            tabs_view: ScrollView::default().with_scroll_h(
39                ScrollBarConfig::default().with_bar_size(8.0).with_smoothing(0.15).with_use_vertical_pointer_scroll(true),
40            ),
41
42            page_view: View::default(),
43
44            tabs: Default::default(),
45
46            drag_tab: Tab::new().with_draw_depth(10.0),
47
48            drag_tab_view: View::default().with_is_overlay(true),
49
50            //tab_fill_color: Color_bg_normal::id(),
51            tab_fill: Background::default(),
52
53            _dragging_tab: None,
54            _tab_now_selected: None,
55            _tab_last_selected: None,
56            _focussed: false,
57            _tab_id_alloc: 0,
58        }
59    }
60
61    pub fn handle_tab_control(&mut self, cx: &mut Cx, event: &mut Event) -> TabControlEvent {
62        let mut tab_control_event = TabControlEvent::None;
63
64        self.tabs_view.handle(cx, event);
65
66        for (tab_id, tab) in self.tabs.iter_mut().enumerate() {
67            match tab.handle(cx, event) {
68                TabEvent::Select => {
69                    cx.request_draw();
70                    // deselect the other tabs
71                    tab_control_event = TabControlEvent::TabSelect { tab_id }
72                }
73                TabEvent::DragMove(pe) => {
74                    self._dragging_tab = Some((pe.clone(), tab_id));
75                    // flag our view as dirty, to trigger
76                    //cx.request_draw();
77                    cx.request_draw();
78
79                    tab_control_event = TabControlEvent::TabDragMove { pe, tab_id };
80                }
81                TabEvent::DragEnd(pe) => {
82                    self._dragging_tab = None;
83                    cx.request_draw();
84
85                    tab_control_event = TabControlEvent::TabDragEnd { pe, tab_id };
86                }
87                TabEvent::Closing => {
88                    // this tab is closing. select the visible one
89                    if tab.selected() {
90                        // only do anything if we are selected
91                        let next_sel = if tab_id == self._tab_id_alloc - 1 {
92                            // last id
93                            if tab_id > 0 {
94                                tab_id - 1
95                            } else {
96                                tab_id
97                            }
98                        } else {
99                            tab_id + 1
100                        };
101                        if tab_id != next_sel {
102                            tab_control_event = TabControlEvent::TabSelect { tab_id: next_sel };
103                        }
104                    }
105                }
106                TabEvent::Close => {
107                    // Sooooo someone wants to close the tab
108                    tab_control_event = TabControlEvent::TabClose { tab_id };
109                }
110                _ => (),
111            }
112        }
113        match tab_control_event {
114            TabControlEvent::TabSelect { tab_id } => {
115                self._focussed = true;
116                for (id, tab) in self.tabs.iter_mut().enumerate() {
117                    if tab_id != id {
118                        tab.set_tab_selected(cx, false);
119                        tab.set_tab_focus(cx, true);
120                    }
121                }
122            }
123            TabControlEvent::TabClose { .. } => {
124                // needed to clear animation state
125                self.tabs.clear();
126            }
127            _ => (),
128        };
129        tab_control_event
130    }
131
132    pub fn get_tab_rects(&mut self, cx: &mut Cx) -> Vec<Rect> {
133        let mut rects = Vec::new();
134        for tab in self.tabs.iter() {
135            rects.push(tab.get_tab_rect(cx))
136        }
137        rects
138    }
139
140    pub fn set_tab_control_focus(&mut self, cx: &mut Cx, focus: bool) {
141        self._focussed = focus;
142        for tab in self.tabs.iter_mut() {
143            tab.set_tab_focus(cx, focus);
144        }
145    }
146
147    pub fn get_tabs_view_rect(&mut self, cx: &Cx) -> Rect {
148        self.tabs_view.get_rect(cx)
149    }
150
151    pub fn get_content_drop_rect(&mut self, cx: &Cx) -> Rect {
152        // we now need to change the y and the new height
153        self.page_view.get_rect(cx)
154    }
155
156    pub fn begin_tabs(&mut self, cx: &mut Cx) {
157        self.tabs_view.begin_view(cx, LayoutSize::new(Width::Fill, Height::Compute));
158        cx.begin_row(Width::Fill, Height::Compute);
159        self._tab_now_selected = None;
160        self._tab_id_alloc = 0;
161    }
162
163    pub fn get_draw_tab(&mut self, cx: &mut Cx, label: &str, selected: bool, closeable: bool) -> &mut Tab {
164        let new_tab = self.tabs.get(self._tab_id_alloc).is_none();
165        if new_tab {
166            self.tabs.push(Tab::default());
167        }
168        let tab = &mut self.tabs[self._tab_id_alloc];
169        if selected {
170            self._tab_now_selected = Some(self._tab_id_alloc);
171        }
172        self._tab_id_alloc += 1;
173        tab.label = label.to_string();
174        tab.is_closeable = closeable;
175        if new_tab {
176            tab.set_tab_state(cx, selected, self._focussed);
177        } else {
178            // animate the tabstate
179            tab.set_tab_selected(cx, selected);
180        }
181        tab
182    }
183
184    pub fn draw_tab(&mut self, cx: &mut Cx, label: &str, selected: bool, closeable: bool) {
185        let tab = self.get_draw_tab(cx, label, selected, closeable);
186        tab.draw_tab(cx);
187    }
188
189    pub fn end_tabs(&mut self, cx: &mut Cx) {
190        self.tab_fill.begin_draw(cx, Width::Fill, Height::Compute, COLOR_BG_NORMAL);
191        self.tab_fill.end_draw(cx);
192
193        self.tabs.truncate(self._tab_id_alloc);
194        if let Some((pe, id)) = &self._dragging_tab {
195            cx.begin_absolute_box();
196            self.drag_tab_view.begin_view(cx, LayoutSize::FILL);
197
198            self.drag_tab.abs_origin = Some(Vec2 { x: pe.abs.x - pe.rel_start.x, y: pe.abs.y - pe.rel_start.y });
199            let origin_tab = &mut self.tabs[*id];
200            self.drag_tab.label = origin_tab.label.clone();
201            self.drag_tab.is_closeable = origin_tab.is_closeable;
202            self.drag_tab.draw_tab(cx);
203
204            self.drag_tab_view.end_view(cx);
205            cx.end_absolute_box();
206        }
207        cx.end_row();
208        self.tabs_view.end_view(cx);
209
210        if self._tab_now_selected != self._tab_last_selected {
211            // lets scroll the thing into view
212            if let Some(tab_id) = self._tab_now_selected {
213                if let Some(tab) = self.tabs.get(tab_id) {
214                    let tab_rect = tab.get_tab_rect(cx);
215                    self.tabs_view.scroll_into_view_abs(cx, tab_rect);
216                }
217            }
218            self._tab_last_selected = self._tab_now_selected;
219        }
220    }
221
222    pub fn begin_tab_page(&mut self, cx: &mut Cx) {
223        cx.draw_new_line();
224        self.page_view.begin_view(cx, LayoutSize::FILL);
225    }
226
227    pub fn end_tab_page(&mut self, cx: &mut Cx) {
228        self.page_view.end_view(cx);
229        // if we are in draggable tab state,
230        // draw our draggable tab
231    }
232}