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 type Item = (LiveId, WidgetRef);
217 fn next(&mut self) -> Option<Self::Item> {
218 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 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 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 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 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 }
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 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 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 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 let tabs_id = *tabs_id;
680 tabs.remove(pos);
681 if tabs.len() == 0 { 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 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 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 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 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 }
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 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 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 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 let Some(DockItem::Tab { name, template: _, kind }) = self.dock_items.get_mut(&tab_item_id) else {
933 return None;
934 };
935 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 let (new_widgetref, was_replaced) = if *existing_kind == new_kind {
944 (existing_widgetref.clone(), false)
945 } else {
946 *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 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 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 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 match action.as_widget_action().cast() {
1009 SplitterAction::Changed {axis, align} => {
1010 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 self.drop_state = None;
1066 self.drop_target_draw_list.redraw(cx);
1067 }
1068
1069 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 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 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 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 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 }
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 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 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 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}