dock: Add id to DockArea and improve DockArea init. (#204)
- Export add_panel_at method for TabPanel to split_panel. - Fix resizeable_panel init size support. - Fix panel resize to let panel that have initial size will not auto resize on window resize. - Fix resize_handle sometimes will invisibly bug. - Update TabPanel new to support give parent StackPanel.
This commit is contained in:
parent
f611b599aa
commit
8ece86cd0c
6 changed files with 263 additions and 85 deletions
|
|
@ -18,7 +18,7 @@ use ui::{
|
|||
modal::Modal,
|
||||
popup_menu::PopupMenuExt,
|
||||
theme::{ActiveTheme, Colorize as _, Theme},
|
||||
ContextModal, IconName, Root, Sizable,
|
||||
ContextModal, IconName, Placement, Root, Sizable,
|
||||
};
|
||||
|
||||
use crate::app_state::AppState;
|
||||
|
|
@ -52,35 +52,49 @@ impl StoryWorkspace {
|
|||
.detach();
|
||||
|
||||
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx));
|
||||
let dock_area = cx.new_view(|cx| DockArea::new(stack_panel.clone(), cx));
|
||||
let dock_area = cx.new_view(|cx| DockArea::new("main-dock", stack_panel.clone(), cx));
|
||||
let weak_dock_area = dock_area.downgrade();
|
||||
|
||||
let tab_panel = cx.new_view(|cx| TabPanel::new(weak_dock_area.clone(), cx));
|
||||
let right_tab_panel = cx.new_view(|cx| TabPanel::new(weak_dock_area.clone(), cx));
|
||||
let right_tab_panel1 = cx.new_view(|cx| TabPanel::new(weak_dock_area.clone(), cx));
|
||||
let center_tab_panel =
|
||||
cx.new_view(|cx| TabPanel::new(Some(stack_panel.clone()), weak_dock_area.clone(), cx));
|
||||
let left_tab_panel =
|
||||
cx.new_view(|cx| TabPanel::new(Some(stack_panel.clone()), weak_dock_area.clone(), cx));
|
||||
let right_tab_panel =
|
||||
cx.new_view(|cx| TabPanel::new(Some(stack_panel.clone()), weak_dock_area.clone(), cx));
|
||||
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.add_panel(tab_panel.clone(), None, weak_dock_area.clone(), cx);
|
||||
|
||||
let stock_panel1 = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
|
||||
let left_stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
|
||||
left_stack_panel.update(cx, |view, cx| {
|
||||
view.add_panel(left_tab_panel.clone(), None, weak_dock_area.clone(), cx);
|
||||
});
|
||||
view.add_panel(
|
||||
stock_panel1.clone(),
|
||||
Some(px(380.)),
|
||||
left_stack_panel.clone(),
|
||||
Some(px(300.)),
|
||||
weak_dock_area.clone(),
|
||||
cx,
|
||||
);
|
||||
|
||||
stock_panel1.update(cx, |view, cx| {
|
||||
view.add_panel(center_tab_panel.clone(), None, weak_dock_area.clone(), cx);
|
||||
|
||||
let right_stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
|
||||
right_stack_panel.update(cx, |view, cx| {
|
||||
view.add_panel(right_tab_panel.clone(), None, weak_dock_area.clone(), cx);
|
||||
view.add_panel(right_tab_panel1.clone(), None, weak_dock_area.clone(), cx);
|
||||
})
|
||||
});
|
||||
view.add_panel(
|
||||
right_stack_panel.clone(),
|
||||
Some(px(340.)),
|
||||
weak_dock_area.clone(),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Buttons",
|
||||
"Displays a button or a component that looks like a button.",
|
||||
ButtonStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -90,7 +104,9 @@ impl StoryWorkspace {
|
|||
"Input",
|
||||
"A control that allows the user to input text.",
|
||||
InputStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -100,7 +116,9 @@ impl StoryWorkspace {
|
|||
"Text",
|
||||
"Links, paragraphs, checkboxes, and more.",
|
||||
TextStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
Some(px(200.)),
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -110,7 +128,9 @@ impl StoryWorkspace {
|
|||
"Switch",
|
||||
"A control that allows the user to toggle between two states.",
|
||||
SwitchStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -120,7 +140,9 @@ impl StoryWorkspace {
|
|||
"Dropdowns",
|
||||
"Displays a list of options for the user to pick from—triggered by a button.",
|
||||
DropdownStory::new(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -130,7 +152,9 @@ impl StoryWorkspace {
|
|||
"Modal",
|
||||
"Modal & Drawer use examples",
|
||||
ModalStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -140,7 +164,9 @@ impl StoryWorkspace {
|
|||
"Popup",
|
||||
"A popup displays content on top of the main page.",
|
||||
PopupStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -150,7 +176,9 @@ impl StoryWorkspace {
|
|||
"Tooltip",
|
||||
"Displays a short message when users hover over an element.",
|
||||
TooltipStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
right_tab_panel.clone(),
|
||||
Some(Placement::Top),
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -160,7 +188,9 @@ impl StoryWorkspace {
|
|||
"List",
|
||||
"A list displays a series of items.",
|
||||
ListStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
left_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -170,7 +200,9 @@ impl StoryWorkspace {
|
|||
"Icon",
|
||||
"Icon use examples",
|
||||
IconStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
left_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
Some(px(200.)),
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -180,7 +212,9 @@ impl StoryWorkspace {
|
|||
"Image",
|
||||
"Render SVG image and Chart",
|
||||
ImageStory::view(cx).into(),
|
||||
right_tab_panel1.clone(),
|
||||
right_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -198,7 +232,9 @@ impl StoryWorkspace {
|
|||
"Table",
|
||||
"Powerful table and datagrids built.",
|
||||
TableStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -208,8 +244,10 @@ impl StoryWorkspace {
|
|||
"Progress",
|
||||
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
|
||||
ProgressStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
true,
|
||||
center_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
Some(px(200.)),
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
|
|
@ -218,7 +256,9 @@ impl StoryWorkspace {
|
|||
"Resizable",
|
||||
"Accessible resizable panel groups and layouts with keyboard support.",
|
||||
ResizableStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -228,7 +268,9 @@ impl StoryWorkspace {
|
|||
"Scrollable",
|
||||
"A scrollable area with scroll bar.",
|
||||
ScrollableStory::view(cx).into(),
|
||||
tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -238,7 +280,9 @@ impl StoryWorkspace {
|
|||
"Calendar",
|
||||
"A calendar component.",
|
||||
CalendarStory::view(cx).into(),
|
||||
right_tab_panel.clone(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ pub use webview_story::WebViewStory;
|
|||
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder as _, px, AnyView, AppContext, Div, EventEmitter,
|
||||
FocusableView, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
|
||||
FocusableView, InteractiveElement, IntoElement, ParentElement, Pixels, Render, SharedString,
|
||||
StatefulInteractiveElement, Styled as _, Task, View, ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ use ui::{
|
|||
h_flex,
|
||||
label::Label,
|
||||
popup_menu::PopupMenu,
|
||||
v_flex,
|
||||
v_flex, Placement,
|
||||
};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
|
|
@ -120,11 +120,14 @@ impl StoryContainer {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn add_panel(
|
||||
name: impl Into<SharedString>,
|
||||
description: impl Into<SharedString>,
|
||||
story: AnyView,
|
||||
tab_panel: View<TabPanel>,
|
||||
placement: Option<Placement>,
|
||||
size: Option<Pixels>,
|
||||
closeable: bool,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<Result<View<Self>>> {
|
||||
|
|
@ -135,7 +138,11 @@ impl StoryContainer {
|
|||
tab_panel.update(&mut cx, |panel, cx| {
|
||||
let view =
|
||||
cx.new_view(|cx| Self::new(name, description, closeable, cx).story(story));
|
||||
panel.add_panel(Arc::new(view.clone()), cx);
|
||||
if let Some(placement) = placement {
|
||||
panel.add_panel_at(Arc::new(view.clone()), placement, size, cx);
|
||||
} else {
|
||||
panel.add_panel(Arc::new(view.clone()), cx);
|
||||
}
|
||||
view
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ mod tab_panel;
|
|||
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder, AnyWeakView, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, Render, Styled, View, ViewContext,
|
||||
ParentElement as _, Render, SharedString, Styled, View, ViewContext,
|
||||
};
|
||||
pub use panel::*;
|
||||
pub use stack_panel::*;
|
||||
|
|
@ -14,18 +14,29 @@ actions!(dock, [ToggleZoom, ClosePanel]);
|
|||
|
||||
/// The main area of the dock.
|
||||
pub struct DockArea {
|
||||
id: SharedString,
|
||||
root: View<StackPanel>,
|
||||
zoom_view: Option<AnyWeakView>,
|
||||
}
|
||||
|
||||
impl DockArea {
|
||||
pub fn new(root: View<StackPanel>, _cx: &mut ViewContext<Self>) -> Self {
|
||||
pub fn new(
|
||||
id: impl Into<SharedString>,
|
||||
root: View<StackPanel>,
|
||||
_cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
root,
|
||||
zoom_view: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the dock area.
|
||||
pub fn id(&self) -> SharedString {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
/// Toggles the zoom view.
|
||||
pub fn toggle_zoom<P: Panel>(&mut self, panel: View<P>, cx: &mut ViewContext<Self>) {
|
||||
if self.zoom_view.is_some() {
|
||||
|
|
@ -35,6 +46,25 @@ impl DockArea {
|
|||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Returns the root stack panel.
|
||||
pub fn root(&self) -> View<StackPanel> {
|
||||
self.root.clone()
|
||||
}
|
||||
|
||||
/// Return the index of the panel.
|
||||
pub fn index_of_panel<P: Panel>(
|
||||
&self,
|
||||
panel: View<P>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<usize> {
|
||||
self.root.read(cx).index_of_panel(panel)
|
||||
}
|
||||
|
||||
/// Return the existing panel by type.
|
||||
pub fn panel<P: Panel>(&self, cx: &mut ViewContext<Self>) -> Option<View<P>> {
|
||||
self.root.read(cx).panel::<P>(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for DockArea {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel};
|
|||
use gpui::{
|
||||
prelude::FluentBuilder as _, Axis, DismissEvent, Entity, EventEmitter, FocusHandle,
|
||||
FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
@ -62,6 +62,24 @@ impl StackPanel {
|
|||
.position(|p| p.view().entity_id() == entity_id)
|
||||
}
|
||||
|
||||
/// Return the existing panel view by type.
|
||||
pub fn panel<P>(&self, cx: &WindowContext) -> Option<View<P>>
|
||||
where
|
||||
P: Panel,
|
||||
{
|
||||
self.panels.iter().find_map(|p| {
|
||||
if let Ok(p) = p.view().downcast::<P>() {
|
||||
Some(p)
|
||||
} else {
|
||||
if let Ok(tab) = p.view().downcast::<TabPanel>() {
|
||||
tab.read(cx).panel(cx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a panel at the end of the stack.
|
||||
pub fn add_panel<P>(
|
||||
&mut self,
|
||||
|
|
@ -76,19 +94,35 @@ impl StackPanel {
|
|||
}
|
||||
|
||||
pub fn add_panel_at<P>(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
placement: Placement,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
self.insert_panel_at(panel, self.panels_len(), placement, size, dock_area, cx);
|
||||
}
|
||||
|
||||
pub fn insert_panel_at<P>(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
ix: usize,
|
||||
placement: Placement,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
match placement {
|
||||
Placement::Top | Placement::Left => self.insert_panel_before(panel, ix, dock_area, cx),
|
||||
Placement::Top | Placement::Left => {
|
||||
self.insert_panel_before(panel, ix, size, dock_area, cx)
|
||||
}
|
||||
Placement::Right | Placement::Bottom => {
|
||||
self.insert_panel_after(panel, ix, dock_area, cx)
|
||||
self.insert_panel_after(panel, ix, size, dock_area, cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,12 +132,13 @@ impl StackPanel {
|
|||
&mut self,
|
||||
panel: View<P>,
|
||||
ix: usize,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
self.insert_panel(panel, ix, None, dock_area, cx);
|
||||
self.insert_panel(panel, ix, size, dock_area, cx);
|
||||
}
|
||||
|
||||
/// Insert a panel after the index.
|
||||
|
|
@ -111,12 +146,13 @@ impl StackPanel {
|
|||
&mut self,
|
||||
panel: View<P>,
|
||||
ix: usize,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
self.insert_panel(panel, ix + 1, None, dock_area, cx);
|
||||
self.insert_panel(panel, ix + 1, size, dock_area, cx);
|
||||
}
|
||||
|
||||
fn new_resizable_panel<P>(panel: View<P>, size: Option<Pixels>) -> ResizablePanel
|
||||
|
|
@ -176,6 +212,12 @@ impl StackPanel {
|
|||
})
|
||||
.detach();
|
||||
|
||||
let ix = if ix > self.panels.len() {
|
||||
self.panels.len()
|
||||
} else {
|
||||
ix
|
||||
};
|
||||
|
||||
self.panels.insert(ix, Arc::new(panel.clone()));
|
||||
self.panel_group.update(cx, |view, cx| {
|
||||
view.insert_child(Self::new_resizable_panel(panel, size), ix, cx)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ use std::sync::Arc;
|
|||
use gpui::{
|
||||
div, prelude::FluentBuilder, rems, AnchorCorner, AppContext, DefiniteLength, DismissEvent,
|
||||
DragMoveEvent, Empty, EventEmitter, FocusHandle, FocusableView, InteractiveElement as _,
|
||||
IntoElement, ParentElement, Render, ScrollHandle, StatefulInteractiveElement, Styled, View,
|
||||
ViewContext, VisualContext as _, WeakView, WindowContext,
|
||||
IntoElement, ParentElement, Pixels, Render, ScrollHandle, StatefulInteractiveElement, Styled,
|
||||
View, ViewContext, VisualContext as _, WeakView, WindowContext,
|
||||
};
|
||||
use rust_i18n::t;
|
||||
|
||||
|
|
@ -71,11 +71,15 @@ pub struct TabPanel {
|
|||
}
|
||||
|
||||
impl TabPanel {
|
||||
pub fn new(dock_area: WeakView<DockArea>, cx: &mut ViewContext<Self>) -> Self {
|
||||
pub fn new(
|
||||
stack_panel: Option<View<StackPanel>>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
dock_area,
|
||||
stack_panel: None,
|
||||
stack_panel,
|
||||
panels: Vec::new(),
|
||||
active_ix: 0,
|
||||
tab_bar_scroll_handle: ScrollHandle::new(),
|
||||
|
|
@ -99,6 +103,21 @@ impl TabPanel {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
/// Return the existing panel view by type.
|
||||
pub fn panel<P: Panel>(&self, cx: &WindowContext) -> Option<View<P>> {
|
||||
self.panels.iter().find_map(|p| {
|
||||
if let Ok(p) = p.view().downcast::<P>() {
|
||||
Some(p)
|
||||
} else {
|
||||
if let Ok(stack) = p.view().downcast::<StackPanel>() {
|
||||
stack.read(cx).panel::<P>(cx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a panel to the end of the tabs
|
||||
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
||||
if self
|
||||
|
|
@ -115,6 +134,19 @@ impl TabPanel {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
/// Add panel to try to split
|
||||
pub fn add_panel_at(
|
||||
&mut self,
|
||||
panel: Arc<dyn PanelView>,
|
||||
placement: Placement,
|
||||
size: Option<Pixels>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.will_split_placement = Some(placement);
|
||||
self.split_panel(panel, placement, size, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn insert_panel_at(
|
||||
&mut self,
|
||||
panel: Arc<dyn PanelView>,
|
||||
|
|
@ -401,7 +433,7 @@ impl TabPanel {
|
|||
|
||||
// Insert into new tabs
|
||||
if let Some(placement) = self.will_split_placement {
|
||||
self.split_panel(panel, placement, cx);
|
||||
self.split_panel(panel, placement, None, cx);
|
||||
} else {
|
||||
if let Some(ix) = ix {
|
||||
self.insert_panel_at(panel, ix, cx)
|
||||
|
|
@ -418,11 +450,12 @@ impl TabPanel {
|
|||
&self,
|
||||
panel: Arc<dyn PanelView>,
|
||||
placement: Placement,
|
||||
size: Option<Pixels>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let dock_area = self.dock_area.clone();
|
||||
// wrap the panel in a TabPanel
|
||||
let new_tab_panel = cx.new_view(|cx| Self::new(dock_area.clone(), cx));
|
||||
let new_tab_panel = cx.new_view(|cx| Self::new(None, dock_area.clone(), cx));
|
||||
new_tab_panel.update(cx, |view, cx| {
|
||||
view.add_panel(panel, cx);
|
||||
});
|
||||
|
|
@ -436,11 +469,11 @@ impl TabPanel {
|
|||
|
||||
if parent_axis.is_vertical() && placement.is_vertical() {
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.add_panel_at(new_tab_panel, ix, placement, dock_area.clone(), cx);
|
||||
view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx);
|
||||
});
|
||||
} else if parent_axis.is_horizontal() && placement.is_horizontal() {
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.add_panel_at(new_tab_panel, ix, placement, dock_area.clone(), cx);
|
||||
view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx);
|
||||
});
|
||||
} else {
|
||||
// 1. Create new StackPanel with new axis
|
||||
|
|
@ -466,12 +499,12 @@ impl TabPanel {
|
|||
|
||||
new_stack_panel.update(cx, |view, cx| match placement {
|
||||
Placement::Left | Placement::Top => {
|
||||
view.add_panel(new_tab_panel, None, dock_area.clone(), cx);
|
||||
view.add_panel(new_tab_panel, size, dock_area.clone(), cx);
|
||||
view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx);
|
||||
}
|
||||
Placement::Right | Placement::Bottom => {
|
||||
view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx);
|
||||
view.add_panel(new_tab_panel, None, dock_area.clone(), cx);
|
||||
view.add_panel(new_tab_panel, size, dock_area.clone(), cx);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ use std::rc::Rc;
|
|||
|
||||
use gpui::{
|
||||
canvas, div, prelude::FluentBuilder, px, Along, AnyElement, AnyView, Axis, Bounds, Element,
|
||||
EntityId, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement,
|
||||
Pixels, Render, StatefulInteractiveElement, Style, Styled, View, ViewContext,
|
||||
Entity, EntityId, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent,
|
||||
ParentElement, Pixels, Render, StatefulInteractiveElement, Style, Styled, View, ViewContext,
|
||||
VisualContext as _, WindowContext,
|
||||
};
|
||||
|
||||
|
|
@ -89,26 +89,19 @@ impl ResizablePanelGroup {
|
|||
self
|
||||
}
|
||||
|
||||
/// When add a child panel to the group, resize other panels each into half of the remaining space.
|
||||
fn default_panel_size(&self) -> Pixels {
|
||||
let container_size = self.bounds.size.along(self.axis);
|
||||
let each_size = container_size / (self.panels.len() + 1) as f32;
|
||||
each_size.round()
|
||||
}
|
||||
|
||||
pub fn add_child(&mut self, panel: ResizablePanel, cx: &mut ViewContext<Self>) {
|
||||
let mut panel = panel;
|
||||
panel.axis = self.axis;
|
||||
panel.size = self.default_panel_size();
|
||||
self.sizes.push(panel.size);
|
||||
panel.group = Some(cx.view().clone());
|
||||
self.sizes.push(panel.size.unwrap_or_default());
|
||||
self.panels.push(cx.new_view(|_| panel));
|
||||
}
|
||||
|
||||
pub fn insert_child(&mut self, panel: ResizablePanel, ix: usize, cx: &mut ViewContext<Self>) {
|
||||
let mut panel = panel;
|
||||
panel.axis = self.axis;
|
||||
panel.size = self.default_panel_size();
|
||||
self.sizes.insert(ix, panel.size);
|
||||
panel.group = Some(cx.view().clone());
|
||||
self.sizes.insert(ix, panel.size.unwrap_or_default());
|
||||
self.panels.insert(ix, cx.new_view(|_| panel));
|
||||
cx.notify()
|
||||
}
|
||||
|
|
@ -122,8 +115,8 @@ impl ResizablePanelGroup {
|
|||
) {
|
||||
let mut panel = panel;
|
||||
panel.axis = self.axis;
|
||||
panel.size = self.default_panel_size();
|
||||
self.sizes[ix] = panel.size;
|
||||
panel.group = Some(cx.view().clone());
|
||||
self.sizes[ix] = panel.size.unwrap_or_default();
|
||||
self.panels[ix] = cx.new_view(|_| panel);
|
||||
cx.notify()
|
||||
}
|
||||
|
|
@ -142,7 +135,7 @@ impl ResizablePanelGroup {
|
|||
|
||||
fn render_resize_handle(&self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let axis = self.axis;
|
||||
let neg_offset = -HANDLE_PADDING + px(1.);
|
||||
let neg_offset = -HANDLE_PADDING;
|
||||
let view = cx.view().clone();
|
||||
|
||||
div()
|
||||
|
|
@ -153,14 +146,14 @@ impl ResizablePanelGroup {
|
|||
.when(self.axis.is_horizontal(), |this| {
|
||||
this.cursor_col_resize()
|
||||
.top_0()
|
||||
.right(neg_offset)
|
||||
.left(neg_offset)
|
||||
.h_full()
|
||||
.w(px(1.))
|
||||
.px(HANDLE_PADDING)
|
||||
})
|
||||
.when(self.axis.is_vertical(), |this| {
|
||||
this.cursor_row_resize()
|
||||
.bottom(neg_offset)
|
||||
.top(neg_offset)
|
||||
.left_0()
|
||||
.w_full()
|
||||
.h(px(1.))
|
||||
|
|
@ -250,7 +243,9 @@ impl ResizablePanelGroup {
|
|||
self.sizes = new_sizes;
|
||||
for (i, panel) in self.panels.iter().enumerate() {
|
||||
let size = self.sizes[i];
|
||||
panel.update(cx, |this, _| this.size = size);
|
||||
if size > px(0.) {
|
||||
panel.update(cx, |this, _| this.changed_size = Some(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,8 +262,8 @@ impl Render for ResizablePanelGroup {
|
|||
container
|
||||
.size_full()
|
||||
.children(self.panels.iter().enumerate().map(|(ix, panel)| {
|
||||
if ix < self.panels.len() - 1 {
|
||||
let handle = self.render_resize_handle(ix, cx);
|
||||
if ix > 0 {
|
||||
let handle = self.render_resize_handle(ix - 1, cx);
|
||||
panel.update(cx, |view, _| {
|
||||
view.resize_handle = Some(handle.into_any_element())
|
||||
});
|
||||
|
|
@ -292,7 +287,9 @@ impl Render for ResizablePanelGroup {
|
|||
}
|
||||
|
||||
pub struct ResizablePanel {
|
||||
size: Pixels,
|
||||
group: Option<View<ResizablePanelGroup>>,
|
||||
size: Option<Pixels>,
|
||||
changed_size: Option<Pixels>,
|
||||
axis: Axis,
|
||||
content_builder: Option<Rc<dyn Fn(&mut WindowContext) -> AnyElement>>,
|
||||
content_view: Option<AnyView>,
|
||||
|
|
@ -304,7 +301,9 @@ pub struct ResizablePanel {
|
|||
impl ResizablePanel {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
size: PANEL_MIN_SIZE,
|
||||
group: None,
|
||||
size: None,
|
||||
changed_size: None,
|
||||
axis: Axis::Horizontal,
|
||||
content_builder: None,
|
||||
content_view: None,
|
||||
|
|
@ -327,9 +326,29 @@ impl ResizablePanel {
|
|||
}
|
||||
|
||||
pub fn size(mut self, size: Pixels) -> Self {
|
||||
self.size = size;
|
||||
self.size = Some(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Save the real panel size, and update group sizes
|
||||
fn update_size(&mut self, bounds: Bounds<Pixels>, cx: &mut ViewContext<Self>) {
|
||||
let new_size = bounds.size.along(self.axis);
|
||||
self.bounds = bounds;
|
||||
|
||||
let panel_view = cx.view().clone();
|
||||
if let Some(group) = self.group.as_ref() {
|
||||
group.update(cx, |view, _| {
|
||||
if let Some(ix) = view
|
||||
.panels
|
||||
.iter()
|
||||
.position(|v| v.entity_id() == panel_view.entity_id())
|
||||
{
|
||||
view.sizes[ix] = new_size;
|
||||
}
|
||||
})
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl FluentBuilder for ResizablePanel {}
|
||||
|
|
@ -337,24 +356,27 @@ impl FluentBuilder for ResizablePanel {}
|
|||
impl Render for ResizablePanel {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let view = cx.view().clone();
|
||||
let axis = self.axis;
|
||||
let size = self.size.max(PANEL_MIN_SIZE);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_grow()
|
||||
.size_full()
|
||||
.relative()
|
||||
.overflow_hidden()
|
||||
.when(self.axis.is_vertical(), |this| this.w_full().h(size))
|
||||
.when(self.axis.is_horizontal(), |this| this.h_full().w(size))
|
||||
.when(self.size.is_none(), |this| this.flex_shrink())
|
||||
.when(self.axis.is_vertical(), |this| this.min_h(PANEL_MIN_SIZE))
|
||||
.when(self.axis.is_horizontal(), |this| this.min_w(PANEL_MIN_SIZE))
|
||||
.when_some(self.size, |this, size| {
|
||||
this.flex_shrink_0()
|
||||
.when(self.axis.is_vertical(), |this| this.h(size))
|
||||
.when(self.axis.is_horizontal(), |this| this.w(size))
|
||||
})
|
||||
.when_some(self.changed_size, |this, size| {
|
||||
this.when(self.axis.is_vertical(), |this| this.h(size))
|
||||
.when(self.axis.is_horizontal(), |this| this.w(size))
|
||||
})
|
||||
.child({
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
view.update(cx, |r, _| {
|
||||
r.size = bounds.size.along(axis);
|
||||
r.bounds = bounds;
|
||||
})
|
||||
},
|
||||
move |bounds, cx| view.update(cx, |r, cx| r.update_size(bounds, cx)),
|
||||
|_, _, _| {},
|
||||
)
|
||||
.absolute()
|
||||
|
|
|
|||
Loading…
Reference in a new issue