From 9768755eab4dfa919bc942a3448ae5afd05b9ed9 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 28 Aug 2024 19:40:35 +0800 Subject: [PATCH] Fix panel resize when window resized. (#181) Co-authored-by: Floyd Wang --- crates/app/src/story_workspace.rs | 3 +- crates/ui/src/button.rs | 4 ++ crates/ui/src/dock/mod.rs | 4 +- crates/ui/src/dock/stack_panel.rs | 2 - crates/ui/src/dock/tab_panel.rs | 12 +++-- crates/ui/src/dropdown.rs | 2 + crates/ui/src/resizable/panel.rs | 88 ++++++++++++++----------------- crates/ui/src/tab/tab.rs | 1 + 8 files changed, 55 insertions(+), 61 deletions(-) diff --git a/crates/app/src/story_workspace.rs b/crates/app/src/story_workspace.rs index 86c2c22e..5f219400 100644 --- a/crates/app/src/story_workspace.rs +++ b/crates/app/src/story_workspace.rs @@ -65,7 +65,7 @@ impl StoryWorkspace { let stock_panel1 = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx)); view.add_panel( stock_panel1.clone(), - Some(px(400.)), + Some(px(380.)), weak_dock_area.clone(), cx, ); @@ -307,7 +307,6 @@ impl Render for StoryWorkspace { .relative() .size_full() .flex() - .flex_1() .flex_col() .bg(cx.theme().background) .text_color(cx.theme().foreground) diff --git a/crates/ui/src/button.rs b/crates/ui/src/button.rs index 1e011e4d..17f0dfd5 100644 --- a/crates/ui/src/button.rs +++ b/crates/ui/src/button.rs @@ -2,6 +2,7 @@ use crate::{ h_flex, indicator::Indicator, theme::{ActiveTheme, Colorize as _}, + tooltip::Tooltip, Disableable, Icon, Selectable, Sizable, Size, }; use gpui::{ @@ -385,6 +386,9 @@ impl RenderOnce for Button { .children(self.children) }) .when(self.loading, |this| this.bg(normal_style.bg.opacity(0.8))) + .when_some(self.tooltip.clone(), |this, tooltip| { + this.tooltip(move |cx| Tooltip::new(tooltip.clone(), cx)) + }) } } diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index bbec7efd..dc8fe939 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -43,9 +43,7 @@ impl Render for DockArea { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { div() .id("dock-area") - .flex() - .flex_grow() - .flex_shrink() + .size_full() .overflow_hidden() .map(|this| match self.zoom_view.clone() { Some(view) => this.bg(cx.theme().tab_bar).p_3().child( diff --git a/crates/ui/src/dock/stack_panel.rs b/crates/ui/src/dock/stack_panel.rs index f7979166..e44ef468 100644 --- a/crates/ui/src/dock/stack_panel.rs +++ b/crates/ui/src/dock/stack_panel.rs @@ -265,8 +265,6 @@ impl Render for StackPanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { h_flex() .size_full() - .flex_grow() - .flex_shrink() .overflow_hidden() .bg(cx.theme().tab_bar) .child(self.panel_group.clone()) diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index 6b188a4a..345f5fbe 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -14,6 +14,7 @@ use crate::{ popup_menu::PopupMenuExt, tab::{Tab, TabBar}, theme::ActiveTheme, + tooltip::Tooltip, v_flex, AxisExt, IconName, Placement, Selectable, Sizable, StyledExt, }; @@ -174,6 +175,7 @@ impl TabPanel { .icon(IconName::Minimize) .xsmall() .ghost() + .tooltip(t!("Dock.Zoom Out")) .on_click( cx.listener(|view, _, cx| view.on_action_toggle_zoom(&ToggleZoom, cx)), ), @@ -203,6 +205,7 @@ impl TabPanel { if self.panels.len() == 1 { let panel = self.panels.get(0).unwrap(); + let title = panel.title(cx); return h_flex() .justify_between() @@ -216,8 +219,9 @@ impl TabPanel { .px_3() .min_w_16() .overflow_hidden() - .whitespace_nowrap() - .child(panel.title(cx)) + .text_ellipsis() + .child(title.clone()) + .tooltip(move |cx| Tooltip::new(title.clone(), cx)) .on_drag( DragPanel { panel: panel.clone(), @@ -302,6 +306,7 @@ impl TabPanel { .id("tab-content") .group("") .overflow_y_scroll() + .overflow_x_hidden() .flex_1() .child(panel.view()) .on_drag_move(cx.listener(Self::on_panel_drag_move)) @@ -500,9 +505,6 @@ impl Render for TabPanel { .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_action_toggle_zoom)) .size_full() - .flex_grow() - .flex_shrink() - .flex_none() .overflow_hidden() .bg(cx.theme().background) .child(self.render_tabs(cx)) diff --git a/crates/ui/src/dropdown.rs b/crates/ui/src/dropdown.rs index a66d0427..16c91d10 100644 --- a/crates/ui/src/dropdown.rs +++ b/crates/ui/src/dropdown.rs @@ -472,6 +472,8 @@ where } fn toggle_menu(&mut self, _: &ClickEvent, cx: &mut ViewContext) { + cx.stop_propagation(); + self.open = !self.open; if self.open { self.list.focus_handle(cx).focus(cx); diff --git a/crates/ui/src/resizable/panel.rs b/crates/ui/src/resizable/panel.rs index 098c684f..9887514d 100644 --- a/crates/ui/src/resizable/panel.rs +++ b/crates/ui/src/resizable/panel.rs @@ -3,12 +3,13 @@ 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, Size, StatefulInteractiveElement, Style, Styled, View, ViewContext, + Pixels, Render, StatefulInteractiveElement, Style, Styled, View, ViewContext, VisualContext as _, WindowContext, }; use crate::{h_flex, theme::ActiveTheme, v_flex, AxisExt}; +const PANEL_MIN_SIZE: Pixels = px(100.); const HANDLE_PADDING: Pixels = px(4.); #[derive(Clone, Render)] @@ -20,29 +21,29 @@ pub struct ResizablePanelGroup { sizes: Vec, axis: Axis, handle_size: Pixels, - size: Pixels, + size: Option, bounds: Bounds, resizing_panel_ix: Option, - last_window_size: Size, } impl ResizablePanelGroup { - pub(super) fn new(cx: &mut ViewContext) -> Self { - // Observe the window size to update the bounds of the resizable panel group. - cx.observe_window_bounds(Self::on_window_resize).detach(); - + pub(super) fn new(_cx: &mut ViewContext) -> Self { Self { axis: Axis::Horizontal, sizes: Vec::new(), panels: Vec::new(), handle_size: px(1.), - size: px(20.), + size: None, bounds: Bounds::default(), resizing_panel_ix: None, - last_window_size: cx.bounds().size, } } + pub fn load(&mut self, sizes: Vec, panels: Vec>) { + self.sizes = sizes; + self.panels = panels; + } + /// Set the axis of the resizable panel group, default is horizontal. pub fn axis(mut self, axis: Axis) -> Self { self.axis = axis; @@ -63,19 +64,19 @@ impl ResizablePanelGroup { } /// Add a resizable panel to the group. - pub fn child(mut self, panel: ResizablePanel, cx: &mut WindowContext) -> Self { + pub fn child(mut self, panel: ResizablePanel, cx: &mut ViewContext) -> Self { self.add_child(panel, cx); self } /// Add a ResizablePanelGroup as a child to the group. - pub fn group(self, group: ResizablePanelGroup, cx: &mut WindowContext) -> Self { + pub fn group(self, group: ResizablePanelGroup, cx: &mut ViewContext) -> Self { let mut group: ResizablePanelGroup = group; group.handle_size = self.handle_size; let size = group.size; let panel = ResizablePanel::new() .content_view(cx.new_view(|_| group).into()) - .size(size); + .when_some(size, |this, size| this.size(size)); self.child(panel, cx) } @@ -84,13 +85,21 @@ impl ResizablePanelGroup { /// - When the axis is horizontal, the size is the height of the group. /// - When the axis is vertical, the size is the width of the group. pub fn size(mut self, size: Pixels) -> Self { - self.size = size; + self.size = Some(size); self } - pub fn add_child(&mut self, panel: ResizablePanel, cx: &mut WindowContext) { + /// 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) { let mut panel = panel; panel.axis = self.axis; + panel.size = self.default_panel_size(); self.sizes.push(panel.size); self.panels.push(cx.new_view(|_| panel)); } @@ -98,6 +107,7 @@ impl ResizablePanelGroup { pub fn insert_child(&mut self, panel: ResizablePanel, ix: usize, cx: &mut ViewContext) { let mut panel = panel; panel.axis = self.axis; + panel.size = self.default_panel_size(); self.sizes.insert(ix, panel.size); self.panels.insert(ix, cx.new_view(|_| panel)); cx.notify() @@ -112,6 +122,7 @@ impl ResizablePanelGroup { ) { let mut panel = panel; panel.axis = self.axis; + panel.size = self.default_panel_size(); self.sizes[ix] = panel.size; self.panels[ix] = cx.new_view(|_| panel); cx.notify() @@ -129,22 +140,6 @@ impl ResizablePanelGroup { cx.notify() } - fn on_window_resize(&mut self, cx: &mut ViewContext) { - let changed_size = cx.bounds().size - self.last_window_size; - self.last_window_size = cx.bounds().size; - let changed = changed_size.along(self.axis); - - // Avg the change in size across all panels. - // The minimum size limited in ResizablePanel. - let avg_change = changed / self.panels.len() as f32; - for (ix, panel) in self.panels.iter().enumerate() { - self.sizes[ix] += avg_change; - panel.update(cx, |this, _| this.size = self.sizes[ix]); - } - - cx.notify(); - } - fn render_resize_handle(&self, ix: usize, cx: &mut ViewContext) -> impl IntoElement { let axis = self.axis; let neg_offset = -HANDLE_PADDING + px(1.); @@ -253,7 +248,7 @@ impl ResizablePanelGroup { } self.sizes = new_sizes; - for (i, panel) in self.panels.iter_mut().enumerate() { + for (i, panel) in self.panels.iter().enumerate() { let size = self.sizes[i]; panel.update(cx, |this, _| this.size = size); } @@ -271,15 +266,6 @@ impl Render for ResizablePanelGroup { container .size_full() - .flex_grow() - .flex_shrink() - // .map(|this| { - // use crate::StyledExt as _; - // match self.axis { - // Axis::Horizontal => this.debug_red(), - // Axis::Vertical => this.debug_blue(), - // } - // }) .children(self.panels.iter().enumerate().map(|(ix, panel)| { if ix < self.panels.len() - 1 { let handle = self.render_resize_handle(ix, cx); @@ -305,8 +291,6 @@ impl Render for ResizablePanelGroup { } } -const PANEL_MIN_SIZE: Pixels = px(100.); - pub struct ResizablePanel { size: Pixels, axis: Axis, @@ -320,7 +304,7 @@ pub struct ResizablePanel { impl ResizablePanel { pub(super) fn new() -> Self { Self { - size: px(20.), + size: PANEL_MIN_SIZE, axis: Axis::Horizontal, content_builder: None, content_view: None, @@ -353,18 +337,24 @@ impl FluentBuilder for ResizablePanel {} impl Render for ResizablePanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let view = cx.view().clone(); + let axis = self.axis; let size = self.size.max(PANEL_MIN_SIZE); div() - .relative() - .size_full() + .flex() .flex_grow() - .flex_shrink() - .when(self.axis.is_vertical(), |this| this.h(size)) - .when(self.axis.is_horizontal(), |this| this.w(size)) + .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)) .child({ canvas( - move |bounds, cx| view.update(cx, |r, _| r.bounds = bounds), + move |bounds, cx| { + view.update(cx, |r, _| { + r.size = bounds.size.along(axis); + r.bounds = bounds; + }) + }, |_, _, _| {}, ) .absolute() diff --git a/crates/ui/src/tab/tab.rs b/crates/ui/src/tab/tab.rs index a1794d75..eeb2d257 100644 --- a/crates/ui/src/tab/tab.rs +++ b/crates/ui/src/tab/tab.rs @@ -75,6 +75,7 @@ impl RenderOnce for Tab { .items_center() .flex_shrink_0() .cursor_pointer() + .overflow_hidden() .text_color(text_color) .bg(bg_color) .border_x_1()