docking: content that fills its body floats at floating_size or 320

A panel body that reaches the bottom of its rect has no height of its
own, so a floated panel or group falls back to floating_size, else 320
points, instead of the whole frame height. A body in a window fitted to
its recorded height keeps that height. Panels leaving a frame fit their
content like panels leaving a dock.
This commit is contained in:
aiongg committed 2026-10-20 00:57:14 +00:00
1 parent b94df6c8b6
commit 30cebdc0f2
5 files changed
+63 -16

No files matched your search

+27
View File
@@ -54,10 +54,16 @@ struct TestContent {
whole_group: bool,
/// Height every body takes; `None` draws a one-line label.
body_height: Option<f32>,
/// Every body takes its whole rect.
fill: bool,
}
impl DockContent<u32> for TestContent {
fn body(&mut self, ui: &mut egui::Ui, _: &u32) {
if self.fill {
ui.allocate_rect(ui.max_rect(), egui::Sense::hover());
return;
}
match self.body_height {
Some(height) => _ = ui.allocate_exact_size(vec2(10.0, height), egui::Sense::hover()),
None => _ = ui.label("Body"),
@@ -593,6 +599,27 @@ fn a_short_docked_panel_floats_at_its_content_height_up_to_its_floating_size() {
assert_eq!(*height, title + strip + 150.0);
}
#[test]
fn a_panel_that_fills_its_frame_floats_at_its_floating_size_or_320() {
let float = |floating_size: Option<Vec2>| {
let content = TestContent { fill: true, floating_size, ..Default::default() };
let mut d = Driver::with(frames(), content, Rect::from_min_size(pos2(0.0, 0.0), vec2(600.0, 600.0)));
let target = pos2(400.0, 300.0);
d.hold(d.tab(3).center(), target);
d.modifiers = Modifiers::COMMAND;
let out = d.step(vec![button(target, false)]);
out.actions
.iter()
.find_map(|a| match a {
Action::Float { panel: 3, rect } => Some(rect[3]),
_ => None,
})
.unwrap_or_else(|| panic!("{:?}", out.actions))
};
assert_eq!(float(None), 320.0);
assert_eq!(float(Some(vec2(300.0, 400.0))), 400.0);
}
#[test]
fn tab_focus_follows_only_the_users_input() {
let mut d = Driver::new(docked());
+1 -1
View File
@@ -218,7 +218,7 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
ui.new_child(UiBuilder::new().id(self.id.with(("body", panel))).max_rect(rect).layout(egui::Layout::top_down(egui::Align::Min)));
child.set_clip_rect(rect.intersect(ui.clip_rect()));
self.content.body(&mut child, panel);
self.float_space.record_content(panel.clone(), child.min_rect().height());
self.float_space.record_content(panel.clone(), child.min_rect().height(), rect.height());
// Any button: a canvas pans with the middle button and opens menus with the secondary.
if child.rect_contains_pointer(rect) && child.input(|i| i.pointer.any_pressed()) {
self.output.focused = Some(panel.clone());
+9 -4
View File
@@ -279,16 +279,21 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
}
/// Size of a panel floated by a drag or the Float menu: the host's floating size, else the
/// source size within the panel limits. Out of a dock column, or with a floating size, the
/// height fits the content the panel last drew, up to that height (320 in a dock).
/// source size within the panel limits. Out of a dock or frame, or with a floating size, the
/// height fits the content the panel last drew, up to that height (else 320). Content that
/// filled its body floats at that height.
pub(super) fn float_size(&self, panel: &P, source: Vec2) -> Vec2 {
let chrome = self.style.title_height + self.style.tab_height;
if let Some(size) = self.content.floating_size(panel).filter(|s| s.is_finite() && s.x > 0.0 && s.y > 0.0) {
return vec2(size.x, self.fitted_height(panel, chrome, size.y).unwrap_or(size.y));
}
let mut size = self.iconic.float_width(panel).map_or(source, |width| vec2(width, DOCKED_FLOAT_HEIGHT));
if matches!(self.layout.place(panel), Some(Place::Dock { .. })) {
size.y = self.fitted_height(panel, chrome, DOCKED_FLOAT_HEIGHT).unwrap_or(size.y.min(DOCKED_FLOAT_HEIGHT));
if matches!(self.layout.place(panel), Some(Place::Dock { .. } | Place::Node(_))) {
size.y = match self.fitted_height(panel, chrome, DOCKED_FLOAT_HEIGHT) {
Some(height) => height,
None if self.float_space.filled(panel) => DOCKED_FLOAT_HEIGHT,
None => size.y.min(DOCKED_FLOAT_HEIGHT),
};
}
let limits = self.limits(panel);
let header = vec2(0.0, self.style.tab_height);
+21 -8
View File
@@ -255,8 +255,8 @@ pub struct FloatSpace<P> {
pub(super) groups: Vec<(P, Vec2)>,
pub(super) columns: Vec<(P, Vec2)>,
/// The height each panel's content used when it was last drawn, which fits a floated group
/// to its content.
pub(super) contents: Vec<(P, f32)>,
/// to its content; `None` for content that filled its body.
pub(super) contents: Vec<(P, Option<f32>)>,
}
impl<P: PartialEq> FloatSpace<P> {
@@ -275,9 +275,16 @@ impl<P: PartialEq> FloatSpace<P> {
}
impl<P: PartialEq> FloatSpace<P> {
pub(super) fn record_content(&mut self, panel: P, height: f32) {
let height = if height.is_finite() { height.max(0.0) } else { 0.0 };
match self.contents.iter_mut().find(|(p, _)| *p == panel) {
/// Record the height `panel`'s content used in a body `available` tall. Content that
/// reaches the body's bottom filled it and has no height of its own, unless the body was
/// sized to the height recorded before (a fitted window).
pub(super) fn record_content(&mut self, panel: P, used: f32, available: f32) {
const TOLERANCE: f32 = 1.0;
let used = if used.is_finite() { used.max(0.0) } else { 0.0 };
let recorded = self.contents.iter_mut().find(|(p, _)| *p == panel);
let fitted = recorded.as_ref().and_then(|(_, height)| *height).is_some_and(|height| height >= available - TOLERANCE);
let height = (used < available - TOLERANCE || fitted).then_some(used);
match recorded {
Some((_, recorded)) => *recorded = height,
None => self.contents.push((panel, height)),
}
@@ -299,8 +306,13 @@ impl<P: PartialEq> FloatSpace<P> {
Self::find(&self.columns, panel)
}
/// Whether `panel`'s content filled its body when it was last drawn.
pub(super) fn filled(&self, panel: &P) -> bool {
self.contents.iter().any(|(p, height)| p == panel && height.is_none())
}
pub(super) fn content(&self, panel: &P) -> Option<f32> {
self.contents.iter().find(|(p, _)| p == panel).map(|(_, height)| *height)
self.contents.iter().find(|(p, _)| p == panel).and_then(|(_, height)| *height)
}
/// The first record wins: an iconic panel is recorded at its icon before its flyout.
@@ -513,8 +525,9 @@ pub trait DockContent<P> {
/// Preferred floating size, independent of the current dock geometry: the width of a
/// floated panel and the most height it takes. A floated panel or group, and a flyout, fit
/// their height to the content the active panel last drew (at least 120 points), up to this
/// height, or up to 320 out of a dock when this is `None`. A rect a command gives and a
/// window the user resized keep their size.
/// height, or up to 320 out of a dock when this is `None`. Content that filled its body
/// takes this height, else 320. A rect a command gives and a window the user resized keep
/// their size.
fn floating_size(&self, _panel: &P) -> Option<Vec2> {
None
}
+5 -3
View File
@@ -156,9 +156,11 @@ the bar itself shrinks it. A tab dropped on the "…" button goes to the end of
A panel or group floated by a drag, a menu or a command without a rect takes its content height:
the title bar, the tab strip and the height the active panel's content used when it was last
drawn (the content ui's `min_rect`), at least 120 points. The most it takes is the height of
`DockContent::floating_size`, or 320 points for a panel or group leaving a dock. A short panel
therefore floats as a short window. A panel's width is its `floating_size` width, else its docked
width; a group keeps its docked width.
`DockContent::floating_size`, or 320 points for a panel leaving a dock or frame and a group
leaving a dock. A short panel therefore floats as a short window. Content that reaches the bottom
of its body (a panel that allocates its whole rect) has no height of its own: it floats at the
`floating_size` height, else at 320 points. A panel's width is its `floating_size` width, else
its docked width; a group keeps its docked width.
A flyout fits its content the same way, without the title bar.
- A panel the area has not drawn floats at the `floating_size` height, else the docked height