1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-02 06:40:06 -04:00

Don't allow resizing Window past minimum content size (#8152)

## Related

- part of #7264 (required for Atoms in window titlebar)
- part of https://github.com/emilk/egui/issues/2921

## What

This implements a fix for this weird edge case when resizing windows
past it's contents minimum allocated width:

Before:



https://github.com/user-attachments/assets/33c6c7b2-3621-4eba-8122-99a3930ff67b


After:


https://github.com/user-attachments/assets/5dd47d8f-32bb-4463-aa01-3a5c8f39b10e


There is a very slight flicker on the very first frame where we detect
the minimum size. We could cover this with a request_discard, but in
practise it should be barely noticable.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Lucas Meurer
2026-05-12 13:57:21 +02:00
committed by GitHub
parent 2925b465c2
commit 1280495301
2 changed files with 84 additions and 2 deletions

View File

@@ -17,6 +17,14 @@ pub(crate) struct State {
/// Externally requested size (e.g. by Window) for the next frame
pub(crate) requested_size: Option<Vec2>,
/// Minimum content width observed during the current interactive resize.
/// `None` until content overflows the offered rect. At that point we record
/// `last_content_size.x` here and clamp `desired_size.x` against it for the
/// rest of the drag, so wrapping/centered widgets see the same width the window will
/// end up clamped to. Reset to `None` whenever a drag is not in progress.
#[cfg_attr(feature = "serde", serde(default))]
observed_min_content_width: Option<f32>,
}
impl State {
@@ -221,6 +229,7 @@ impl Resize {
desired_size: default_size,
last_content_size: vec2(0.0, 0.0),
requested_size: None,
observed_min_content_width: None,
}
});
@@ -242,13 +251,20 @@ impl Resize {
user_requested_size = Some(pointer_pos - position + 0.5 * corner_response.rect.size());
}
if let Some(user_requested_size) = user_requested_size {
if let Some(mut user_requested_size) = user_requested_size {
// We know the minimum width from a previous frame, so lets not shrink past that
if let Some(observed_min) = state.observed_min_content_width {
user_requested_size.x = user_requested_size.x.at_least(observed_min);
}
state.desired_size = user_requested_size;
} else {
// We are not being actively resized, so auto-expand to include size of last frame.
// This prevents auto-shrinking if the contents contain width-filling widgets (separators etc)
// but it makes a lot of interactions with [`Window`]s nicer.
state.desired_size = state.desired_size.max(state.last_content_size);
// No active drag, discard the observed min width, we'll rediscover on next drag,
// in case content changed.
state.observed_min_content_width = None;
}
state.desired_size = state
@@ -305,6 +321,15 @@ impl Resize {
state.last_content_size = content_ui.min_size();
// The content overflowed the rect we provided. This means last_content_size.x is now at the
// very minimum size the content will allow to shrink to. We remember this so we can prevent
// any previous wrapping content from shrinking further.
// 4.0 is a bit of safety margin to ensure we don't prevent shrinking on rounding errors.
let overflowed_x = state.last_content_size.x > state.desired_size.x + 4.0;
if overflowed_x && state.observed_min_content_width.is_none() {
state.observed_min_content_width = Some(state.last_content_size.x);
}
// ------------------------------
let mut size = state.last_content_size;