diff --git a/crates/egui/src/atomics/atom_layout.rs b/crates/egui/src/atomics/atom_layout.rs
index 7894273f3..1b44c986b 100644
--- a/crates/egui/src/atomics/atom_layout.rs
+++ b/crates/egui/src/atomics/atom_layout.rs
@@ -226,7 +226,7 @@ impl<'a> AtomLayout<'a> {
max_size.x = f32::INFINITY;
}
- let available_size = ui.available_size().at_most(max_size);
+ let available_size = ui.available_size().at_most(max_size).at_least(min_size);
// The size available for the content
let available_inner_size = available_size - frame.total_margin().sum();
diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs
index 09488058d..10be6307a 100644
--- a/crates/egui/src/containers/area.rs
+++ b/crates/egui/src/containers/area.rs
@@ -594,10 +594,6 @@ fn round_area_position(ctx: &Context, pos: Pos2) -> Pos2 {
}
impl Prepared {
- pub(crate) fn state(&self) -> &AreaState {
- &self.state
- }
-
pub(crate) fn state_mut(&mut self) -> &mut AreaState {
&mut self.state
}
diff --git a/crates/egui/src/containers/collapsing_header.rs b/crates/egui/src/containers/collapsing_header.rs
index de3581288..d921a5cbf 100644
--- a/crates/egui/src/containers/collapsing_header.rs
+++ b/crates/egui/src/containers/collapsing_header.rs
@@ -2,8 +2,8 @@ use std::hash::Hash;
use crate::{
Context, Id, InnerResponse, NumExt as _, Rect, Response, Sense, Stroke, TextStyle,
- TextWrapMode, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, WidgetInfo, WidgetText, WidgetType,
- emath, epaint, pos2, remap, remap_clamp, vec2,
+ TextWrapMode, Ui, UiBuilder, UiKind, UiStackInfo, WidgetInfo, WidgetText, WidgetType, emath,
+ epaint, pos2, remap, remap_clamp, vec2,
};
use emath::GuiRounding as _;
use epaint::{Shape, StrokeKind};
@@ -81,30 +81,6 @@ impl CollapsingState {
}
}
- /// Will toggle when clicked, etc.
- pub(crate) fn show_default_button_with_size(
- &mut self,
- ui: &mut Ui,
- button_size: Vec2,
- ) -> Response {
- let (_id, rect) = ui.allocate_space(button_size);
- let response = ui.interact(rect, self.id, Sense::click());
- response.widget_info(|| {
- WidgetInfo::labeled(
- WidgetType::Button,
- ui.is_enabled(),
- if self.is_open() { "Hide" } else { "Show" },
- )
- });
-
- if response.clicked() {
- self.toggle(ui);
- }
- let openness = self.openness(ui.ctx());
- paint_default_icon(ui, openness, &response);
- response
- }
-
/// Will toggle when clicked, etc.
fn show_default_button_indented(&mut self, ui: &mut Ui) -> Response {
self.show_button_indented(ui, paint_default_icon)
diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs
index c537ac140..679b92c8e 100644
--- a/crates/egui/src/containers/resize.rs
+++ b/crates/egui/src/containers/resize.rs
@@ -50,7 +50,7 @@ pub struct Resize {
pub(crate) min_size: Vec2,
pub(crate) max_size: Vec2,
- default_size: Vec2,
+ pub(crate) default_size: Vec2,
with_stroke: bool,
}
diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs
index 9f25d6131..2c249254f 100644
--- a/crates/egui/src/containers/window.rs
+++ b/crates/egui/src/containers/window.rs
@@ -1,9 +1,7 @@
// WARNING: the code in here is horrible. It is a behemoth that needs breaking up into simpler parts.
-use std::sync::Arc;
-
use emath::GuiRounding as _;
-use epaint::{CornerRadiusF32, RectShape};
+use epaint::CornerRadiusF32;
use crate::collapsing_header::CollapsingState;
use crate::*;
@@ -33,9 +31,9 @@ use super::{Area, Frame, Resize, ScrollArea, area, resize};
/// Note that this is NOT a native OS window.
/// To create a new native OS window, use [`crate::Context::show_viewport_deferred`].
#[must_use = "You should call .show()"]
-pub struct Window<'open> {
- title: WidgetText,
- open: Option<&'open mut bool>,
+pub struct Window<'a> {
+ title: Atoms<'a>,
+ open: Option<&'a mut bool>,
area: Area,
frame: Option,
resize: Resize,
@@ -44,14 +42,15 @@ pub struct Window<'open> {
default_open: bool,
with_title_bar: bool,
fade_out: bool,
+ auto_sized: bool,
}
-impl<'open> Window<'open> {
+impl<'a> Window<'a> {
/// The window title is used as a unique [`Id`] and must be unique, and should not change.
/// This is true even if you disable the title bar with `.title_bar(false)`.
/// If you need a changing title, you must call `window.id(…)` with a fixed id.
- pub fn new(title: impl Into) -> Self {
- let title = title.into().fallback_text_style(TextStyle::Heading);
+ pub fn new(title: impl IntoAtoms<'a>) -> Self {
+ let title: Atoms<'_> = title.into_atoms();
let area = Area::new(Id::new(title.text())).kind(UiKind::Window);
Self {
title,
@@ -61,12 +60,13 @@ impl<'open> Window<'open> {
resize: Resize::default()
.with_stroke(false)
.min_size([96.0, 32.0])
- .default_size([340.0, 420.0]), // Default inner size of a window
+ .default_size([340.0, 420.0]), // Default outer size of a window (includes frame margins, stroke, and title bar)
scroll: ScrollArea::neither().auto_shrink(false),
collapsible: true,
default_open: true,
with_title_bar: true,
fade_out: true,
+ auto_sized: false,
}
}
@@ -118,7 +118,7 @@ impl<'open> Window<'open> {
/// * If `*open == true`, the window will have a close button.
/// * If the close button is pressed, `*open` will be set to `false`.
#[inline]
- pub fn open(mut self, open: &'open mut bool) -> Self {
+ pub fn open(mut self, open: &'a mut bool) -> Self {
self.open = Some(open);
self
}
@@ -213,6 +213,9 @@ impl<'open> Window<'open> {
}
/// Set minimum size of the window, equivalent to calling both `min_width` and `min_height`.
+ ///
+ /// The size refers to the *outer* window size, including the frame's `inner_margin`,
+ /// `outer_margin`, `stroke`, and the title bar.
#[inline]
pub fn min_size(mut self, min_size: impl Into) -> Self {
self.resize = self.resize.min_size(min_size);
@@ -234,6 +237,9 @@ impl<'open> Window<'open> {
}
/// Set maximum size of the window, equivalent to calling both `max_width` and `max_height`.
+ ///
+ /// The size refers to the *outer* window size, including the frame's `inner_margin`,
+ /// `outer_margin`, `stroke`, and the title bar.
#[inline]
pub fn max_size(mut self, max_size: impl Into) -> Self {
self.resize = self.resize.max_size(max_size);
@@ -320,6 +326,9 @@ impl<'open> Window<'open> {
}
/// Set initial size of the window.
+ ///
+ /// The size refers to the *outer* window size, including frame margins, stroke,
+ /// and the title bar.
#[inline]
pub fn default_size(mut self, default_size: impl Into) -> Self {
let default_size: Vec2 = default_size.into();
@@ -345,6 +354,9 @@ impl<'open> Window<'open> {
}
/// Sets the window size and prevents it from being resized by dragging its edges.
+ ///
+ /// The size refers to the *outer* window size, including the frame's `inner_margin`,
+ /// `outer_margin`, `stroke`, and the title bar.
#[inline]
pub fn fixed_size(mut self, size: impl Into) -> Self {
self.resize = self.resize.fixed_size(size);
@@ -399,6 +411,7 @@ impl<'open> Window<'open> {
pub fn auto_sized(mut self) -> Self {
self.resize = self.resize.auto_sized();
self.scroll = ScrollArea::neither();
+ self.auto_sized = true;
self
}
@@ -473,13 +486,12 @@ impl Window<'_> {
default_open,
with_title_bar,
fade_out,
+ auto_sized,
} = self;
let style = ctx.global_style();
- let header_color =
- frame.map_or_else(|| style.visuals.widgets.open.weak_bg_fill, |f| f.fill);
- let mut window_frame = frame.unwrap_or_else(|| Frame::window(&style));
+ let window_frame = frame.unwrap_or_else(|| Frame::window(&style));
let is_explicitly_closed = matches!(open, Some(false));
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
@@ -507,64 +519,37 @@ impl Window<'_> {
let on_top = Some(area_layer_id) == ctx.top_layer_id();
let mut area = area.begin(ctx);
- area.with_widget_info(|| WidgetInfo::labeled(WidgetType::Window, true, title.text()));
-
- // Calculate roughly how much larger the full window inner size is compared to the content rect
- let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar {
- let title_bar_inner_height = ctx
- .fonts_mut(|fonts| title.font_height(fonts, &style))
- .at_least(style.spacing.interact_size.y);
- let title_bar_inner_height = title_bar_inner_height + window_frame.inner_margin.sum().y;
- let half_height = (title_bar_inner_height / 2.0).round() as _;
- window_frame.corner_radius.ne = window_frame.corner_radius.ne.clamp(0, half_height);
- window_frame.corner_radius.nw = window_frame.corner_radius.nw.clamp(0, half_height);
-
- let title_content_spacing = if is_collapsed {
- 0.0
- } else {
- window_frame.stroke.width
- };
- (title_bar_inner_height, title_content_spacing)
- } else {
- (0.0, 0.0)
- };
+ area.with_widget_info(|| {
+ WidgetInfo::labeled(
+ WidgetType::Window,
+ true,
+ title.text().as_deref().unwrap_or(""),
+ )
+ });
{
// Prevent window from becoming larger than the constrain rect.
+ // `resize.max_size` is still in outer-window coordinates here, matching `constrain_rect`.
let constrain_rect = area.constrain_rect();
let max_width = constrain_rect.width();
- let max_height =
- constrain_rect.height() - title_bar_height_with_margin - title_content_spacing;
+ let max_height = constrain_rect.height();
resize.max_size.x = resize.max_size.x.min(max_width);
resize.max_size.y = resize.max_size.y.min(max_height);
}
- // First check for resize to avoid frame delay:
- let last_frame_outer_rect = area.state().rect();
- let resize_interaction = do_resize_interaction(
- ctx,
- possible,
- area.id(),
- area_layer_id,
- last_frame_outer_rect,
- window_frame,
- );
-
+ // The user-supplied min/max/default sizes on `Window` refer to the *outer* window size
+ // (the total footprint, including frame margins, stroke, and title bar). `Resize` sizes
+ // the title bar + inner content area, so we subtract the extra frame margin (the part
+ // outside of `Resize`).
{
- let margins = window_frame.total_margin().sum()
- + vec2(0.0, title_bar_height_with_margin + title_content_spacing);
-
- resize_response(
- resize_interaction,
- ctx,
- margins,
- area_layer_id,
- &mut area,
- resize_id,
- );
+ let frame_margin = window_frame.total_margin().sum();
+ resize.min_size = (resize.min_size - frame_margin).at_least(Vec2::ZERO);
+ resize.max_size = (resize.max_size - frame_margin).at_least(Vec2::ZERO);
+ resize.default_size = (resize.default_size - frame_margin).at_least(Vec2::ZERO);
}
let mut area_content_ui = area.content_ui(ctx);
+
if is_open {
// `Area` already takes care of fade-in animations,
// so we only need to handle fade-out animations here.
@@ -573,55 +558,33 @@ impl Window<'_> {
}
let content_inner = {
- // BEGIN FRAME --------------------------------
- let mut frame = window_frame.begin(&mut area_content_ui);
-
- let show_close_button = open.is_some();
-
- let where_to_put_header_background = &area_content_ui.painter().add(Shape::Noop);
-
- let title_bar = if with_title_bar {
- let title_bar = TitleBar::new(
- &frame.content_ui,
- title,
- show_close_button,
- collapsible,
- window_frame,
- title_bar_height_with_margin,
- );
- resize.min_size.x = resize.min_size.x.at_least(title_bar.inner_rect.width()); // Prevent making window smaller than title bar width
-
- frame.content_ui.set_min_size(title_bar.inner_rect.size());
-
- // Skip the title bar (and separator):
- if is_collapsed {
- frame.content_ui.add_space(title_bar.inner_rect.height());
- } else {
- frame.content_ui.add_space(
- title_bar.inner_rect.height()
- + title_content_spacing
- + window_frame.inner_margin.sum().y,
- );
- }
-
- Some(title_bar)
- } else {
- None
- };
-
- let (content_inner, content_response) = collapsing
- .show_body_unindented(&mut frame.content_ui, |ui| {
- resize.show(ui, |ui| {
- if scroll.is_any_scroll_enabled() {
- scroll.show(ui, add_contents).inner
- } else {
- add_contents(ui)
- }
- })
+ let outer_response = window_frame.show(&mut area_content_ui, |ui| {
+ resize.show(ui, |ui| {
+ if with_title_bar {
+ title_ui(
+ ui,
+ title,
+ window_frame,
+ &mut collapsing,
+ collapsible,
+ on_top,
+ open.as_deref_mut(),
+ auto_sized,
+ );
+ }
+ collapsing
+ .show_body_unindented(ui, |ui| {
+ if scroll.is_any_scroll_enabled() {
+ scroll.show(ui, add_contents).inner
+ } else {
+ add_contents(ui)
+ }
+ })
+ .map(|inner| inner.inner)
})
- .map_or((None, None), |ir| (Some(ir.inner), Some(ir.response)));
+ });
- let outer_rect = frame.end(&mut area_content_ui).rect;
+ let outer_rect = outer_response.response.rect;
// Do resize interaction _again_, to move their widget rectangles on TOP of the rest of the window.
let resize_interaction = do_resize_interaction(
@@ -629,7 +592,7 @@ impl Window<'_> {
possible,
area.id(),
area_layer_id,
- last_frame_outer_rect,
+ outer_rect,
window_frame,
);
@@ -641,50 +604,25 @@ impl Window<'_> {
resize_interaction,
);
- // END FRAME --------------------------------
+ {
+ let margins = window_frame.total_margin().sum();
- if let Some(mut title_bar) = title_bar {
- title_bar.inner_rect = outer_rect.shrink(window_frame.stroke.width);
- title_bar.inner_rect.max.y =
- title_bar.inner_rect.min.y + title_bar_height_with_margin;
-
- if on_top && area_content_ui.visuals().window_highlight_topmost {
- let mut round =
- window_frame.corner_radius - window_frame.stroke.width.round() as u8;
-
- if !is_collapsed {
- round.se = 0;
- round.sw = 0;
- }
-
- area_content_ui.painter().set(
- *where_to_put_header_background,
- RectShape::filled(title_bar.inner_rect, round, header_color),
- );
- }
-
- if false {
- ctx.debug_painter().debug_rect(
- title_bar.inner_rect,
- Color32::LIGHT_BLUE,
- "title_bar.rect",
- );
- }
-
- title_bar.ui(
- &mut area_content_ui,
- content_response.as_ref(),
- open.as_deref_mut(),
- &mut collapsing,
- collapsible,
+ resize_response(
+ resize_interaction,
+ ctx,
+ margins,
+ area_layer_id,
+ &mut area,
+ resize_id,
);
}
+ // END FRAME --------------------------------
collapsing.store(ctx);
paint_frame_interaction(&area_content_ui, outer_rect, resize_interaction);
- content_inner
+ outer_response.inner
};
let full_response = area.end(ctx, area_content_ui);
@@ -992,7 +930,7 @@ fn do_resize_interaction(
let side_grab_radius = style.interaction.resize_grab_radius_side;
let corner_grab_radius = style.interaction.resize_grab_radius_corner;
- let vetrtical_rect = |a: Pos2, b: Pos2| {
+ let vertical_rect = |a: Pos2, b: Pos2| {
Rect::from_min_max(a, b).expand2(vec2(side_grab_radius, -corner_grab_radius))
};
let horizontal_rect = |a: Pos2, b: Pos2| {
@@ -1009,14 +947,14 @@ fn do_resize_interaction(
if possible.resize_right {
let response = side_response(
- vetrtical_rect(rect.right_top(), rect.right_bottom()),
+ vertical_rect(rect.right_top(), rect.right_bottom()),
id.with("right"),
);
right |= response;
}
if possible.resize_left {
let response = side_response(
- vetrtical_rect(rect.left_top(), rect.left_bottom()),
+ vertical_rect(rect.left_top(), rect.left_bottom()),
id.with("left"),
);
left |= response;
@@ -1177,176 +1115,165 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction)
// ----------------------------------------------------------------------------
-struct TitleBar {
- window_frame: Frame,
+/// Show the window titlebar.
+///
+/// Should be placed inside a `Frame::window`. The [`Frame`] it was placed inside should be passed as
+/// an arg and will be used to paint the divider line at the bottom and the highlighted background
+/// when `active` is true.
+#[expect(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
+fn title_ui(
+ ui: &mut Ui,
+ mut title: Atoms<'_>,
+ frame: Frame,
+ collapsing: &mut CollapsingState,
+ collapsible: bool,
+ active: bool,
+ open: Option<&mut bool>,
+ auto_sized: bool,
+) -> Response {
+ let shape_idx = ui.painter().add(Shape::Noop);
- /// Prepared text in the title
- title_galley: Arc,
+ let mut atoms = Atoms::default();
- /// Size of the title bar in an expanded state. This size become known only
- /// after expanding window and painting its content.
- ///
- /// Does not include the stroke, nor the separator line between the title bar and the window contents.
- inner_rect: Rect,
-}
+ let button_size = Vec2::splat(ui.spacing().icon_width);
-impl TitleBar {
- fn new(
- ui: &Ui,
- title: WidgetText,
- show_close_button: bool,
- collapsible: bool,
- window_frame: Frame,
- title_bar_height_with_margin: f32,
- ) -> Self {
- if false {
- ui.debug_painter()
- .debug_rect(ui.min_rect(), Color32::GREEN, "outer_min_rect");
- }
+ // Since the heading height is higher than the button size, we need to allocate the buttons
+ // with the headers height as size, otherwise they'd look slightly off-center.
+ // The shrink is then used to render the buttons with the right size.
+ let heading_font_height =
+ ui.fonts_mut(|f| f.row_height(&TextStyle::Heading.resolve(ui.style())));
+ let button_allocation_size = Vec2::splat(heading_font_height);
+ let button_shrink = (button_allocation_size - button_size) / 2.0;
- let inner_height = title_bar_height_with_margin - window_frame.inner_margin.sum().y;
+ let collapse_atom_id = Id::new("__window_collapse_button");
+ let close_atom_id = Id::new("__window_close_button");
- let item_spacing = ui.spacing().item_spacing;
- let button_size = Vec2::splat(ui.spacing().icon_width.at_most(inner_height));
+ let expanded = collapsing.openness(ui.ctx()) > 0.0;
- let left_pad = ((inner_height - button_size.y) / 2.0).round_ui(); // calculated so that the icon is on the diagonal (if window padding is symmetrical)
+ if collapsible {
+ atoms.push_right(Atom::custom(collapse_atom_id, button_allocation_size));
+ }
- let title_galley = title.into_galley(
- ui,
- Some(crate::TextWrapMode::Extend),
- f32::INFINITY,
- TextStyle::Heading,
- );
+ atoms.push_right(Atom::grow());
- let minimum_width = if collapsible || show_close_button {
- // If at least one button is shown we make room for both buttons (since title should be centered):
- 2.0 * (left_pad + button_size.x + item_spacing.x) + title_galley.size().x
+ if !auto_sized
+ && !title.any_shrink()
+ && let Some(first_text) = title
+ .iter_mut()
+ .find(|a| matches!(a.kind, AtomKind::Text(..)))
+ {
+ first_text.shrink = true;
+ }
+ atoms.extend_right(title);
+
+ atoms.push_right(Atom::grow());
+
+ if open.is_some() {
+ atoms.push_right(Atom::custom(close_atom_id, button_allocation_size));
+ }
+
+ let spacing = ui.spacing().item_spacing.x;
+
+ let mut child_ui = ui.new_child(UiBuilder::new());
+
+ let mut layout = AtomLayout::new(atoms)
+ .gap(spacing)
+ .fallback_font(TextStyle::Heading)
+ .wrap_mode(TextWrapMode::Truncate);
+
+ if expanded {
+ let min_width = if auto_sized {
+ // During auto size, the resize is essentially disabled, meaning we don't get an
+ // available_width we can rely on. Instead, check of large the content grew last frame
+ // and use that for sizing the title bar. Unfortunately this adds a frame delay.
+ ui.response().rect.width()
} else {
- left_pad + title_galley.size().x + left_pad
+ child_ui.available_width()
};
- let min_inner_size = vec2(minimum_width, inner_height);
- let min_rect = Rect::from_min_size(ui.min_rect().min, min_inner_size);
- if false {
- ui.debug_painter()
- .debug_rect(min_rect, Color32::LIGHT_BLUE, "min_rect");
- }
-
- Self {
- window_frame,
- title_galley,
- inner_rect: min_rect, // First estimate - will be refined later
- }
+ layout = layout.min_size(Vec2::new(min_width, 0.0));
}
- /// Finishes painting of the title bar when the window content size already known.
- ///
- /// # Parameters
- ///
- /// - `ui`:
- /// - `outer_rect`:
- /// - `content_response`: if `None`, window is collapsed at this frame, otherwise contains
- /// a result of rendering the window content
- /// - `open`: if `None`, no "Close" button will be rendered, otherwise renders and processes
- /// the "Close" button and writes a `false` if window was closed
- /// - `collapsing`: holds the current expanding state. Can be changed by double click on the
- /// title if `collapsible` is `true`
- /// - `collapsible`: if `true`, double click on the title bar will be handled for a change
- /// of `collapsing` state
- fn ui(
- self,
- ui: &mut Ui,
- content_response: Option<&Response>,
- open: Option<&mut bool>,
- collapsing: &mut CollapsingState,
- collapsible: bool,
- ) {
- let window_frame = self.window_frame;
- let title_inner_rect = self.inner_rect;
+ let layout_response = layout.show(&mut child_ui);
- if false {
- ui.debug_painter()
- .debug_rect(self.inner_rect, Color32::RED, "TitleBar");
- }
+ let mut title_click_rect = layout_response.response.rect + frame.total_margin();
- if collapsible {
- // Show collapse-button:
- let button_center = Align2::LEFT_CENTER
- .align_size_within_rect(Vec2::splat(self.inner_rect.height()), self.inner_rect)
- .center();
- let button_size = Vec2::splat(ui.spacing().icon_width);
- let button_rect = Rect::from_center_size(button_center, button_size);
- let button_rect = button_rect.round_ui();
-
- ui.scope_builder(UiBuilder::new().max_rect(button_rect), |ui| {
- collapsing.show_default_button_with_size(ui, button_size);
- });
- }
-
- if let Some(open) = open {
- // Add close button now that we know our full width:
- if self.close_button_ui(ui).clicked() {
- *open = false;
- }
- }
-
- let text_pos =
- emath::align::center_size_in_rect(self.title_galley.size(), title_inner_rect)
- .left_top();
- let text_pos = text_pos - self.title_galley.rect.min.to_vec2();
- ui.painter().galley(
- text_pos,
- Arc::clone(&self.title_galley),
- ui.visuals().text_color(),
+ // Collapse triangle icon
+ if collapsible && let Some(rect) = layout_response.rect(collapse_atom_id) {
+ let rect = rect.shrink2(button_shrink);
+ title_click_rect = title_click_rect.with_min_x(rect.max.x);
+ let icon_response = child_ui.interact(
+ rect,
+ child_ui.auto_id_with("collapse_button"),
+ Sense::click(),
);
-
- if let Some(content_response) = content_response {
- // Paint separator between title and content:
- let content_rect = content_response.rect;
- if false {
- ui.debug_painter()
- .debug_rect(content_rect, Color32::RED, "content_rect");
- }
- let y = title_inner_rect.bottom() + window_frame.stroke.width / 2.0;
-
- // To verify the sanity of this, use a very wide window stroke
- ui.painter()
- .hline(title_inner_rect.x_range(), y, window_frame.stroke);
+ icon_response.widget_info(|| {
+ WidgetInfo::labeled(
+ WidgetType::Button,
+ child_ui.is_enabled(),
+ if collapsing.is_open() { "Hide" } else { "Show" },
+ )
+ });
+ if icon_response.clicked() {
+ collapsing.toggle(&child_ui);
}
+ let openness = collapsing.openness(child_ui.ctx());
+ crate::collapsing_header::paint_default_icon(&mut child_ui, openness, &icon_response);
+ }
- // Don't cover the close- and collapse buttons:
- let double_click_rect = title_inner_rect.shrink2(vec2(32.0, 0.0));
-
- if false {
- ui.debug_painter()
- .debug_rect(double_click_rect, Color32::GREEN, "double_click_rect");
+ // Close button
+ if let Some(open) = open
+ && let Some(rect) = layout_response.rect(close_atom_id)
+ {
+ let rect = rect.shrink2(button_shrink);
+ title_click_rect = title_click_rect.with_max_x(rect.min.x);
+ if close_button(&mut child_ui, rect).clicked() {
+ *open = false;
}
+ }
- let id = ui.unique_id().with("__window_title_bar");
-
- if ui
- .interact(double_click_rect, id, Sense::CLICK)
+ if collapsible
+ && child_ui
+ .interact(
+ title_click_rect,
+ child_ui.auto_id_with("window_title_click"),
+ Sense::click(),
+ )
.double_clicked()
- && collapsible
- {
- collapsing.toggle(ui);
- }
+ {
+ collapsing.toggle(&child_ui);
}
- /// Paints the "Close" button at the right side of the title bar
- /// and processes clicks on it.
- ///
- /// The button is square and its size is determined by the
- /// [`crate::style::Spacing::icon_width`] setting.
- fn close_button_ui(&self, ui: &mut Ui) -> Response {
- let button_center = Align2::RIGHT_CENTER
- .align_size_within_rect(Vec2::splat(self.inner_rect.height()), self.inner_rect)
- .center();
- let button_size = Vec2::splat(ui.spacing().icon_width);
- let button_rect = Rect::from_center_size(button_center, button_size);
- let button_rect = button_rect.round_to_pixels(ui.pixels_per_point());
- close_button(ui, button_rect)
+ child_ui.set_clip_rect(Rect::EVERYTHING);
+ let mut header_frame = frame.shadow(Shadow::NONE);
+ if active {
+ header_frame = header_frame.fill(ui.visuals().widgets.open.weak_bg_fill);
}
+ if expanded {
+ header_frame.corner_radius.sw = 0;
+ header_frame.corner_radius.se = 0;
+ }
+ child_ui
+ .painter()
+ .set(shape_idx, header_frame.paint(layout_response.rect));
+
+ let mut advance_rect = child_ui.min_rect();
+
+ if auto_sized {
+ // We may not allocate in the horizontal direction as that would break auto sizing.
+ // Allocate a rect with 0 width:
+ advance_rect = advance_rect.with_max_x(advance_rect.min.x);
+ }
+ if expanded {
+ // Account for the margin of the title frame + the margin of the window contents
+ // - the default ui spacing egui would add on this call
+ advance_rect.max.y += frame.total_margin().bottom + frame.inner_margin.top as f32
+ - child_ui.spacing().item_spacing.y;
+ }
+
+ ui.advance_cursor_after_rect(advance_rect);
+
+ layout_response.response
}
/// Paints the "Close" button of the window and processes clicks on it.
diff --git a/crates/egui/src/widget_text.rs b/crates/egui/src/widget_text.rs
index 13e8175a0..8670398fa 100644
--- a/crates/egui/src/widget_text.rs
+++ b/crates/egui/src/widget_text.rs
@@ -1,4 +1,3 @@
-use emath::GuiRounding as _;
use epaint::text::{IntoTag, TextFormat, VariationCoords};
use std::fmt::Formatter;
use std::{borrow::Cow, sync::Arc};
@@ -692,22 +691,6 @@ impl WidgetText {
self.map_rich_text(|text| text.background_color(background_color))
}
- /// Returns a value rounded to [`emath::GUI_ROUNDING`].
- pub(crate) fn font_height(&self, fonts: &mut epaint::FontsView<'_>, style: &Style) -> f32 {
- match self {
- Self::Text(_) => fonts.row_height(&FontSelection::Default.resolve(style)),
- Self::RichText(text) => text.font_height(fonts, style),
- Self::LayoutJob(job) => job.font_height(fonts),
- Self::Galley(galley) => {
- if let Some(placed_row) = galley.rows.first() {
- placed_row.height().round_ui()
- } else {
- galley.size().y.round_ui()
- }
- }
- }
- }
-
pub fn into_layout_job(
self,
style: &Style,
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png
index 96d7706ee..aa52b8bda 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:10d64e017d1d0eba736a4471d28b1602a0cb69d8e2ab53f4ee604b01c9343116
-size 32475
+oid sha256:7c777cd6b36219b92c88ebf5987f9239a5750d25be4a87a58841bcf6db259599
+size 32422
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png
index c9a05a629..faadcbef3 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ab0d730ce0cf5f1d79947601def4f60c0a015e23a5dcd780df65c7ddc7ae7156
-size 27194
+oid sha256:157996b151dd0f450abca6a9cbad4c8237e236300546521e50f95637d9a89c05
+size 27206
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png
index 6134dfad9..12b0c24d3 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6fccbda741a056ae30a7bfd7497f7b0adfb337bdb885d247fbdfef43cc24b54c
-size 26948
+oid sha256:2d941c979def6f30fd227c144d20c7f138f9c06c3338d563ac075c5e17e9b795
+size 26911
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png
index d43e856aa..17f557c8d 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:26d247655398bae33c724ad3c3bdcab330d194093b07442708d5069e256f636b
-size 76542
+oid sha256:6df3c0a298d48a5f2e1b36207909e20ea545a72a97461a3ae0792d21e0554f93
+size 76567
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png
index 60d3993ef..2954609c5 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e3389491f9f5c54cfc2e295abe76ad53f223c31e9489e1d07a8323cf14fcf37d
-size 62628
+oid sha256:e1aa359d3d2767ad0d0ecb635152371b50b3d551f486fb45c6f7fa96969bc8f1
+size 62291
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png
index c59717683..ec34487f1 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6e42e801758bb9e6130a4e94bd0857d6f254e68a7dedebec5af5cb7f7d896068
-size 27822
+oid sha256:05527c073b1ee2f6a15052a9097d1ad515331ff32d572cf510086f9ebb7a7bbc
+size 27253
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png
index f3c65337f..d5f1d13c5 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:41e68fd3a679e2a6e3dd81129de5aa1ded3a8f24cc7b1f2ba0b876240d309d9b
-size 21019
+oid sha256:9aa1b0f9a1ff5167b7f66894e6d38112e73bd3b6a692b4b468fb6a75717d01bd
+size 21009
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png
index 6a050e0e8..99850aa43 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3db502ec416b322e0f98e9737faa52d5d2fd308d47649710fffa0b0bc5996f52
-size 10783
+oid sha256:04f66c848c6f24607d19b809f411a9287ad45067b18da40cf48a77ff65ae9d91
+size 10875
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png
index b187b4cfd..449c2eac0 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c39ae3420fe01d696a032d8d052c405c5623a9208fc673f5cf09188b1e6a539b
-size 115447
+oid sha256:157424625dbb855e116a29a008172f29697a37b03512ef06a2e92e502d5e4128
+size 101816
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png
index 658cb0922..bacae8846 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:19322248e8335301b2ce31fc1f1352993a374dda945d227784fecea2ee831761
-size 25088
+oid sha256:79ee28a9c0d8ef80d53560584312a33569f668e0a49f6ad5d277ffef507f2818
+size 25116
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png
index f6f9a3cda..d7a4c8e1e 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:98f8865d866a6f28ae3e3a16c815770ed691a031b1d06f7d3662a7e94f564606
-size 99318
+oid sha256:ff02fa99b92059d7b4f8286b8a2fd2e27a7f8f005328da97f0b9786aac9bbb17
+size 99316
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png
index ab7de6c2d..b81ed23e0 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:39208c3c2c95f68fb37880b011f866bedd8dbccee33d163a725cb2a5cc6bb1b3
-size 18290
+oid sha256:165821b21d980c98612a45cf54d9ca3578dec023ab0e2194d2166db087019cc3
+size 18293
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png b/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png
index b899d7356..e9aebce92 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d251ffd6c34e4ffa7b5de7fe5ae57a2d8f48ba7c2e03711da2c3f1a3fd84648c
-size 113998
+oid sha256:2027f21fdb132542e01fc592d3150aa876cf2ebb4a4268b8c170ff4f523657f0
+size 114074
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Input Event History.png b/crates/egui_demo_lib/tests/snapshots/demos/Input Event History.png
index 5b3a63aeb..9b1a5e30c 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Input Event History.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Input Event History.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4d959e17ee5c8a32534ab98b6f08db884b2fbf25476d8dc2dc90edc79bd87ea4
-size 25821
+oid sha256:03c4660ce72440f5ebf3db05b4bce1e8caf1899907723d30699d9d65d442f562
+size 24540
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Input Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Input Test.png
index 85c205be0..c1627ebba 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Input Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Input Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c923f523cc77d678929f294b360f60b9f546ddec66d5317ad0eb44bd61a5f927
-size 51733
+oid sha256:3e2f29678c41e45bedc916dbe845f05c343bfad5528a4143c32cac6c3dee41e7
+size 50512
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png
index b7d365f3a..7653e9170 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c9c98d7bfa08e22e217dd9e7031cc49e4b4486f1a9fdd223bd122be07af72365
-size 22550
+oid sha256:d70cd39499c8f9e0edf689b642ef7cc98115c751bf787305f92323f42aae3fd4
+size 21814
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png
index a4a82635b..710b51b78 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:68afa93605427e12fe527f3ca9613095664b4983f1f585a60f14bc2370c0a1f2
-size 47224
+oid sha256:7705ef738605c31bbf067363f5544ce57e698ab869feb8a88e4be4eabf4de7b6
+size 47087
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png
index b0cb0c27b..89e4a647b 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:33959851f1bd2386fcca8fb5700133d14d90db5a8f783fbfaa9f3aebb7b0d5b2
-size 23119
+oid sha256:345ab9ac3586c3cb9d3b02d46c590a81c7bc31778a3b631874520e57b6694076
+size 22994
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png
index 28e6d1845..817eebf8f 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7b918565d66594fe53ed62bb14e357d3489335f3d037ccb088f198d944eb367d
-size 65307
+oid sha256:6e00e1dd95278a003c383f5a3f16d25ee4943073171d862db50cb17e90e421bc
+size 60272
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png
index 7f0357df1..32dc226c9 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f8c3c2912a3a11892e65f94792c77c79400a81d8c913109d39e8ce12f5b095c6
-size 33503
+oid sha256:58d56dce5f1e9766dbfdca4ea98fb71f7062b23a0ee4b08ce7f76056513aa9a5
+size 33440
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png
index 59609d76a..b3c10c6a2 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0c2c7b0d4913b59ef932f6d6349ddab5cc8619b2e8e9a8b5eb3e055a62e6ed60
-size 38382
+oid sha256:353d92d99e9c2350267a43bff7bb9100d3109c82f8d54f9f7a9e3a312ea0c4e6
+size 35378
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png
index 4d4b5c355..9a5859bf5 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9567f56f2dd030608798347e1ab755d95730ba5c5dd1721f1c61147be7216e87
-size 18304
+oid sha256:218cce13bd96c97aca6a529ba49baaeda5a126cf657e6af1788646cb845725d0
+size 17686
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png
index 553f8aa0b..2437ae223 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d19d694e6a70ab6acb45668b391751e82f7beab19f9918e23821c667e8cc9cdb
-size 249733
+oid sha256:fc1d270e0171cc055fe99579d3f7ab52c63783c793c59aaf8c82027bf1dd3ab8
+size 216484
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Popups.png b/crates/egui_demo_lib/tests/snapshots/demos/Popups.png
index 6a18dc9e5..c452f78da 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Popups.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Popups.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9e1c73e28020371429b3e03d540f72dbf886fd40f0ba08bb194e868bbb3c95ff
-size 57230
+oid sha256:3edbe6debf700364949ef481ba1e8b2319db624ddc316570b6180636dcd88935
+size 57262
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/SVG Test.png b/crates/egui_demo_lib/tests/snapshots/demos/SVG Test.png
index 92d19c470..db3c561ce 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/SVG Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/SVG Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c2facd3881e6b107a0dcce9d6e00008a3c9b0f31ad270c35357a87e487180f56
-size 19814
+oid sha256:ebe84ae10b084df7d8373bc4499c5d50a1446a6b707e8220c786a198341d76c8
+size 19189
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png
index de52524ac..16b02f86d 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0ad81eb762150360368a97858ef30bb0d5aff72e71743fa40c3fd4d70ec84cdc
-size 33400
+oid sha256:141271330d4cef517c4ec2c4c2c41306f8b5de386738381a0e4446fa89df7cdf
+size 30450
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png
index 14603f2f2..603404a9b 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c22130891755ac095d73e0494f11ee8e89d0fd0c31a321d3afb969648ece11ef
-size 23675
+oid sha256:be289c58296a7656c8550d25ff92b8cd0b03c5c9c527b9ca4a38acda8bbe3ac1
+size 23807
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png
index 525f23435..c9b177a18 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:465f33e53ebe15b776e2d6d0710f13f2453f00bab1f6ef4319b3491f1d1d3a26
-size 173487
+oid sha256:10e4658168d6a93779463ebb81520f821cbf6eac59391c60d30f5d40ebdf910d
+size 152623
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png
index 34ec70a65..0f2836dd1 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:001f7a1310ddf37be4d9a7f56a95a3079f713b741824a348544561bb16c291fa
-size 118614
+oid sha256:ad81eeb593663cb26295e7fc51987b85a7084b32504e26364f54f9c2129cb790
+size 118048
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png
index b9dea21f0..9473551aa 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8105f7c2b519716cc0a45dffd5f08980f53f35c6c6b788592e1d82506cdacccc
-size 26665
+oid sha256:3c8aaf89b6d5dad6ae7788b58f642f5d80384777ca80dc3819f1aefc5867b148
+size 26086
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Table.png b/crates/egui_demo_lib/tests/snapshots/demos/Table.png
index 909b766b9..d64d3fca7 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Table.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Table.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a9e83d5e83e3003830f7f719b02dd93273733a9b72d388aa42083387d02c1a20
-size 76310
+oid sha256:66f35217b0becce12e251c4e7063c31ac92e080340a0318891771fdff54593db
+size 67388
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png
index b1ed6bcde..ad87167a3 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3d46c87417c49c8462fac6c488e46b1482bbc75f70fe8f7af8391f0d5d28dac3
-size 70271
+oid sha256:97452108d7809775756f1a229644076f5b4c75a6a6d8b8edc082c2448ae35e94
+size 60701
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png
index 0bd5b9ceb..bda94ab51 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:661570ed4bdf24d52ab049e7d3cf22ef4d50542ee5486d133e0a618a6146da42
-size 98582
+oid sha256:29e23e0e45a6e557e569358c89a34dc768ff0b33b47112f864ae8ed119ac6e6e
+size 81119
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png
index 839a15faa..d5513b0ec 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:94c4af5715992f4dbb5bbec6ce67eec1e2f66cfc078a3e704ec386bdb482cac4
-size 30064
+oid sha256:7ad81306e153798d72724e37f23a4037bf986f98e0ecbe3e8d294d23a92ef77c
+size 29975
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png
index bea2050ab..b3447e66a 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9d69b9a12e777ee559a481aec012935a5bfb2ca8b0d48725ecd33e7f0880b2b8
-size 64273
+oid sha256:dde445e82732c45f84acbfa83761348b9b89dd2a4fec500ba5124b0c1e58dd29
+size 63060
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png
index 728dc59b6..a605d136d 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f6c020860fe9cb7503cea548afbf298ebb9dc620133870b528fe7508d04150c8
-size 13691
+oid sha256:789700adbf20ec5508abdfc95d843197715fbec9f35051799ea18e9927207b30
+size 13650
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png
index 951a6fd41..ecc93a7d4 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d033935ca18ebee7e3c35629233cc3e3a73766ac8c0627fcdd8a12660eed703c
-size 35873
+oid sha256:ab0a8201038b2b066c5aff1fd1a35bc7aed95e74cf50c293eee4a76d66623822
+size 35171
diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png
index 933343f25..adc67e7ab 100644
--- a/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png
+++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:77c9058b036770a644f1bfcf9ed1ba7a29a7c98107b1823474c55ccc2880e9e7
-size 485880
+oid sha256:f9c8eecaedce2bbcbdc4b7975a349b9f986463f189041c7ad6bed64809f18bcd
+size 445367
diff --git a/crates/egui_demo_lib/tests/snapshots/modals_1.png b/crates/egui_demo_lib/tests/snapshots/modals_1.png
index d1abde4fa..b2d76916a 100644
--- a/crates/egui_demo_lib/tests/snapshots/modals_1.png
+++ b/crates/egui_demo_lib/tests/snapshots/modals_1.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8bd53b56322123940496700cbd2a73e336fd80eaf49dcb19a958888d66570ddb
-size 47159
+oid sha256:01493c64acb62a24d19c69579a8f8cb8e080a60747792bbbb98e1cccdfe9214f
+size 47175
diff --git a/crates/egui_demo_lib/tests/snapshots/modals_2.png b/crates/egui_demo_lib/tests/snapshots/modals_2.png
index 8f6976ea5..0995b1814 100644
--- a/crates/egui_demo_lib/tests/snapshots/modals_2.png
+++ b/crates/egui_demo_lib/tests/snapshots/modals_2.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d15954e6183558141e05e1b566ec4a794d372503baf436da2a8c8c67299056f1
-size 48238
+oid sha256:4b069ad508fd3a518df3a39e1740d1786c4af4c3c4a479db04c00f701980efa8
+size 48277
diff --git a/crates/egui_demo_lib/tests/snapshots/modals_3.png b/crates/egui_demo_lib/tests/snapshots/modals_3.png
index ab4a99a12..182b7336d 100644
--- a/crates/egui_demo_lib/tests/snapshots/modals_3.png
+++ b/crates/egui_demo_lib/tests/snapshots/modals_3.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1738ecf660979888c70b1046dd759fe0b082e4958814fb19077c4fed2fb4bdef
-size 44338
+oid sha256:e92e72c651e0ad2c0fc38008d24d9ddf28a70e3b35b26898af78e74028e2c251
+size 44357
diff --git a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png
index cbac0d342..7d1519d01 100644
--- a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png
+++ b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dae7239dc065068147387eb313afdbaa3f0df8b81d060dbbc29f5a6a31ad76c8
-size 44309
+oid sha256:1a5f7bc46c7baf62b559458b3596e386d66bf2281b7c35e5fdb6c5c4a64569f2
+size 44347
diff --git a/crates/egui_kittest/tests/regression_tests.rs b/crates/egui_kittest/tests/regression_tests.rs
index e6f056da4..d68920974 100644
--- a/crates/egui_kittest/tests/regression_tests.rs
+++ b/crates/egui_kittest/tests/regression_tests.rs
@@ -1,6 +1,7 @@
use egui::accesskit::{self, Role};
use egui::{
- Button, ComboBox, Image, Label, Modifiers, Popup, Pos2, Rect, Vec2, Widget as _, Window,
+ Align2, Button, ComboBox, FontId, Image, Label, Modifiers, Popup, Pos2, Rect, Stroke,
+ StrokeKind, Vec2, Widget as _, Window,
};
#[cfg(all(feature = "wgpu", feature = "snapshot"))]
use egui_kittest::SnapshotResults;
@@ -511,3 +512,91 @@ fn window_resize_wraps_to_content_min_width() {
window past the non-wrapping label's natural width"
);
}
+
+/// Ensure that the size passed to window is actually treated as outer size (including
+/// margins and borders).
+#[test]
+fn window_fixed_size_is_outer_size() {
+ use egui::{Color32, Frame, Margin, Pos2, Shape};
+
+ let outer_pos = Pos2::new(50.0, 50.0);
+ let outer_size = Vec2::new(300.0, 200.0);
+ let outer_margin = Margin::same(10);
+ let expected_rect = Rect::from_min_size(outer_pos, outer_size);
+
+ let mut harness = Harness::builder()
+ .with_size(Vec2::new(800.0, 600.0))
+ .build_ui(move |ui| {
+ let frame = Frame::window(ui.style()).outer_margin(outer_margin);
+ Window::new("size_test")
+ .frame(frame)
+ .fixed_pos(outer_pos)
+ .fixed_size(outer_size)
+ .show(ui.ctx(), |ui| {
+ // Fill the available space so `Resize` doesn't auto-shrink the window
+ // below the requested fixed size.
+ ui.allocate_space(ui.available_size());
+ });
+
+ // Paint a debug rect on top of everything that marks the expected outer
+ // window rect. In the snapshot this should line up exactly with the
+ // painted window frame.
+ let painter = ui.ctx().debug_painter();
+ painter.rect_stroke(
+ expected_rect,
+ 0.0,
+ Stroke::new(2.0, Color32::RED),
+ StrokeKind::Outside,
+ );
+ painter.text(
+ expected_rect.left_top() + Vec2::new(0.0, -4.0),
+ Align2::LEFT_BOTTOM,
+ "should perfectly match the outer window size/position",
+ FontId::default(),
+ Color32::RED,
+ );
+
+ // Also paint the expected *visible frame* rect (outer rect shrunk by the
+ // frame's outer_margin). In the snapshot this should line up exactly with
+ // the painted window frame.
+ let expected_frame_rect = expected_rect - outer_margin;
+ painter.debug_rect(
+ expected_frame_rect,
+ Color32::GREEN,
+ "should perfectly match the painted window frame",
+ );
+ });
+
+ harness.run();
+
+ #[cfg(all(feature = "wgpu", feature = "snapshot"))]
+ harness.snapshot("window_outer_size");
+
+ fn collect_filled_rect_sizes(shape: &Shape, out: &mut Vec) {
+ match shape {
+ // Skip stroke-only rects (fill == TRANSPARENT), so the debug overlay
+ // doesn't trivially satisfy the size check.
+ Shape::Rect(r) if r.fill != Color32::TRANSPARENT => out.push(r.rect.size()),
+ Shape::Vec(v) => v.iter().for_each(|s| collect_filled_rect_sizes(s, out)),
+ _ => {}
+ }
+ }
+
+ let mut sizes = Vec::new();
+ for clipped in &harness.output().shapes {
+ collect_filled_rect_sizes(&clipped.shape, &mut sizes);
+ }
+
+ // The shape will have the inner size
+ let painted_size = outer_size - outer_margin.sum();
+ let found = sizes
+ .iter()
+ .any(|s| (s.x - painted_size.x).abs() < 0.5 && (s.y - painted_size.y).abs() < 0.5);
+
+ assert!(
+ found,
+ "expected a filled RectShape with size {painted_size:?} (outer size {outer_size:?} \
+ minus outer margin {outer_margin:?}) in the paint output, but no painted rect matched. \
+ Found filled-rect sizes: {sizes:?}"
+ );
+}
diff --git a/crates/egui_kittest/tests/snapshots/window_outer_size.png b/crates/egui_kittest/tests/snapshots/window_outer_size.png
new file mode 100644
index 000000000..bae4f22cb
--- /dev/null
+++ b/crates/egui_kittest/tests/snapshots/window_outer_size.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cba6fbd64df18b2a41635af59c1f50d1352b4de8ddefd7d5389a4f9e518b6c86
+size 23543