UI.7: preserve split resizing beside panel scrollbars

This commit is contained in:
skylar taylor-barrick
2026-10-10 09:40:28 -06:00
parent c4fbe220bc
commit fc77f4cc7f
2 changed files with 67 additions and 6 deletions
+8 -6
View File
@@ -218,6 +218,14 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
let (lo, hi) = split_bounds(wanted.available(), *axis, self.node_limits(first), self.node_limits(second));
let first_size = wanted.first_size().clamp(lo, hi);
let Some(split) = split_rect(rect, *axis, SplitSize::FixedFirst(first_size), self.style.gap, 0.0) else { return };
path.push(false);
self.node(ui, first, split.first, path);
path.pop();
path.push(true);
self.node(ui, second, split.second, path);
path.pop();
// The divider's inner-edge hit margin must win over child scrollbars, just
// as accordion dividers win over the adjacent panel's content.
let response = ResizeHandle::new(
self.id.with(("split", &*path)),
*axis,
@@ -232,12 +240,6 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
{
self.output.actions.push(Action::ResizeSplit { path: path.clone(), size });
}
path.push(false);
self.node(ui, first, split.first, path);
path.pop();
path.push(true);
self.node(ui, second, split.second, path);
path.pop();
}
Node::Tabs { panels, active } => self.group(ui, panels, *active, rect, false),
Node::Stack { entries } => {
+59
View File
@@ -509,6 +509,65 @@ fn stack_dividers_resize_adjacent_open_bodies_by_pointer_and_keyboard() {
assert!((entries[0].height.unwrap() + entries[2].height.unwrap() - 300.0).abs() < 0.001);
}
#[test]
fn split_inner_edge_remains_draggable_beside_a_scrollbar() {
let ctx = Context::default();
let mut layout = Layout {
root: Some(Node::Split {
axis: SplitAxis::Horizontal,
size: SplitSize::FixedFirst(220.0),
first: Box::new(tabs(&[1], 0)),
second: Box::new(tabs(&[2], 0)),
}),
floating: Vec::new(),
};
let frame = |layout: &mut Layout<u32>, events| {
let mut output = DockOutput::default();
let mut sidebar = Rect::NOTHING;
ctx.run_ui(RawInput { screen_rect: Some(Rect::from_min_size(pos2(0.0, 0.0), vec2(800.0, 600.0))), events, ..Default::default() }, |ui| {
output = DockArea::new(Id::new("scrolling-dock")).show(
ui,
layout,
&DockStyle::from_ui(ui),
|p| format!("Panel {p}"),
|_| Permissions::default(),
|ui, panel| {
if *panel == 1 {
sidebar = ui.max_rect();
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysVisible)
.show(ui, |ui| {
for row in 0..100 {
ui.label(format!("Library item {row}"));
}
});
} else {
ui.label("Document");
}
},
);
})
.textures_delta
.clear();
assert!(output.error.is_none());
for action in output.actions {
layout.apply(action).unwrap();
}
sidebar
};
for _ in 0..3 {
frame(&mut layout, Vec::new());
}
let sidebar = frame(&mut layout, Vec::new());
let start = pos2(sidebar.right() - 2.0, sidebar.center().y);
frame(&mut layout, vec![Event::PointerMoved(start), pointer_button(start, true)]);
frame(&mut layout, vec![Event::PointerMoved(start + vec2(40.0, 0.0))]);
frame(&mut layout, vec![pointer_button(start + vec2(40.0, 0.0), false)]);
let Node::Split { size: SplitSize::FixedFirst(width), .. } = layout.root.unwrap() else { panic!() };
assert!((width - 260.0).abs() < 0.001, "sidebar width after inner-edge drag: {width}");
}
#[test]
fn restoring_a_panel_beside_a_nested_subtree_preserves_the_complete_workspace() {
let mut layout = Layout {