makepad_widgets/
dock.rs

1use std::collections::HashMap;
2use crate::{
3    makepad_derive_widget::*,
4    widget::*,
5    makepad_draw::*,
6    splitter::{SplitterAction, Splitter, SplitterAlign},
7    tab::{TabClosable},
8    tab_bar::{TabBarAction, TabBar},
9};
10
11live_design!{
12    DrawRoundCorner = {{DrawRoundCorner}} {}
13    DockBase = {{Dock}} {}
14}
15
16#[derive(Live, LiveHook)]
17#[repr(C)]
18pub struct DrawRoundCorner {
19    #[deref] draw_super: DrawQuad,
20    #[live] border_radius: f32,
21    #[live] flip: Vec2,
22}
23
24impl DrawRoundCorner {
25    fn draw_corners(&mut self, cx: &mut Cx2d, rect: Rect) {
26        self.flip = vec2(0.0, 0.0);
27        let rad = dvec2(self.border_radius as f64, self.border_radius as f64);
28        let pos = rect.pos;
29        let size = rect.size;
30        self.draw_abs(cx, Rect {pos, size: rad});
31        self.flip = vec2(1.0, 0.0);
32        self.draw_abs(cx, Rect {pos: pos + dvec2(size.x - rad.x, 0.), size: rad});
33        self.flip = vec2(1.0, 1.0);
34        self.draw_abs(cx, Rect {pos: pos + dvec2(size.x - rad.x, size.y - rad.y), size: rad});
35        self.flip = vec2(0.0, 1.0);
36        self.draw_abs(cx, Rect {pos: pos + dvec2(0., size.y - rad.y), size: rad});
37    }
38}
39
40#[derive(Live)]
41pub struct Dock {
42    #[rust] draw_state: DrawStateWrap<Vec<DrawStackItem >>,
43    #[walk] walk: Walk,
44    #[layout] layout: Layout,
45    #[live] drop_target_draw_list: DrawList2d,
46    #[live] round_corner: DrawRoundCorner,
47    #[live] padding_fill: DrawColor,
48    #[live] border_size: f64,
49    #[live] drag_quad: DrawColor,
50    
51    #[live] tab_bar: Option<LivePtr>,
52    #[live] splitter: Option<LivePtr>,
53    
54    #[rust] area: Area,
55    
56    #[rust] tab_bars: ComponentMap<LiveId, TabBarWrap>,
57    #[rust] splitters: ComponentMap<LiveId, Splitter>,
58    
59    #[rust] dock_items: HashMap<LiveId, DockItem>,
60    #[rust] templates: HashMap<LiveId, LivePtr>,
61    #[rust] items: ComponentMap<LiveId, (LiveId, WidgetRef)>,
62    #[rust] drop_state: Option<DropPosition>,
63    #[rust] dock_item_iter_stack: Vec<(LiveId, usize)>,
64}
65
66pub struct DockVisibleItemIterator<'a> {
67    stack: &'a mut Vec<(LiveId, usize)>,
68    dock_items: &'a HashMap<LiveId, DockItem>,
69    items: &'a ComponentMap<LiveId, (LiveId, WidgetRef)>,
70}
71
72impl<'a> Iterator for DockVisibleItemIterator<'a> {
73    // We can refer to this type using Self::Item
74    type Item = (LiveId, WidgetRef);
75    fn next(&mut self) -> Option<Self::Item> {
76        // alright so lets fetch the item on the top of the stack
77        while let Some((item_id, index)) = self.stack.pop() {
78            if let Some(dock_item) = self.dock_items.get(&item_id) {
79                match dock_item {
80                    DockItem::Splitter {a, b, ..} => {
81                        if index == 0 {
82                            self.stack.push((item_id, 1));
83                            self.stack.push((*a, 0));
84                        }
85                        else {
86                            self.stack.push((*b, 0));
87                        }
88                    }
89                    DockItem::Tabs {tabs, selected, ..} => {
90                        if let Some(tab_id) = tabs.get(*selected) {
91                            self.stack.push((*tab_id, 0));
92                        }
93                    }
94                    DockItem::Tab {..} => {
95                        if let Some((_, widget)) = self.items.get(&item_id) {
96                            return Some((item_id, widget.clone()))
97                        }
98                    }
99                }
100            }
101        }
102        None
103    }
104}
105
106#[derive(Clone, Copy, Hash, PartialEq, Eq)]
107pub struct DockItemId {
108    pub kind: LiveId,
109    pub id: LiveId
110}
111
112
113struct TabBarWrap {
114    tab_bar: TabBar,
115    contents_draw_list: DrawList2d,
116    contents_rect: Rect
117}
118
119#[derive(Copy, Debug, Clone)]
120enum DrawStackItem {
121    Invalid,
122    SplitLeft {id: LiveId},
123    SplitRight {id: LiveId},
124    SplitEnd {id: LiveId},
125    Tabs {id: LiveId},
126    TabLabel {id: LiveId, index: usize},
127    Tab {id: LiveId},
128    TabContent {id: LiveId}
129}
130
131impl DrawStackItem {
132    fn from_dock_item(id: LiveId, dock_item: Option<&DockItem>) -> Self {
133        match dock_item {
134            None => DrawStackItem::Invalid,
135            Some(DockItem::Splitter {..}) => {
136                DrawStackItem::SplitLeft {id}
137            }
138            Some(DockItem::Tabs {..}) => {
139                DrawStackItem::Tabs {id}
140            }
141            Some(DockItem::Tab {..}) => {
142                DrawStackItem::Tab {id}
143            }
144        }
145    }
146}
147
148#[derive(Clone, WidgetAction)]
149pub enum DockAction {
150    SplitPanelChanged {panel_id: LiveId, axis: Axis, align: SplitterAlign},
151    TabWasPressed(LiveId),
152    TabCloseWasPressed(LiveId),
153    ShouldTabStartDrag(LiveId),
154    Drag(DragHitEvent),
155    Drop(DropHitEvent),
156    None
157}
158
159
160#[derive(Clone, Copy, Debug, PartialEq)]
161pub struct DropPosition {
162    part: DropPart,
163    rect: Rect,
164    id: LiveId
165}
166
167#[derive(Clone, Copy, Debug, PartialEq)]
168pub enum DropPart {
169    Left,
170    Right,
171    Top,
172    Bottom,
173    Center,
174    TabBar,
175    Tab
176}
177
178#[derive(Clone, Debug, Live, LiveHook)]
179#[live_ignore]
180pub enum DockItem {
181    #[live {axis: Axis::Vertical, align: SplitterAlign::Weighted(0.5), a: LiveId(0), b: LiveId(0)}]
182    Splitter {
183        axis: Axis,
184        align: SplitterAlign,
185        a: LiveId,
186        b: LiveId
187    },
188    #[live {tabs: vec![], selected: 0, closable: false}]
189    Tabs {
190        tabs: Vec<LiveId>,
191        selected: usize,
192        closable: bool
193    },
194    #[pick {name: "Tab".to_string(), kind: LiveId(0), closable: false}]
195    Tab {
196        name: String,
197        closable: bool,
198        kind: LiveId
199    }
200}
201
202impl LiveHook for Dock {
203    fn apply_value_instance(&mut self, cx: &mut Cx, from: ApplyFrom, index: usize, nodes: &[LiveNode]) -> usize {
204        let id = nodes[index].id;
205        match from {
206            ApplyFrom::NewFromDoc {file_id} | ApplyFrom::UpdateFromDoc {file_id} => {
207                if nodes[index].origin.has_prop_type(LivePropType::Instance) {
208                    if nodes[index].value.is_enum() {
209                        let mut dock_item = DockItem::new(cx);
210                        let index = dock_item.apply(cx, from, index, nodes);
211                        self.dock_items.insert(id, dock_item);
212                        
213                        return index;
214                    }
215                    else {
216                        let live_ptr = cx.live_registry.borrow().file_id_index_to_live_ptr(file_id, index);
217                        self.templates.insert(id, live_ptr);
218                        // lets apply this thing over all our childnodes with that template
219                        for (kind, node) in self.items.values_mut() {
220                            if *kind == id {
221                                node.apply(cx, from, index, nodes);
222                            }
223                        }
224                    }
225                }
226                else {
227                    cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
228                }
229            }
230            _ => ()
231        }
232        nodes.skip_node(index)
233    }
234    
235    // alright lets update our tabs and splitters as well
236    fn after_apply(&mut self, cx: &mut Cx, from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
237        if let Some(index) = nodes.child_by_name(index, live_id!(tab_bar).as_field()) {
238            for tab_bar in self.tab_bars.values_mut() {
239                tab_bar.tab_bar.apply(cx, from, index, nodes);
240            }
241        }
242        if let Some(index) = nodes.child_by_name(index, live_id!(splitter).as_field()) {
243            for splitter in self.splitters.values_mut() {
244                splitter.apply(cx, from, index, nodes);
245            }
246        }
247    }
248    
249    fn after_new_from_doc(&mut self, cx: &mut Cx) {
250        // make sure our items exist
251        let mut items = Vec::new();
252        for (item_id, item) in self.dock_items.iter() {
253            if let DockItem::Tab {kind, ..} = item {
254                items.push((*item_id, *kind));
255            }
256        }
257        for (item_id, kind) in items {
258            self.item_or_create(cx, item_id, kind);
259        }
260    }
261    
262    fn before_live_design(cx: &mut Cx) {
263        register_widget!(cx, Dock)
264    }
265}
266
267impl Dock {
268    
269    fn begin(&mut self, cx: &mut Cx2d, walk: Walk) {
270        cx.begin_turtle(walk, self.layout);
271        //self.drop_zones.clear();
272    }
273    
274    fn end(&mut self, cx: &mut Cx2d) {
275        
276        if self.drop_target_draw_list.begin(cx, Walk::default()).is_redrawing() {
277            if let Some(pos) = &self.drop_state {
278                self.drag_quad.draw_abs(cx, pos.rect);
279            }
280            self.drop_target_draw_list.end(cx);
281        }
282        
283        self.tab_bars.retain_visible();
284        self.splitters.retain_visible();
285        
286        // lets draw the corners here
287        for splitter in self.splitters.values() {
288            self.round_corner.draw_corners(cx, splitter.area_a().get_rect(cx));
289            self.round_corner.draw_corners(cx, splitter.area_b().get_rect(cx));
290        }
291        self.round_corner.draw_corners(cx, cx.turtle().rect());
292        
293        cx.end_turtle_with_area(&mut self.area);
294    }
295    
296    fn find_drop_position(&self, cx: &Cx, abs: DVec2) -> Option<DropPosition> {
297        for (tab_bar_id, tab_bar) in self.tab_bars.iter() {
298            let rect = tab_bar.contents_rect;
299            if let Some((tab_id, rect)) = tab_bar.tab_bar.is_over_tab(cx, abs) {
300                return Some(DropPosition {
301                    part: DropPart::Tab,
302                    id: tab_id,
303                    rect
304                })
305            }
306            else if let Some(rect) = tab_bar.tab_bar.is_over_tab_bar(cx, abs) {
307                return Some(DropPosition {
308                    part: DropPart::TabBar,
309                    id: *tab_bar_id,
310                    rect
311                })
312            }
313            else if rect.contains(abs) {
314                let top_left = rect.pos;
315                let bottom_right = rect.pos + rect.size;
316                if (abs.x - top_left.x) / rect.size.x < 0.1 {
317                    return Some(DropPosition {
318                        part: DropPart::Left,
319                        id: *tab_bar_id,
320                        rect: Rect {
321                            pos: rect.pos,
322                            size: DVec2 {
323                                x: rect.size.x / 2.0,
324                                y: rect.size.y,
325                            },
326                        }
327                    })
328                } else if (bottom_right.x - abs.x) / rect.size.x < 0.1 {
329                    return Some(DropPosition {
330                        part: DropPart::Right,
331                        id: *tab_bar_id,
332                        rect: Rect {
333                            pos: DVec2 {
334                                x: rect.pos.x + rect.size.x / 2.0,
335                                y: rect.pos.y,
336                            },
337                            size: DVec2 {
338                                x: rect.size.x / 2.0,
339                                y: rect.size.y,
340                            },
341                        }
342                    })
343                } else if (abs.y - top_left.y) / rect.size.y < 0.1 {
344                    return Some(DropPosition {
345                        part: DropPart::Top,
346                        id: *tab_bar_id,
347                        rect: Rect {
348                            pos: rect.pos,
349                            size: DVec2 {
350                                x: rect.size.x,
351                                y: rect.size.y / 2.0,
352                            },
353                        }
354                    })
355                } else if (bottom_right.y - abs.y) / rect.size.y < 0.1 {
356                    return Some(DropPosition {
357                        part: DropPart::Bottom,
358                        id: *tab_bar_id,
359                        rect: Rect {
360                            pos: DVec2 {
361                                x: rect.pos.x,
362                                y: rect.pos.y + rect.size.y / 2.0,
363                            },
364                            size: DVec2 {
365                                x: rect.size.x,
366                                y: rect.size.y / 2.0,
367                            },
368                        }
369                    })
370                } else {
371                    return Some(DropPosition {
372                        part: DropPart::Center,
373                        id: *tab_bar_id,
374                        rect
375                    })
376                }
377            }
378        }
379        None
380    }
381    
382    pub fn item(&mut self, entry_id: LiveId) -> Option<WidgetRef> {
383        if let Some(entry) = self.items.get(&entry_id) {
384            return Some(entry.1.clone())
385        }
386        None
387    }
388    
389    pub fn item_or_create(&mut self, cx: &mut Cx, entry_id: LiveId, template: LiveId) -> Option<WidgetRef> {
390        if let Some(ptr) = self.templates.get(&template) {
391            let entry = self.items.get_or_insert(cx, entry_id, | cx | {
392                (template, WidgetRef::new_from_ptr(cx, Some(*ptr)))
393            });
394            return Some(entry.1.clone())
395        }
396        else {
397            log!("PortalList template not found {}", template);
398        }
399        None
400    }
401    
402    pub fn items(&mut self) -> &ComponentMap<LiveId, (LiveId, WidgetRef)> {
403        &self.items
404    }
405    
406    pub fn visible_items(&mut self) -> DockVisibleItemIterator {
407        self.dock_item_iter_stack.clear();
408        self.dock_item_iter_stack.push((live_id!(root), 0));
409        DockVisibleItemIterator {
410            stack: &mut self.dock_item_iter_stack,
411            dock_items: &self.dock_items,
412            items: &self.items
413        }
414    }
415    
416    fn set_parent_split(&mut self, what_item: LiveId, replace_item: LiveId) {
417        for item in self.dock_items.values_mut() {
418            match item {
419                DockItem::Splitter {a, b, ..} => {
420                    if what_item == *a {
421                        *a = replace_item;
422                        return
423                    }
424                    else if what_item == *b {
425                        *b = replace_item;
426                        return
427                    }
428                }
429                _ => ()
430            }
431        }
432    }
433    
434    fn redraw_item(&mut self, cx: &mut Cx, what_item_id: LiveId) {
435        if let Some(tab_bar) = self.tab_bars.get_mut(&what_item_id) {
436            tab_bar.contents_draw_list.redraw(cx);
437        }
438        for (item_id, (_kind, item)) in self.items.iter_mut() {
439            if *item_id == what_item_id {
440                item.redraw(cx);
441            }
442        }
443    }
444    
445    fn unsplit_tabs(&mut self, cx: &mut Cx, tabs_id: LiveId) {
446        for (splitter_id, item) in self.dock_items.iter_mut() {
447            match *item {
448                DockItem::Splitter {a, b, ..} => {
449                    let splitter_id = *splitter_id;
450                    if tabs_id == a {
451                        self.set_parent_split(splitter_id, b);
452                        self.dock_items.remove(&splitter_id);
453                        self.dock_items.remove(&tabs_id);
454                        self.redraw_item(cx, b);
455                        return
456                    }
457                    else if tabs_id == b {
458                        self.set_parent_split(splitter_id, a);
459                        self.dock_items.remove(&splitter_id);
460                        self.dock_items.remove(&tabs_id);
461                        self.redraw_item(cx, a);
462                        return
463                    }
464                }
465                _ => ()
466            }
467        }
468    }
469    
470    fn select_tab(&mut self, cx: &mut Cx, tab_id: LiveId) {
471        for (tabs_id, item) in self.dock_items.iter_mut() {
472            match item {
473                DockItem::Tabs {tabs, selected, ..} => if let Some(pos) = tabs.iter().position( | v | *v == tab_id) {
474                    *selected = pos;
475                    // ok now lets redraw the area of the tab
476                    if let Some(tab_bar) = self.tab_bars.get(&tabs_id) {
477                        tab_bar.contents_draw_list.redraw(cx);
478                    }
479                }
480                _ => ()
481            }
482        }
483    }
484    
485    
486    fn set_tab_title(&mut self, cx: &mut Cx, tab_id: LiveId, new_name:String) {
487        if let Some(DockItem::Tab{name, ..}) = self.dock_items.get_mut(&tab_id){
488            *name = new_name;
489            self.redraw_tab(cx, tab_id);
490        }
491    }
492    
493    fn redraw_tab(&mut self, cx: &mut Cx, tab_id: LiveId) {
494        for (tabs_id, item) in self.dock_items.iter_mut() {
495            match item {
496                DockItem::Tabs {tabs, ..} => if let Some(_) = tabs.iter().position( | v | *v == tab_id) {
497                    if let Some(tab_bar) = self.tab_bars.get(&tabs_id) {
498                        tab_bar.contents_draw_list.redraw(cx);
499                    }
500                }
501                _ => ()
502            }
503        }
504    }
505    
506    fn find_tab_bar_of_tab(&mut self, tab_id: LiveId) -> Option<LiveId> {
507        for (tabs_id, item) in self.dock_items.iter_mut() {
508            match item {
509                DockItem::Tabs {tabs, ..} => if let Some(_) = tabs.iter().position( | v | *v == tab_id) {
510                    return Some(*tabs_id)
511                }
512                _ => ()
513            }
514        }
515        None
516    }
517    
518    fn close_tab(&mut self, cx: &mut Cx, tab_id: LiveId, keep_item: bool) -> Option<LiveId> {
519        // ok so we have to find the tab id in our tab bars / tabs and remove it
520        // if we are the last tab we need to remove a splitter
521        for (tabs_id, item) in self.dock_items.iter_mut() {
522            match item {
523                DockItem::Tabs {tabs, selected, closable} => if let Some(pos) = tabs.iter().position( | v | *v == tab_id) {
524                    // remove from the tabs array
525                    let tabs_id = *tabs_id;
526                    tabs.remove(pos);
527                    if tabs.len() == 0 { // unsplit
528                        if *closable {
529                            self.unsplit_tabs(cx, tabs_id);
530                        }
531                        self.area.redraw(cx);
532                        return None
533                    }
534                    else {
535                        let next_tab = if *selected >= tabs.len() {tabs[*selected - 1]} else {tabs[*selected]};
536                        self.select_tab(cx, next_tab);
537                        if !keep_item {
538                            self.dock_items.remove(&tab_id);
539                        }
540                        self.area.redraw(cx);
541                        return Some(tabs_id)
542                    }
543                }
544                _ => ()
545            }
546        }
547        None
548    }
549    
550    fn check_drop_is_noop(&mut self, tab_id: LiveId, item_id: LiveId) -> bool {
551        // ok so we have to find the tab id in our tab bars / tabs and remove it
552        // if we are the last tab we need to remove a splitter
553        for (tabs_id, item) in self.dock_items.iter_mut() {
554            match item {
555                DockItem::Tabs {tabs, ..} => if let Some(_) = tabs.iter().position( | v | *v == tab_id) {
556                    if *tabs_id == item_id && tabs.len() == 1 {
557                        return true
558                    }
559                }
560                _ => ()
561            }
562        }
563        false
564    }
565    
566    fn handle_drop(&mut self, cx: &mut Cx, abs: DVec2, item: LiveId, is_move: bool) -> bool {
567        if let Some(pos) = self.find_drop_position(cx, abs) {
568            // ok now what
569            // we have a pos
570            match pos.part {
571                DropPart::Left | DropPart::Right | DropPart::Top | DropPart::Bottom => {
572                    if is_move {
573                        if self.check_drop_is_noop(item, pos.id) {
574                            return false
575                        }
576                        self.close_tab(cx, item, true);
577                    }
578                    let new_split = LiveId::unique();
579                    let new_tabs = LiveId::unique();
580                    self.set_parent_split(pos.id, new_split);
581                    self.dock_items.insert(new_split, match pos.part {
582                        DropPart::Left => DockItem::Splitter {
583                            axis: Axis::Horizontal,
584                            align: SplitterAlign::Weighted(0.5),
585                            a: new_tabs,
586                            b: pos.id,
587                        },
588                        DropPart::Right => DockItem::Splitter {
589                            axis: Axis::Horizontal,
590                            align: SplitterAlign::Weighted(0.5),
591                            a: pos.id,
592                            b: new_tabs
593                        },
594                        DropPart::Top => DockItem::Splitter {
595                            axis: Axis::Vertical,
596                            align: SplitterAlign::Weighted(0.5),
597                            a: new_tabs,
598                            b: pos.id,
599                        },
600                        DropPart::Bottom => DockItem::Splitter {
601                            axis: Axis::Vertical,
602                            align: SplitterAlign::Weighted(0.5),
603                            a: pos.id,
604                            b: new_tabs,
605                        },
606                        _ => panic!()
607                    });
608                    self.dock_items.insert(new_tabs, DockItem::Tabs {
609                        tabs: vec![item],
610                        closable: true,
611                        selected: 0,
612                    });
613                    return true
614                }
615                DropPart::Center => {
616                    if is_move {
617                        if self.check_drop_is_noop(item, pos.id) {
618                            return false
619                        }
620                        self.close_tab(cx, item, true);
621                    }
622                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get_mut(&pos.id) {
623                        tabs.push(item);
624                        *selected = tabs.len() - 1;
625                        if let Some(tab_bar) = self.tab_bars.get(&pos.id) {
626                            tab_bar.contents_draw_list.redraw(cx);
627                        }
628                    }
629                    return true
630                }
631                DropPart::TabBar => {
632                    if is_move {
633                        if self.check_drop_is_noop(item, pos.id) {
634                            return false
635                        }
636                        self.close_tab(cx, item, true);
637                    }
638                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get_mut(&pos.id) {
639                        tabs.push(item);
640                        *selected = tabs.len() - 1;
641                        if let Some(tab_bar) = self.tab_bars.get(&pos.id) {
642                            tab_bar.contents_draw_list.redraw(cx);
643                        }
644                    }
645                    return true
646                }
647                // insert the ta
648                DropPart::Tab => {
649                    if is_move {
650                        if pos.id == item {
651                            return false
652                        }
653                        self.close_tab(cx, item, true);
654                    }
655                    let tab_bar_id = self.find_tab_bar_of_tab(pos.id).unwrap();
656                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get_mut(&tab_bar_id) {
657                        if let Some(pos) = tabs.iter().position( | v | *v == pos.id) {
658                            let old = tabs[pos];
659                            tabs[pos] = item;
660                            tabs.push(old);
661                            *selected = pos;
662                            if let Some(tab_bar) = self.tab_bars.get(&tab_bar_id) {
663                                tab_bar.contents_draw_list.redraw(cx);
664                            }
665                        }
666                    }
667                    return true
668                }
669            }
670        }
671        false
672    }
673    
674    fn drop_create(&mut self, cx: &mut Cx, abs: DVec2, item: LiveId, kind: LiveId, name: String, closable:TabClosable) {
675        // lets add a tab
676        
677        if self.handle_drop(cx, abs, item, false) {
678            self.dock_items.insert(item, DockItem::Tab {
679                name,
680                closable: closable.as_bool(),
681                kind
682            });
683            self.item_or_create(cx, item, kind);
684            self.select_tab(cx, item);
685            self.area.redraw(cx);
686        }
687    }
688    
689    fn drop_clone(&mut self, cx: &mut Cx, abs: DVec2, item: LiveId, new_item: LiveId) {
690        // lets add a tab
691        if let Some(DockItem::Tab {name, kind, ..}) = self.dock_items.get(&item) {
692            let name = name.clone();
693            let kind = kind.clone();
694            if self.handle_drop(cx, abs, new_item, false) {
695                self.dock_items.insert(new_item, DockItem::Tab {
696                    name,
697                    closable: true,
698                    kind
699                });
700                self.item_or_create(cx, new_item, kind);
701                self.select_tab(cx, new_item);
702            }
703        }
704    }
705    
706    fn create_and_select_tab(&mut self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, closable:TabClosable) {
707        self.create_tab(cx, parent, item, kind, name, closable);
708        self.select_tab(cx, item);
709    }
710    
711    fn create_tab(&mut self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, closable:TabClosable) {
712        if let Some(DockItem::Tabs {tabs, ..}) = self.dock_items.get_mut(&parent) {
713            tabs.push(item);
714            self.dock_items.insert(item, DockItem::Tab {
715                name,
716                closable: closable.as_bool(),
717                kind
718            });
719            self.item_or_create(cx, item, kind);
720        }
721    }
722    
723    pub fn drawing_item_id(&self) -> Option<LiveId> {
724        if let Some(stack) = self.draw_state.as_ref() {
725            match stack.last() {
726                Some(DrawStackItem::Tab {id}) => {
727                    return Some(*id)
728                }
729                _ => ()
730            }
731        }
732        None
733    }
734    
735}
736
737
738impl Widget for Dock {
739    fn redraw(&mut self, cx: &mut Cx) {
740        self.area.redraw(cx);
741    }
742    
743    fn handle_widget_event_with(&mut self, cx: &mut Cx, event: &Event, dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem)) {
744        // call handle on all tab bars, splitters,
745        let uid = self.widget_uid();
746        let dock_items = &mut self.dock_items;
747        for (panel_id, splitter) in self.splitters.iter_mut() {
748            splitter
749                .handle_event_with(cx, event, &mut | cx, action | match action {
750                SplitterAction::Changed {axis, align} => {
751                    // lets move the splitter
752                    if let Some(DockItem::Splitter {axis: _axis, align: _align, ..}) = dock_items.get_mut(&panel_id) {
753                        *_axis = axis;
754                        *_align = align;
755                    }
756                    dispatch_action(cx, DockAction::SplitPanelChanged {panel_id: *panel_id, axis, align}.into_action(uid));
757                },
758                _ => ()
759            });
760        }
761        for (panel_id, tab_bar) in self.tab_bars.iter_mut() {
762            let contents_view = &mut tab_bar.contents_draw_list;
763            for action in tab_bar.tab_bar.handle_event(cx, event) {
764                match action {
765                    TabBarAction::ShouldTabStartDrag(item) => dispatch_action(
766                        cx,
767                        DockAction::ShouldTabStartDrag(item).into_action(uid),
768                    ),
769                    TabBarAction::TabWasPressed(tab_id) => {
770                        if let Some(DockItem::Tabs {tabs, selected, ..}) = dock_items.get_mut(&panel_id) {
771                            if let Some(sel) = tabs.iter().position( | v | *v == tab_id) {
772                                *selected = sel;
773                                contents_view.redraw(cx);
774                                dispatch_action(cx, DockAction::TabWasPressed(tab_id).into_action(uid))
775                            }
776                            else {
777                                log!("Cannot find tab {}", tab_id.0);
778                            }
779                        }
780                    }
781                    TabBarAction::TabCloseWasPressed(tab_id) => {
782                        dispatch_action(cx, DockAction::TabCloseWasPressed(tab_id).into_action(uid))
783                    }
784                }
785            };
786        }
787        for (_, item) in self.items.values_mut() {
788            item.handle_widget_event_with(cx, event, dispatch_action);
789        }
790        
791        if let Event::DragEnd = event {
792            // end our possible dragstate
793            self.drop_state = None;
794            self.drop_target_draw_list.redraw(cx);
795        }
796        
797        // alright lets manage the drag areas
798        match event.drag_hits(cx, self.area) {
799            DragHit::Drag(f) => {
800                self.drop_state = None;
801                self.drop_target_draw_list.redraw(cx);
802                match f.state {
803                    DragState::In | DragState::Over => {
804                        dispatch_action(cx, DockAction::Drag(f.clone()).into_action(uid))
805                    }
806                    DragState::Out => {}
807                }
808            }
809            DragHit::Drop(f) => {
810                self.drop_state = None;
811                self.drop_target_draw_list.redraw(cx);
812                dispatch_action(cx, DockAction::Drop(f.clone()).into_action(uid))
813            }
814            _ => {}
815        }
816        
817    }
818    
819    fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
820        if let Some((_, widget)) = self.items.get_mut(&path[0]) {
821            if path.len()>1 {
822                widget.find_widgets(&path[1..], cached, results);
823            }
824            else {
825                results.push(widget.clone());
826            }
827        }
828        else {
829            for (_, widget) in self.items.values_mut() {
830                widget.find_widgets(path, cached, results);
831            }
832        }
833    }
834    
835    fn walk(&mut self, _cx: &mut Cx) -> Walk {self.walk}
836    
837    fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
838        if self.draw_state.begin_with(cx, &self.dock_items, | _, dock_items | {
839            let id = live_id!(root);
840            vec![DrawStackItem::from_dock_item(id, dock_items.get(&id))]
841        }) {
842            self.begin(cx, walk);
843        }
844        
845        while let Some(stack) = self.draw_state.as_mut() {
846            match stack.pop() {
847                Some(DrawStackItem::SplitLeft {id}) => {
848                    stack.push(DrawStackItem::SplitRight {id});
849                    // top becomes splitleft
850                    let splitter = self.splitter;
851                    let splitter = self.splitters.get_or_insert(cx, id, | cx | {
852                        Splitter::new_from_ptr(cx, splitter)
853                    });
854                    if let Some(DockItem::Splitter {axis, align, a, ..}) = self.dock_items.get(&id) {
855                        splitter.set_axis(*axis);
856                        splitter.set_align(*align);
857                        splitter.begin(cx, Walk::default());
858                        stack.push(DrawStackItem::from_dock_item(*a, self.dock_items.get(&a)));
859                        continue;
860                    }
861                    else {panic!()}
862                }
863                Some(DrawStackItem::SplitRight {id}) => {
864                    // lets create the 4 split dropzones of this splitter
865                    stack.push(DrawStackItem::SplitEnd {id});
866                    let splitter = self.splitters.get_mut(&id).unwrap();
867                    splitter.middle(cx);
868                    if let Some(DockItem::Splitter {b, ..}) = self.dock_items.get(&id) {
869                        stack.push(DrawStackItem::from_dock_item(*b, self.dock_items.get(&b)));
870                        continue;
871                    }
872                    else {panic!()}
873                }
874                Some(DrawStackItem::SplitEnd {id}) => {
875                    // 4 more dropzones
876                    let splitter = self.splitters.get_mut(&id).unwrap();
877                    splitter.end(cx);
878                }
879                Some(DrawStackItem::Tabs {id}) => {
880                    if let Some(DockItem::Tabs {selected, ..}) = self.dock_items.get(&id) {
881                        // lets draw the tabs
882                        let tab_bar = self.tab_bar;
883                        let tab_bar = self.tab_bars.get_or_insert(cx, id, | cx | {
884                            TabBarWrap {
885                                tab_bar: TabBar::new_from_ptr(cx, tab_bar),
886                                contents_draw_list: DrawList2d::new(cx),
887                                contents_rect: Rect::default(),
888                                //full_rect: Rect::default(),
889                            }
890                        });
891                        tab_bar.tab_bar.begin(cx, Some(*selected));
892                        stack.push(DrawStackItem::TabLabel {id, index: 0});
893                    }
894                    else {panic!()}
895                }
896                Some(DrawStackItem::TabLabel {id, index}) => {
897                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get(&id) {
898                        let tab_bar = self.tab_bars.get_mut(&id).unwrap();
899                        if index < tabs.len() {
900                            if let Some(DockItem::Tab {name, closable, ..}) = self.dock_items.get(&tabs[index]) {
901                                tab_bar.tab_bar.draw_tab(cx, tabs[index].into(), name, if *closable {TabClosable::Yes}else {TabClosable::No});
902                            }
903                            stack.push(DrawStackItem::TabLabel {id, index: index + 1});
904                        }
905                        else {
906                            tab_bar.tab_bar.end(cx);
907                            tab_bar.contents_rect = cx.turtle().rect();
908                            if tabs.len()>0 && tab_bar.contents_draw_list.begin(cx, Walk::default()).is_redrawing() {
909                                stack.push(DrawStackItem::TabContent {id});
910                                if *selected < tabs.len() {
911                                    stack.push(DrawStackItem::Tab {id: tabs[*selected]});
912                                }
913                            }
914                        }
915                    }
916                    else {panic!()}
917                }
918                Some(DrawStackItem::Tab {id}) => {
919                    stack.push(DrawStackItem::Tab {id});
920                    if let Some(DockItem::Tab {kind, ..}) = self.dock_items.get(&id) {
921                        if let Some(ptr) = self.templates.get(&kind) {
922                            let (_, entry) = self.items.get_or_insert(cx, id, | cx | {
923                                (*kind, WidgetRef::new_from_ptr(cx, Some(*ptr)))
924                            });
925                            entry.draw_widget(cx) ?;
926                        }
927                    }
928                    stack.pop();
929                }
930                Some(DrawStackItem::TabContent {id}) => {
931                    if let Some(DockItem::Tabs {..}) = self.dock_items.get(&id) {
932                        let tab_bar = self.tab_bars.get_mut(&id).unwrap();
933                        // lets create the full dropzone for this contentview
934                        
935                        tab_bar.contents_draw_list.end(cx);
936                    }
937                    else {panic!()}
938                }
939                Some(DrawStackItem::Invalid) => {}
940                None => {
941                    break
942                }
943            }
944        }
945        
946        self.end(cx);
947        self.draw_state.end();
948        
949        WidgetDraw::done()
950    }
951}
952
953#[derive(Clone, Debug, PartialEq, WidgetRef)]
954pub struct DockRef(WidgetRef);
955
956impl DockRef {
957    
958    
959    pub fn item(&self, entry_id: LiveId) -> WidgetRef {
960        if let Some(mut dock) = self.borrow_mut() {
961            if let Some(item) = dock.item(entry_id) {
962                return item
963            }
964        }
965        WidgetRef::empty()
966    }
967    
968    pub fn item_or_create(&self, cx: &mut Cx, entry_id: LiveId, template: LiveId) -> Option<WidgetRef> {
969        if let Some(mut dock) = self.borrow_mut() {
970            return dock.item_or_create(cx, entry_id, template);
971        }
972        None
973    }
974    
975    
976    pub fn close_tab(&self, cx: &mut Cx, tab_id: LiveId) {
977        if let Some(mut dock) = self.borrow_mut() {
978            dock.close_tab(cx, tab_id, false);
979        }
980    }
981    
982    pub fn clicked_tab_close(&self, actions: &WidgetActions) -> Option<LiveId> {
983        if let Some(item) = actions.find_single_action(self.widget_uid()) {
984            if let DockAction::TabCloseWasPressed(tab_id) = item.action() {
985                return Some(tab_id)
986            }
987        }
988        None
989    }
990    
991    pub fn should_tab_start_drag(&self, actions: &WidgetActions) -> Option<LiveId> {
992        if let Some(item) = actions.find_single_action(self.widget_uid()) {
993            if let DockAction::ShouldTabStartDrag(tab_id) = item.action() {
994                return Some(tab_id)
995            }
996        }
997        None
998    }
999    
1000    pub fn should_accept_drag(&self, actions: &WidgetActions) -> Option<DragHitEvent> {
1001        if let Some(item) = actions.find_single_action(self.widget_uid()) {
1002            if let DockAction::Drag(drag_event) = item.action() {
1003                return Some(drag_event.clone())
1004            }
1005        }
1006        None
1007    }
1008    
1009    pub fn has_drop(&self, actions: &WidgetActions) -> Option<DropHitEvent> {
1010        if let Some(item) = actions.find_single_action(self.widget_uid()) {
1011            if let DockAction::Drop(drag_event) = item.action() {
1012                return Some(drag_event.clone())
1013            }
1014        }
1015        None
1016    }
1017    
1018    // user wants to drag, set dh accordingly
1019    pub fn accept_drag(&self, cx: &mut Cx, dh: DragHitEvent, dr: DragResponse) {
1020        if let Some(mut dock) = self.borrow_mut() {
1021            if let Some(pos) = dock.find_drop_position(cx, dh.abs) {
1022                dh.response.set(dr);
1023                dock.drop_state = Some(pos);
1024            }
1025            else {
1026                dock.drop_state = None;
1027            }
1028        }
1029    }
1030    
1031    pub fn drawing_item_id(&self) -> Option<LiveId> {
1032        if let Some(dock) = self.borrow() {
1033            return dock.drawing_item_id();
1034        }
1035        None
1036    }
1037    
1038    pub fn drop_clone(&self, cx: &mut Cx, abs: DVec2, old_item: LiveId, new_item: LiveId) {
1039        if let Some(mut dock) = self.borrow_mut() {
1040            dock.drop_clone(cx, abs, old_item, new_item);
1041        }
1042    }
1043    
1044    pub fn drop_move(&self, cx: &mut Cx, abs: DVec2, item: LiveId) {
1045        if let Some(mut dock) = self.borrow_mut() {
1046            dock.handle_drop(cx, abs, item, true);
1047        }
1048    }
1049    
1050    pub fn drop_create(&self, cx: &mut Cx, abs: DVec2, item: LiveId, kind: LiveId, name: String, closable:TabClosable) {
1051        if let Some(mut dock) = self.borrow_mut() {
1052            dock.drop_create(cx, abs, item, kind, name, closable);
1053        }
1054    }
1055    
1056    pub fn create_and_select_tab(&self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, closable:TabClosable) {
1057        if let Some(mut dock) = self.borrow_mut() {
1058            dock.create_and_select_tab(cx, parent, item, kind, name, closable);
1059        }
1060    }
1061    
1062    pub fn create_tab(&self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, closable:TabClosable) {
1063        if let Some(mut dock) = self.borrow_mut() {
1064            dock.create_tab(cx, parent, item, kind, name, closable);
1065        }
1066    }
1067    
1068    pub fn set_tab_title(&self, cx: &mut Cx, tab:LiveId, title:String) {
1069        if let Some(mut dock) = self.borrow_mut() {
1070            dock.set_tab_title(cx, tab, title);
1071        }
1072    }
1073    
1074    
1075    pub fn find_tab_bar_of_tab(&self, tab_id: LiveId) -> Option<LiveId> {
1076        if let Some(mut dock) = self.borrow_mut() {
1077            return dock.find_tab_bar_of_tab(tab_id);
1078        }
1079        None
1080    }
1081    
1082    
1083    pub fn select_tab(&self, cx: &mut Cx, item: LiveId) {
1084        if let Some(mut dock) = self.borrow_mut() {
1085            dock.select_tab(cx, item);
1086        }
1087    }
1088    
1089    pub fn redraw_tab(&self, cx: &mut Cx, tab_id: LiveId) {
1090        if let Some(mut dock) = self.borrow_mut() {
1091            dock.redraw_tab(cx, tab_id);
1092        }
1093    }
1094    
1095    pub fn tab_start_drag(&self, cx: &mut Cx, _tab_id: LiveId, item: DragItem) {
1096        cx.start_dragging(vec![item]);
1097    }
1098}
1099
1100#[derive(Clone, WidgetSet)]
1101pub struct DockSet(WidgetSet);