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 items: Vec<DockItem>,
82 sizes: Vec<Option<Pixels>>,
83 view: Entity<StackPanel>,
84 },
85 Tabs {
87 items: Vec<Arc<dyn PanelView>>,
88 active_ix: usize,
89 view: Entity<TabPanel>,
90 },
91 Panel { view: Arc<dyn PanelView> },
93 Tiles {
95 items: Vec<TileItem>,
96 view: Entity<Tiles>,
97 },
98}
99
100impl std::fmt::Debug for DockItem {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 DockItem::Split {
104 axis, items, sizes, ..
105 } => f
106 .debug_struct("Split")
107 .field("axis", axis)
108 .field("items", &items.len())
109 .field("sizes", sizes)
110 .finish(),
111 DockItem::Tabs {
112 items, active_ix, ..
113 } => f
114 .debug_struct("Tabs")
115 .field("items", &items.len())
116 .field("active_ix", active_ix)
117 .finish(),
118 DockItem::Panel { .. } => f.debug_struct("Panel").finish(),
119 DockItem::Tiles { .. } => f.debug_struct("Tiles").finish(),
120 }
121 }
122}
123
124impl DockItem {
125 pub fn split(
127 axis: Axis,
128 items: Vec<DockItem>,
129 dock_area: &WeakEntity<DockArea>,
130 window: &mut Window,
131 cx: &mut App,
132 ) -> Self {
133 let sizes = vec![None; items.len()];
134 Self::split_with_sizes(axis, items, sizes, dock_area, window, cx)
135 }
136
137 pub fn split_with_sizes(
142 axis: Axis,
143 items: Vec<DockItem>,
144 sizes: Vec<Option<Pixels>>,
145 dock_area: &WeakEntity<DockArea>,
146 window: &mut Window,
147 cx: &mut App,
148 ) -> Self {
149 let mut items = items;
150 let stack_panel = cx.new(|cx| {
151 let mut stack_panel = StackPanel::new(axis, window, cx);
152 for (i, item) in items.iter_mut().enumerate() {
153 let view = item.view();
154 let size = sizes.get(i).copied().flatten();
155 stack_panel.add_panel(view.clone(), size, dock_area.clone(), window, cx)
156 }
157
158 for (i, item) in items.iter().enumerate() {
159 let view = item.view();
160 let size = sizes.get(i).copied().flatten();
161 stack_panel.add_panel(view.clone(), size, dock_area.clone(), window, cx)
162 }
163
164 stack_panel
165 });
166
167 window.defer(cx, {
168 let stack_panel = stack_panel.clone();
169 let dock_area = dock_area.clone();
170 move |window, cx| {
171 _ = dock_area.update(cx, |this, cx| {
172 this.subscribe_panel(&stack_panel, window, cx);
173 });
174 }
175 });
176
177 Self::Split {
178 axis,
179 items,
180 sizes,
181 view: stack_panel,
182 }
183 }
184
185 pub fn panel(panel: Arc<dyn PanelView>) -> Self {
187 Self::Panel { view: panel }
188 }
189
190 pub fn tiles(
194 items: Vec<DockItem>,
195 metas: Vec<impl Into<TileMeta> + Copy>,
196 dock_area: &WeakEntity<DockArea>,
197 window: &mut Window,
198 cx: &mut App,
199 ) -> Self {
200 assert!(items.len() == metas.len());
201
202 let tile_panel = cx.new(|cx| {
203 let mut tiles = Tiles::new(window, cx);
204 for (ix, item) in items.clone().into_iter().enumerate() {
205 match item {
206 DockItem::Tabs { view, .. } => {
207 let meta: TileMeta = metas[ix].into();
208 let tile_item =
209 TileItem::new(Arc::new(view), meta.bounds).z_index(meta.z_index);
210 tiles.add_item(tile_item, dock_area, window, cx);
211 }
212 DockItem::Panel { view } => {
213 let meta: TileMeta = metas[ix].into();
214 let tile_item =
215 TileItem::new(view.clone(), meta.bounds).z_index(meta.z_index);
216 tiles.add_item(tile_item, dock_area, window, cx);
217 }
218 _ => {
219 }
221 }
222 }
223 tiles
224 });
225
226 window.defer(cx, {
227 let tile_panel = tile_panel.clone();
228 let dock_area = dock_area.clone();
229 move |window, cx| {
230 _ = dock_area.update(cx, |this, cx| {
231 this.subscribe_panel(&tile_panel, window, cx);
232 this.subscribe_tiles_item_drop(&tile_panel, window, cx);
233 });
234 }
235 });
236
237 Self::Tiles {
238 items: tile_panel.read(cx).panels.clone(),
239 view: tile_panel,
240 }
241 }
242
243 pub fn tabs(
247 items: Vec<Arc<dyn PanelView>>,
248 active_ix: Option<usize>,
249 dock_area: &WeakEntity<DockArea>,
250 window: &mut Window,
251 cx: &mut App,
252 ) -> Self {
253 let mut new_items: Vec<Arc<dyn PanelView>> = vec![];
254 for item in items.into_iter() {
255 new_items.push(item)
256 }
257 Self::new_tabs(new_items, active_ix, dock_area, window, cx)
258 }
259
260 pub fn tab<P: Panel>(
261 item: Entity<P>,
262 dock_area: &WeakEntity<DockArea>,
263 window: &mut Window,
264 cx: &mut App,
265 ) -> Self {
266 Self::new_tabs(vec![Arc::new(item.clone())], None, dock_area, window, cx)
267 }
268
269 fn new_tabs(
270 items: Vec<Arc<dyn PanelView>>,
271 active_ix: Option<usize>,
272 dock_area: &WeakEntity<DockArea>,
273 window: &mut Window,
274 cx: &mut App,
275 ) -> Self {
276 let active_ix = active_ix.unwrap_or(0);
277 let tab_panel = cx.new(|cx| {
278 let mut tab_panel = TabPanel::new(None, dock_area.clone(), window, cx);
279 for item in items.iter() {
280 tab_panel.add_panel(item.clone(), window, cx)
281 }
282 tab_panel.active_ix = active_ix;
283 tab_panel
284 });
285
286 Self::Tabs {
287 items,
288 active_ix,
289 view: tab_panel,
290 }
291 }
292
293 pub fn view(&self) -> Arc<dyn PanelView> {
295 match self {
296 Self::Split { view, .. } => Arc::new(view.clone()),
297 Self::Tabs { view, .. } => Arc::new(view.clone()),
298 Self::Tiles { view, .. } => Arc::new(view.clone()),
299 Self::Panel { view, .. } => view.clone(),
300 }
301 }
302
303 pub fn find_panel(&self, panel: Arc<dyn PanelView>) -> Option<Arc<dyn PanelView>> {
305 match self {
306 Self::Split { items, .. } => {
307 items.iter().find_map(|item| item.find_panel(panel.clone()))
308 }
309 Self::Tabs { items, .. } => items.iter().find(|item| *item == &panel).cloned(),
310 Self::Panel { view } => Some(view.clone()),
311 Self::Tiles { items, .. } => items.iter().find_map(|item| {
312 if &item.panel == &panel {
313 Some(item.panel.clone())
314 } else {
315 None
316 }
317 }),
318 }
319 }
320
321 pub fn add_panel(
323 &mut self,
324 panel: Arc<dyn PanelView>,
325 dock_area: &WeakEntity<DockArea>,
326 bounds: Option<Bounds<Pixels>>,
327 window: &mut Window,
328 cx: &mut App,
329 ) {
330 match self {
331 Self::Tabs { view, items, .. } => {
332 items.push(panel.clone());
333 view.update(cx, |tab_panel, cx| {
334 tab_panel.add_panel(panel, window, cx);
335 });
336 }
337 Self::Split { view, items, .. } => {
338 for item in items.into_iter() {
340 if let DockItem::Tabs { view, .. } = item {
341 view.update(cx, |tab_panel, cx| {
342 tab_panel.add_panel(panel.clone(), window, cx);
343 });
344 return;
345 }
346 }
347
348 let new_item = Self::tabs(vec![panel.clone()], None, dock_area, window, cx);
350 items.push(new_item.clone());
351 view.update(cx, |stack_panel, cx| {
352 stack_panel.add_panel(new_item.view(), None, dock_area.clone(), window, cx);
353 });
354 }
355 Self::Tiles { view, items } => {
356 let tile_item = TileItem::new(
357 Arc::new(cx.new(|cx| {
358 let mut tab_panel = TabPanel::new(None, dock_area.clone(), window, cx);
359 tab_panel.add_panel(panel.clone(), window, cx);
360 tab_panel
361 })),
362 bounds.unwrap_or_else(|| TileMeta::default().bounds),
363 );
364
365 items.push(tile_item.clone());
366 view.update(cx, |tiles, cx| {
367 tiles.add_item(tile_item, dock_area, window, cx);
368 });
369 }
370 Self::Panel { .. } => {}
371 }
372 }
373
374 pub fn remove_panel(&self, panel: Arc<dyn PanelView>, window: &mut Window, cx: &mut App) {
376 match self {
377 DockItem::Tabs { view, .. } => {
378 view.update(cx, |tab_panel, cx| {
379 tab_panel.remove_panel(panel, window, cx);
380 });
381 }
382 DockItem::Split { items, view, .. } => {
383 for item in items {
385 item.remove_panel(panel.clone(), window, cx);
386 }
387 view.update(cx, |split, cx| {
388 split.remove_panel(panel, window, cx);
389 });
390 }
391 DockItem::Tiles { view, .. } => {
392 view.update(cx, |tiles, cx| {
393 tiles.remove(panel, window, cx);
394 });
395 }
396 DockItem::Panel { .. } => {}
397 }
398 }
399
400 pub fn set_collapsed(&self, collapsed: bool, window: &mut Window, cx: &mut App) {
401 match self {
402 DockItem::Tabs { view, .. } => {
403 view.update(cx, |tab_panel, cx| {
404 tab_panel.set_collapsed(collapsed, window, cx);
405 });
406 }
407 DockItem::Split { items, .. } => {
408 for item in items {
410 item.set_collapsed(collapsed, window, cx);
411 }
412 }
413 DockItem::Tiles { .. } => {}
414 DockItem::Panel { view } => view.set_active(!collapsed, window, cx),
415 }
416 }
417
418 pub(crate) fn left_top_tab_panel(&self, cx: &App) -> Option<Entity<TabPanel>> {
420 match self {
421 DockItem::Tabs { view, .. } => Some(view.clone()),
422 DockItem::Split { view, .. } => view.read(cx).left_top_tab_panel(true, cx),
423 DockItem::Tiles { .. } => None,
424 DockItem::Panel { .. } => None,
425 }
426 }
427
428 pub(crate) fn right_top_tab_panel(&self, cx: &App) -> Option<Entity<TabPanel>> {
430 match self {
431 DockItem::Tabs { view, .. } => Some(view.clone()),
432 DockItem::Split { view, .. } => view.read(cx).right_top_tab_panel(true, cx),
433 DockItem::Tiles { .. } => None,
434 DockItem::Panel { .. } => None,
435 }
436 }
437}
438
439impl DockArea {
440 pub fn new(
441 id: impl Into<SharedString>,
442 version: Option<usize>,
443 window: &mut Window,
444 cx: &mut Context<Self>,
445 ) -> Self {
446 let stack_panel = cx.new(|cx| StackPanel::new(Axis::Horizontal, window, cx));
447
448 let dock_item = DockItem::Split {
449 axis: Axis::Horizontal,
450 items: vec![],
451 sizes: vec![],
452 view: stack_panel.clone(),
453 };
454
455 let mut this = Self {
456 id: id.into(),
457 version,
458 bounds: Bounds::default(),
459 items: dock_item,
460 zoom_view: None,
461 toggle_button_panels: Edges::default(),
462 toggle_button_visible: true,
463 left_dock: None,
464 right_dock: None,
465 bottom_dock: None,
466 locked: false,
467 panel_style: PanelStyle::default(),
468 _subscriptions: vec![],
469 };
470
471 this.subscribe_panel(&stack_panel, window, cx);
472
473 this
474 }
475
476 pub fn bounds(&self) -> Bounds<Pixels> {
478 self.bounds
479 }
480
481 pub fn items(&self) -> &DockItem {
483 &self.items
484 }
485
486 fn subscribe_tiles_item_drop(
488 &mut self,
489 tile_panel: &Entity<Tiles>,
490 _: &mut Window,
491 cx: &mut Context<Self>,
492 ) {
493 self._subscriptions
494 .push(cx.subscribe(tile_panel, move |_, _, evt: &DragDrop, cx| {
495 let item = evt.0.clone();
496 cx.emit(DockEvent::DragDrop(item));
497 }));
498 }
499
500 pub fn panel_style(mut self, style: PanelStyle) -> Self {
502 self.panel_style = style;
503 self
504 }
505
506 pub fn set_version(&mut self, version: usize, _: &mut Window, cx: &mut Context<Self>) {
508 self.version = Some(version);
509 cx.notify();
510 }
511
512 #[deprecated(note = "Use `set_center` instead")]
514 pub fn set_root(&mut self, item: DockItem, window: &mut Window, cx: &mut Context<Self>) {
515 self.set_center(item, window, cx);
516 }
517
518 pub fn set_center(&mut self, item: DockItem, window: &mut Window, cx: &mut Context<Self>) {
522 self.subscribe_item(&item, window, cx);
523 self.items = item;
524 self.update_toggle_button_tab_panels(window, cx);
525 cx.notify();
526 }
527
528 pub fn set_left_dock(
529 &mut self,
530 panel: DockItem,
531 size: Option<Pixels>,
532 open: bool,
533 window: &mut Window,
534 cx: &mut Context<Self>,
535 ) {
536 self.subscribe_item(&panel, window, cx);
537 let weak_self = cx.entity().downgrade();
538 self.left_dock = Some(cx.new(|cx| {
539 let mut dock = Dock::left(weak_self.clone(), window, cx);
540 if let Some(size) = size {
541 dock.set_size(size, window, cx);
542 }
543 dock.set_panel(panel, window, cx);
544 dock.set_open(open, window, cx);
545 dock
546 }));
547 self.update_toggle_button_tab_panels(window, cx);
548 }
549
550 pub fn set_bottom_dock(
551 &mut self,
552 panel: DockItem,
553 size: Option<Pixels>,
554 open: bool,
555 window: &mut Window,
556 cx: &mut Context<Self>,
557 ) {
558 self.subscribe_item(&panel, window, cx);
559 let weak_self = cx.entity().downgrade();
560 self.bottom_dock = Some(cx.new(|cx| {
561 let mut dock = Dock::bottom(weak_self.clone(), window, cx);
562 if let Some(size) = size {
563 dock.set_size(size, window, cx);
564 }
565 dock.set_panel(panel, window, cx);
566 dock.set_open(open, window, cx);
567 dock
568 }));
569 self.update_toggle_button_tab_panels(window, cx);
570 }
571
572 pub fn set_right_dock(
573 &mut self,
574 panel: DockItem,
575 size: Option<Pixels>,
576 open: bool,
577 window: &mut Window,
578 cx: &mut Context<Self>,
579 ) {
580 self.subscribe_item(&panel, window, cx);
581 let weak_self = cx.entity().downgrade();
582 self.right_dock = Some(cx.new(|cx| {
583 let mut dock = Dock::right(weak_self.clone(), window, cx);
584 if let Some(size) = size {
585 dock.set_size(size, window, cx);
586 }
587 dock.set_panel(panel, window, cx);
588 dock.set_open(open, window, cx);
589 dock
590 }));
591 self.update_toggle_button_tab_panels(window, cx);
592 }
593
594 pub fn set_locked(&mut self, locked: bool, _window: &mut Window, _cx: &mut App) {
596 self.locked = locked;
597 }
598
599 #[inline]
601 pub fn is_locked(&self) -> bool {
602 self.locked
603 }
604
605 pub fn has_dock(&self, placement: DockPlacement) -> bool {
607 match placement {
608 DockPlacement::Left => self.left_dock.is_some(),
609 DockPlacement::Bottom => self.bottom_dock.is_some(),
610 DockPlacement::Right => self.right_dock.is_some(),
611 DockPlacement::Center => false,
612 }
613 }
614
615 pub fn is_dock_open(&self, placement: DockPlacement, cx: &App) -> bool {
617 match placement {
618 DockPlacement::Left => self
619 .left_dock
620 .as_ref()
621 .map(|dock| dock.read(cx).is_open())
622 .unwrap_or(false),
623 DockPlacement::Bottom => self
624 .bottom_dock
625 .as_ref()
626 .map(|dock| dock.read(cx).is_open())
627 .unwrap_or(false),
628 DockPlacement::Right => self
629 .right_dock
630 .as_ref()
631 .map(|dock| dock.read(cx).is_open())
632 .unwrap_or(false),
633 DockPlacement::Center => false,
634 }
635 }
636
637 pub fn set_dock_collapsible(
641 &mut self,
642 collapsible_edges: Edges<bool>,
643 window: &mut Window,
644 cx: &mut Context<Self>,
645 ) {
646 if let Some(left_dock) = self.left_dock.as_ref() {
647 left_dock.update(cx, |dock, cx| {
648 dock.set_collapsible(collapsible_edges.left, window, cx);
649 });
650 }
651
652 if let Some(bottom_dock) = self.bottom_dock.as_ref() {
653 bottom_dock.update(cx, |dock, cx| {
654 dock.set_collapsible(collapsible_edges.bottom, window, cx);
655 });
656 }
657
658 if let Some(right_dock) = self.right_dock.as_ref() {
659 right_dock.update(cx, |dock, cx| {
660 dock.set_collapsible(collapsible_edges.right, window, cx);
661 });
662 }
663 }
664
665 pub fn is_dock_collapsible(&self, placement: DockPlacement, cx: &App) -> bool {
667 match placement {
668 DockPlacement::Left => self
669 .left_dock
670 .as_ref()
671 .map(|dock| dock.read(cx).collapsible)
672 .unwrap_or(false),
673 DockPlacement::Bottom => self
674 .bottom_dock
675 .as_ref()
676 .map(|dock| dock.read(cx).collapsible)
677 .unwrap_or(false),
678 DockPlacement::Right => self
679 .right_dock
680 .as_ref()
681 .map(|dock| dock.read(cx).collapsible)
682 .unwrap_or(false),
683 DockPlacement::Center => false,
684 }
685 }
686
687 pub fn toggle_dock(
689 &self,
690 placement: DockPlacement,
691 window: &mut Window,
692 cx: &mut Context<Self>,
693 ) {
694 let dock = match placement {
695 DockPlacement::Left => &self.left_dock,
696 DockPlacement::Bottom => &self.bottom_dock,
697 DockPlacement::Right => &self.right_dock,
698 DockPlacement::Center => return,
699 };
700
701 if let Some(dock) = dock {
702 dock.update(cx, |view, cx| {
703 view.toggle_open(window, cx);
704 })
705 }
706 }
707
708 pub fn set_toggle_button_visible(&mut self, visible: bool, _: &mut Context<Self>) {
710 self.toggle_button_visible = visible;
711 }
712
713 pub fn add_panel(
715 &mut self,
716 panel: Arc<dyn PanelView>,
717 placement: DockPlacement,
718 bounds: Option<Bounds<Pixels>>,
719 window: &mut Window,
720 cx: &mut Context<Self>,
721 ) {
722 let weak_self = cx.entity().downgrade();
723 match placement {
724 DockPlacement::Left => {
725 if let Some(dock) = self.left_dock.as_ref() {
726 dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
727 } else {
728 self.set_left_dock(
729 DockItem::tabs(vec![panel], None, &weak_self, window, cx),
730 None,
731 true,
732 window,
733 cx,
734 );
735 }
736 }
737 DockPlacement::Bottom => {
738 if let Some(dock) = self.bottom_dock.as_ref() {
739 dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
740 } else {
741 self.set_bottom_dock(
742 DockItem::tabs(vec![panel], None, &weak_self, window, cx),
743 None,
744 true,
745 window,
746 cx,
747 );
748 }
749 }
750 DockPlacement::Right => {
751 if let Some(dock) = self.right_dock.as_ref() {
752 dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
753 } else {
754 self.set_right_dock(
755 DockItem::tabs(vec![panel], None, &weak_self, window, cx),
756 None,
757 true,
758 window,
759 cx,
760 );
761 }
762 }
763 DockPlacement::Center => {
764 self.items
765 .add_panel(panel, &cx.entity().downgrade(), bounds, window, cx);
766 }
767 }
768 }
769
770 pub fn remove_panel(
772 &mut self,
773 panel: Arc<dyn PanelView>,
774 placement: DockPlacement,
775 window: &mut Window,
776 cx: &mut Context<Self>,
777 ) {
778 match placement {
779 DockPlacement::Left => {
780 if let Some(dock) = self.left_dock.as_mut() {
781 dock.update(cx, |dock, cx| {
782 dock.remove_panel(panel, window, cx);
783 });
784 }
785 }
786 DockPlacement::Right => {
787 if let Some(dock) = self.right_dock.as_mut() {
788 dock.update(cx, |dock, cx| {
789 dock.remove_panel(panel, window, cx);
790 });
791 }
792 }
793 DockPlacement::Bottom => {
794 if let Some(dock) = self.bottom_dock.as_mut() {
795 dock.update(cx, |dock, cx| {
796 dock.remove_panel(panel, window, cx);
797 });
798 }
799 }
800 DockPlacement::Center => {
801 self.items.remove_panel(panel, window, cx);
802 }
803 }
804 cx.notify();
805 }
806
807 pub fn remove_panel_from_all_docks(
809 &mut self,
810 panel: Arc<dyn PanelView>,
811 window: &mut Window,
812 cx: &mut Context<Self>,
813 ) {
814 self.remove_panel(panel.clone(), DockPlacement::Center, window, cx);
815 self.remove_panel(panel.clone(), DockPlacement::Left, window, cx);
816 self.remove_panel(panel.clone(), DockPlacement::Right, window, cx);
817 self.remove_panel(panel.clone(), DockPlacement::Bottom, window, cx);
818 }
819
820 pub fn load(
824 &mut self,
825 state: DockAreaState,
826 window: &mut Window,
827 cx: &mut Context<Self>,
828 ) -> Result<()> {
829 self.version = state.version;
830 let weak_self = cx.entity().downgrade();
831
832 if let Some(left_dock_state) = state.left_dock {
833 self.left_dock = Some(left_dock_state.to_dock(weak_self.clone(), window, cx));
834 }
835
836 if let Some(right_dock_state) = state.right_dock {
837 self.right_dock = Some(right_dock_state.to_dock(weak_self.clone(), window, cx));
838 }
839
840 if let Some(bottom_dock_state) = state.bottom_dock {
841 self.bottom_dock = Some(bottom_dock_state.to_dock(weak_self.clone(), window, cx));
842 }
843
844 self.items = state.center.to_item(weak_self, window, cx);
845 self.update_toggle_button_tab_panels(window, cx);
846 Ok(())
847 }
848
849 pub fn dump(&self, cx: &App) -> DockAreaState {
853 let root = self.items.view();
854 let center = root.dump(cx);
855
856 let left_dock = self
857 .left_dock
858 .as_ref()
859 .map(|dock| DockState::new(dock.clone(), cx));
860 let right_dock = self
861 .right_dock
862 .as_ref()
863 .map(|dock| DockState::new(dock.clone(), cx));
864 let bottom_dock = self
865 .bottom_dock
866 .as_ref()
867 .map(|dock| DockState::new(dock.clone(), cx));
868
869 DockAreaState {
870 version: self.version,
871 center,
872 left_dock,
873 right_dock,
874 bottom_dock,
875 }
876 }
877
878 #[allow(clippy::only_used_in_recursion)]
880 fn subscribe_item(&mut self, item: &DockItem, window: &mut Window, cx: &mut Context<Self>) {
881 match item {
882 DockItem::Split { items, view, .. } => {
883 for item in items {
884 self.subscribe_item(item, window, cx);
885 }
886
887 self._subscriptions.push(cx.subscribe_in(
888 view,
889 window,
890 move |_, _, event, window, cx| match event {
891 PanelEvent::LayoutChanged => {
892 cx.spawn_in(window, async move |view, window| {
893 _ = view.update_in(window, |view, window, cx| {
894 view.update_toggle_button_tab_panels(window, cx)
895 });
896 })
897 .detach();
898 cx.emit(DockEvent::LayoutChanged);
899 }
900 _ => {}
901 },
902 ));
903 }
904 DockItem::Tabs { .. } => {
905 }
907 DockItem::Tiles { .. } => {
908 }
910 DockItem::Panel { .. } => {
911 }
913 }
914 }
915
916 pub(crate) fn subscribe_panel<P: Panel>(
918 &mut self,
919 view: &Entity<P>,
920 window: &mut Window,
921 cx: &mut Context<DockArea>,
922 ) {
923 let subscription =
924 cx.subscribe_in(
925 view,
926 window,
927 move |_, panel, event, window, cx| match event {
928 PanelEvent::ZoomIn => {
929 let panel = panel.clone();
930 cx.spawn_in(window, async move |view, window| {
931 _ = view.update_in(window, |view, window, cx| {
932 view.set_zoomed_in(panel, window, cx);
933 cx.notify();
934 });
935 })
936 .detach();
937 }
938 PanelEvent::ZoomOut => cx
939 .spawn_in(window, async move |view, window| {
940 _ = view.update_in(window, |view, window, cx| {
941 view.set_zoomed_out(window, cx);
942 });
943 })
944 .detach(),
945 PanelEvent::LayoutChanged => {
946 cx.spawn_in(window, async move |view, window| {
947 _ = view.update_in(window, |view, window, cx| {
948 view.update_toggle_button_tab_panels(window, cx)
949 });
950 })
951 .detach();
952 cx.emit(DockEvent::LayoutChanged);
953 }
954 },
955 );
956
957 self._subscriptions.push(subscription);
958 }
959
960 pub fn id(&self) -> SharedString {
962 self.id.clone()
963 }
964
965 pub fn set_zoomed_in<P: Panel>(
966 &mut self,
967 panel: Entity<P>,
968 _: &mut Window,
969 cx: &mut Context<Self>,
970 ) {
971 self.zoom_view = Some(panel.into());
972 cx.notify();
973 }
974
975 pub fn set_zoomed_out(&mut self, _: &mut Window, cx: &mut Context<Self>) {
976 self.zoom_view = None;
977 cx.notify();
978 }
979
980 fn render_items(&self, _window: &mut Window, _cx: &mut Context<Self>) -> AnyElement {
981 match &self.items {
982 DockItem::Split { view, .. } => view.clone().into_any_element(),
983 DockItem::Tabs { view, .. } => view.clone().into_any_element(),
984 DockItem::Tiles { view, .. } => view.clone().into_any_element(),
985 DockItem::Panel { view, .. } => view.clone().view().into_any_element(),
986 }
987 }
988
989 pub fn update_toggle_button_tab_panels(&mut self, _: &mut Window, cx: &mut Context<Self>) {
990 self.toggle_button_panels.left = self
992 .items
993 .left_top_tab_panel(cx)
994 .map(|view| view.entity_id());
995
996 self.toggle_button_panels.right = self
998 .items
999 .right_top_tab_panel(cx)
1000 .map(|view| view.entity_id());
1001
1002 self.toggle_button_panels.bottom = self
1004 .bottom_dock
1005 .as_ref()
1006 .and_then(|dock| dock.read(cx).panel.left_top_tab_panel(cx))
1007 .map(|view| view.entity_id());
1008 }
1009}
1010impl EventEmitter<DockEvent> for DockArea {}
1011impl Render for DockArea {
1012 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
1013 let view = cx.entity().clone();
1014
1015 div()
1016 .id("dock-area")
1017 .relative()
1018 .size_full()
1019 .overflow_hidden()
1020 .child(
1021 canvas(
1022 move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds),
1023 |_, _, _, _| {},
1024 )
1025 .absolute()
1026 .size_full(),
1027 )
1028 .map(|this| {
1029 if let Some(zoom_view) = self.zoom_view.clone() {
1030 this.child(zoom_view)
1031 } else {
1032 match &self.items {
1033 DockItem::Tiles { view, .. } => {
1034 this.child(view.clone())
1036 }
1037 _ => {
1038 this.child(
1040 div()
1041 .flex()
1042 .flex_row()
1043 .h_full()
1044 .when_some(self.left_dock.clone(), |this, dock| {
1046 this.child(div().flex().flex_none().child(dock))
1047 })
1048 .child(
1050 div()
1051 .flex()
1052 .flex_1()
1053 .flex_col()
1054 .overflow_hidden()
1055 .child(
1057 div()
1058 .flex_1()
1059 .overflow_hidden()
1060 .child(self.render_items(window, cx)),
1061 )
1062 .when_some(self.bottom_dock.clone(), |this, dock| {
1064 this.child(dock)
1065 }),
1066 )
1067 .when_some(self.right_dock.clone(), |this, dock| {
1069 this.child(div().flex().flex_none().child(dock))
1070 }),
1071 )
1072 }
1073 }
1074 }
1075 })
1076 }
1077}