dock: Fix zoom support for side dock panel. (#327)
- Fix emit `PanelEvent::LayoutChanged` when panel resized.
This commit is contained in:
parent
2198134832
commit
5915618928
5 changed files with 72 additions and 32 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue