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 {
|
pub fn new(axis: Axis, _: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let state = cx.new(|_| ResizableState::default());
|
let state = cx.new(|_| ResizableState::default());
|
||||||
|
|
||||||
// Bubble up the resize event.
|
let _subscriptions = vec![
|
||||||
let _subscriptions = vec![cx.subscribe(&state, |_, _, _: &ResizablePanelEvent, cx| {
|
// Bubble up the resize event.
|
||||||
cx.emit(PanelEvent::LayoutChanged)
|
cx.subscribe(&state, |_, _, _: &ResizablePanelEvent, cx| {
|
||||||
})];
|
cx.emit(PanelEvent::LayoutChanged)
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
axis,
|
axis,
|
||||||
|
|
@ -253,7 +255,7 @@ impl StackPanel {
|
||||||
Some(size) => size,
|
Some(size) => size,
|
||||||
None => {
|
None => {
|
||||||
let state = self.state.read(cx);
|
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 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;
|
use crate::PixelsExt;
|
||||||
|
|
||||||
|
|
@ -66,23 +68,56 @@ impl ResizableState {
|
||||||
..Default::default()
|
..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 {
|
if let Some(ix) = ix {
|
||||||
self.panels.insert(ix, panel_state);
|
self.panels.insert(ix, panel_state);
|
||||||
self.sizes.insert(ix, size.unwrap_or(PANEL_MIN_SIZE));
|
self.sizes.insert(ix, size);
|
||||||
} else {
|
} else {
|
||||||
self.panels.push(panel_state);
|
self.panels.push(panel_state);
|
||||||
self.sizes.push(size.unwrap_or(PANEL_MIN_SIZE));
|
self.sizes.push(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.notify();
|
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;
|
self.axis = axis;
|
||||||
|
|
||||||
if panels_count > self.panels.len() {
|
if panels_count > self.panels.len() {
|
||||||
let diff = panels_count - self.panels.len();
|
let diff = panels_count - self.panels.len();
|
||||||
self.panels
|
self.panels
|
||||||
.extend(vec![ResizablePanelState::default(); diff]);
|
.extend(vec![ResizablePanelState::default(); diff]);
|
||||||
self.sizes.extend(vec![PANEL_MIN_SIZE; 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);
|
self.resizing_panel_ix = Some(resizing_panel_ix - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cx.notify();
|
self.adjust_to_container_size(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn replace_panel(
|
pub(crate) fn replace_panel(
|
||||||
|
|
@ -127,7 +162,7 @@ impl ResizableState {
|
||||||
|
|
||||||
self.panels[panel_ix] = panel;
|
self.panels[panel_ix] = panel;
|
||||||
self.sizes[panel_ix] = old_size;
|
self.sizes[panel_ix] = old_size;
|
||||||
cx.notify();
|
self.adjust_to_container_size(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear(&mut self) {
|
pub(crate) fn clear(&mut self) {
|
||||||
|
|
@ -135,8 +170,9 @@ impl ResizableState {
|
||||||
self.sizes.clear();
|
self.sizes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn total_size(&self) -> Pixels {
|
#[inline]
|
||||||
self.sizes.iter().map(|s| s.as_f32()).sum::<f32>().into()
|
pub(crate) fn container_size(&self) -> Pixels {
|
||||||
|
self.bounds.size.along(self.axis)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn done_resizing(&mut self, cx: &mut Context<Self>) {
|
pub(crate) fn done_resizing(&mut self, cx: &mut Context<Self>) {
|
||||||
|
|
@ -168,7 +204,7 @@ impl ResizableState {
|
||||||
if ix >= old_sizes.len() - 1 {
|
if ix >= old_sizes.len() - 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let container_size = self.bounds.size.along(self.axis);
|
let container_size = self.container_size();
|
||||||
self.sync_real_panel_sizes(cx);
|
self.sync_real_panel_sizes(cx);
|
||||||
|
|
||||||
let move_changed = size - old_sizes[ix];
|
let move_changed = size - old_sizes[ix];
|
||||||
|
|
@ -226,21 +262,25 @@ impl ResizableState {
|
||||||
cx.notify();
|
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.
|
/// 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>) {
|
fn adjust_to_container_size(&mut self, cx: &mut Context<Self>) {
|
||||||
// At least 1px so we don't divide by zero.
|
if self.container_size().is_zero() {
|
||||||
let container_size = self.bounds.size.along(self.axis).max(px(1.));
|
return;
|
||||||
let total_size = self.total_size();
|
}
|
||||||
|
|
||||||
|
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() {
|
for i in 0..self.panels.len() {
|
||||||
let size = self.sizes[i];
|
let size = self.sizes[i];
|
||||||
let fractional_size = size / total_size;
|
let ratio = size / total_size;
|
||||||
let new_size = container_size * fractional_size;
|
let new_size = container_size * ratio;
|
||||||
|
|
||||||
self.sizes[i] = new_size;
|
self.sizes[i] = new_size;
|
||||||
self.panels[i].size = Some(new_size);
|
self.panels[i].size = Some(new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,8 +136,8 @@ impl RenderOnce for ResizablePanelGroup {
|
||||||
|
|
||||||
// Sync panels to the state
|
// Sync panels to the state
|
||||||
let panels_count = self.children.len();
|
let panels_count = self.children.len();
|
||||||
state.update(cx, |state, _| {
|
state.update(cx, |state, cx| {
|
||||||
state.sync_panels_count(self.axis, panels_count);
|
state.sync_panels_count(self.axis, panels_count, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
container
|
container
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue