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 type Item = (LiveId, WidgetRef);
75 fn next(&mut self) -> Option<Self::Item> {
76 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 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 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 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 }
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 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 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 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 let tabs_id = *tabs_id;
526 tabs.remove(pos);
527 if tabs.len() == 0 { 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 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 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 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 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 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 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 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 self.drop_state = None;
794 self.drop_target_draw_list.redraw(cx);
795 }
796
797 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 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 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 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 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 }
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 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 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);