diff --git a/crates/ui/src/dock/stack_panel.rs b/crates/ui/src/dock/stack_panel.rs index 870ae2e0..a2dc7960 100644 --- a/crates/ui/src/dock/stack_panel.rs +++ b/crates/ui/src/dock/stack_panel.rs @@ -56,10 +56,12 @@ impl StackPanel { pub fn new(axis: Axis, _: &mut Window, cx: &mut Context) -> Self { let state = cx.new(|_| ResizableState::default()); - // Bubble up the resize event. - let _subscriptions = vec![cx.subscribe(&state, |_, _, _: &ResizablePanelEvent, cx| { - cx.emit(PanelEvent::LayoutChanged) - })]; + let _subscriptions = vec![ + // Bubble up the resize event. + cx.subscribe(&state, |_, _, _: &ResizablePanelEvent, cx| { + cx.emit(PanelEvent::LayoutChanged) + }), + ]; Self { axis, @@ -253,7 +255,7 @@ impl StackPanel { Some(size) => size, None => { let state = self.state.read(cx); - (state.total_size() / (state.sizes().len() + 1) as f32).max(PANEL_MIN_SIZE) + (state.container_size() / (state.sizes().len() + 1) as f32).max(PANEL_MIN_SIZE) } }; diff --git a/crates/ui/src/resizable/mod.rs b/crates/ui/src/resizable/mod.rs index 9b19e02f..7d310fd5 100644 --- a/crates/ui/src/resizable/mod.rs +++ b/crates/ui/src/resizable/mod.rs @@ -1,6 +1,8 @@ use std::ops::Range; -use gpui::{px, Along, App, Axis, Bounds, Context, ElementId, EventEmitter, Pixels, Window}; +use gpui::{ + px, Along, App, Axis, Bounds, Context, ElementId, EventEmitter, IsZero, Pixels, Window, +}; use crate::PixelsExt; @@ -66,23 +68,56 @@ impl ResizableState { ..Default::default() }; + let size = size.unwrap_or(PANEL_MIN_SIZE); + + // We make sure that the size always sums up to the container size + // by reducing the size of all other panels first. + let container_size = self.container_size().max(px(1.)); + let total_leftover_size = (container_size - size).max(px(1.)); + + for (i, panel) in self.panels.iter_mut().enumerate() { + let ratio = self.sizes[i] / container_size; + self.sizes[i] = total_leftover_size * ratio; + panel.size = Some(self.sizes[i]); + } + if let Some(ix) = ix { self.panels.insert(ix, panel_state); - self.sizes.insert(ix, size.unwrap_or(PANEL_MIN_SIZE)); + self.sizes.insert(ix, size); } else { self.panels.push(panel_state); - self.sizes.push(size.unwrap_or(PANEL_MIN_SIZE)); + self.sizes.push(size); }; + cx.notify(); } - pub(crate) fn sync_panels_count(&mut self, axis: Axis, panels_count: usize) { + pub(crate) fn sync_panels_count( + &mut self, + axis: Axis, + panels_count: usize, + cx: &mut Context, + ) { + let mut changed = self.axis != axis; self.axis = axis; + if panels_count > self.panels.len() { let diff = panels_count - self.panels.len(); self.panels .extend(vec![ResizablePanelState::default(); diff]); self.sizes.extend(vec![PANEL_MIN_SIZE; diff]); + changed = true; + } + + if panels_count < self.panels.len() { + self.panels.truncate(panels_count); + self.sizes.truncate(panels_count); + changed = true; + } + + if changed { + // We need to make sure the total size is in line with the container size. + self.adjust_to_container_size(cx); } } @@ -114,7 +149,7 @@ impl ResizableState { self.resizing_panel_ix = Some(resizing_panel_ix - 1); } } - cx.notify(); + self.adjust_to_container_size(cx); } pub(crate) fn replace_panel( @@ -127,7 +162,7 @@ impl ResizableState { self.panels[panel_ix] = panel; self.sizes[panel_ix] = old_size; - cx.notify(); + self.adjust_to_container_size(cx); } pub(crate) fn clear(&mut self) { @@ -135,8 +170,9 @@ impl ResizableState { self.sizes.clear(); } - pub(crate) fn total_size(&self) -> Pixels { - self.sizes.iter().map(|s| s.as_f32()).sum::().into() + #[inline] + pub(crate) fn container_size(&self) -> Pixels { + self.bounds.size.along(self.axis) } pub(crate) fn done_resizing(&mut self, cx: &mut Context) { @@ -168,7 +204,7 @@ impl ResizableState { if ix >= old_sizes.len() - 1 { return; } - let container_size = self.bounds.size.along(self.axis); + let container_size = self.container_size(); self.sync_real_panel_sizes(cx); let move_changed = size - old_sizes[ix]; @@ -226,21 +262,25 @@ impl ResizableState { cx.notify(); } + /// Adjust panel sizes according to the container size. + /// /// When the container size changes, the panels should take up the same percentage as they did before. fn adjust_to_container_size(&mut self, cx: &mut Context) { - // At least 1px so we don't divide by zero. - let container_size = self.bounds.size.along(self.axis).max(px(1.)); - let total_size = self.total_size(); + if self.container_size().is_zero() { + return; + } + + let container_size = self.container_size(); + let total_size = px(self.sizes.iter().map(|s| s.as_f32()).sum::()); for i in 0..self.panels.len() { let size = self.sizes[i]; - let fractional_size = size / total_size; - let new_size = container_size * fractional_size; + let ratio = size / total_size; + let new_size = container_size * ratio; self.sizes[i] = new_size; self.panels[i].size = Some(new_size); } - cx.notify(); } } diff --git a/crates/ui/src/resizable/panel.rs b/crates/ui/src/resizable/panel.rs index 73684c5d..04f6383c 100644 --- a/crates/ui/src/resizable/panel.rs +++ b/crates/ui/src/resizable/panel.rs @@ -136,8 +136,8 @@ impl RenderOnce for ResizablePanelGroup { // Sync panels to the state let panels_count = self.children.len(); - state.update(cx, |state, _| { - state.sync_panels_count(self.axis, panels_count); + state.update(cx, |state, cx| { + state.sync_panels_count(self.axis, panels_count, cx); }); container