makepad_widgets/
dock.rs

1use std::collections::HashMap;
2use crate::{
3    makepad_micro_serde::*,
4    makepad_derive_widget::*,
5    widget::*,
6    makepad_draw::*,
7    splitter::{SplitterAction, Splitter, SplitterAlign, SplitterAxis},
8    tab_bar::{TabBarAction, TabBar},
9};
10
11live_design!{
12    link widgets;
13    use link::theme::*;
14    use link::widgets::*;
15    use makepad_draw::shader::std::*;
16    use crate::view_ui::RectShadowView;
17
18    pub DrawRoundCorner = {{DrawRoundCorner}} {}
19    pub DockBase = {{Dock}} {}
20    pub Dock = <DockBase> {
21        flow: Down,
22
23        tab_bar: <TabBarGradientY> {}
24        splitter: <Splitter> {}
25
26        padding: {left: (THEME_DOCK_BORDER_SIZE), top: 0, right: (THEME_DOCK_BORDER_SIZE), bottom: (THEME_DOCK_BORDER_SIZE)}
27
28        round_corner: {
29            border_radius: 20.
30            uniform color: (THEME_COLOR_BG_APP)
31            fn pixel(self) -> vec4 {
32                let pos = vec2(
33                    mix(self.pos.x, 1.0 - self.pos.x, self.flip.x),
34                    mix(self.pos.y, 1.0 - self.pos.y, self.flip.y)
35                )
36
37                let sdf = Sdf2d::viewport(pos * self.rect_size);
38                sdf.rect(-10., -10., self.rect_size.x * 2.0, self.rect_size.y * 2.0);
39                sdf.box(
40                    0.25,
41                    0.25,
42                    self.rect_size.x * 2.0,
43                    self.rect_size.y * 2.0,
44                    4.0
45                );
46
47                sdf.subtract()
48
49                sdf.fill(self.color)
50                return sdf.result
51            }
52        }
53        drag_target_preview: {
54            draw_depth: 10.0
55            color: (THEME_COLOR_DRAG_TARGET_PREVIEW)
56        }
57    }
58
59    pub DockFlat = <DockBase> {
60        flow: Down,
61
62        tab_bar: <TabBarFlat> {}
63        splitter: <Splitter> {}
64
65        padding: {left: (THEME_DOCK_BORDER_SIZE), top: 0, right: (THEME_DOCK_BORDER_SIZE), bottom: (THEME_DOCK_BORDER_SIZE)}
66
67        round_corner: {
68            border_radius: 20.
69
70            fn pixel(self) -> vec4 {
71                let pos = vec2(
72                    mix(self.pos.x, 1.0 - self.pos.x, self.flip.x),
73                    mix(self.pos.y, 1.0 - self.pos.y, self.flip.y)
74                )
75
76                let sdf = Sdf2d::viewport(pos * self.rect_size);
77                sdf.rect(-10., -10., self.rect_size.x * 2.0, self.rect_size.y * 2.0);
78                sdf.box(
79                    0.25,
80                    0.25,
81                    self.rect_size.x * 2.0,
82                    self.rect_size.y * 2.0,
83                    4.0
84                );
85
86                sdf.subtract()
87                return sdf.fill(THEME_COLOR_BG_APP)
88            }
89        }
90
91        drag_target_preview: {
92            draw_depth: 10.0
93            color: (THEME_COLOR_DRAG_TARGET_PREVIEW)
94        }
95    }
96
97    pub DockToolbar = <RectShadowView> {
98        width: Fill, height: 38.,
99        flow: Down,
100        align: { x: 0., y: 0. }
101        margin: { top: -1. }
102        padding: <THEME_MSPACE_2> {}
103        spacing: 0.,
104
105        draw_bg: {
106            border_size: 0.0
107            border_color: (THEME_COLOR_BEVEL_OUTSET_1)
108            shadow_color: (THEME_COLOR_D_4)
109            shadow_radius: 7.5
110            shadow_offset: vec2(0.0, 0.0)
111            color: (THEME_COLOR_FG_APP),
112        }
113
114        content = <View> {
115            flow: Right,
116            width: Fill, height: Fill,
117            margin: 0.
118            padding: 0.
119            align: { x: 0., y: 0. }
120            spacing: (THEME_SPACE_3)
121        }
122    }
123}
124
125#[derive(Live, LiveHook, LiveRegister)]
126#[repr(C)]
127pub struct DrawRoundCorner {
128    #[deref] draw_super: DrawQuad,
129    #[live] border_radius: f32,
130    #[live] flip: Vec2,
131}
132
133impl DrawRoundCorner {
134    fn draw_corners(&mut self, cx: &mut Cx2d, rect: Rect) {
135        self.flip = vec2(0.0, 0.0);
136        let rad = dvec2(self.border_radius as f64, self.border_radius as f64);
137        let pos = rect.pos;
138        let size = rect.size;
139        self.draw_abs(cx, Rect {pos, size: rad});
140        self.flip = vec2(1.0, 0.0);
141        self.draw_abs(cx, Rect {pos: pos + dvec2(size.x - rad.x, 0.), size: rad});
142        self.flip = vec2(1.0, 1.0);
143        self.draw_abs(cx, Rect {pos: pos + dvec2(size.x - rad.x, size.y - rad.y), size: rad});
144        self.flip = vec2(0.0, 1.0);
145        self.draw_abs(cx, Rect {pos: pos + dvec2(0., size.y - rad.y), size: rad});
146    }
147}
148
149#[derive(Live, LiveRegisterWidget, WidgetRef, WidgetSet)]
150pub struct Dock {
151    #[rust] draw_state: DrawStateWrap<Vec<DrawStackItem >>,
152    #[walk] walk: Walk,
153    #[layout] layout: Layout,
154    #[live] drop_target_draw_list: DrawList2d,
155    #[live] round_corner: DrawRoundCorner,
156    #[live] drag_target_preview: DrawColor,
157
158    #[live] tab_bar: Option<LivePtr>,
159    #[live] splitter: Option<LivePtr>,
160
161    #[rust] needs_save: bool,
162    #[rust] area: Area,
163
164    #[rust] tab_bars: ComponentMap<LiveId, TabBarWrap>,
165    #[rust] splitters: ComponentMap<LiveId, Splitter>,
166
167    #[rust] dock_items: HashMap<LiveId, DockItem>,
168    #[rust] templates: HashMap<LiveId, LivePtr>,
169    #[rust] items: ComponentMap<LiveId, (LiveId, WidgetRef)>,
170    #[rust] drop_state: Option<DropPosition>,
171    #[rust] dock_item_iter_stack: Vec<(LiveId, usize)>,
172}
173
174impl WidgetNode for Dock{
175    fn walk(&mut self, _cx:&mut Cx) -> Walk{
176        self.walk
177    }
178    fn area(&self)->Area{self.area}
179
180    fn redraw(&mut self, cx: &mut Cx){
181        self.area.redraw(cx)
182    }
183
184    fn find_widgets(&self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
185        if let Some((_, widget)) = self.items.get(&path[0]) {
186            if path.len()>1 {
187                widget.find_widgets(&path[1..], cached, results);
188            }
189            else {
190                results.push(widget.clone());
191            }
192        }
193        else {
194            for (_, widget) in self.items.values() {
195                widget.find_widgets(path, cached, results);
196            }
197        }
198    }
199    fn uid_to_widget(&self, uid:WidgetUid)->WidgetRef{
200        for (_, widget) in self.items.values() {
201            let x = widget.uid_to_widget(uid);
202            if !x.is_empty(){return x}
203        }
204        WidgetRef::empty()
205    }
206}
207
208pub struct DockVisibleItemIterator<'a> {
209    stack: &'a mut Vec<(LiveId, usize)>,
210    dock_items: &'a HashMap<LiveId, DockItem>,
211    items: &'a ComponentMap<LiveId, (LiveId, WidgetRef)>,
212}
213
214impl<'a> Iterator for DockVisibleItemIterator<'a> {
215    // We can refer to this type using Self::Item
216    type Item = (LiveId, WidgetRef);
217    fn next(&mut self) -> Option<Self::Item> {
218        // alright so lets fetch the item on the top of the stack
219        while let Some((item_id, index)) = self.stack.pop() {
220            if let Some(dock_item) = self.dock_items.get(&item_id) {
221                match dock_item {
222                    DockItem::Splitter {a, b, ..} => {
223                        if index == 0 {
224                            self.stack.push((item_id, 1));
225                            self.stack.push((*a, 0));
226                        }
227                        else {
228                            self.stack.push((*b, 0));
229                        }
230                    }
231                    DockItem::Tabs {tabs, selected, ..} => {
232                        if let Some(tab_id) = tabs.get(*selected) {
233                            self.stack.push((*tab_id, 0));
234                        }
235                    }
236                    DockItem::Tab {..} => {
237                        if let Some((_, widget)) = self.items.get(&item_id) {
238                            return Some((item_id, widget.clone()))
239                        }
240                    }
241                }
242            }
243        }
244        None
245    }
246}
247
248struct TabBarWrap {
249    tab_bar: TabBar,
250    contents_draw_list: DrawList2d,
251    contents_rect: Rect
252}
253
254#[derive(Copy, Debug, Clone)]
255enum DrawStackItem {
256    Invalid,
257    SplitLeft {id: LiveId},
258    SplitRight {id: LiveId},
259    SplitEnd {id: LiveId},
260    Tabs {id: LiveId},
261    TabLabel {id: LiveId, index: usize},
262    Tab {id: LiveId},
263    TabContent {id: LiveId}
264}
265
266impl DrawStackItem {
267    fn from_dock_item(id: LiveId, dock_item: Option<&DockItem>) -> Self {
268        match dock_item {
269            None => DrawStackItem::Invalid,
270            Some(DockItem::Splitter {..}) => {
271                DrawStackItem::SplitLeft {id}
272            }
273            Some(DockItem::Tabs {..}) => {
274                DrawStackItem::Tabs {id}
275            }
276            Some(DockItem::Tab {..}) => {
277                DrawStackItem::Tab {id}
278            }
279        }
280    }
281}
282
283#[derive(Clone, Debug, DefaultNone)]
284pub enum DockAction {
285    SplitPanelChanged {panel_id: LiveId, axis: SplitterAxis, align: SplitterAlign},
286    TabWasPressed(LiveId),
287    TabCloseWasPressed(LiveId),
288    ShouldTabStartDrag(LiveId),
289    Drag(DragHitEvent),
290    Drop(DropHitEvent),
291    None
292}
293
294
295#[derive(Clone, Copy, Debug, PartialEq)]
296pub struct DropPosition {
297    part: DropPart,
298    rect: Rect,
299    id: LiveId
300}
301
302#[derive(Clone, Copy, Debug, PartialEq)]
303pub enum DropPart {
304    Left,
305    Right,
306    Top,
307    Bottom,
308    Center,
309    TabBar,
310    Tab
311}
312
313#[derive(Clone, Debug, Live, LiveHook, SerRon, DeRon)]
314#[live_ignore]
315pub enum DockItem {
316    #[live {axis: SplitterAxis::Vertical, align: SplitterAlign::Weighted(0.5), a: LiveId(0), b: LiveId(0)}]
317    Splitter {
318        axis: SplitterAxis,
319        align: SplitterAlign,
320        a: LiveId,
321        b: LiveId
322    },
323    #[live {tabs: vec![], selected: 0, closable: true}]
324    Tabs {
325        tabs: Vec<LiveId>,
326        selected: usize,
327        closable: bool
328    },
329    #[pick {name: "Tab".to_string(), kind: LiveId(0), template: live_id!(PermanentTab)}]
330    Tab {
331        name: String,
332        template: LiveId,
333        kind: LiveId
334    }
335}
336
337
338
339impl LiveHook for Dock {
340    fn apply_value_instance(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) -> usize {
341        let id = nodes[index].id;
342        match apply.from {
343            ApplyFrom::NewFromDoc {file_id} | ApplyFrom::UpdateFromDoc {file_id,..} => {
344                if nodes[index].origin.has_prop_type(LivePropType::Instance) {
345                    if nodes[index].value.is_enum() {
346                        // only do this in newfromdoc
347                        if apply.from.is_new_from_doc(){
348                            let mut dock_item = DockItem::new(cx);
349                            let index = dock_item.apply(cx, apply, index, nodes);
350                            self.dock_items.insert(id, dock_item);
351                            return index;
352                        }
353                        else{
354                            return nodes.skip_node(index)
355                        }
356                    }
357                    else {
358                        let live_ptr = cx.live_registry.borrow().file_id_index_to_live_ptr(file_id, index);
359                        self.templates.insert(id, live_ptr);
360                        // lets apply this thing over all our childnodes with that template
361                        for (kind, node) in self.items.values_mut() {
362                            if *kind == id {
363                                node.apply(cx, apply, index, nodes);
364                            }
365                        }
366                    }
367                }
368                else {
369                    cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
370                }
371            }
372            _ => ()
373        }
374        nodes.skip_node(index)
375    }
376
377    // alright lets update our tabs and splitters as well
378    fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
379        if let Some(index) = nodes.child_by_name(index, live_id!(tab_bar).as_field()) {
380            for tab_bar in self.tab_bars.values_mut() {
381                tab_bar.tab_bar.apply(cx, apply, index, nodes);
382            }
383        }
384        if let Some(index) = nodes.child_by_name(index, live_id!(splitter).as_field()) {
385            for splitter in self.splitters.values_mut() {
386                splitter.apply(cx, apply, index, nodes);
387            }
388        }
389    }
390
391    fn after_new_from_doc(&mut self, cx: &mut Cx) {
392        self.create_all_items(cx);
393    }
394}
395
396impl Dock {
397    pub fn unique_id(&self, base:u64)->LiveId{
398        let mut id = LiveId(base);
399        let mut i = 0u32;
400        while self.dock_items.get(&id).is_some(){
401            id = id.bytes_append(&i.to_be_bytes());
402            i += 1;
403        }
404        return id;
405    }
406    
407    fn create_all_items(&mut self, cx: &mut Cx) {
408        // make sure our items exist
409        let mut items = Vec::new();
410        for (item_id, item) in self.dock_items.iter() {
411            if let DockItem::Tab {kind, ..} = item {
412                items.push((*item_id, *kind));
413            }
414        }
415        for (item_id, kind) in items {
416            self.item_or_create(cx, item_id, kind);
417        }
418    }
419
420    fn begin(&mut self, cx: &mut Cx2d, walk: Walk) {
421        cx.begin_turtle(walk, self.layout);
422        //self.drop_zones.clear();
423    }
424
425    fn end(&mut self, cx: &mut Cx2d) {
426
427        if self.drop_target_draw_list.begin(cx, Walk::default()).is_redrawing() {
428            if let Some(pos) = &self.drop_state {
429                self.drag_target_preview.draw_abs(cx, pos.rect);
430            }
431            self.drop_target_draw_list.end(cx);
432        }
433
434        self.tab_bars.retain_visible();
435        self.splitters.retain_visible();
436
437        // lets draw the corners here
438        for splitter in self.splitters.values() {
439            self.round_corner.draw_corners(cx, splitter.area_a().rect(cx));
440            self.round_corner.draw_corners(cx, splitter.area_b().rect(cx));
441        }
442        self.round_corner.draw_corners(cx, cx.turtle().rect());
443
444        cx.end_turtle_with_area(&mut self.area);
445    }
446
447    fn find_drop_position(&self, cx: &Cx, abs: DVec2) -> Option<DropPosition> {
448        for (tab_bar_id, tab_bar) in self.tab_bars.iter() {
449            let rect = tab_bar.contents_rect;
450            if let Some((tab_id, rect)) = tab_bar.tab_bar.is_over_tab(cx, abs) {
451                return Some(DropPosition {
452                    part: DropPart::Tab,
453                    id: tab_id,
454                    rect
455                })
456            }
457            else if let Some(rect) = tab_bar.tab_bar.is_over_tab_bar(cx, abs) {
458                return Some(DropPosition {
459                    part: DropPart::TabBar,
460                    id: *tab_bar_id,
461                    rect
462                })
463            }
464            else if rect.contains(abs) {
465                let top_left = rect.pos;
466                let bottom_right = rect.pos + rect.size;
467                if (abs.x - top_left.x) / rect.size.x < 0.1 {
468                    return Some(DropPosition {
469                        part: DropPart::Left,
470                        id: *tab_bar_id,
471                        rect: Rect {
472                            pos: rect.pos,
473                            size: DVec2 {
474                                x: rect.size.x / 2.0,
475                                y: rect.size.y,
476                            },
477                        }
478                    })
479                } else if (bottom_right.x - abs.x) / rect.size.x < 0.1 {
480                    return Some(DropPosition {
481                        part: DropPart::Right,
482                        id: *tab_bar_id,
483                        rect: Rect {
484                            pos: DVec2 {
485                                x: rect.pos.x + rect.size.x / 2.0,
486                                y: rect.pos.y,
487                            },
488                            size: DVec2 {
489                                x: rect.size.x / 2.0,
490                                y: rect.size.y,
491                            },
492                        }
493                    })
494                } else if (abs.y - top_left.y) / rect.size.y < 0.1 {
495                    return Some(DropPosition {
496                        part: DropPart::Top,
497                        id: *tab_bar_id,
498                        rect: Rect {
499                            pos: rect.pos,
500                            size: DVec2 {
501                                x: rect.size.x,
502                                y: rect.size.y / 2.0,
503                            },
504                        }
505                    })
506                } else if (bottom_right.y - abs.y) / rect.size.y < 0.1 {
507                    return Some(DropPosition {
508                        part: DropPart::Bottom,
509                        id: *tab_bar_id,
510                        rect: Rect {
511                            pos: DVec2 {
512                                x: rect.pos.x,
513                                y: rect.pos.y + rect.size.y / 2.0,
514                            },
515                            size: DVec2 {
516                                x: rect.size.x,
517                                y: rect.size.y / 2.0,
518                            },
519                        }
520                    })
521                } else {
522                    return Some(DropPosition {
523                        part: DropPart::Center,
524                        id: *tab_bar_id,
525                        rect
526                    })
527                }
528            }
529        }
530        None
531    }
532
533    pub fn item(&mut self, entry_id: LiveId) -> Option<WidgetRef> {
534        if let Some(entry) = self.items.get(&entry_id) {
535            return Some(entry.1.clone())
536        }
537        None
538    }
539
540    pub fn item_or_create(&mut self, cx: &mut Cx, entry_id: LiveId, template: LiveId) -> Option<WidgetRef> {
541        if let Some(ptr) = self.templates.get(&template) {
542            let entry = self.items.get_or_insert(cx, entry_id, | cx | {
543                (template, WidgetRef::new_from_ptr(cx, Some(*ptr)))
544            });
545            Some(entry.1.clone())
546        }
547        else {
548            warning!("Template not found: {template}. Did you add it to the <Dock> instance in `live_design!{{}}`?");
549            None
550        }
551    }
552
553    pub fn items(&mut self) -> &ComponentMap<LiveId, (LiveId, WidgetRef)> {
554        &self.items
555    }
556
557    pub fn visible_items(&mut self) -> DockVisibleItemIterator {
558        self.dock_item_iter_stack.clear();
559        self.dock_item_iter_stack.push((live_id!(root), 0));
560        DockVisibleItemIterator {
561            stack: &mut self.dock_item_iter_stack,
562            dock_items: &self.dock_items,
563            items: &self.items
564        }
565    }
566
567    fn set_parent_split(&mut self, what_item: LiveId, replace_item: LiveId) {
568        for item in self.dock_items.values_mut() {
569            match item {
570                DockItem::Splitter {a, b, ..} => {
571                    if what_item == *a {
572                        *a = replace_item;
573                        return
574                    }
575                    else if what_item == *b {
576                        *b = replace_item;
577                        return
578                    }
579                }
580                _ => ()
581            }
582        }
583    }
584
585    fn redraw_item(&mut self, cx: &mut Cx, what_item_id: LiveId) {
586        if let Some(tab_bar) = self.tab_bars.get_mut(&what_item_id) {
587            tab_bar.contents_draw_list.redraw(cx);
588        }
589        for (item_id, (_kind, item)) in self.items.iter_mut() {
590            if *item_id == what_item_id {
591                item.redraw(cx);
592            }
593        }
594    }
595
596    fn unsplit_tabs(&mut self, cx: &mut Cx, tabs_id: LiveId) {
597        self.needs_save = true;
598        for (splitter_id, item) in self.dock_items.iter_mut() {
599            match *item {
600                DockItem::Splitter {a, b, ..} => {
601                    let splitter_id = *splitter_id;
602                    if tabs_id == a {
603                        self.set_parent_split(splitter_id, b);
604                        self.dock_items.remove(&splitter_id);
605                        self.dock_items.remove(&tabs_id);
606                        self.redraw_item(cx, b);
607                        return
608                    }
609                    else if tabs_id == b {
610                        self.set_parent_split(splitter_id, a);
611                        self.dock_items.remove(&splitter_id);
612                        self.dock_items.remove(&tabs_id);
613                        self.redraw_item(cx, a);
614                        return
615                    }
616                }
617                _ => ()
618            }
619        }
620    }
621
622    fn select_tab(&mut self, cx: &mut Cx, tab_id: LiveId) {
623        self.needs_save = true;
624        for (tabs_id, item) in self.dock_items.iter_mut() {
625            match item {
626                DockItem::Tabs {tabs, selected, ..} => if let Some(pos) = tabs.iter().position( | v | *v == tab_id) {
627                    *selected = pos;
628                    // ok now lets redraw the area of the tab
629                    if let Some(tab_bar) = self.tab_bars.get(&tabs_id) {
630                        tab_bar.contents_draw_list.redraw(cx);
631                    }
632                }
633                _ => ()
634            }
635        }
636    }
637
638    fn set_tab_title(&mut self, cx: &mut Cx, tab_id: LiveId, new_name:String) {
639        self.needs_save = true;
640        if let Some(DockItem::Tab{name, ..}) = self.dock_items.get_mut(&tab_id){
641            *name = new_name;
642            self.redraw_tab(cx, tab_id);
643        }
644    }
645
646    fn redraw_tab(&mut self, cx: &mut Cx, tab_id: LiveId) {
647        for (tabs_id, item) in self.dock_items.iter_mut() {
648            match item {
649                DockItem::Tabs {tabs, ..} => if let Some(_) = tabs.iter().position( | v | *v == tab_id) {
650                    if let Some(tab_bar) = self.tab_bars.get(&tabs_id) {
651                        tab_bar.contents_draw_list.redraw(cx);
652                    }
653                }
654                _ => ()
655            }
656        }
657    }
658
659    fn find_tab_bar_of_tab(&mut self, tab_id: LiveId) -> Option<(LiveId, usize)> {
660        for (tabs_id, item) in self.dock_items.iter_mut() {
661            match item {
662                DockItem::Tabs {tabs, ..} => if let Some(pos) = tabs.iter().position( | v | *v == tab_id) {
663                    return Some((*tabs_id, pos))
664                }
665                _ => ()
666            }
667        }
668        None
669    }
670
671    fn close_tab(&mut self, cx: &mut Cx, tab_id: LiveId, keep_item: bool) -> Option<LiveId> {
672        self.needs_save = true;
673        // ok so we have to find the tab id in our tab bars / tabs and remove it
674        // if we are the last tab we need to remove a splitter
675        for (tabs_id, item) in self.dock_items.iter_mut() {
676            match item {
677                DockItem::Tabs {tabs, selected, closable} => if let Some(pos) = tabs.iter().position( | v | *v == tab_id) {
678                    // remove from the tabs array
679                    let tabs_id = *tabs_id;
680                    tabs.remove(pos);
681                    if tabs.len() == 0 { // unsplit
682                        if *closable {
683                            self.unsplit_tabs(cx, tabs_id);
684                        }
685                        if !keep_item {
686                            self.dock_items.remove(&tab_id);
687                            self.items.remove(&tab_id);
688                        }
689                        self.area.redraw(cx);
690                        return None
691                    }
692                    else {
693                        let next_tab = if *selected >= tabs.len() {tabs[*selected - 1]} else {tabs[*selected]};
694                        self.select_tab(cx, next_tab);
695                        if !keep_item {
696                            self.dock_items.remove(&tab_id);
697                            self.items.remove(&tab_id);
698                        }
699                        self.area.redraw(cx);
700                        return Some(tabs_id)
701                    }
702                }
703                _ => ()
704            }
705        }
706        None
707    }
708
709    fn check_drop_is_noop(&mut self, tab_id: LiveId, item_id: LiveId) -> bool {
710        // ok so we have to find the tab id in our tab bars / tabs and remove it
711        // if we are the last tab we need to remove a splitter
712        for (tabs_id, item) in self.dock_items.iter_mut() {
713            match item {
714                DockItem::Tabs {tabs, ..} => if let Some(_) = tabs.iter().position( | v | *v == tab_id) {
715                    if *tabs_id == item_id && tabs.len() == 1 {
716                        return true
717                    }
718                }
719                _ => ()
720            }
721        }
722        false
723    }
724
725    fn handle_drop(&mut self, cx: &mut Cx, abs: DVec2, item: LiveId, is_move: bool) -> bool {
726        if let Some(pos) = self.find_drop_position(cx, abs) {
727            self.needs_save = true;
728            // ok now what
729            // we have a pos
730            match pos.part {
731                DropPart::Left | DropPart::Right | DropPart::Top | DropPart::Bottom => {
732                    if is_move {
733                        if self.check_drop_is_noop(item, pos.id) {
734                            return false
735                        }
736                        self.close_tab(cx, item, true);
737                    }
738                    let new_tabs = self.unique_id(self.dock_items.len() as u64);
739                    self.dock_items.insert(new_tabs, DockItem::Tabs {
740                        tabs: vec![item],
741                        closable: true,
742                        selected: 0,
743                    });
744                    let new_split = self.unique_id(self.dock_items.len() as u64);
745                    self.set_parent_split(pos.id, new_split);
746                    self.dock_items.insert(new_split, match pos.part {
747                        DropPart::Left => DockItem::Splitter {
748                            axis: SplitterAxis::Horizontal,
749                            align: SplitterAlign::Weighted(0.5),
750                            a: new_tabs,
751                            b: pos.id,
752                        },
753                        DropPart::Right => DockItem::Splitter {
754                            axis: SplitterAxis::Horizontal,
755                            align: SplitterAlign::Weighted(0.5),
756                            a: pos.id,
757                            b: new_tabs
758                        },
759                        DropPart::Top => DockItem::Splitter {
760                            axis: SplitterAxis::Vertical,
761                            align: SplitterAlign::Weighted(0.5),
762                            a: new_tabs,
763                            b: pos.id,
764                        },
765                        DropPart::Bottom => DockItem::Splitter {
766                            axis: SplitterAxis::Vertical,
767                            align: SplitterAlign::Weighted(0.5),
768                            a: pos.id,
769                            b: new_tabs,
770                        },
771                        _ => panic!()
772                    });
773                    
774                    return true
775                }
776                DropPart::Center => {
777                    if is_move {
778                        if self.check_drop_is_noop(item, pos.id) {
779                            return false
780                        }
781                        self.close_tab(cx, item, true);
782                    }
783                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get_mut(&pos.id) {
784                        tabs.push(item);
785                        *selected = tabs.len() - 1;
786                        if let Some(tab_bar) = self.tab_bars.get(&pos.id) {
787                            tab_bar.contents_draw_list.redraw(cx);
788                        }
789                    }
790                    return true
791                }
792                DropPart::TabBar => {
793                    if is_move {
794                        if self.check_drop_is_noop(item, pos.id) {
795                            return false
796                        }
797                        self.close_tab(cx, item, true);
798                    }
799                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get_mut(&pos.id) {
800                        tabs.push(item);
801                        *selected = tabs.len() - 1;
802                        if let Some(tab_bar) = self.tab_bars.get(&pos.id) {
803                            tab_bar.contents_draw_list.redraw(cx);
804                        }
805                    }
806                    return true
807                }
808                // insert the tab
809                DropPart::Tab => {
810                    if is_move {
811                        if pos.id == item {
812                            return false
813                        }
814                        self.close_tab(cx, item, true);
815                    }
816                    let (tab_bar_id, pos) = self.find_tab_bar_of_tab(pos.id).unwrap();
817                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get_mut(&tab_bar_id) {
818                        //if let Some(pos) = tabs.iter().position( | v | *v == pos.id) {
819                            let old = tabs[pos];
820                            tabs[pos] = item;
821                            tabs.push(old);
822                            *selected = pos;
823                            if let Some(tab_bar) = self.tab_bars.get(&tab_bar_id) {
824                                tab_bar.contents_draw_list.redraw(cx);
825                            }
826                        //}
827                    }
828                    return true
829                }
830            }
831        }
832        false
833    }
834
835    fn drop_create(&mut self, cx: &mut Cx, abs: DVec2, item: LiveId, kind: LiveId, name: String, template:LiveId) {
836        // lets add a tab
837        if self.handle_drop(cx, abs, item, false) {
838            self.needs_save = true;
839            self.dock_items.insert(item, DockItem::Tab {
840                name,
841                template,
842                kind
843            });
844            self.item_or_create(cx, item, kind);
845            self.select_tab(cx, item);
846            self.area.redraw(cx);
847        }
848    }
849
850    fn drop_clone(&mut self, cx: &mut Cx, abs: DVec2, item: LiveId, new_item: LiveId, template:LiveId) {
851        // lets add a tab
852        if let Some(DockItem::Tab {name, kind, ..}) = self.dock_items.get(&item) {
853            let name = name.clone();
854            let kind = kind.clone();
855            if self.handle_drop(cx, abs, new_item, false) {
856                self.needs_save = true;
857                self.dock_items.insert(new_item, DockItem::Tab {
858                    name,
859                    template,
860                    kind
861                });
862                self.item_or_create(cx, new_item, kind);
863                self.select_tab(cx, new_item);
864            }
865        }
866    }
867
868    fn create_and_select_tab(&mut self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, template:LiveId, insert_after:Option<usize>)->Option<WidgetRef> {
869        if let Some(widgetref) = self.items.get(&item).map(|(_, w)| w.clone()) {
870            self.select_tab(cx, item);
871            Some(widgetref)
872        }
873        else {
874            let ret =self.create_tab(cx, parent, item, kind, name, template, insert_after);
875            self.select_tab(cx, item);
876            ret
877        }
878    }
879
880    fn create_tab(&mut self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, template:LiveId, insert_after:Option<usize>)->Option<WidgetRef> {
881        if let Some(DockItem::Tabs {tabs, ..}) = self.dock_items.get_mut(&parent) {
882            if let Some(after) = insert_after{
883                tabs.insert(after+1, item);
884            }
885            else{
886                tabs.push(item);
887            }
888            self.needs_save = true;
889            self.dock_items.insert(item, DockItem::Tab {
890                name,
891                template,
892                kind
893            });
894            self.item_or_create(cx, item, kind)
895        }
896        else{
897            None
898        }
899    }
900
901    /// Performs an in-place replacement of the inner widget (within an existing tab)
902    /// with a completely new widget of a different kind.
903    /// The type of the tab itself is not changed, but the widget inside of it is.
904    ///
905    /// Optionally, you can give the tab a new name and select it.
906    ///
907    /// ## Arguments
908    /// * `cx`: A mutable reference to the Cx context.
909    /// * `tab_item_id`: The ID of the tab to be replaced.
910    /// * `new_kind`: The new widget that should be shown inside of the tab.
911    /// * `new_name`: An optional new name for the tab.
912    /// * `select`: A boolean indicating whether to select the new tab after replacement.
913    ///   If `false`, the tab will be not be explicitly selected, but of course
914    ///   if it was already selected, it will remain selected.
915    ///
916    /// ## Return
917    /// * Returns `None` if the `tab_item_id` does not exist in the dock
918    ///   or if the `new_kind` template was not listed in the Dock's live DSL.
919    /// * Returns `Some((new_widget, true))` if the existing tab's inner widget
920    ///   was successfully replaced with the new one.
921    ///   * Returns `Some((existing_widget, false))`` if the `new_kind` is the same
922    ///     as existing tab's inner widget kind, meaning that no replacement occurred.
923    fn replace_tab(
924        &mut self,
925        cx: &mut Cx,
926        tab_item_id: LiveId,
927        new_kind: LiveId,
928        new_name: Option<String>,
929        select: bool,
930    ) -> Option<(WidgetRef, bool)> {
931        // Check that the tab item ID already exists in the dock items.
932        let Some(DockItem::Tab { name, template: _, kind }) = self.dock_items.get_mut(&tab_item_id) else {
933            return None;
934        };
935        // Before modifying the existing tab, ensure that the `new_kind` template exists
936        // and that we can instantiate a new widget from it.
937        if let Some(ptr) = self.templates.get(&new_kind) {
938            let Some((existing_kind, existing_widgetref)) = self.items.get_mut(&tab_item_id) else {
939                return None;
940            };
941            // If the existing tab had the same inner widget kind,
942            // then no changes are needed and we can just reuse it.
943            let (new_widgetref, was_replaced) = if *existing_kind == new_kind {
944                (existing_widgetref.clone(), false)
945            } else {
946                // If the existing tab had a different inner widget kind,
947                // we need to replace it with the new one
948                // and update both the `items` and `dock_items` entries.
949                *existing_kind = new_kind;
950                *existing_widgetref = WidgetRef::new_from_ptr(cx, Some(*ptr));
951                *kind = new_kind;
952                (existing_widgetref.clone(), true)
953            };
954
955            // Optionally update the tab's name and select it.
956            if let Some(new_name) = new_name {
957                *name = new_name;
958            }
959            if select {
960                self.select_tab(cx, tab_item_id);
961            }
962            self.needs_save = true;
963            self.redraw_tab(cx, tab_item_id);
964            Some((new_widgetref, was_replaced))
965        }
966        else {
967            warning!("Template not found: {new_kind}. Did you add it to the <Dock> instance in `live_design!{{}}`?");
968            None
969        }
970    }
971
972    pub fn drawing_item_id(&self) -> Option<LiveId> {
973        if let Some(stack) = self.draw_state.as_ref() {
974            match stack.last() {
975                Some(DrawStackItem::Tab {id}) => {
976                    return Some(*id)
977                }
978                _ => ()
979            }
980        }
981        None
982    }
983
984    pub fn load_state(&mut self, cx: &mut Cx, dock_items: HashMap<LiveId, DockItem>,) {
985        // NOTE if you have a collision problem
986        // Don't use LiveId::unique() as they are only unique when you run it.
987        // Use dock.unique_tab_id(somerandombase) instead
988        self.dock_items = dock_items;
989        self.items.clear();
990        self.tab_bars.clear();
991        self.splitters.clear();
992        self.area.redraw(cx);
993        self.create_all_items(cx);
994    }
995}
996
997
998impl Widget for Dock {
999
1000    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope:&mut Scope) {
1001        // call handle on all tab bars, splitters,
1002        let uid = self.widget_uid();
1003        let dock_items = &mut self.dock_items;
1004        for (panel_id, splitter) in self.splitters.iter_mut() {
1005            for action in cx.capture_actions(|cx| splitter.handle_event(cx, event, scope)) {
1006                // alright so here we need to redraw the left/right area.. how?
1007
1008                match action.as_widget_action().cast() {
1009                    SplitterAction::Changed {axis, align} => {
1010                        // lets move the splitter
1011                        if let Some(DockItem::Splitter {axis: _axis, align: _align, ..}) = dock_items.get_mut(&panel_id) {
1012                            *_axis = axis;
1013                            *_align = align;
1014                        }
1015                        self.needs_save = true;
1016                        cx.widget_action(uid, &scope.path, DockAction::SplitPanelChanged {panel_id: *panel_id, axis, align});
1017                    },
1018                    _ => ()
1019                }
1020            };
1021        }
1022        for (panel_id, tab_bar) in self.tab_bars.iter_mut() {
1023            let contents_view = &mut tab_bar.contents_draw_list;
1024            for action in cx.capture_actions(|cx| tab_bar.tab_bar.handle_event(cx, event, scope)) {
1025                match action.as_widget_action().cast() {
1026                    TabBarAction::ShouldTabStartDrag(item) => cx.widget_action(uid, &scope.path, DockAction::ShouldTabStartDrag(item)),
1027                    TabBarAction::TabWasPressed(tab_id) => {
1028                        self.needs_save = true;
1029                        if let Some(DockItem::Tabs {tabs, selected, ..}) = dock_items.get_mut(&panel_id) {
1030                            if let Some(sel) = tabs.iter().position( | v | *v == tab_id) {
1031                                *selected = sel;
1032                                contents_view.redraw(cx);
1033                                cx.widget_action(uid, &scope.path, DockAction::TabWasPressed(tab_id))
1034                            }
1035                            else {
1036                                log!("Cannot find tab {}", tab_id.0);
1037                            }
1038                        }
1039                    }
1040                    TabBarAction::TabCloseWasPressed(tab_id) => {
1041                        cx.widget_action(uid, &scope.path, DockAction::TabCloseWasPressed(tab_id));
1042                        self.needs_save = true;
1043                    }
1044                    TabBarAction::None=>()
1045                }
1046            };
1047        }
1048        if event.requires_visibility(){
1049            for (id, item) in self.visible_items(){
1050                scope.with_id(id, |scope|{
1051                    item.handle_event(cx, event, scope);
1052                });
1053            }
1054        }
1055        else{
1056            for (id,(_templ_id, item)) in self.items.iter_mut() {
1057                scope.with_id(*id, |scope|{
1058                item.handle_event(cx, event, scope);
1059                });
1060            }
1061        }
1062    
1063        if let Event::DragEnd = event {
1064            // end our possible dragstate
1065            self.drop_state = None;
1066            self.drop_target_draw_list.redraw(cx);
1067        }
1068
1069        // alright lets manage the drag areas
1070        match event.drag_hits(cx, self.area) {
1071            DragHit::Drag(f) => {
1072                self.drop_state = None;
1073                self.drop_target_draw_list.redraw(cx);
1074                match f.state {
1075                    DragState::In | DragState::Over => {
1076                        cx.widget_action(uid, &scope.path, DockAction::Drag(f.clone()))
1077                    }
1078                    DragState::Out => {}
1079                }
1080            }
1081            DragHit::Drop(f) => {
1082                self.needs_save = true;
1083                self.drop_state = None;
1084                self.drop_target_draw_list.redraw(cx);
1085                cx.widget_action(uid, &scope.path, DockAction::Drop(f.clone()))
1086            }
1087            _ => {}
1088        }
1089    }
1090
1091    fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk: Walk) -> DrawStep {
1092        if self.draw_state.begin_with(cx, &self.dock_items, | _, dock_items | {
1093            let id = live_id!(root);
1094            vec![DrawStackItem::from_dock_item(id, dock_items.get(&id))]
1095        }) {
1096            self.begin(cx, walk);
1097        }
1098
1099        while let Some(stack) = self.draw_state.as_mut() {
1100            match stack.pop() {
1101                Some(DrawStackItem::SplitLeft {id}) => {
1102                    stack.push(DrawStackItem::SplitRight {id});
1103                    // top becomes splitleft
1104                    let splitter = self.splitter;
1105                    let splitter = self.splitters.get_or_insert(cx, id, | cx | {
1106                        Splitter::new_from_ptr(cx, splitter)
1107                    });
1108                    if let Some(DockItem::Splitter {axis, align, a, ..}) = self.dock_items.get(&id) {
1109                        splitter.set_axis(*axis);
1110                        splitter.set_align(*align);
1111                        splitter.begin(cx, Walk::default());
1112                        stack.push(DrawStackItem::from_dock_item(*a, self.dock_items.get(&a)));
1113                        continue;
1114                    }
1115                    else {panic!()}
1116                }
1117                Some(DrawStackItem::SplitRight {id}) => {
1118                    // lets create the 4 split dropzones of this splitter
1119                    stack.push(DrawStackItem::SplitEnd {id});
1120                    let splitter = self.splitters.get_mut(&id).unwrap();
1121                    splitter.middle(cx);
1122                    if let Some(DockItem::Splitter {b, ..}) = self.dock_items.get(&id) {
1123                        stack.push(DrawStackItem::from_dock_item(*b, self.dock_items.get(&b)));
1124                        continue;
1125                    }
1126                    else {panic!()}
1127                }
1128                Some(DrawStackItem::SplitEnd {id}) => {
1129                    // 4 more dropzones
1130                    let splitter = self.splitters.get_mut(&id).unwrap();
1131                    splitter.end(cx);
1132                }
1133                Some(DrawStackItem::Tabs {id}) => {
1134                    if let Some(DockItem::Tabs {selected, ..}) = self.dock_items.get(&id) {
1135                        // lets draw the tabs
1136                        let tab_bar = self.tab_bar;
1137                        let tab_bar = self.tab_bars.get_or_insert(cx, id, | cx | {
1138                            TabBarWrap {
1139                                tab_bar: TabBar::new_from_ptr(cx, tab_bar),
1140                                contents_draw_list: DrawList2d::new(cx),
1141                                contents_rect: Rect::default(),
1142                                //full_rect: Rect::default(),
1143                            }
1144                        });
1145                        let walk = tab_bar.tab_bar.walk(cx);
1146                        tab_bar.tab_bar.begin(cx, Some(*selected), walk);
1147                        stack.push(DrawStackItem::TabLabel {id, index: 0});
1148                    }
1149                    else {panic!()}
1150                }
1151                Some(DrawStackItem::TabLabel {id, index}) => {
1152                    if let Some(DockItem::Tabs {tabs, selected, ..}) = self.dock_items.get(&id) {
1153                        let tab_bar = self.tab_bars.get_mut(&id).unwrap();
1154                        if index < tabs.len() {
1155                            if let Some(DockItem::Tab {name, template, ..}) = self.dock_items.get(&tabs[index]) {
1156                                tab_bar.tab_bar.draw_tab(cx, tabs[index].into(), name, *template);
1157                            }
1158                            stack.push(DrawStackItem::TabLabel {id, index: index + 1});
1159                        }
1160                        else {
1161                            tab_bar.tab_bar.end(cx);
1162                            tab_bar.contents_rect = cx.turtle().rect();
1163                            if tabs.len()>0 && tab_bar.contents_draw_list.begin(cx, Walk::default()).is_redrawing() {
1164                                stack.push(DrawStackItem::TabContent {id});
1165                                if *selected < tabs.len() {
1166                                    stack.push(DrawStackItem::Tab {id: tabs[*selected]});
1167                                }
1168                            }
1169                        }
1170                    }
1171                    else {panic!()}
1172                }
1173                Some(DrawStackItem::Tab {id}) => {
1174                    stack.push(DrawStackItem::Tab {id});
1175                    if let Some(DockItem::Tab {kind, ..}) = self.dock_items.get(&id) {
1176                        if let Some(ptr) = self.templates.get(&kind) {
1177                            let (_, entry) = self.items.get_or_insert(cx, id, | cx | {
1178                                (*kind, WidgetRef::new_from_ptr(cx, Some(*ptr)))
1179                            });
1180                            scope.with_id(id, |scope|{
1181                                entry.draw(cx, scope)
1182                            })?;
1183                        }
1184                    }
1185                    stack.pop();
1186                }
1187                Some(DrawStackItem::TabContent {id}) => {
1188                    if let Some(DockItem::Tabs {..}) = self.dock_items.get(&id) {
1189                        let tab_bar = self.tab_bars.get_mut(&id).unwrap();
1190                        // lets create the full dropzone for this contentview
1191
1192                        tab_bar.contents_draw_list.end(cx);
1193                    }
1194                    else {panic!()}
1195                }
1196                Some(DrawStackItem::Invalid) => {}
1197                None => {
1198                    break
1199                }
1200            }
1201        }
1202
1203        self.end(cx);
1204        self.draw_state.end();
1205
1206        DrawStep::done()
1207    }
1208}
1209
1210impl DockRef {
1211    pub fn item(&self, entry_id: LiveId) -> WidgetRef {
1212        if let Some(mut dock) = self.borrow_mut() {
1213            if let Some(item) = dock.item(entry_id) {
1214                return item
1215            }
1216        }
1217        WidgetRef::empty()
1218    }
1219
1220    pub fn item_or_create(&self, cx: &mut Cx, entry_id: LiveId, template: LiveId) -> Option<WidgetRef> {
1221        if let Some(mut dock) = self.borrow_mut() {
1222            return dock.item_or_create(cx, entry_id, template);
1223        }
1224        None
1225    }
1226
1227    pub fn close_tab(&self, cx: &mut Cx, tab_id: LiveId) {
1228        if let Some(mut dock) = self.borrow_mut() {
1229            dock.close_tab(cx, tab_id, false);
1230        }
1231    }
1232
1233    // user wants to drag, set dh accordingly
1234    pub fn accept_drag(&self, cx: &mut Cx, dh: DragHitEvent, dr: DragResponse) {
1235        if let Some(mut dock) = self.borrow_mut() {
1236            if let Some(pos) = dock.find_drop_position(cx, dh.abs) {
1237                *dh.response.lock().unwrap() = dr;
1238                dock.drop_state = Some(pos);
1239            }
1240            else {
1241                dock.drop_state = None;
1242            }
1243        }
1244    }
1245
1246    pub fn drawing_item_id(&self) -> Option<LiveId> {
1247        if let Some(dock) = self.borrow() {
1248            return dock.drawing_item_id();
1249        }
1250        None
1251    }
1252
1253    pub fn drop_clone(&self, cx: &mut Cx, abs: DVec2, old_item: LiveId, new_item: LiveId, template:LiveId) {
1254        if let Some(mut dock) = self.borrow_mut() {
1255            dock.drop_clone(cx, abs, old_item, new_item, template);
1256        }
1257    }
1258
1259    pub fn drop_move(&self, cx: &mut Cx, abs: DVec2, item: LiveId) {
1260        if let Some(mut dock) = self.borrow_mut() {
1261            dock.handle_drop(cx, abs, item, true);
1262        }
1263    }
1264
1265    pub fn drop_create(&self, cx: &mut Cx, abs: DVec2, item: LiveId, kind: LiveId, name: String, template:LiveId) {
1266        if let Some(mut dock) = self.borrow_mut() {
1267            dock.drop_create(cx, abs, item, kind, name, template);
1268        }
1269    }
1270
1271    pub fn create_and_select_tab(&self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, template:LiveId, insert_after:Option<usize>)->Option<WidgetRef> {
1272        if let Some(mut dock) = self.borrow_mut() {
1273            dock.create_and_select_tab(cx, parent, item, kind, name, template, insert_after)
1274        }
1275        else{
1276            None
1277        }
1278    }
1279
1280    pub fn create_tab(&self, cx: &mut Cx, parent: LiveId, item: LiveId, kind: LiveId, name: String, template:LiveId, insert_after:Option<usize>)->Option<WidgetRef> {
1281        if let Some(mut dock) = self.borrow_mut() {
1282            dock.create_tab(cx, parent, item, kind, name, template, insert_after)
1283        }
1284        else{
1285            None
1286        }
1287    }
1288
1289    /// See [`Dock::replace_tab()`].
1290    pub fn replace_tab(
1291        &self,
1292        cx: &mut Cx,
1293        tab_item_id: LiveId,
1294        new_kind: LiveId,
1295        new_name: Option<String>,
1296        select: bool,
1297    ) -> Option<(WidgetRef, bool)> {
1298        let Some(mut dock) = self.borrow_mut() else { return None };
1299        dock.replace_tab(cx, tab_item_id, new_kind, new_name, select)
1300    }
1301
1302    pub fn set_tab_title(&self, cx: &mut Cx, tab:LiveId, title:String) {
1303        if let Some(mut dock) = self.borrow_mut() {
1304            dock.set_tab_title(cx, tab, title);
1305        }
1306    }
1307
1308
1309    pub fn find_tab_bar_of_tab(&self, tab_id: LiveId) -> Option<(LiveId, usize)> {
1310        if let Some(mut dock) = self.borrow_mut() {
1311            return dock.find_tab_bar_of_tab(tab_id);
1312        }
1313        None
1314    }
1315
1316
1317    pub fn select_tab(&self, cx: &mut Cx, item: LiveId) {
1318        if let Some(mut dock) = self.borrow_mut() {
1319            dock.select_tab(cx, item);
1320        }
1321    }
1322
1323    pub fn redraw_tab(&self, cx: &mut Cx, tab_id: LiveId) {
1324        if let Some(mut dock) = self.borrow_mut() {
1325            dock.redraw_tab(cx, tab_id);
1326        }
1327    }
1328
1329    pub fn unique_id(&self, base:u64)->LiveId{
1330        if let Some(dock) = self.borrow() {
1331            return dock.unique_id(base);
1332        }
1333        LiveId(0)
1334    }
1335
1336    pub fn check_and_clear_need_save(&self)->bool{
1337        if let Some(mut dock) = self.borrow_mut() {
1338            if dock.needs_save{
1339                dock.needs_save = false;
1340                return true
1341            }
1342        }
1343        false
1344    }
1345
1346    pub fn clone_state(&self)->Option<HashMap<LiveId, DockItem>>{
1347        if let Some(dock) = self.borrow(){
1348            return Some(dock.dock_items.clone());
1349        }
1350        None
1351    }
1352
1353    pub fn tab_start_drag(&self, cx: &mut Cx, _tab_id: LiveId, item: DragItem) {
1354        cx.start_dragging(vec![item]);
1355    }
1356}