makepad_widget/
tabcontrol.rs

1use makepad_render::*;
2
3use crate::scrollbar::*;
4use crate::scrollview::*;
5use crate::tab::*;
6use crate::widgetstyle::*;
7
8#[derive(Clone)]
9pub struct TabControl {
10    pub tabs_view: ScrollView,
11    pub tabs: Elements<usize, Tab, Tab>,
12    pub drag_tab_view: View,
13    pub drag_tab: Tab,
14    pub page_view: View,
15    pub hover: Quad,
16    //pub tab_fill_color: ColorId,
17    pub tab_fill: Quad,
18    pub animator: Animator,
19    
20    pub _dragging_tab: Option<(FingerMoveEvent, usize)>,
21    pub _tab_id_alloc: usize,
22    pub _tab_now_selected: Option<usize>,
23    pub _tab_last_selected:Option<usize>,
24    pub _focussed: bool
25}
26
27#[derive(Clone, PartialEq)]
28pub enum TabControlEvent {
29    None,
30    TabDragMove {fe: FingerMoveEvent, tab_id: usize},
31    TabDragEnd {fe: FingerUpEvent, tab_id: usize},
32    TabSelect {tab_id: usize},
33    TabClose {tab_id: usize}
34}
35
36impl TabControl {
37    pub fn proto(cx: &mut Cx) -> Self {
38        Self {
39            tabs_view: ScrollView {
40                scroll_h: Some(ScrollBar {
41                    bar_size: 8.0,
42                    smoothing: Some(0.15),
43                    use_vertical_finger_scroll: true,
44                    ..ScrollBar::proto(cx)
45                }),
46                ..ScrollView::proto(cx)
47            },
48            page_view: View::proto(cx),
49            tabs: Elements::new(Tab::proto(cx)),
50            drag_tab: Tab {
51                z: 10.,
52                ..Tab::proto(cx)
53            },
54            drag_tab_view: View {
55                is_overlay: true,
56                ..View::proto(cx)
57            },
58            hover: Quad {
59                color: color("purple"),
60                ..Quad::proto(cx)
61            },
62            //tab_fill_color: Color_bg_normal::id(),
63            tab_fill: Quad::proto(cx),
64            animator: Animator::default(),
65            _dragging_tab: None,
66            _tab_now_selected:None,
67            _tab_last_selected:None,
68            _focussed: false,
69            _tab_id_alloc: 0
70        }
71    }
72    
73    pub fn handle_tab_control(&mut self, cx: &mut Cx, event: &mut Event) -> TabControlEvent {
74        let mut tab_control_event = TabControlEvent::None;
75        
76        self.tabs_view.handle_scroll_bars(cx, event);
77        
78        for (id, tab) in self.tabs.enumerate() {
79            
80            match tab.handle_tab(cx, event) {
81                TabEvent::Select => {
82                    self.page_view.redraw_view_area(cx);
83                    // deselect the other tabs
84                    tab_control_event = TabControlEvent::TabSelect {tab_id: *id}
85                },
86                TabEvent::DragMove(fe) => {
87                    self._dragging_tab = Some((fe.clone(), *id));
88                    // flag our view as dirty, to trigger
89                    self.tabs_view.redraw_view_area(cx);
90                    self.drag_tab_view.redraw_view_area(cx);
91                    
92                    tab_control_event = TabControlEvent::TabDragMove {fe: fe, tab_id: *id};
93                },
94                TabEvent::DragEnd(fe) => {
95                    self._dragging_tab = None;
96                    self.drag_tab_view.redraw_view_area(cx);
97                    
98                    tab_control_event = TabControlEvent::TabDragEnd {fe, tab_id: *id};
99                },
100                TabEvent::Closing => { // this tab is closing. select the visible one
101                    if tab._is_selected { // only do anything if we are selected
102                        let next_sel = if *id == self._tab_id_alloc - 1 { // last id
103                            if *id > 0 {
104                                *id - 1
105                            }
106                            else {
107                                *id
108                            }
109                        }
110                        else {
111                            *id + 1
112                        };
113                        if *id != next_sel {
114                            tab_control_event = TabControlEvent::TabSelect {tab_id: next_sel};
115                        }
116                    }
117                },
118                TabEvent::Close => {
119                    // Sooooo someone wants to close the tab
120                    tab_control_event = TabControlEvent::TabClose {tab_id: *id};
121                },
122                _ => ()
123            }
124        };
125        match tab_control_event {
126            TabControlEvent::TabSelect {tab_id} => {
127                self._focussed = true;
128                for (id, tab) in self.tabs.enumerate() {
129                    if tab_id != *id {
130                        tab.set_tab_selected(cx, false);
131                        tab.set_tab_focus(cx, true);
132                    }
133                }
134            },
135            TabControlEvent::TabClose {..} => { // needed to clear animation state
136                self.tabs.clear(cx, | _, _ | ());
137            },
138            _ => ()
139        };
140        tab_control_event
141    }
142    
143    pub fn tab_control_style()->StyleId{uid!()}
144
145    pub fn style(cx:&mut Cx, opt:&StyleOptions){
146        cx.begin_style(Self::tab_control_style());
147        ScrollBar::bar_size().set(cx, 8. * opt.scale.powf(0.5));
148        cx.end_style();
149    }
150    
151    pub fn get_tab_rects(&mut self, cx: &Cx) -> Vec<Rect> {
152        let mut rects = Vec::new();
153        for tab in self.tabs.iter() {
154            rects.push(tab.get_tab_rect(cx))
155        }
156        return rects
157    }
158    
159    pub fn set_tab_control_focus(&mut self, cx: &mut Cx, focus: bool) {
160        self._focussed = focus;
161        for tab in self.tabs.iter() {
162            tab.set_tab_focus(cx, focus);
163        }
164    }
165    
166    pub fn get_tabs_view_rect(&mut self, cx: &Cx) -> Rect {
167        self.tabs_view.get_rect(cx)
168    }
169    
170    pub fn get_content_drop_rect(&mut self, cx: &Cx) -> Rect {
171        let pr = self.page_view.get_rect(cx);
172        // we now need to change the y and the new height
173        Rect {
174            x: pr.x,
175            y: pr.y,
176            w: pr.w,
177            h: pr.h
178        }
179    }
180    
181    // data free APIs for the win!
182    pub fn begin_tabs(&mut self, cx: &mut Cx) -> ViewRedraw {
183        //cx.begin_turtle(&Layout{
184        if let Err(_) = self.tabs_view.begin_view(cx, Layout {
185            walk:Walk::wh(Width::Fill, Height::Compute),
186            ..Layout::default()
187        }) {
188            return Err(())
189        }
190        self._tab_now_selected = None;
191        self._tab_id_alloc = 0;
192        Ok(())
193    }
194
195    pub fn get_draw_tab(&mut self, cx: &mut Cx, label: &str, selected: bool, closeable: bool)->&mut Tab{
196        let new_tab = self.tabs.get(self._tab_id_alloc).is_none();
197        let tab = self.tabs.get_draw(cx, self._tab_id_alloc, | _cx, tmpl | tmpl.clone());
198        if selected{
199            self._tab_now_selected = Some(self._tab_id_alloc);
200        }
201        self._tab_id_alloc += 1;
202        tab.label = label.to_string();
203        tab.is_closeable = closeable;
204        if new_tab {
205            tab.set_tab_state(cx, selected, self._focussed);
206        }
207        else { // animate the tabstate
208            tab.set_tab_selected(cx, selected);
209        }
210        tab
211    }
212
213    pub fn draw_tab(&mut self, cx: &mut Cx, label: &str, selected: bool, closeable: bool) {
214        let tab = self.get_draw_tab(cx, label, selected, closeable);
215        tab.draw_tab(cx);
216    }
217    
218    pub fn end_tabs(&mut self, cx: &mut Cx) {
219        self.tab_fill.color = Theme::color_bg_normal().get(cx);
220        self.tab_fill.draw_quad(cx, Walk::wh(Width::Fill, Height::Fill));
221        self.tabs.sweep(cx, | _, _ | ());
222        if let Some((fe, id)) = &self._dragging_tab {
223            if let Ok(()) = self.drag_tab_view.begin_view(cx, Layout {
224                abs_origin: Some(Vec2::default()),
225                ..Default::default()
226            }) {
227                
228                self.drag_tab.abs_origin = Some(Vec2 {x: fe.abs.x - fe.rel_start.x, y: fe.abs.y - fe.rel_start.y});
229                let origin_tab = self.tabs.get_draw(cx, *id, | _cx, tmpl | tmpl.clone());
230                self.drag_tab.label = origin_tab.label.clone();
231                self.drag_tab.is_closeable = origin_tab.is_closeable;
232                self.drag_tab.draw_tab(cx);
233                
234                self.drag_tab_view.end_view(cx);
235            }
236        }
237        cx.begin_style(Self::tab_control_style());
238        self.tabs_view.end_view(cx);
239        cx.end_style();
240        if self._tab_now_selected != self._tab_last_selected{
241            // lets scroll the thing into view
242            if let Some(tab_id) = self._tab_now_selected{
243                if let Some(tab) = self.tabs.get(tab_id){
244                    let tab_rect = tab._bg_area.get_rect(cx);
245                    self.tabs_view.scroll_into_view_abs(cx, tab_rect);
246                }
247            }
248            self._tab_last_selected = self._tab_now_selected;
249        }
250    }
251    
252    pub fn begin_tab_page(&mut self, cx: &mut Cx) -> ViewRedraw {
253        cx.turtle_new_line();
254        self.page_view.begin_view(cx, Layout::default())
255    }
256    
257    pub fn end_tab_page(&mut self, cx: &mut Cx) {
258        self.page_view.end_view(cx);
259        //cx.end_turtle(Area::Empty);
260        // if we are in draggable tab state,
261        // draw our draggable tab
262    }
263}