From abe79fb8c13c51866fcc0724e346e7f583f6fef9 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 16:19:11 +0200 Subject: [PATCH 01/15] Create a `UiBuilder` --- crates/egui/src/lib.rs | 4 +- crates/egui/src/ui.rs | 50 +++++++++++++++++--- crates/egui/src/ui_builder.rs | 86 +++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 crates/egui/src/ui_builder.rs diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 92436215f2f..9dc8dea614a 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -400,6 +400,7 @@ mod sense; pub mod style; pub mod text_selection; mod ui; +mod ui_builder; mod ui_stack; pub mod util; pub mod viewport; @@ -442,7 +443,7 @@ pub mod text { }; } -pub use { +pub use self::{ containers::*, context::{Context, RepaintCause, RequestRepaintInfo}, data::{ @@ -467,6 +468,7 @@ pub use { style::{FontSelection, Style, TextStyle, Visuals}, text::{Galley, TextFormat}, ui::Ui, + ui_builder::UiBuilder, ui_stack::*, viewport::*, widget_rect::{WidgetRect, WidgetRects}, diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index b50dadbbff6..d6fd2d84752 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -136,7 +136,12 @@ impl Ui { layout: Layout, ui_stack_info: Option, ) -> Self { - self.child_ui_with_id_source(max_rect, layout, "child", ui_stack_info) + self.child_from_builder( + UiBuilder::new() + .layout(layout) + .ui_stack_info(ui_stack_info.unwrap_or_default()), + max_rect, + ) } /// Create a new [`Ui`] at a specific region with a specific id. @@ -145,10 +150,41 @@ impl Ui { pub fn child_ui_with_id_source( &mut self, max_rect: Rect, - mut layout: Layout, + layout: Layout, id_source: impl Hash, ui_stack_info: Option, ) -> Self { + self.child_from_builder( + UiBuilder::new() + .id_source(id_source) + .layout(layout) + .ui_stack_info(ui_stack_info.unwrap_or_default()), + max_rect, + ) + } + + /// Create a child `Ui` with the properties of the given builder. + pub fn child_from_builder(&mut self, ui_builder: UiBuilder, max_rect: Rect) -> Self { + let UiBuilder { + id_source, + ui_stack_info, + layout, + disabled, + invisible, + sizing_pass, + style, + } = ui_builder; + + let enabled = self.enabled && !disabled; + let sizing_pass = self.sizing_pass || sizing_pass; + let id_source = id_source.unwrap_or_else(|| Id::from("child")); + let mut layout = layout.unwrap_or(*self.layout()); + let style = style.unwrap_or_else(|| self.style.clone()); + let mut painter = self.painter.clone(); + if invisible { + painter.set_invisible(); + } + if self.sizing_pass { // During the sizing pass we want widgets to use up as little space as possible, // so that we measure the only the space we _need_. @@ -167,7 +203,7 @@ impl Ui { let ui_stack = UiStack { id: new_id, layout_direction: layout.main_dir, - info: ui_stack_info.unwrap_or_default(), + info: ui_stack_info, parent: Some(self.stack.clone()), min_rect: placer.min_rect(), max_rect: placer.max_rect(), @@ -175,11 +211,11 @@ impl Ui { let child_ui = Ui { id: new_id, next_auto_id_source, - painter: self.painter.clone(), - style: self.style.clone(), + painter, + style, placer, - enabled: self.enabled, - sizing_pass: self.sizing_pass, + enabled, + sizing_pass, menu_state: self.menu_state.clone(), stack: Arc::new(ui_stack), }; diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs new file mode 100644 index 00000000000..620446f89e0 --- /dev/null +++ b/crates/egui/src/ui_builder.rs @@ -0,0 +1,86 @@ +use std::{hash::Hash, sync::Arc}; + +use crate::{Id, Layout, Style, UiStackInfo}; + +#[derive(Default)] +pub struct UiBuilder { + pub id_source: Option, + pub ui_stack_info: UiStackInfo, + pub layout: Option, + pub disabled: bool, + pub invisible: bool, + pub sizing_pass: bool, + pub style: Option>, +} + +impl UiBuilder { + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Seed the child `Ui` with this `id_source`, which will be mixed + /// with the [`Ui::id`] of the parent. + /// + /// You should give each [`Ui`] an `id_source` that is unique + /// within the parent, or give it none at all. + #[inline] + pub fn id_source(mut self, id_source: impl Hash) -> Self { + self.id_source = Some(Id::new(id_source)); + self + } + + /// Provide some information about the new `Ui` being built. + #[inline] + pub fn ui_stack_info(mut self, ui_stack_info: UiStackInfo) -> Self { + self.ui_stack_info = ui_stack_info; + self + } + + /// Override the layout. + /// + /// Will otherwise be inherited from the parent. + #[inline] + pub fn layout(mut self, layout: Layout) -> Self { + self.layout = Some(layout); + self + } + + /// Make the new `Ui` disabled, i.e. grayed-out and non-interactive. + /// + /// Note that if the parent `Ui` is disabled, the child will always be disabled. + #[inline] + pub fn disabled(mut self) -> Self { + self.disabled = true; + self + } + + /// Make the contents invisible. + /// + /// If the parent `Ui` is invisible, the child will always be invisible. + #[inline] + pub fn invisible(mut self) -> Self { + self.invisible = true; + self + } + + /// Set to true in special cases where we do one frame + /// where we size up the contents of the Ui, without actually showing it. + /// + /// If the `sizing_pass` flag is set on the parent, + /// the child will inherit it automatically. + #[inline] + pub fn sizing_pass(mut self) -> Self { + self.sizing_pass = true; + self + } + + /// Override the style. + /// + /// Otherwise will inherit the style of the parent. + #[inline] + pub fn style(mut self, style: impl Into>) -> Self { + self.style = Some(style.into()); + self + } +} From e0e81e18fb116143f2d7a2e3fc58c9c19d5580bb Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 16:39:14 +0200 Subject: [PATCH 02/15] Deprecate `child_ui_with_id_source` --- crates/egui/src/containers/panel.rs | 30 +++++++++++++++-------------- crates/egui/src/ui.rs | 29 +++++++++++++++------------- crates/egui_extras/src/layout.rs | 11 ++++++----- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 3b16c9c1fa8..e4c42d884ff 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -261,14 +261,15 @@ impl SidePanel { } } - let mut panel_ui = ui.child_ui_with_id_source( + let mut panel_ui = ui.child_from_builder( panel_rect, - Layout::top_down(Align::Min), - id, - Some(UiStackInfo::new(match side { - Side::Left => UiKind::LeftPanel, - Side::Right => UiKind::RightPanel, - })), + UiBuilder::new() + .id_source(id) + .ui_stack_info(UiStackInfo::new(match side { + Side::Left => UiKind::LeftPanel, + Side::Right => UiKind::RightPanel, + })) + .layout(Layout::top_down(Align::Min)), ); panel_ui.expand_to_include_rect(panel_rect); panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475) @@ -750,14 +751,15 @@ impl TopBottomPanel { } } - let mut panel_ui = ui.child_ui_with_id_source( + let mut panel_ui = ui.child_from_builder( panel_rect, - Layout::top_down(Align::Min), - id, - Some(UiStackInfo::new(match side { - TopBottomSide::Top => UiKind::TopPanel, - TopBottomSide::Bottom => UiKind::BottomPanel, - })), + UiBuilder::new() + .id_source(id) + .ui_stack_info(UiStackInfo::new(match side { + TopBottomSide::Top => UiKind::TopPanel, + TopBottomSide::Bottom => UiKind::BottomPanel, + })) + .layout(Layout::top_down(Align::Min)), ); panel_ui.expand_to_include_rect(panel_rect); panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index d6fd2d84752..dce10cf4ed6 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -137,16 +137,17 @@ impl Ui { ui_stack_info: Option, ) -> Self { self.child_from_builder( + max_rect, UiBuilder::new() .layout(layout) .ui_stack_info(ui_stack_info.unwrap_or_default()), - max_rect, ) } /// Create a new [`Ui`] at a specific region with a specific id. /// /// When in doubt, use `None` for the `UiStackInfo` argument. + #[deprecated = "Use ui.child_from_builder instead"] pub fn child_ui_with_id_source( &mut self, max_rect: Rect, @@ -155,16 +156,16 @@ impl Ui { ui_stack_info: Option, ) -> Self { self.child_from_builder( + max_rect, UiBuilder::new() .id_source(id_source) .layout(layout) .ui_stack_info(ui_stack_info.unwrap_or_default()), - max_rect, ) } /// Create a child `Ui` with the properties of the given builder. - pub fn child_from_builder(&mut self, ui_builder: UiBuilder, max_rect: Rect) -> Self { + pub fn child_from_builder(&mut self, max_rect: Rect, ui_builder: UiBuilder) -> Self { let UiBuilder { id_source, ui_stack_info, @@ -2022,7 +2023,10 @@ impl Ui { id_source: impl Hash, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.scope_dyn(Box::new(add_contents), Id::new(id_source), None) + self.scope_dyn( + UiBuilder::new().id_source(id_source), + Box::new(add_contents), + ) } /// Push another level onto the [`UiStack`]. @@ -2034,9 +2038,8 @@ impl Ui { add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { self.scope_dyn( + UiBuilder::new().ui_stack_info(ui_stack_info), Box::new(add_contents), - Id::new("child"), - Some(ui_stack_info), ) } @@ -2053,19 +2056,19 @@ impl Ui { /// # }); /// ``` pub fn scope(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse { - self.scope_dyn(Box::new(add_contents), Id::new("child"), None) + self.scope_dyn(UiBuilder::new(), Box::new(add_contents)) } - fn scope_dyn<'c, R>( + /// Allocate a child with [`Self::available_rect_before_wrap`] as its `max_rect`, + /// add content to it, and then allocate only what was used in the parent `Ui`. + pub fn scope_dyn<'c, R>( &mut self, + ui_builder: UiBuilder, add_contents: Box R + 'c>, - id_source: Id, - ui_stack_info: Option, ) -> InnerResponse { let child_rect = self.available_rect_before_wrap(); let next_auto_id_source = self.next_auto_id_source; - let mut child_ui = - self.child_ui_with_id_source(child_rect, *self.layout(), id_source, ui_stack_info); + let mut child_ui = self.child_from_builder(child_rect, ui_builder); self.next_auto_id_source = next_auto_id_source; // HACK: we want `scope` to only increment this once, so that `ui.scope` is equivalent to `ui.allocate_space`. let ret = add_contents(&mut child_ui); let response = self.allocate_rect(child_ui.min_rect(), Sense::hover()); @@ -2125,7 +2128,7 @@ impl Ui { child_rect.min.x += indent; let mut child_ui = - self.child_ui_with_id_source(child_rect, *self.layout(), id_source, None); + self.child_from_builder(child_rect, UiBuilder::new().id_source(id_source)); let ret = add_contents(&mut child_ui); let left_vline = self.visuals().indent_has_left_vline; diff --git a/crates/egui_extras/src/layout.rs b/crates/egui_extras/src/layout.rs index 1c8f6b0c967..1edf740bd1e 100644 --- a/crates/egui_extras/src/layout.rs +++ b/crates/egui_extras/src/layout.rs @@ -1,4 +1,4 @@ -use egui::{Id, Pos2, Rect, Response, Sense, Ui}; +use egui::{Id, Pos2, Rect, Response, Sense, Ui, UiBuilder}; #[derive(Clone, Copy)] pub(crate) enum CellSize { @@ -197,11 +197,12 @@ impl<'l> StripLayout<'l> { child_ui_id_source: egui::Id, add_cell_contents: impl FnOnce(&mut Ui), ) -> Ui { - let mut child_ui = self.ui.child_ui_with_id_source( + let mut child_ui = self.ui.child_from_builder( max_rect, - self.cell_layout, - child_ui_id_source, - Some(egui::UiStackInfo::new(egui::UiKind::TableCell)), + UiBuilder::new() + .id_source(child_ui_id_source) + .ui_stack_info(egui::UiStackInfo::new(egui::UiKind::TableCell)) + .layout(self.cell_layout), ); if flags.clip { From 5075f0cf2faeb4d909457940754d757f4056c6a3 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 16:46:53 +0200 Subject: [PATCH 03/15] Add `max_rect` to `UiBuilder` --- crates/egui/src/containers/panel.rs | 4 ++-- crates/egui/src/ui.rs | 25 +++++++++++++------------ crates/egui/src/ui_builder.rs | 26 +++++++++++++++++++++++++- crates/egui_extras/src/layout.rs | 2 +- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index e4c42d884ff..86ed909f749 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -262,13 +262,13 @@ impl SidePanel { } let mut panel_ui = ui.child_from_builder( - panel_rect, UiBuilder::new() .id_source(id) .ui_stack_info(UiStackInfo::new(match side { Side::Left => UiKind::LeftPanel, Side::Right => UiKind::RightPanel, })) + .max_rect(panel_rect) .layout(Layout::top_down(Align::Min)), ); panel_ui.expand_to_include_rect(panel_rect); @@ -752,13 +752,13 @@ impl TopBottomPanel { } let mut panel_ui = ui.child_from_builder( - panel_rect, UiBuilder::new() .id_source(id) .ui_stack_info(UiStackInfo::new(match side { TopBottomSide::Top => UiKind::TopPanel, TopBottomSide::Bottom => UiKind::BottomPanel, })) + .max_rect(panel_rect) .layout(Layout::top_down(Align::Min)), ); panel_ui.expand_to_include_rect(panel_rect); diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index dce10cf4ed6..9bb4833d035 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -137,8 +137,8 @@ impl Ui { ui_stack_info: Option, ) -> Self { self.child_from_builder( - max_rect, UiBuilder::new() + .max_rect(max_rect) .layout(layout) .ui_stack_info(ui_stack_info.unwrap_or_default()), ) @@ -156,19 +156,20 @@ impl Ui { ui_stack_info: Option, ) -> Self { self.child_from_builder( - max_rect, UiBuilder::new() .id_source(id_source) + .max_rect(max_rect) .layout(layout) .ui_stack_info(ui_stack_info.unwrap_or_default()), ) } /// Create a child `Ui` with the properties of the given builder. - pub fn child_from_builder(&mut self, max_rect: Rect, ui_builder: UiBuilder) -> Self { + pub fn child_from_builder(&mut self, ui_builder: UiBuilder) -> Self { let UiBuilder { id_source, ui_stack_info, + max_rect, layout, disabled, invisible, @@ -176,15 +177,17 @@ impl Ui { style, } = ui_builder; - let enabled = self.enabled && !disabled; - let sizing_pass = self.sizing_pass || sizing_pass; + let mut painter = self.painter.clone(); + let id_source = id_source.unwrap_or_else(|| Id::from("child")); + let max_rect = max_rect.unwrap_or_else(|| self.available_rect_before_wrap()); let mut layout = layout.unwrap_or(*self.layout()); - let style = style.unwrap_or_else(|| self.style.clone()); - let mut painter = self.painter.clone(); + let enabled = self.enabled && !disabled; if invisible { painter.set_invisible(); } + let sizing_pass = self.sizing_pass || sizing_pass; + let style = style.unwrap_or_else(|| self.style.clone()); if self.sizing_pass { // During the sizing pass we want widgets to use up as little space as possible, @@ -2059,16 +2062,14 @@ impl Ui { self.scope_dyn(UiBuilder::new(), Box::new(add_contents)) } - /// Allocate a child with [`Self::available_rect_before_wrap`] as its `max_rect`, - /// add content to it, and then allocate only what was used in the parent `Ui`. + /// Create a child, add content to it, and then allocate only what was used in the parent `Ui`. pub fn scope_dyn<'c, R>( &mut self, ui_builder: UiBuilder, add_contents: Box R + 'c>, ) -> InnerResponse { - let child_rect = self.available_rect_before_wrap(); let next_auto_id_source = self.next_auto_id_source; - let mut child_ui = self.child_from_builder(child_rect, ui_builder); + let mut child_ui = self.child_from_builder(ui_builder); self.next_auto_id_source = next_auto_id_source; // HACK: we want `scope` to only increment this once, so that `ui.scope` is equivalent to `ui.allocate_space`. let ret = add_contents(&mut child_ui); let response = self.allocate_rect(child_ui.min_rect(), Sense::hover()); @@ -2128,7 +2129,7 @@ impl Ui { child_rect.min.x += indent; let mut child_ui = - self.child_from_builder(child_rect, UiBuilder::new().id_source(id_source)); + self.child_from_builder(UiBuilder::new().id_source(id_source).max_rect(child_rect)); let ret = add_contents(&mut child_ui); let left_vline = self.visuals().indent_has_left_vline; diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 620446f89e0..36068b240f2 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -1,11 +1,17 @@ use std::{hash::Hash, sync::Arc}; -use crate::{Id, Layout, Style, UiStackInfo}; +use crate::{Id, Layout, Rect, Style, UiStackInfo}; +/// Build a [`Ui`] as the chlild of another [`Ui`]. +/// +/// By default, everyhting is inherited from the parent, +/// except for `max_rect` which by default is set to +/// the parent [`Ui::available_rect_before_wrap`]. #[derive(Default)] pub struct UiBuilder { pub id_source: Option, pub ui_stack_info: UiStackInfo, + pub max_rect: Option, pub layout: Option, pub disabled: bool, pub invisible: bool, @@ -37,6 +43,24 @@ impl UiBuilder { self } + /// Set the max rectangle, within which widgets will go. + /// + /// New widgets will *try* to fit within this rectangle. + /// + /// Text labels will wrap to fit within `max_rect`. + /// Separator lines will span the `max_rect`. + /// + /// If a new widget doesn't fit within the `max_rect` then the + /// [`Ui`] will make room for it by expanding both `min_rect` and + /// + /// If not set, this will be set to the parent + /// [`Ui::available_rect_before_wrap`]. + #[inline] + pub fn max_rect(mut self, max_rect: Rect) -> Self { + self.max_rect = Some(max_rect); + self + } + /// Override the layout. /// /// Will otherwise be inherited from the parent. diff --git a/crates/egui_extras/src/layout.rs b/crates/egui_extras/src/layout.rs index 1edf740bd1e..eea552462cf 100644 --- a/crates/egui_extras/src/layout.rs +++ b/crates/egui_extras/src/layout.rs @@ -198,10 +198,10 @@ impl<'l> StripLayout<'l> { add_cell_contents: impl FnOnce(&mut Ui), ) -> Ui { let mut child_ui = self.ui.child_from_builder( - max_rect, UiBuilder::new() .id_source(child_ui_id_source) .ui_stack_info(egui::UiStackInfo::new(egui::UiKind::TableCell)) + .max_rect(max_rect) .layout(self.cell_layout), ); From 02b11ff204e50a0350e53ab579f9e2f7993ee793 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 16:59:18 +0200 Subject: [PATCH 04/15] Deprecate `ui.child_ui` --- crates/egui/src/containers/combo_box.rs | 2 +- crates/egui/src/containers/frame.rs | 8 +++--- crates/egui/src/containers/panel.rs | 13 +++++----- crates/egui/src/containers/resize.rs | 8 +++--- crates/egui/src/containers/scroll_area.rs | 8 +++--- crates/egui/src/ui.rs | 31 ++++++++++++++--------- crates/egui_extras/src/layout.rs | 2 +- examples/custom_window_frame/src/main.rs | 2 +- tests/test_viewports/src/main.rs | 4 +-- 9 files changed, 43 insertions(+), 35 deletions(-) diff --git a/crates/egui/src/containers/combo_box.rs b/crates/egui/src/containers/combo_box.rs index cb29f277644..5c6af803b3e 100644 --- a/crates/egui/src/containers/combo_box.rs +++ b/crates/egui/src/containers/combo_box.rs @@ -425,7 +425,7 @@ fn button_frame( outer_rect.set_height(outer_rect.height().at_least(interact_size.y)); let inner_rect = outer_rect.shrink2(margin); - let mut content_ui = ui.child_ui(inner_rect, *ui.layout(), None); + let mut content_ui = ui.new_child(UiBuilder::new().max_rect(inner_rect)); add_contents(&mut content_ui); let mut outer_rect = content_ui.min_rect().expand2(margin); diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index 07cd679acea..f59598400b4 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -250,10 +250,10 @@ impl Frame { inner_rect.max.x = inner_rect.max.x.max(inner_rect.min.x); inner_rect.max.y = inner_rect.max.y.max(inner_rect.min.y); - let content_ui = ui.child_ui( - inner_rect, - *ui.layout(), - Some(UiStackInfo::new(UiKind::Frame).with_frame(self)), + let content_ui = ui.new_child( + UiBuilder::new() + .ui_stack_info(UiStackInfo::new(UiKind::Frame).with_frame(self)) + .max_rect(inner_rect), ); // content_ui.set_clip_rect(outer_rect_bounds.shrink(self.stroke.width * 0.5)); // Can't do this since we don't know final size yet diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 86ed909f749..dab3e35ceec 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -261,7 +261,7 @@ impl SidePanel { } } - let mut panel_ui = ui.child_from_builder( + let mut panel_ui = ui.new_child( UiBuilder::new() .id_source(id) .ui_stack_info(UiStackInfo::new(match side { @@ -751,7 +751,7 @@ impl TopBottomPanel { } } - let mut panel_ui = ui.child_from_builder( + let mut panel_ui = ui.new_child( UiBuilder::new() .id_source(id) .ui_stack_info(UiStackInfo::new(match side { @@ -1093,10 +1093,11 @@ impl CentralPanel { let Self { frame } = self; let panel_rect = ui.available_rect_before_wrap(); - let mut panel_ui = ui.child_ui( - panel_rect, - Layout::top_down(Align::Min), - Some(UiStackInfo::new(UiKind::CentralPanel)), + let mut panel_ui = ui.new_child( + UiBuilder::new() + .ui_stack_info(UiStackInfo::new(UiKind::CentralPanel)) + .max_rect(panel_rect) + .layout(Layout::top_down(Align::Min)), ); panel_ui.set_clip_rect(panel_rect); // If we overflow, don't do so visibly (#4475) diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs index 4980c9c623d..54a00af0eb7 100644 --- a/crates/egui/src/containers/resize.rs +++ b/crates/egui/src/containers/resize.rs @@ -270,10 +270,10 @@ impl Resize { content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); // Respect parent region - let mut content_ui = ui.child_ui( - inner_rect, - *ui.layout(), - Some(UiStackInfo::new(UiKind::Resize)), + let mut content_ui = ui.new_child( + UiBuilder::new() + .ui_stack_info(UiStackInfo::new(UiKind::Resize)) + .max_rect(inner_rect), ); content_ui.set_clip_rect(content_clip_rect); diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 3f0e3a2df35..45f3f9632dc 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -560,10 +560,10 @@ impl ScrollArea { } let content_max_rect = Rect::from_min_size(inner_rect.min - state.offset, content_max_size); - let mut content_ui = ui.child_ui( - content_max_rect, - *ui.layout(), - Some(UiStackInfo::new(UiKind::ScrollArea)), + let mut content_ui = ui.new_child( + UiBuilder::new() + .ui_stack_info(UiStackInfo::new(UiKind::ScrollArea)) + .max_rect(content_max_rect), ); { diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 9bb4833d035..5dc67227b25 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -130,13 +130,14 @@ impl Ui { /// [`Self::scope`] if needed. /// /// When in doubt, use `None` for the `UiStackInfo` argument. + #[deprecated = "Use ui.child_from_builder instead"] pub fn child_ui( &mut self, max_rect: Rect, layout: Layout, ui_stack_info: Option, ) -> Self { - self.child_from_builder( + self.new_child( UiBuilder::new() .max_rect(max_rect) .layout(layout) @@ -155,7 +156,7 @@ impl Ui { id_source: impl Hash, ui_stack_info: Option, ) -> Self { - self.child_from_builder( + self.new_child( UiBuilder::new() .id_source(id_source) .max_rect(max_rect) @@ -165,7 +166,7 @@ impl Ui { } /// Create a child `Ui` with the properties of the given builder. - pub fn child_from_builder(&mut self, ui_builder: UiBuilder) -> Self { + pub fn new_child(&mut self, ui_builder: UiBuilder) -> Self { let UiBuilder { id_source, ui_stack_info, @@ -1171,7 +1172,7 @@ impl Ui { let frame_rect = self.placer.next_space(desired_size, item_spacing); let child_rect = self.placer.justify_and_align(frame_rect, desired_size); - let mut child_ui = self.child_ui(child_rect, layout, None); + let mut child_ui = self.new_child(UiBuilder::new().max_rect(child_rect).layout(layout)); let ret = add_contents(&mut child_ui); let final_child_rect = child_ui.min_rect(); @@ -1193,7 +1194,7 @@ impl Ui { add_contents: impl FnOnce(&mut Self) -> R, ) -> InnerResponse { debug_assert!(max_rect.is_finite()); - let mut child_ui = self.child_ui(max_rect, *self.layout(), None); + let mut child_ui = self.new_child(UiBuilder::new().max_rect(max_rect)); let ret = add_contents(&mut child_ui); let final_child_rect = child_ui.min_rect(); @@ -2069,7 +2070,7 @@ impl Ui { add_contents: Box R + 'c>, ) -> InnerResponse { let next_auto_id_source = self.next_auto_id_source; - let mut child_ui = self.child_from_builder(ui_builder); + let mut child_ui = self.new_child(ui_builder); self.next_auto_id_source = next_auto_id_source; // HACK: we want `scope` to only increment this once, so that `ui.scope` is equivalent to `ui.allocate_space`. let ret = add_contents(&mut child_ui); let response = self.allocate_rect(child_ui.min_rect(), Sense::hover()); @@ -2129,7 +2130,7 @@ impl Ui { child_rect.min.x += indent; let mut child_ui = - self.child_from_builder(UiBuilder::new().id_source(id_source).max_rect(child_rect)); + self.new_child(UiBuilder::new().id_source(id_source).max_rect(child_rect)); let ret = add_contents(&mut child_ui); let left_vline = self.visuals().indent_has_left_vline; @@ -2351,7 +2352,7 @@ impl Ui { layout: Layout, add_contents: Box R + 'c>, ) -> InnerResponse { - let mut child_ui = self.child_ui(self.available_rect_before_wrap(), layout, None); + let mut child_ui = self.new_child(UiBuilder::new().layout(layout)); let inner = add_contents(&mut child_ui); let rect = child_ui.min_rect(); let item_spacing = self.spacing().item_spacing; @@ -2434,8 +2435,11 @@ impl Ui { pos, pos2(pos.x + column_width, self.max_rect().right_bottom().y), ); - let mut column_ui = - self.child_ui(child_rect, Layout::top_down_justified(Align::LEFT), None); + let mut column_ui = self.new_child( + UiBuilder::new() + .max_rect(child_rect) + .layout(Layout::top_down_justified(Align::LEFT)), + ); column_ui.set_width(column_width); column_ui }) @@ -2488,8 +2492,11 @@ impl Ui { pos, pos2(pos.x + column_width, self.max_rect().right_bottom().y), ); - let mut column_ui = - self.child_ui(child_rect, Layout::top_down_justified(Align::LEFT), None); + let mut column_ui = self.new_child( + UiBuilder::new() + .max_rect(child_rect) + .layout(Layout::top_down_justified(Align::LEFT)), + ); column_ui.set_width(column_width); column_ui }); diff --git a/crates/egui_extras/src/layout.rs b/crates/egui_extras/src/layout.rs index eea552462cf..6eb951c8552 100644 --- a/crates/egui_extras/src/layout.rs +++ b/crates/egui_extras/src/layout.rs @@ -197,7 +197,7 @@ impl<'l> StripLayout<'l> { child_ui_id_source: egui::Id, add_cell_contents: impl FnOnce(&mut Ui), ) -> Ui { - let mut child_ui = self.ui.child_from_builder( + let mut child_ui = self.ui.new_child( UiBuilder::new() .id_source(child_ui_id_source) .ui_stack_info(egui::UiStackInfo::new(egui::UiKind::TableCell)) diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index 2912e5bba17..463d9ce9a09 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -71,7 +71,7 @@ fn custom_window_frame(ctx: &egui::Context, title: &str, add_contents: impl FnOn rect } .shrink(4.0); - let mut content_ui = ui.child_ui(content_rect, *ui.layout(), None); + let mut content_ui = ui.new_child(UiBuilder::new().max_rect(content_rect)); add_contents(&mut content_ui); }); } diff --git a/tests/test_viewports/src/main.rs b/tests/test_viewports/src/main.rs index b1d271677bf..e15baa7510b 100644 --- a/tests/test_viewports/src/main.rs +++ b/tests/test_viewports/src/main.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use eframe::egui; -use egui::{mutex::RwLock, Id, InnerResponse, ViewportBuilder, ViewportId}; +use egui::{mutex::RwLock, Id, InnerResponse, UiBuilder, ViewportBuilder, ViewportId}; // Drag-and-drop between windows is not yet implemented, but if you wanna work on it, enable this: pub const DRAG_AND_DROP_TEST: bool = false; @@ -457,7 +457,7 @@ fn drop_target( let available_rect = ui.available_rect_before_wrap(); let inner_rect = available_rect.shrink2(margin); - let mut content_ui = ui.child_ui(inner_rect, *ui.layout(), None); + let mut content_ui = ui.new_child(UiBuilder::new().max_rect(inner_rect)); let ret = body(&mut content_ui); let outer_rect = From 24009899c990fb7514b0ba100590ce62139fabdc Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 17:33:19 +0200 Subject: [PATCH 05/15] Use `UiBuilder` in `Ui::new` --- crates/egui/src/containers/area.rs | 9 ++-- crates/egui/src/containers/panel.rs | 18 +++----- crates/egui/src/ui.rs | 71 +++++++++++++++++++++-------- crates/egui/src/ui_builder.rs | 6 +++ 4 files changed, 68 insertions(+), 36 deletions(-) diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index 062261af31b..e58ba026dc2 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -532,16 +532,15 @@ impl Prepared { pub(crate) fn content_ui(&self, ctx: &Context) -> Ui { let max_rect = self.state.rect(); - let clip_rect = self.constrain_rect; // Don't paint outside our bounds - let mut ui = Ui::new( ctx.clone(), self.layer_id, self.layer_id.id, - max_rect, - clip_rect, - UiStackInfo::new(self.kind), + UiBuilder::new() + .ui_stack_info(UiStackInfo::new(self.kind)) + .max_rect(max_rect), ); + ui.set_clip_rect(self.constrain_rect); // Don't paint outside our bounds if self.fade_in { if let Some(last_became_visible_at) = self.state.last_became_visible_at { diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index dab3e35ceec..9aafa0cfae1 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -368,15 +368,13 @@ impl SidePanel { let layer_id = LayerId::background(); let side = self.side; let available_rect = ctx.available_rect(); - let clip_rect = ctx.screen_rect(); let mut panel_ui = Ui::new( ctx.clone(), layer_id, self.id, - available_rect, - clip_rect, - UiStackInfo::default(), + UiBuilder::new().max_rect(available_rect), ); + panel_ui.set_clip_rect(ctx.screen_rect()); let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); let rect = inner_response.response.rect; @@ -859,15 +857,13 @@ impl TopBottomPanel { let available_rect = ctx.available_rect(); let side = self.side; - let clip_rect = ctx.screen_rect(); let mut panel_ui = Ui::new( ctx.clone(), layer_id, self.id, - available_rect, - clip_rect, - UiStackInfo::default(), // set by show_inside_dyn + UiBuilder::new().max_rect(available_rect), ); + panel_ui.set_clip_rect(ctx.screen_rect()); let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); let rect = inner_response.response.rect; @@ -1127,15 +1123,13 @@ impl CentralPanel { let layer_id = LayerId::background(); let id = Id::new((ctx.viewport_id(), "central_panel")); - let clip_rect = ctx.screen_rect(); let mut panel_ui = Ui::new( ctx.clone(), layer_id, id, - available_rect, - clip_rect, - UiStackInfo::default(), // set by show_inside_dyn + UiBuilder::new().max_rect(available_rect), ); + panel_ui.set_clip_rect(ctx.screen_rect()); let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 5dc67227b25..2ff5393fae7 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -75,20 +75,34 @@ impl Ui { // ------------------------------------------------------------------------ // Creation: - /// Create a new [`Ui`]. + /// Create a new top-level [`Ui`]. /// /// Normally you would not use this directly, but instead use /// [`SidePanel`], [`TopBottomPanel`], [`CentralPanel`], [`Window`] or [`Area`]. - pub fn new( - ctx: Context, - layer_id: LayerId, - id: Id, - max_rect: Rect, - clip_rect: Rect, - ui_stack_info: UiStackInfo, - ) -> Self { - let style = ctx.style(); - let layout = Layout::default(); + pub fn new(ctx: Context, layer_id: LayerId, id: Id, ui_builder: UiBuilder) -> Self { + let UiBuilder { + id_source, + ui_stack_info, + max_rect, + layout, + disabled, + invisible, + sizing_pass, + style, + } = ui_builder; + + debug_assert!( + id_source.is_none(), + "Top-level Ui:s should not have an id_source" + ); + + let max_rect = max_rect.unwrap_or_else(|| ctx.screen_rect()); + let clip_rect = max_rect; + let layout = layout.unwrap_or_default(); + let invisible = invisible || sizing_pass; + let disabled = disabled || invisible || sizing_pass; + let style = style.unwrap_or_else(|| ctx.style()); + let placer = Placer::new(max_rect, layout); let ui_stack = UiStack { id, @@ -98,7 +112,7 @@ impl Ui { min_rect: placer.min_rect(), max_rect: placer.max_rect(), }; - let ui = Ui { + let mut ui = Ui { id, next_auto_id_source: id.with("auto").value(), painter: Painter::new(ctx, layer_id, clip_rect), @@ -121,6 +135,16 @@ impl Ui { enabled: ui.enabled, }); + if disabled { + ui.disable(); + } + if invisible { + ui.set_invisible(); + } + if sizing_pass { + ui.set_sizing_pass(); + } + ui } @@ -183,7 +207,8 @@ impl Ui { let id_source = id_source.unwrap_or_else(|| Id::from("child")); let max_rect = max_rect.unwrap_or_else(|| self.available_rect_before_wrap()); let mut layout = layout.unwrap_or(*self.layout()); - let enabled = self.enabled && !disabled; + let invisible = invisible || sizing_pass; + let enabled = self.enabled && !disabled && !invisible && !sizing_pass; if invisible { painter.set_invisible(); } @@ -1527,12 +1552,11 @@ impl Ui { visible: bool, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.scope(|ui| { - if !visible { - ui.set_invisible(); - } - add_contents(ui) - }) + let mut ui_builder = UiBuilder::new(); + if !visible { + ui_builder = ui_builder.invisible(); + } + self.scope_builder(ui_builder, add_contents) } /// Add extra space before the next widget. @@ -2063,6 +2087,15 @@ impl Ui { self.scope_dyn(UiBuilder::new(), Box::new(add_contents)) } + /// Create a child, add content to it, and then allocate only what was used in the parent `Ui`. + pub fn scope_builder( + &mut self, + ui_builder: UiBuilder, + add_contents: impl FnOnce(&mut Ui) -> R, + ) -> InnerResponse { + self.scope_dyn(ui_builder, Box::new(add_contents)) + } + /// Create a child, add content to it, and then allocate only what was used in the parent `Ui`. pub fn scope_dyn<'c, R>( &mut self, diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 36068b240f2..5d180864828 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -7,6 +7,7 @@ use crate::{Id, Layout, Rect, Style, UiStackInfo}; /// By default, everyhting is inherited from the parent, /// except for `max_rect` which by default is set to /// the parent [`Ui::available_rect_before_wrap`]. +#[must_use] #[derive(Default)] pub struct UiBuilder { pub id_source: Option, @@ -81,10 +82,13 @@ impl UiBuilder { /// Make the contents invisible. /// + /// Will also diable the `Ui` (see [`Self::disable`]). + /// /// If the parent `Ui` is invisible, the child will always be invisible. #[inline] pub fn invisible(mut self) -> Self { self.invisible = true; + self.disabled = true; self } @@ -96,6 +100,8 @@ impl UiBuilder { #[inline] pub fn sizing_pass(mut self) -> Self { self.sizing_pass = true; + self.invisible = true; + self.disabled = true; self } From e239649341659c6f46452553db6ec95370b97dae Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 17:34:49 +0200 Subject: [PATCH 06/15] Use `UiBuilder` in `WidgetGallery` --- crates/egui_demo_lib/src/demo/widget_gallery.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/egui_demo_lib/src/demo/widget_gallery.rs b/crates/egui_demo_lib/src/demo/widget_gallery.rs index f942c8626d6..7be6e20c040 100644 --- a/crates/egui_demo_lib/src/demo/widget_gallery.rs +++ b/crates/egui_demo_lib/src/demo/widget_gallery.rs @@ -61,10 +61,15 @@ impl crate::Demo for WidgetGallery { impl crate::View for WidgetGallery { fn ui(&mut self, ui: &mut egui::Ui) { - ui.add_enabled_ui(self.enabled, |ui| { - if !self.visible { - ui.set_invisible(); - } + let mut ui_builder = egui::UiBuilder::new(); + if !self.enabled { + ui_builder = ui_builder.disabled(); + } + if !self.visible { + ui_builder = ui_builder.invisible(); + } + + ui.scope_builder(ui_builder, |ui| { ui.multiply_opacity(self.opacity); egui::Grid::new("my_grid") From f53cf876c341476912b205c33072f9920361fce4 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:01:05 +0200 Subject: [PATCH 07/15] Area: better use of `UiBuilder` --- crates/egui/src/containers/area.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index e58ba026dc2..196c3de2cab 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -532,14 +532,18 @@ impl Prepared { pub(crate) fn content_ui(&self, ctx: &Context) -> Ui { let max_rect = self.state.rect(); - let mut ui = Ui::new( - ctx.clone(), - self.layer_id, - self.layer_id.id, - UiBuilder::new() - .ui_stack_info(UiStackInfo::new(self.kind)) - .max_rect(max_rect), - ); + let mut ui_builder = UiBuilder::new() + .ui_stack_info(UiStackInfo::new(self.kind)) + .max_rect(max_rect); + + if !self.enabled { + ui_builder = ui_builder.disabled(); + } + if self.sizing_pass { + ui_builder = ui_builder.sizing_pass(); + } + + let mut ui = Ui::new(ctx.clone(), self.layer_id, self.layer_id.id, ui_builder); ui.set_clip_rect(self.constrain_rect); // Don't paint outside our bounds if self.fade_in { @@ -555,12 +559,6 @@ impl Prepared { } } - if !self.enabled { - ui.disable(); - } - if self.sizing_pass { - ui.set_sizing_pass(); - } ui } From ca75dba77c027370f7dc0fadeb40bc0eacc16843 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:01:18 +0200 Subject: [PATCH 08/15] Add `Ui::allocate_new_ui` --- crates/egui/src/ui.rs | 46 ++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 2ff5393fae7..55bd136d757 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -1218,19 +1218,35 @@ impl Ui { max_rect: Rect, add_contents: impl FnOnce(&mut Self) -> R, ) -> InnerResponse { - debug_assert!(max_rect.is_finite()); - let mut child_ui = self.new_child(UiBuilder::new().max_rect(max_rect)); - let ret = add_contents(&mut child_ui); - let final_child_rect = child_ui.min_rect(); + self.allocate_new_ui(UiBuilder::new().max_rect(max_rect), add_contents) + } - self.placer.advance_after_rects( - final_child_rect, - final_child_rect, - self.spacing().item_spacing, - ); + /// Allocated space (`UiBuilder::max_rect`) and then add content to it. + /// + /// If the contents overflow, more space will be allocated. + /// When finished, the amount of space actually used (`min_rect`) will be allocated in the parent. + /// So you can request a lot of space and then use less. + pub fn allocate_new_ui( + &mut self, + ui_builder: UiBuilder, + add_contents: impl FnOnce(&mut Self) -> R, + ) -> InnerResponse { + self.allocate_new_ui_dyn(ui_builder, Box::new(add_contents)) + } - let response = self.interact(final_child_rect, child_ui.id, Sense::hover()); - InnerResponse::new(ret, response) + fn allocate_new_ui_dyn<'c, R>( + &mut self, + ui_builder: UiBuilder, + add_contents: Box R + 'c>, + ) -> InnerResponse { + let mut child_ui = self.new_child(ui_builder); + let inner = add_contents(&mut child_ui); + let rect = child_ui.min_rect(); + let item_spacing = self.spacing().item_spacing; + self.placer.advance_after_rects(rect, rect, item_spacing); + + let response = self.interact(rect, child_ui.id, Sense::hover()); + InnerResponse::new(inner, response) } /// Convenience function to get a region to paint on. @@ -2385,13 +2401,7 @@ impl Ui { layout: Layout, add_contents: Box R + 'c>, ) -> InnerResponse { - let mut child_ui = self.new_child(UiBuilder::new().layout(layout)); - let inner = add_contents(&mut child_ui); - let rect = child_ui.min_rect(); - let item_spacing = self.spacing().item_spacing; - self.placer.advance_after_rects(rect, rect, item_spacing); - - InnerResponse::new(inner, self.interact(rect, child_ui.id, Sense::hover())) + self.allocate_new_ui_dyn(UiBuilder::new().layout(layout), add_contents) } /// This will make the next added widget centered and justified in the available space. From 83b740d6fb3e68257a4e30c7b6546fba694f0c8d Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:10:11 +0200 Subject: [PATCH 09/15] Unify some functions --- crates/egui/src/ui.rs | 47 +++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 55bd136d757..fe5edb4428c 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -1196,16 +1196,10 @@ impl Ui { let item_spacing = self.spacing().item_spacing; let frame_rect = self.placer.next_space(desired_size, item_spacing); let child_rect = self.placer.justify_and_align(frame_rect, desired_size); - - let mut child_ui = self.new_child(UiBuilder::new().max_rect(child_rect).layout(layout)); - let ret = add_contents(&mut child_ui); - let final_child_rect = child_ui.min_rect(); - - self.placer - .advance_after_rects(final_child_rect, final_child_rect, item_spacing); - - let response = self.interact(final_child_rect, child_ui.id, Sense::hover()); - InnerResponse::new(ret, response) + self.allocate_new_ui( + UiBuilder::new().max_rect(child_rect).layout(layout), + add_contents, + ) } /// Allocated the given rectangle and then adds content to that rectangle. @@ -1244,7 +1238,6 @@ impl Ui { let rect = child_ui.min_rect(); let item_spacing = self.spacing().item_spacing; self.placer.advance_after_rects(rect, rect, item_spacing); - let response = self.interact(rect, child_ui.id, Sense::hover()); InnerResponse::new(inner, response) } @@ -2330,7 +2323,10 @@ impl Ui { /// See also [`Self::with_layout`] for more options. #[inline] pub fn vertical(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse { - self.with_layout_dyn(Layout::top_down(Align::Min), Box::new(add_contents)) + self.allocate_new_ui( + UiBuilder::new().layout(Layout::top_down(Align::Min)), + add_contents, + ) } /// Start a ui with vertical layout. @@ -2349,7 +2345,10 @@ impl Ui { &mut self, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.with_layout_dyn(Layout::top_down(Align::Center), Box::new(add_contents)) + self.allocate_new_ui( + UiBuilder::new().layout(Layout::top_down(Align::Center)), + add_contents, + ) } /// Start a ui with vertical layout. @@ -2367,9 +2366,9 @@ impl Ui { &mut self, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse { - self.with_layout_dyn( - Layout::top_down(Align::Center).with_cross_justify(true), - Box::new(add_contents), + self.allocate_new_ui( + UiBuilder::new().layout(Layout::top_down(Align::Center).with_cross_justify(true)), + add_contents, ) } @@ -2393,15 +2392,7 @@ impl Ui { layout: Layout, add_contents: impl FnOnce(&mut Self) -> R, ) -> InnerResponse { - self.with_layout_dyn(layout, Box::new(add_contents)) - } - - fn with_layout_dyn<'c, R>( - &mut self, - layout: Layout, - add_contents: Box R + 'c>, - ) -> InnerResponse { - self.allocate_new_ui_dyn(UiBuilder::new().layout(layout), add_contents) + self.allocate_new_ui(UiBuilder::new().layout(layout), add_contents) } /// This will make the next added widget centered and justified in the available space. @@ -2411,9 +2402,9 @@ impl Ui { &mut self, add_contents: impl FnOnce(&mut Self) -> R, ) -> InnerResponse { - self.with_layout_dyn( - Layout::centered_and_justified(Direction::TopDown), - Box::new(add_contents), + self.allocate_new_ui( + UiBuilder::new().layout(Layout::centered_and_justified(Direction::TopDown)), + add_contents, ) } From 46e860f3702d9bd4bca9c48c72af971feac4b368 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:10:27 +0200 Subject: [PATCH 10/15] typos --- crates/egui/src/ui_builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 5d180864828..3726abf0ce4 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -4,7 +4,7 @@ use crate::{Id, Layout, Rect, Style, UiStackInfo}; /// Build a [`Ui`] as the chlild of another [`Ui`]. /// -/// By default, everyhting is inherited from the parent, +/// By default, everything is inherited from the parent, /// except for `max_rect` which by default is set to /// the parent [`Ui::available_rect_before_wrap`]. #[must_use] @@ -82,7 +82,7 @@ impl UiBuilder { /// Make the contents invisible. /// - /// Will also diable the `Ui` (see [`Self::disable`]). + /// Will also disable the `Ui` (see [`Self::disable`]). /// /// If the parent `Ui` is invisible, the child will always be invisible. #[inline] From 76d9b4b9c83e0346e35a10abc5af71c5d20de2df Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:23:53 +0200 Subject: [PATCH 11/15] Fix deprecation note --- crates/egui/src/ui.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index fe5edb4428c..52e0c78d7dc 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -154,7 +154,7 @@ impl Ui { /// [`Self::scope`] if needed. /// /// When in doubt, use `None` for the `UiStackInfo` argument. - #[deprecated = "Use ui.child_from_builder instead"] + #[deprecated = "Use ui.new_child() instead"] pub fn child_ui( &mut self, max_rect: Rect, @@ -172,7 +172,7 @@ impl Ui { /// Create a new [`Ui`] at a specific region with a specific id. /// /// When in doubt, use `None` for the `UiStackInfo` argument. - #[deprecated = "Use ui.child_from_builder instead"] + #[deprecated = "Use ui.new_child() instead"] pub fn child_ui_with_id_source( &mut self, max_rect: Rect, @@ -1269,7 +1269,10 @@ impl Ui { let painter = self.painter().with_clip_rect(clip_rect); (response, painter) } +} +/// # Scrolling +impl Ui { /// Adjust the scroll position of any parent [`ScrollArea`] so that the given [`Rect`] becomes visible. /// /// If `align` is [`Align::TOP`] it means "put the top of the rect at the top of the scroll area", etc. @@ -2655,7 +2658,10 @@ impl Ui { (InnerResponse { inner, response }, payload) } +} +/// # Menues +impl Ui { /// Close the menu we are in (including submenus), if any. /// /// See also: [`Self::menu_button`] and [`Response::context_menu`]. From 2d48af761d5fe700c8de9c24dbae07220f4a1681 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:25:40 +0200 Subject: [PATCH 12/15] typo --- crates/egui/src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 52e0c78d7dc..9c14412f221 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2660,7 +2660,7 @@ impl Ui { } } -/// # Menues +/// # Menus impl Ui { /// Close the menu we are in (including submenus), if any. /// From 532c1274610eda6f3088e600c71cb4e5a16b65e2 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 15 Aug 2024 18:27:00 +0200 Subject: [PATCH 13/15] Fix doclinks --- crates/egui/src/ui_builder.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 3726abf0ce4..7f2e8b92478 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -2,6 +2,9 @@ use std::{hash::Hash, sync::Arc}; use crate::{Id, Layout, Rect, Style, UiStackInfo}; +#[allow(unused_imports)] // Used for doclinks +use crate::Ui; + /// Build a [`Ui`] as the chlild of another [`Ui`]. /// /// By default, everything is inherited from the parent, @@ -82,7 +85,7 @@ impl UiBuilder { /// Make the contents invisible. /// - /// Will also disable the `Ui` (see [`Self::disable`]). + /// Will also disable the `Ui` (see [`Self::disabled`]). /// /// If the parent `Ui` is invisible, the child will always be invisible. #[inline] From b0ffd771e5765823a88f475f2f5eafedd77ee23c Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 16 Aug 2024 11:39:33 +0200 Subject: [PATCH 14/15] Deprecate `ui.allocate_ui_at_rect` --- crates/egui/src/containers/scroll_area.rs | 2 +- crates/egui/src/grid.rs | 13 ++++++++----- crates/egui/src/ui.rs | 10 +++++++--- examples/custom_window_frame/src/main.rs | 11 +++++++---- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 45f3f9632dc..20fd4e4f7c5 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -732,7 +732,7 @@ impl ScrollArea { let rect = Rect::from_x_y_ranges(ui.max_rect().x_range(), y_min..=y_max); - ui.allocate_ui_at_rect(rect, |viewport_ui| { + ui.allocate_new_ui(UiBuilder::new().max_rect(rect), |viewport_ui| { viewport_ui.skip_ahead_auto_ids(min_row); // Make sure we get consistent IDs. add_contents(viewport_ui, min_row..max_row) }) diff --git a/crates/egui/src/grid.rs b/crates/egui/src/grid.rs index 2466c41e9b7..e46f2e33949 100644 --- a/crates/egui/src/grid.rs +++ b/crates/egui/src/grid.rs @@ -425,11 +425,14 @@ impl Grid { // then we should pick a default layout that matches that alignment, // which we do here: let max_rect = ui.cursor().intersect(ui.max_rect()); - ui.allocate_ui_at_rect(max_rect, |ui| { - if prev_state.is_none() { - // Hide the ui this frame, and make things as narrow as possible. - ui.set_sizing_pass(); - } + + let mut ui_builder = UiBuilder::new().max_rect(max_rect); + if prev_state.is_none() { + // Hide the ui this frame, and make things as narrow as possible. + ui_builder = ui_builder.sizing_pass(); + } + + ui.allocate_new_ui(ui_builder, |ui| { ui.horizontal(|ui| { let is_color = color_picker.is_some(); let mut grid = GridLayout { diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 9c14412f221..736decea021 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -1207,6 +1207,7 @@ impl Ui { /// If the contents overflow, more space will be allocated. /// When finished, the amount of space actually used (`min_rect`) will be allocated. /// So you can request a lot of space and then use less. + #[deprecated = "Use `allocate_new_ui` instead"] pub fn allocate_ui_at_rect( &mut self, max_rect: Rect, @@ -1445,9 +1446,12 @@ impl Ui { /// /// See also [`Self::add`] and [`Self::add_sized`]. pub fn put(&mut self, max_rect: Rect, widget: impl Widget) -> Response { - self.allocate_ui_at_rect(max_rect, |ui| { - ui.centered_and_justified(|ui| ui.add(widget)).inner - }) + self.allocate_new_ui( + UiBuilder::new() + .max_rect(max_rect) + .layout(Layout::centered_and_justified(Direction::TopDown)), + |ui| ui.add(widget), + ) .inner } diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index 463d9ce9a09..bdf1fa63eca 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -116,14 +116,17 @@ fn title_bar_ui(ui: &mut egui::Ui, title_bar_rect: eframe::epaint::Rect, title: ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag); } - ui.allocate_ui_at_rect(title_bar_rect, |ui| { - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.allocate_new_ui( + UiBuilder::new() + .max_rect(title_bar_rect) + .layout(egui::Layout::right_to_left(egui::Align::Center)), + |ui| { ui.spacing_mut().item_spacing.x = 0.0; ui.visuals_mut().button_frame = false; ui.add_space(8.0); close_maximize_minimize(ui); - }); - }); + }, + ); } /// Show some close/maximize/minimize buttons for the native window. From 2c95eede9d7ae21027fa3787f4109c973fb8a1ba Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 16 Aug 2024 11:45:56 +0200 Subject: [PATCH 15/15] Deprecate `add_visible_ui` and `push_stack_info` --- crates/egui/src/ui.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 736decea021..7e180a8fc88 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -1563,6 +1563,7 @@ impl Ui { /// }); /// # }); /// ``` + #[deprecated = "Use 'ui.scope_builder' instead"] pub fn add_visible_ui( &mut self, visible: bool, @@ -2076,6 +2077,7 @@ impl Ui { /// Push another level onto the [`UiStack`]. /// /// You can use this, for instance, to tag a group of widgets. + #[deprecated = "Use 'ui.scope_builder' instead"] pub fn push_stack_info( &mut self, ui_stack_info: UiStackInfo,