dock: Fix zoom support for side dock panel. (#327)

- Fix emit `PanelEvent::LayoutChanged` when panel resized.
This commit is contained in:
Jason Lee 2024-10-10 15:16:16 +08:00 committed by GitHub
parent 2198134832
commit 5915618928
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 72 additions and 32 deletions

View file

@ -6,7 +6,7 @@ use gpui::{
div, prelude::FluentBuilder as _, px, Axis, Element, InteractiveElement as _, IntoElement, div, prelude::FluentBuilder as _, px, Axis, Element, InteractiveElement as _, IntoElement,
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render,
StatefulInteractiveElement, Style, Styled as _, View, ViewContext, VisualContext as _, StatefulInteractiveElement, Style, Styled as _, View, ViewContext, VisualContext as _,
WeakView, WeakView, WindowContext,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -74,10 +74,11 @@ impl Dock {
let panel = cx.new_view(|cx| { let panel = cx.new_view(|cx| {
let mut tab = TabPanel::new(None, dock_area.clone(), cx); let mut tab = TabPanel::new(None, dock_area.clone(), cx);
tab.closeable = false; tab.closeable = false;
tab.zoomable = false;
tab tab
}); });
Self::subscribe_panel_events(dock_area.clone(), panel.clone(), cx);
Self { Self {
placement, placement,
dock_area, dock_area,
@ -106,7 +107,10 @@ impl Dock {
size: Pixels, size: Pixels,
panel: View<TabPanel>, panel: View<TabPanel>,
open: bool, open: bool,
cx: &mut WindowContext,
) -> Self { ) -> Self {
Self::subscribe_panel_events(dock_area.clone(), panel.clone(), cx);
Self { Self {
placement, placement,
dock_area, dock_area,
@ -117,6 +121,21 @@ impl Dock {
} }
} }
fn subscribe_panel_events(
dock_area: WeakView<DockArea>,
panel: View<TabPanel>,
cx: &mut WindowContext,
) {
// Subscribe the panel to the dock area.
cx.defer({
move |cx| {
_ = dock_area.update(cx, |this, cx| {
this.subscribe_panel(&panel, cx);
});
}
});
}
pub fn set_panels(&mut self, panels: Vec<Arc<dyn PanelView>>, cx: &mut ViewContext<Self>) { pub fn set_panels(&mut self, panels: Vec<Arc<dyn PanelView>>, cx: &mut ViewContext<Self>) {
self.panel.update(cx, |tab_panel, _| { self.panel.update(cx, |tab_panel, _| {
tab_panel.panels = panels; tab_panel.panels = panels;

View file

@ -10,7 +10,7 @@ pub use dock::*;
use gpui::{ use gpui::{
actions, canvas, div, prelude::FluentBuilder, AnyElement, AnyView, AppContext, Axis, Bounds, actions, canvas, div, prelude::FluentBuilder, AnyElement, AnyView, AppContext, Axis, Bounds,
EventEmitter, InteractiveElement as _, IntoElement, ParentElement as _, Pixels, Render, EventEmitter, InteractiveElement as _, IntoElement, ParentElement as _, Pixels, Render,
SharedString, Styled, View, ViewContext, VisualContext, WeakView, WindowContext, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
}; };
pub use panel::*; pub use panel::*;
pub use stack_panel::*; pub use stack_panel::*;
@ -50,6 +50,8 @@ pub struct DockArea {
right_dock: Option<View<Dock>>, right_dock: Option<View<Dock>>,
/// The top zoom view of the dockarea, if any. /// The top zoom view of the dockarea, if any.
zoom_view: Option<AnyView>, zoom_view: Option<AnyView>,
_subscriptions: Vec<Subscription>,
} }
/// DockItem is a tree structure that represents the layout of the dock. /// DockItem is a tree structure that represents the layout of the dock.
@ -108,6 +110,17 @@ impl DockItem {
stack_panel stack_panel
}); });
cx.defer({
let stack_panel = stack_panel.clone();
let dock_area = dock_area.clone();
move |cx| {
_ = dock_area.update(cx, |this, cx| {
this.subscribe_panel(&stack_panel, cx);
});
}
});
Self::Split { Self::Split {
axis, axis,
items, items,
@ -189,6 +202,7 @@ impl DockArea {
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx)); let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx));
let dock_item = DockItem::Split { let dock_item = DockItem::Split {
axis: Axis::Horizontal, axis: Axis::Horizontal,
items: vec![], items: vec![],
@ -196,7 +210,7 @@ impl DockArea {
view: stack_panel.clone(), view: stack_panel.clone(),
}; };
Self { let mut this = Self {
id: id.into(), id: id.into(),
version, version,
bounds: Bounds::default(), bounds: Bounds::default(),
@ -205,7 +219,12 @@ impl DockArea {
left_dock: None, left_dock: None,
right_dock: None, right_dock: None,
bottom_dock: None, bottom_dock: None,
} _subscriptions: vec![],
};
this.subscribe_panel(&stack_panel, cx);
this
} }
/// Set version of the dock area. /// Set version of the dock area.
@ -372,18 +391,18 @@ impl DockArea {
/// Subscribe event on the panels /// Subscribe event on the panels
#[allow(clippy::only_used_in_recursion)] #[allow(clippy::only_used_in_recursion)]
fn subscribe_item(&self, item: &DockItem, cx: &mut ViewContext<Self>) { fn subscribe_item(&mut self, item: &DockItem, cx: &mut ViewContext<Self>) {
match item { match item {
DockItem::Split { items, view, .. } => { DockItem::Split { items, view, .. } => {
for item in items { for item in items {
self.subscribe_item(item, cx); self.subscribe_item(item, cx);
} }
cx.subscribe(view, move |_, _, event, cx| match event { self._subscriptions
PanelEvent::LayoutChanged => cx.emit(DockEvent::LayoutChanged), .push(cx.subscribe(view, move |_, _, event, cx| match event {
_ => {} PanelEvent::LayoutChanged => cx.emit(DockEvent::LayoutChanged),
}) _ => {}
.detach(); }));
} }
DockItem::Tabs { .. } => { DockItem::Tabs { .. } => {
// We subscribe the tab panel event is in StackPanel insert_panel // We subscribe the tab panel event is in StackPanel insert_panel
@ -392,8 +411,12 @@ impl DockArea {
} }
/// Subscribe zoom event on the panel /// Subscribe zoom event on the panel
pub(crate) fn subscribe_panel<P: Panel>(view: &View<P>, cx: &mut ViewContext<DockArea>) { pub(crate) fn subscribe_panel<P: Panel>(
cx.subscribe(view, move |_, panel, event, cx| match event { &mut self,
view: &View<P>,
cx: &mut ViewContext<DockArea>,
) {
let subscription = cx.subscribe(view, move |_, panel, event, cx| match event {
PanelEvent::ZoomIn => { PanelEvent::ZoomIn => {
let dock_area = cx.view().clone(); let dock_area = cx.view().clone();
let panel = panel.clone(); let panel = panel.clone();
@ -417,8 +440,9 @@ impl DockArea {
.detach() .detach()
} }
PanelEvent::LayoutChanged => cx.emit(DockEvent::LayoutChanged), PanelEvent::LayoutChanged => cx.emit(DockEvent::LayoutChanged),
}) });
.detach();
self._subscriptions.push(subscription);
} }
/// Returns the ID of the dock area. /// Returns the ID of the dock area.

View file

@ -14,8 +14,8 @@ use crate::{
use super::{DockArea, DockItemState, Panel, PanelEvent, PanelView, TabPanel}; use super::{DockArea, DockItemState, Panel, PanelEvent, PanelView, TabPanel};
use gpui::{ use gpui::{
prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, Entity, EventEmitter, FocusHandle, prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, Entity, EventEmitter, FocusHandle,
FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription, View,
VisualContext, WeakView, ViewContext, VisualContext, WeakView,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
@ -25,6 +25,7 @@ pub struct StackPanel {
focus_handle: FocusHandle, focus_handle: FocusHandle,
pub(crate) panels: SmallVec<[Arc<dyn PanelView>; 2]>, pub(crate) panels: SmallVec<[Arc<dyn PanelView>; 2]>,
panel_group: View<ResizablePanelGroup>, panel_group: View<ResizablePanelGroup>,
_subscriptions: Vec<Subscription>,
} }
impl Panel for StackPanel { impl Panel for StackPanel {
@ -59,10 +60,10 @@ impl StackPanel {
}); });
// Bubble up the resize event. // Bubble up the resize event.
cx.subscribe(&panel_group, |_, _, _: &ResizablePanelEvent, cx| { let _subscriptions = vec![cx
cx.emit(PanelEvent::LayoutChanged) .subscribe(&panel_group, |_, _, _: &ResizablePanelEvent, cx| {
}) cx.emit(PanelEvent::LayoutChanged)
.detach(); })];
Self { Self {
axis, axis,
@ -70,6 +71,7 @@ impl StackPanel {
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
panels: SmallVec::new(), panels: SmallVec::new(),
panel_group, panel_group,
_subscriptions,
} }
} }
@ -184,9 +186,11 @@ impl StackPanel {
} }
// Subscribe to the panel's layout change event. // Subscribe to the panel's layout change event.
_ = dock_area.update(cx, |_, cx| { _ = dock_area.update(cx, |this, cx| {
if let Ok(tab_panel) = panel.view().downcast::<TabPanel>() { if let Ok(tab_panel) = panel.view().downcast::<TabPanel>() {
DockArea::subscribe_panel(&tab_panel, cx); this.subscribe_panel(&tab_panel, cx);
} else if let Ok(stack_panel) = panel.view().downcast::<Self>() {
this.subscribe_panel(&stack_panel, cx);
} }
}); });
} }

View file

@ -51,13 +51,14 @@ impl DockState {
) -> Result<View<Dock>> { ) -> Result<View<Dock>> {
let view = self.panel.to_item(dock_area.clone(), cx).view(); let view = self.panel.to_item(dock_area.clone(), cx).view();
if let Ok(tab_panel) = view.view().downcast::<TabPanel>() { if let Ok(tab_panel) = view.view().downcast::<TabPanel>() {
let dock = cx.new_view(|_| { let dock = cx.new_view(|cx| {
Dock::from_state( Dock::from_state(
dock_area.clone(), dock_area.clone(),
self.placement, self.placement,
self.size, self.size,
tab_panel, tab_panel,
self.open, self.open,
cx,
) )
}); });

View file

@ -68,9 +68,6 @@ pub struct TabPanel {
/// If this is true, the Panel closeable will follow the active panel's closeable, /// If this is true, the Panel closeable will follow the active panel's closeable,
/// otherwise this TabPanel will not able to close /// otherwise this TabPanel will not able to close
pub(crate) closeable: bool, pub(crate) closeable: bool,
/// If this is true, the Panel zoomable will follow the active panel's zoomable,
/// otherwise this TabPanel will not able to zoom
pub(crate) zoomable: bool,
/// When drag move, will get the placement of the panel to be split /// When drag move, will get the placement of the panel to be split
will_split_placement: Option<Placement>, will_split_placement: Option<Placement>,
@ -98,10 +95,6 @@ impl Panel for TabPanel {
} }
fn zoomable(&self, cx: &WindowContext) -> bool { fn zoomable(&self, cx: &WindowContext) -> bool {
if !self.zoomable {
return false;
}
self.active_panel() self.active_panel()
.map(|panel| panel.zoomable(cx)) .map(|panel| panel.zoomable(cx))
.unwrap_or(false) .unwrap_or(false)
@ -147,7 +140,6 @@ impl TabPanel {
will_split_placement: None, will_split_placement: None,
is_zoomed: false, is_zoomed: false,
closeable: true, closeable: true,
zoomable: true,
} }
} }