resizable, dock: Improve Panel adjust size logic on layout changed. (#1588)
This is a follow up to #1560 I noticed a couple small issues with resizable panels and their use in docks after experimenting a lot: - Sometimes the resizable group would take about ~500ms to update after a resize. This was caused by the `StackPanel` not getting notified and therefore not redrawn when the `ResizableState` was notified, as well as the notify in `adjust_to_container_size` not actually firing properly. The latter was fixed by defering the notify, though I'm still not quite sure why that is necessary. - In a couple of places, the total size all the panel missmatches the container size, causing small glitches the first time a panel is resized. Update the code in those places to properly adjust all the panel sizes so they match the container size. --------- Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
parent
2a98628b5e
commit
78efbfdd7b
3 changed files with 64 additions and 22 deletions
|
|
@ -56,10 +56,12 @@ impl StackPanel {
|
|||
pub fn new(axis: Axis, _: &mut Window, cx: &mut Context<Self>) -> 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)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Self>,
|
||||
) {
|
||||
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::<f32>().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<Self>) {
|
||||
|
|
@ -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<Self>) {
|
||||
// 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::<f32>());
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue