1mod dock;
2mod invalid_panel;
3mod panel;
4mod stack_panel;
5mod state;
6mod tab_panel;
7mod tiles;
8
9use anyhow::Result;
10use gpui::{
11 AnyElement, AnyView, App, AppContext, Axis, Bounds, Context, Edges, Entity, EntityId,
12 EventEmitter, InteractiveElement as _, IntoElement, ParentElement as _, Pixels, Render,
13 SharedString, Styled, Subscription, WeakEntity, Window, actions, canvas, div,
14 prelude::FluentBuilder,
15};
16use std::sync::Arc;
17
18pub use dock::*;
19pub use panel::*;
20pub use stack_panel::*;
21pub use state::*;
22pub use tab_panel::*;
23pub use tiles::*;
24
25pub(crate) fn init(cx: &mut App) {
26 PanelRegistry::init(cx);
27}
28
29actions!(dock, [ToggleZoom, ClosePanel]);
30
31pub enum DockEvent {
32 LayoutChanged,
37
38 DragDrop(AnyDrag),
40}
41
42pub struct DockArea {
44 id: SharedString,
45 version: Option<usize>,
47 pub(crate) bounds: Bounds<Pixels>,
48
49 items: DockItem,
51
52 toggle_button_panels: Edges<Option<EntityId>>,
54
55 toggle_button_visible: bool,
57 left_dock: Option<Entity<Dock>>,
59 bottom_dock: Option<Entity<Dock>>,
61 right_dock: Option<Entity<Dock>>,
63 zoom_view: Option<AnyView>,
65
66 locked: bool,
68
69 pub(crate) panel_style: PanelStyle,
71
72 _subscriptions: Vec<Subscription>,
73}
74
75#[derive(Clone)]
77pub enum DockItem {
78 Split {
80 axis: Axis,
81 size: Option<Pixels>,
83 items: Vec<DockItem>,
84 sizes: Vec<Option<Pixels>>,
86 view: Entity<StackPanel>,
87 },
88 Tabs {
90 size: Option<Pixels>,
92 items: Vec<Arc<dyn PanelView>>,
93 active_ix: usize,
94 view: Entity<TabPanel>,
95 },
96 Panel {
98 size: Option<Pixels>,
100 view: Arc<dyn PanelView>,
101 },
102 Tiles {
104 size: Option<Pixels>,
106 items: Vec<TileItem>,
107 view: Entity<Tiles>,
108 },
109}
110
111impl std::fmt::Debug for DockItem {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 DockItem::Split {
115 axis, items, sizes, ..
116 } => f
117 .debug_struct("Split")
118 .field("axis", axis)
119 .field("items", &items.len())
120 .field("sizes", sizes)
121 .finish(),
122 DockItem::Tabs {
123 items, active_ix, ..
124 } => f
125 .debug_struct("Tabs")
126 .field("items", &items.len())
127 .field("active_ix", active_ix)
128 .finish(),
129 DockItem::Panel { .. } => f.debug_struct("Panel").finish(),
130 DockItem::Tiles { .. } => f.debug_struct("Tiles").finish(),
131 }
132 }
133}
134
135impl DockItem {
136 fn get_size(&self) -> Option<Pixels> {
138 match self {
139 Self::Split { size, .. } => *size,
140 Self::Tabs { size, .. } => *size,
141 Self::Panel { size, .. } => *size,
142 Self::Tiles { size, .. } => *size,
143 }
144 }
145
146 pub fn size(mut self, new_size: impl Into<Pixels>) -> Self {
148 let new_size: Option<Pixels> = Some(new_size.into());
149 match self {
150 Self::Split { ref mut size, .. } => *size = new_size,
151 Self::Tabs { ref mut size, .. } => *size = new_size,
152 Self::Tiles { ref mut size, .. } => *size = new_size,
153 Self::Panel { ref mut size, .. } => *size = new_size,
154 }
155 self
156 }
157
158 pub fn active_index(mut self, new_active_ix: usize) -> Self {
160 debug_assert!(
161 matches!(self, Self::Tabs { .. }),
162 "active_ix can only be set for DockItem::Tabs"
163 );
164
165 if let Self::Tabs {
166 ref mut active_ix, ..
167 } = self
168 {
169 *active_ix = new_active_ix;
170 }
171 self
172 }
173
174 pub fn split(
176 axis: Axis,
177 items: Vec<DockItem>,
178 dock_area: &WeakEntity<DockArea>,
179 window: &mut Window,
180 cx: &mut App,
181 ) -> Self {
182 let sizes = items.iter().map(|item| item.get_size()).collect();
183 Self::split_with_sizes(axis, items, sizes, dock_area, window, cx)
184 }
185
186 pub fn v_split(
188 items: Vec<DockItem>,
189 dock_area: &WeakEntity<DockArea>,
190 window: &mut Window,
191 cx: &mut App,
192 ) -> Self {
193 Self::split(Axis::Vertical, items, dock_area, window, cx)
194 }
195
196 pub fn h_split(
198 items: Vec<DockItem>,
199 dock_area: &WeakEntity<DockArea>,
200 window: &mut Window,
201 cx: &mut App,
202 ) -> Self {
203 Self::split(Axis::Horizontal, items, dock_area, window, cx)
204 }
205
206 pub fn split_with_sizes(
211 axis: Axis,
212 items: Vec<DockItem>,
213 sizes: Vec<Option<Pixels>>,
214 dock_area: &WeakEntity<DockArea>,
215 window: &mut Window,
216 cx: &mut App,
217 ) -> Self {
218 let mut items = items;
219 let stack_panel = cx.new(|cx| {
220 let mut stack_panel = StackPanel::new(axis, window, cx);
221 for (i, item) in items.iter_mut().enumerate() {
222 let view = item.view();
223 let size = sizes.get(i).copied().flatten();
224 stack_panel.add_panel(view.clone(), size, dock_area.clone(), window, cx)
225 }
226
227 for (i, item) in items.iter().enumerate() {
228 let view = item.view();
229 let size = sizes.get(i).copied().flatten();
230 stack_panel.add_panel(view.clone(), size, dock_area.clone(), window, cx)
231 }
232
233 stack_panel
234 });
235
236 window.defer(cx, {
237 let stack_panel = stack_panel.clone();
238 let dock_area = dock_area.clone();
239 move |window, cx| {
240 _ = dock_area.update(cx, |this, cx| {
241 this.subscribe_panel(&stack_panel, window, cx);
242 });
243 }
244 });
245
246 Self::Split {
247 axis,
248 size: None,
249 items,
250 sizes,
251 view: stack_panel,
252 }
253 }
254
255 pub fn panel(panel: Arc<dyn PanelView>) -> Self {
257 Self::Panel {
258 size: None,
259 view: panel,
260 }
261 }
262
263 pub fn tiles(
267 items: Vec<DockItem>,
268 metas: Vec<impl Into<TileMeta> + Copy>,
269 dock_area: &WeakEntity<DockArea>,
270 window: &mut Window,
271 cx: &mut App,
272 ) -> Self {
273 assert!(items.len() == metas.len());
274
275 let tile_panel = cx.new(|cx| {
276 let mut tiles = Tiles::new(window, cx);
277 for (ix, item) in items.clone().into_iter().enumerate() {
278 match item {
279 DockItem::Tabs { view, .. } => {
280 let meta: TileMeta = metas[ix].into();
281 let tile_item =
282 TileItem::new(Arc::new(view), meta.bounds).z_index(meta.z_index);
283 tiles.add_item(tile_item, dock_area, window, cx);
284 }
285 DockItem::Panel { view, .. } => {
286 let meta: TileMeta = metas[ix].into();
287 let tile_item =
288 TileItem::new(view.clone(), meta.bounds).z_index(meta.z_index);
289 tiles.add_item(tile_item, dock_area, window, cx);
290 }
291 _ => {
292 }
294 }
295 }
296 tiles
297 });
298
299 window.defer(cx, {
300 let tile_panel = tile_panel.clone();
301 let dock_area = dock_area.clone();
302 move |window, cx| {
303 _ = dock_area.update(cx, |this, cx| {
304 this.subscribe_panel(&tile_panel, window, cx);
305 this.subscribe_tiles_item_drop(&tile_panel, window, cx);
306 });
307 }
308 });
309
310 Self::Tiles {
311 size: None,
312 items: tile_panel.read(cx).panels.clone(),
313 view: tile_panel,
314 }
315 }
316
317 pub fn tabs(
321 items: Vec<Arc<dyn PanelView>>,
322 dock_area: &WeakEntity<DockArea>,
323 window: &mut Window,
324 cx: &mut App,
325 ) -> Self {
326 let mut new_items: Vec<Arc<dyn PanelView>> = vec![];
327 for item in items.into_iter() {
328 new_items.push(item)
329 }
330 Self::new_tabs(new_items, None, dock_area, window, cx)
331 }
332
333 pub fn tab<P: Panel>(
334 item: Entity<P>,
335 dock_area: &WeakEntity<DockArea>,
336 window: &mut Window,
337 cx: &mut App,
338 ) -> Self {
339 Self::new_tabs(vec![Arc::new(item.clone())], None, dock_area, window, cx)
340 }
341
342 fn new_tabs(
343 items: Vec<Arc<dyn PanelView>>,
344 active_ix: Option<usize>,
345 dock_area: &WeakEntity<DockArea>,
346 window: &mut Window,
347 cx: &mut App,
348 ) -> Self {
349 let active_ix = active_ix.unwrap_or(0);
350 let tab_panel = cx.new(|cx| {
351 let mut tab_panel = TabPanel::new(None, dock_area.clone(), window, cx);
352 for item in items.iter() {
353 tab_panel.add_panel(item.clone(), window, cx)
354 }
355 tab_panel.active_ix = active_ix;
356 tab_panel
357 });
358
359 Self::Tabs {
360 size: None,
361 items,
362 active_ix,
363 view: tab_panel,
364 }
365 }
366
367 pub fn view(&self) -> Arc<dyn PanelView> {
369 match self {
370 Self::Split { view, .. } => Arc::new(view.clone()),
371 Self::Tabs { view, .. } => Arc::new(view.clone()),
372 Self::Tiles { view, .. } => Arc::new(view.clone()),
373 Self::Panel { view, .. } => view.clone(),
374 }
375 }
376
377 pub fn find_panel(&self, panel: Arc<dyn PanelView>) -> Option<Arc<dyn PanelView>> {
379 match self {
380 Self::Split { items, .. } => {
381 items.iter().find_map(|item| item.find_panel(panel.clone()))
382 }
383 Self::Tabs { items, .. } => items.iter().find(|item| *item == &panel).cloned(),
384 Self::Panel { view, .. } => Some(view.clone()),
385 Self::Tiles { items, .. } => items.iter().find_map(|item| {
386 if &item.panel == &panel {
387 Some(item.panel.clone())
388 } else {
389 None
390 }
391 }),
392 }
393 }
394
395 pub fn add_panel(
397 &mut self,
398 panel: Arc<dyn PanelView>,
399 dock_area: &WeakEntity<DockArea>,
400 bounds: Option<Bounds<Pixels>>,
401 window: &mut Window,
402 cx: &mut App,
403 ) {
404 match self {
405 Self::Tabs { view, items, .. } => {
406 items.push(panel.clone());
407 view.update(cx, |tab_panel, cx| {
408 tab_panel.add_panel(panel, window, cx);
409 });
410 }
411 Self::Split { view, items, .. } => {
412 for item in items.into_iter() {
414 if let DockItem::Tabs { view, .. } = item {
415 view.update(cx, |tab_panel, cx| {
416 tab_panel.add_panel(panel.clone(), window, cx);
417 });
418 return;
419 }
420 }
421
422 let new_item = Self::tabs(vec![panel.clone()], dock_area, window, cx);
424 items.push(new_item.clone());
425 view.update(cx, |stack_panel, cx| {
426 stack_panel.add_panel(new_item.view(), None, dock_area.clone(), window, cx);
427 });
428 }
429 Self::Tiles { view, items, .. } => {
430 let tile_item = TileItem::new(
431 Arc::new(cx.new(|cx| {
432 let mut tab_panel = TabPanel::new(None, dock_area.clone(), window, cx);
433 tab_panel.add_panel(panel.clone(), window, cx);
434 tab_panel
435 })),
436 bounds.unwrap_or_else(|| TileMeta::default().bounds),
437 );
438
439 items.push(tile_item.clone());
440 view.update(cx, |tiles, cx| {
441 tiles.add_item(tile_item, dock_area, window, cx);
442 });
443 }
444 Self::Panel { .. } => {}
445 }
446 }
447
448 pub fn remove_panel(&self, panel: Arc<dyn PanelView>, window: &mut Window, cx: &mut App) {
450 match self {
451 DockItem::Tabs { view, .. } => {
452 view.update(cx, |tab_panel, cx| {
453 tab_panel.remove_panel(panel, window, cx);
454 });
455 }
456 DockItem::Split { items, view, .. } => {
457 for item in items {
459 item.remove_panel(panel.clone(), window, cx);
460 }
461 view.update(cx, |split, cx| {
462 split.remove_panel(panel, window, cx);
463 });
464 }
465 DockItem::Tiles { view, .. } => {
466 view.update(cx, |tiles, cx| {
467 tiles.remove(panel, window, cx);
468 });
469 }
470 DockItem::Panel { .. } => {}
471 }
472 }
473
474 pub fn set_collapsed(&self, collapsed: bool, window: &mut Window, cx: &mut App) {
475 match self {
476 DockItem::Tabs { view, .. } => {
477 view.update(cx, |tab_panel, cx| {
478 tab_panel.set_collapsed(collapsed, window, cx);
479 });
480 }
481 DockItem::Split { items, .. } => {
482 for item in items {
484 item.set_collapsed(collapsed, window, cx);
485 }
486 }
487 DockItem::Tiles { .. } => {}
488 DockItem::Panel { view, .. } => view.set_active(!collapsed, window, cx),
489 }
490 }
491
492 pub(crate) fn left_top_tab_panel(&self, cx: &App) -> Option<Entity<TabPanel>> {
494 match self {
495 DockItem::Tabs { view, .. } => Some(view.clone()),
496 DockItem::Split { view, .. } => view.read(cx).left_top_tab_panel(true, cx),
497 DockItem::Tiles { .. } => None,
498 DockItem::Panel { .. } => None,
499 }
500 }
501
502 pub(crate) fn right_top_tab_panel(&self, cx: &App) -> Option<Entity<TabPanel>> {
504 match self {
505 DockItem::Tabs { view, .. } => Some(view.clone()),
506 DockItem::Split { view, .. } => view.read(cx).right_top_tab_panel(true, cx),
507 DockItem::Tiles { .. } => None,
508 DockItem::Panel { .. } => None,
509 }
510 }
511}
512
513impl DockArea {
514 pub fn new(
515 id: impl Into<SharedString>,
516 version: Option<usize>,
517 window: &mut Window,
518 cx: &mut Context<Self>,
519 ) -> Self {
520 let stack_panel = cx.new(|cx| StackPanel::new(Axis::Horizontal, window, cx));
521
522 let dock_item = DockItem::Split {
523 axis: Axis::Horizontal,
524 size: None,
525 items: vec![],
526 sizes: vec![],
527 view: stack_panel.clone(),
528 };
529
530 let mut this = Self {
531 id: id.into(),
532 version,
533 bounds: Bounds::default(),
534 items: dock_item,
535 zoom_view: None,
536 toggle_button_panels: Edges::default(),
537 toggle_button_visible: true,
538 left_dock: None,
539 right_dock: None,
540 bottom_dock: None,
541 locked: false,
542 panel_style: PanelStyle::default(),
543 _subscriptions: vec![],
544 };
545
546 this.subscribe_panel(&stack_panel, window, cx);
547
548 this
549 }
550
551 pub fn bounds(&self) -> Bounds<Pixels> {
553 self.bounds
554 }
555
556 pub fn items(&self) -> &DockItem {
558 &self.items
559 }
560
561 fn subscribe_tiles_item_drop(
563 &mut self,
564 tile_panel: &Entity<Tiles>,
565 _: &mut Window,
566 cx: &mut Context<Self>,
567 ) {
568 self._subscriptions
569 .push(cx.subscribe(tile_panel, move |_, _, evt: &DragDrop, cx| {
570 let item = evt.0.clone();
571 cx.emit(DockEvent::DragDrop(item));
572 }));
573 }
574
575 pub fn panel_style(mut self, style: PanelStyle) -> Self {
577 self.panel_style = style;
578 self
579 }
580
581 pub fn set_version(&mut self, version: usize, _: &mut Window, cx: &mut Context<Self>) {
583 self.version = Some(version);
584 cx.notify();
585 }
586
587 #[deprecated(note = "Use `set_center` instead")]
589 pub fn set_root(&mut self, item: DockItem, window: &mut Window, cx: &mut Context<Self>) {
590 self.set_center(item, window, cx);
591 }
592
593 pub fn set_center(&mut self, item: DockItem, window: &mut Window, cx: &mut Context<Self>) {
597 self.subscribe_item(&item, window, cx);
598 self.items = item;
599 self.update_toggle_button_tab_panels(window, cx);
600 cx.notify();
601 }
602
603 pub fn set_left_dock(
604 &mut self,
605 panel: DockItem,
606 size: Option<Pixels>,
607 open: bool,
608 window: &mut Window,
609 cx: &mut Context<Self>,
610 ) {
611 self.subscribe_item(&panel, window, cx);
612 let weak_self = cx.entity().downgrade();
613 self.left_dock = Some(cx.new(|cx| {
614 let mut dock = Dock::left(weak_self.clone(), window, cx);
615 if let Some(size) = size {
616 dock.set_size(size, window, cx);
617 }
618 dock.set_panel(panel, window, cx);
619 dock.set_open(open, window, cx);
620 dock
621 }));
622 self.update_toggle_button_tab_panels(window, cx);
623 }
624
625 pub fn set_bottom_dock(
626 &mut self,
627 panel: DockItem,
628 size: Option<Pixels>,
629 open: bool,
630 window: &mut Window,
631 cx: &mut Context<Self>,
632 ) {
633 self.subscribe_item(&panel, window, cx);
634 let weak_self = cx.entity().downgrade();
635 self.bottom_dock = Some(cx.new(|cx| {
636 let mut dock = Dock::bottom(weak_self.clone(), window, cx);
637 if let Some(size) = size {
638 dock.set_size(size, window, cx);
639 }
640 dock.set_panel(panel, window, cx);
641 dock.set_open(open, window, cx);
642 dock
643 }));
644 self.update_toggle_button_tab_panels(window, cx);
645 }
646
647 pub fn set_right_dock(
648 &mut self,
649 panel: DockItem,
650 size: Option<Pixels>,
651 open: bool,
652 window: &mut Window,
653 cx: &mut Context<Self>,
654 ) {
655 self.subscribe_item(&panel, window, cx);
656 let weak_self = cx.entity().downgrade();
657 self.right_dock = Some(cx.new(|cx| {
658 let mut dock = Dock::right(weak_self.clone(), window, cx);
659 if let Some(size) = size {
660 dock.set_size(size, window, cx);
661 }
662 dock.set_panel(panel, window, cx);
663 dock.set_open(open, window, cx);
664 dock
665 }));
666 self.update_toggle_button_tab_panels(window, cx);
667 }
668
669 pub fn set_locked(&mut self, locked: bool, _window: &mut Window, _cx: &mut App) {
671 self.locked = locked;
672 }
673
674 #[inline]
676 pub fn is_locked(&self) -> bool {
677 self.locked
678 }
679
680 pub fn has_dock(&self, placement: DockPlacement) -> bool {
682 match placement {
683 DockPlacement::Left => self.left_dock.is_some(),
684 DockPlacement::Bottom => self.bottom_dock.is_some(),
685 DockPlacement::Right => self.right_dock.is_some(),
686 DockPlacement::Center => false,
687 }
688 }
689
690 pub fn is_dock_open(&self, placement: DockPlacement, cx: &App) -> bool {
692 match placement {
693 DockPlacement::Left => self
694 .left_dock
695 .as_ref()
696 .map(|dock| dock.read(cx).is_open())
697 .unwrap_or(false),
698 DockPlacement::Bottom => self
699 .bottom_dock
700 .as_ref()
701 .map(|dock| dock.read(cx).is_open())
702 .unwrap_or(false),
703 DockPlacement::Right => self
704 .right_dock
705 .as_ref()
706 .map(|dock| dock.read(cx).is_open())
707 .unwrap_or(false),
708 DockPlacement::Center => false,
709 }
710 }
711
712 pub fn set_dock_collapsible(
716 &mut self,
717 collapsible_edges: Edges<bool>,
718 window: &mut Window,
719 cx: &mut Context<Self>,
720 ) {
721 if let Some(left_dock) = self.left_dock.as_ref() {
722 left_dock.update(cx, |dock, cx| {
723 dock.set_collapsible(collapsible_edges.left, window, cx);
724 });
725 }
726
727 if let Some(bottom_dock) = self.bottom_dock.as_ref() {
728 bottom_dock.update(cx, |dock, cx| {
729 dock.set_collapsible(collapsible_edges.bottom, window, cx);
730 });
731 }
732
733 if let Some(right_dock) = self.right_dock.as_ref() {
734 right_dock.update(cx, |dock, cx| {
735 dock.set_collapsible(collapsible_edges.right, window, cx);
736 });
737 }
738 }
739
740 pub fn is_dock_collapsible(&self, placement: DockPlacement, cx: &App) -> bool {
742 match placement {
743 DockPlacement::Left => self
744 .left_dock
745 .as_ref()
746 .map(|dock| dock.read(cx).collapsible)
747 .unwrap_or(false),
748 DockPlacement::Bottom => self
749 .bottom_dock
750 .as_ref()
751 .map(|dock| dock.read(cx).collapsible)
752 .unwrap_or(false),
753 DockPlacement::Right => self
754 .right_dock
755 .as_ref()
756 .map(|dock| dock.read(cx).collapsible)
757 .unwrap_or(false),
758 DockPlacement::Center => false,
759 }
760 }
761
762 pub fn toggle_dock(
764 &self,
765 placement: DockPlacement,
766 window: &mut Window,
767 cx: &mut Context<Self>,
768 ) {
769 let dock = match placement {
770 DockPlacement::Left => &self.left_dock,
771 DockPlacement::Bottom => &self.bottom_dock,
772 DockPlacement::Right => &self.right_dock,
773 DockPlacement::Center => return,
774 };
775
776 if let Some(dock) = dock {
777 dock.update(cx, |view, cx| {
778 view.toggle_open(window, cx);
779 })
780 }
781 }
782
783 pub fn set_toggle_button_visible(&mut self, visible: bool, _: &mut Context<Self>) {
785 self.toggle_button_visible = visible;
786 }
787
788 pub fn add_panel(
790 &mut self,
791 panel: Arc<dyn PanelView>,
792 placement: DockPlacement,
793 bounds: Option<Bounds<Pixels>>,
794 window: &mut Window,
795 cx: &mut Context<Self>,
796 ) {
797 let weak_self = cx.entity().downgrade();
798 match placement {
799 DockPlacement::Left => {
800 if let Some(dock) = self.left_dock.as_ref() {
801 dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
802 } else {
803 self.set_left_dock(
804 DockItem::tabs(vec![panel], &weak_self, window, cx),
805 None,
806 true,
807 window,
808 cx,
809 );
810 }
811 }
812 DockPlacement::Bottom => {
813 if let Some(dock) = self.bottom_dock.as_ref() {
814 dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
815 } else {
816 self.set_bottom_dock(
817 DockItem::tabs(vec![panel], &weak_self, window, cx),
818 None,
819 true,
820 window,
821 cx,
822 );
823 }
824 }
825 DockPlacement::Right => {
826 if let Some(dock) = self.right_dock.as_ref() {
827 dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
828 } else {
829 self.set_right_dock(
830 DockItem::tabs(vec![panel], &weak_self, window, cx),
831 None,
832 true,
833 window,
834 cx,
835 );
836 }
837 }
838 DockPlacement::Center => {
839 self.items
840 .add_panel(panel, &cx.entity().downgrade(), bounds, window, cx);
841 }
842 }
843 }
844
845 pub fn remove_panel(
847 &mut self,
848 panel: Arc<dyn PanelView>,
849 placement: DockPlacement,
850 window: &mut Window,
851 cx: &mut Context<Self>,
852 ) {
853 match placement {
854 DockPlacement::Left => {
855 if let Some(dock) = self.left_dock.as_mut() {
856 dock.update(cx, |dock, cx| {
857 dock.remove_panel(panel, window, cx);
858 });
859 }
860 }
861 DockPlacement::Right => {
862 if let Some(dock) = self.right_dock.as_mut() {
863 dock.update(cx, |dock, cx| {
864 dock.remove_panel(panel, window, cx);
865 });
866 }
867 }
868 DockPlacement::Bottom => {
869 if let Some(dock) = self.bottom_dock.as_mut() {
870 dock.update(cx, |dock, cx| {
871 dock.remove_panel(panel, window, cx);
872 });
873 }
874 }
875 DockPlacement::Center => {
876 self.items.remove_panel(panel, window, cx);
877 }
878 }
879 cx.notify();
880 }
881
882 pub fn remove_panel_from_all_docks(
884 &mut self,
885 panel: Arc<dyn PanelView>,
886 window: &mut Window,
887 cx: &mut Context<Self>,
888 ) {
889 self.remove_panel(panel.clone(), DockPlacement::Center, window, cx);
890 self.remove_panel(panel.clone(), DockPlacement::Left, window, cx);
891 self.remove_panel(panel.clone(), DockPlacement::Right, window, cx);
892 self.remove_panel(panel.clone(), DockPlacement::Bottom, window, cx);
893 }
894
895 pub fn load(
899 &mut self,
900 state: DockAreaState,
901 window: &mut Window,
902 cx: &mut Context<Self>,
903 ) -> Result<()> {
904 self.version = state.version;
905 let weak_self = cx.entity().downgrade();
906
907 if let Some(left_dock_state) = state.left_dock {
908 self.left_dock = Some(left_dock_state.to_dock(weak_self.clone(), window, cx));
909 }
910
911 if let Some(right_dock_state) = state.right_dock {
912 self.right_dock = Some(right_dock_state.to_dock(weak_self.clone(), window, cx));
913 }
914
915 if let Some(bottom_dock_state) = state.bottom_dock {
916 self.bottom_dock = Some(bottom_dock_state.to_dock(weak_self.clone(), window, cx));
917 }
918
919 self.items = state.center.to_item(weak_self, window, cx);
920 self.update_toggle_button_tab_panels(window, cx);
921 Ok(())
922 }
923
924 pub fn dump(&self, cx: &App) -> DockAreaState {
928 let root = self.items.view();
929 let center = root.dump(cx);
930
931 let left_dock = self
932 .left_dock
933 .as_ref()
934 .map(|dock| DockState::new(dock.clone(), cx));
935 let right_dock = self
936 .right_dock
937 .as_ref()
938 .map(|dock| DockState::new(dock.clone(), cx));
939 let bottom_dock = self
940 .bottom_dock
941 .as_ref()
942 .map(|dock| DockState::new(dock.clone(), cx));
943
944 DockAreaState {
945 version: self.version,
946 center,
947 left_dock,
948 right_dock,
949 bottom_dock,
950 }
951 }
952
953 #[allow(clippy::only_used_in_recursion)]
955 fn subscribe_item(&mut self, item: &DockItem, window: &mut Window, cx: &mut Context<Self>) {
956 match item {
957 DockItem::Split { items, view, .. } => {
958 for item in items {
959 self.subscribe_item(item, window, cx);
960 }
961
962 self._subscriptions.push(cx.subscribe_in(
963 view,
964 window,
965 move |_, _, event, window, cx| match event {
966 PanelEvent::LayoutChanged => {
967 cx.spawn_in(window, async move |view, window| {
968 _ = view.update_in(window, |view, window, cx| {
969 view.update_toggle_button_tab_panels(window, cx)
970 });
971 })
972 .detach();
973 cx.emit(DockEvent::LayoutChanged);
974 }
975 _ => {}
976 },
977 ));
978 }
979 DockItem::Tabs { .. } => {
980 }
982 DockItem::Tiles { .. } => {
983 }
985 DockItem::Panel { .. } => {
986 }
988 }
989 }
990
991 pub(crate) fn subscribe_panel<P: Panel>(
993 &mut self,
994 view: &Entity<P>,
995 window: &mut Window,
996 cx: &mut Context<DockArea>,
997 ) {
998 let subscription =
999 cx.subscribe_in(
1000 view,
1001 window,
1002 move |_, panel, event, window, cx| match event {
1003 PanelEvent::ZoomIn => {
1004 let panel = panel.clone();
1005 cx.spawn_in(window, async move |view, window| {
1006 _ = view.update_in(window, |view, window, cx| {
1007 view.set_zoomed_in(panel, window, cx);
1008 cx.notify();
1009 });
1010 })
1011 .detach();
1012 }
1013 PanelEvent::ZoomOut => cx
1014 .spawn_in(window, async move |view, window| {
1015 _ = view.update_in(window, |view, window, cx| {
1016 view.set_zoomed_out(window, cx);
1017 });
1018 })
1019 .detach(),
1020 PanelEvent::LayoutChanged => {
1021 cx.spawn_in(window, async move |view, window| {
1022 _ = view.update_in(window, |view, window, cx| {
1023 view.update_toggle_button_tab_panels(window, cx)
1024 });
1025 })
1026 .detach();
1027 cx.emit(DockEvent::LayoutChanged);
1028 }
1029 },
1030 );
1031
1032 self._subscriptions.push(subscription);
1033 }
1034
1035 pub fn id(&self) -> SharedString {
1037 self.id.clone()
1038 }
1039
1040 pub fn set_zoomed_in<P: Panel>(
1041 &mut self,
1042 panel: Entity<P>,
1043 _: &mut Window,
1044 cx: &mut Context<Self>,
1045 ) {
1046 self.zoom_view = Some(panel.into());
1047 cx.notify();
1048 }
1049
1050 pub fn set_zoomed_out(&mut self, _: &mut Window, cx: &mut Context<Self>) {
1051 self.zoom_view = None;
1052 cx.notify();
1053 }
1054
1055 fn render_items(&self, _window: &mut Window, _cx: &mut Context<Self>) -> AnyElement {
1056 match &self.items {
1057 DockItem::Split { view, .. } => view.clone().into_any_element(),
1058 DockItem::Tabs { view, .. } => view.clone().into_any_element(),
1059 DockItem::Tiles { view, .. } => view.clone().into_any_element(),
1060 DockItem::Panel { view, .. } => view.clone().view().into_any_element(),
1061 }
1062 }
1063
1064 pub fn update_toggle_button_tab_panels(&mut self, _: &mut Window, cx: &mut Context<Self>) {
1065 self.toggle_button_panels.left = self
1067 .items
1068 .left_top_tab_panel(cx)
1069 .map(|view| view.entity_id());
1070
1071 self.toggle_button_panels.right = self
1073 .items
1074 .right_top_tab_panel(cx)
1075 .map(|view| view.entity_id());
1076
1077 self.toggle_button_panels.bottom = self
1079 .bottom_dock
1080 .as_ref()
1081 .and_then(|dock| dock.read(cx).panel.left_top_tab_panel(cx))
1082 .map(|view| view.entity_id());
1083 }
1084}
1085impl EventEmitter<DockEvent> for DockArea {}
1086impl Render for DockArea {
1087 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
1088 let view = cx.entity().clone();
1089
1090 div()
1091 .id("dock-area")
1092 .relative()
1093 .size_full()
1094 .overflow_hidden()
1095 .child(
1096 canvas(
1097 move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds),
1098 |_, _, _, _| {},
1099 )
1100 .absolute()
1101 .size_full(),
1102 )
1103 .map(|this| {
1104 if let Some(zoom_view) = self.zoom_view.clone() {
1105 this.child(zoom_view)
1106 } else {
1107 match &self.items {
1108 DockItem::Tiles { view, .. } => {
1109 this.child(view.clone())
1111 }
1112 _ => {
1113 this.child(
1115 div()
1116 .flex()
1117 .flex_row()
1118 .h_full()
1119 .when_some(self.left_dock.clone(), |this, dock| {
1121 this.child(div().flex().flex_none().child(dock))
1122 })
1123 .child(
1125 div()
1126 .flex()
1127 .flex_1()
1128 .flex_col()
1129 .overflow_hidden()
1130 .child(
1132 div()
1133 .flex_1()
1134 .overflow_hidden()
1135 .child(self.render_items(window, cx)),
1136 )
1137 .when_some(self.bottom_dock.clone(), |this, dock| {
1139 this.child(dock)
1140 }),
1141 )
1142 .when_some(self.right_dock.clone(), |this, dock| {
1144 this.child(div().flex().flex_none().child(dock))
1145 }),
1146 )
1147 }
1148 }
1149 }
1150 })
1151 }
1152}