From cc7cfd27cabc62233a7b7dbb452f39f544093abe Mon Sep 17 00:00:00 2001 From: ilya sheprut Date: Tue, 24 Mar 2026 13:37:37 +0300 Subject: [PATCH] Fix `horizontal_wrapping` row height after using `text_edit_multiline` (#8000) * [x] I have followed the instructions in the PR template This PR have two commits: * **First commit** - introduction of tests and their canonization image. Expected behaviour is that `horizontal_wrapped_multiline_row_height` would match `horizontal_wrapped_multiline_row_height_reference`, but it doesn't. There is a bug in `horizontal_wrapped` that breaks line height after using `text_edit_multiline`. * **Second commit** - fix. You can see that `horizontal_wrapped_multiline_row_height` now looks like `horizontal_wrapped_multiline_row_height_reference` (although it's not a perfect match, upd: found, this is because of this issue: https://github.com/emilk/egui/issues/4921). I have used LLM to help me with this PR (codex + claude code). BTW, I'm using horizontal_wrapped with end_row instead of vertical + horizontal alternation, because I automatically generate my UI through some complex interactions between elements in my code, and it's can be that my `horizontal` starts in one function, and ends in another. Something like `begin_horizontal`/`end_horizontal`/`get_current_layout` would be very handy, related to https://github.com/emilk/egui/issues/1004. Also, I would like indent to be supported in `horizontal_wrapped`, or also, to have `indent_start`/`indent_end`. This is why I used `monospace("| ")` in my example, it simulates my use-case. --- crates/egui/src/layout.rs | 20 ++++- tests/egui_tests/tests/regression_tests.rs | 85 +++++++++++++++++++ ...orizontal_wrapped_multiline_row_height.png | 3 + ...wrapped_multiline_row_height_reference.png | 3 + 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height.png create mode 100644 tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height_reference.png diff --git a/crates/egui/src/layout.rs b/crates/egui/src/layout.rs index c44c928bb..c35fd254b 100644 --- a/crates/egui/src/layout.rs +++ b/crates/egui/src/layout.rs @@ -623,12 +623,24 @@ impl Layout { if (self.is_vertical() && self.horizontal_align() == Align::Center) || self.horizontal_justify() { - frame_size.x = frame_size.x.max(available_rect.width()); // fill full width + // For wrapping layouts, fill the current column width, not the entire layout width. + let width = if self.main_wrap { + region.cursor.width() + } else { + available_rect.width() + }; + frame_size.x = frame_size.x.max(width); // fill full width } if (self.is_horizontal() && self.vertical_align() == Align::Center) || self.vertical_justify() { - frame_size.y = frame_size.y.max(available_rect.height()); // fill full height + // For wrapping layouts, fill the current row height, not the entire layout height. + let height = if self.main_wrap { + region.cursor.height() + } else { + available_rect.height() + }; + frame_size.y = frame_size.y.max(height); // fill full height } let align2 = match self.main_dir { @@ -791,14 +803,14 @@ impl Layout { let new_top = region.cursor.bottom() + spacing.y; region.cursor = Rect::from_min_max( pos2(region.max_rect.left(), new_top), - pos2(INFINITY, new_top + region.cursor.height()), + pos2(INFINITY, new_top), ); } Direction::RightToLeft => { let new_top = region.cursor.bottom() + spacing.y; region.cursor = Rect::from_min_max( pos2(-INFINITY, new_top), - pos2(region.max_rect.right(), new_top + region.cursor.height()), + pos2(region.max_rect.right(), new_top), ); } Direction::TopDown | Direction::BottomUp => {} diff --git a/tests/egui_tests/tests/regression_tests.rs b/tests/egui_tests/tests/regression_tests.rs index d92a77103..2d6ab5c67 100644 --- a/tests/egui_tests/tests/regression_tests.rs +++ b/tests/egui_tests/tests/regression_tests.rs @@ -279,3 +279,88 @@ fn warn_if_rect_changes_id() { "Should warn when a widget rect changes Id between passes" ); } + +#[test] +fn horizontal_wrapped_multiline_row_height() { + let mut harness = Harness::builder().with_size((350.0, 300.0)).build_ui(|ui| { + ui.style_mut().interaction.tooltip_delay = 0.0; + ui.style_mut().interaction.show_tooltips_only_when_still = false; + + let mut string = String::new(); + + ui.horizontal_wrapped(|ui| { + ui.monospace("| "); + let _ = ui.button("A"); + let _ = ui.button("B"); + ui.end_row(); + + ui.monospace("| "); + let _ = ui.button("C"); + let _ = ui.button("D"); + let _ = ui.button("E"); + ui.end_row(); + + ui.monospace("| "); + ui.text_edit_multiline(&mut string); + ui.end_row(); + + ui.monospace("| "); + let _ = ui.button("F"); + let _ = ui.button("G"); + ui.end_row(); + + ui.monospace("| "); + let _ = ui.button("H"); + let _ = ui.button("I"); + let _ = ui.button("K"); + ui.end_row(); + }); + }); + + harness.snapshot("horizontal_wrapped_multiline_row_height"); +} + +#[test] +fn horizontal_wrapped_multiline_row_height_reference() { + let mut harness = Harness::builder().with_size((350.0, 300.0)).build_ui(|ui| { + ui.style_mut().interaction.tooltip_delay = 0.0; + ui.style_mut().interaction.show_tooltips_only_when_still = false; + + let mut string = String::new(); + + ui.vertical(|ui| { + ui.horizontal(|ui| { + ui.monospace("| "); + let _ = ui.button("A"); + let _ = ui.button("B"); + }); + + ui.horizontal(|ui| { + ui.monospace("| "); + let _ = ui.button("C"); + let _ = ui.button("D"); + let _ = ui.button("E"); + }); + + ui.horizontal(|ui| { + ui.monospace("| "); + ui.text_edit_multiline(&mut string); + }); + + ui.horizontal(|ui| { + ui.monospace("| "); + let _ = ui.button("F"); + let _ = ui.button("G"); + }); + + ui.horizontal(|ui| { + ui.monospace("| "); + let _ = ui.button("H"); + let _ = ui.button("I"); + let _ = ui.button("K"); + }); + }); + }); + + harness.snapshot("horizontal_wrapped_multiline_row_height_reference"); +} diff --git a/tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height.png b/tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height.png new file mode 100644 index 000000000..e6ac8e446 --- /dev/null +++ b/tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef21b42f90401f6b85685e1cc37d07970b38d2b40394f53bbde5bd4f0d54fb95 +size 5340 diff --git a/tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height_reference.png b/tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height_reference.png new file mode 100644 index 000000000..a533a8401 --- /dev/null +++ b/tests/egui_tests/tests/snapshots/horizontal_wrapped_multiline_row_height_reference.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5025f4cb528ae5edc387149f1d14523ab4b93058f0862e775a1c2276a3e77af6 +size 5377