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