diff --git a/egui/src/containers/collapsing_header.rs b/egui/src/containers/collapsing_header.rs index c70b06a5a..cc9589729 100644 --- a/egui/src/containers/collapsing_header.rs +++ b/egui/src/containers/collapsing_header.rs @@ -81,14 +81,13 @@ impl State { clip_rect.max.y = clip_rect.max.y.min(child_ui.max_rect().top() + max_height); child_ui.set_clip_rect(clip_rect); - let left_top = child_ui.left_top(); let r = add_contents(child_ui); self.open_height = Some(child_ui.min_size().y); // Pretend children took up less space: let mut min_rect = child_ui.min_rect(); - min_rect.max.y = min_rect.max.y.min(left_top.y + max_height); + min_rect.max.y = min_rect.max.y.min(min_rect.top() + max_height); child_ui.force_set_min_rect(min_rect); r })) diff --git a/egui/src/containers/resize.rs b/egui/src/containers/resize.rs index eab9f6a08..898e1006a 100644 --- a/egui/src/containers/resize.rs +++ b/egui/src/containers/resize.rs @@ -261,7 +261,7 @@ impl Resize { // ------------------------------ if self.with_stroke && corner_response.is_some() { - let rect = Rect::from_min_size(content_ui.left_top(), state.desired_size); + let rect = Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size); let rect = rect.expand(2.0); // breathing room for content ui.painter().add(paint::PaintCmd::Rect { rect, @@ -283,12 +283,12 @@ impl Resize { if ui.ctx().style().visuals.debug_resize { ui.ctx().debug_painter().debug_rect( - Rect::from_min_size(content_ui.left_top(), state.desired_size), + Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size), color::GREEN, "desired_size", ); ui.ctx().debug_painter().debug_rect( - Rect::from_min_size(content_ui.left_top(), state.last_content_size), + Rect::from_min_size(content_ui.min_rect().left_top(), state.last_content_size), color::LIGHT_BLUE, "last_content_size", ); diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index f1dc6231d..bab61ef62 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -643,7 +643,7 @@ fn show_title_bar( ), Vec2::splat(button_size), ); - ui.expand_to_include_child(close_rect); + ui.expand_to_include_rect(close_rect); } TitleBar { @@ -724,7 +724,7 @@ impl TitleBar { fn close_button(ui: &mut Ui, rect: Rect) -> Response { let close_id = ui.make_child_id("window_close_button"); let response = ui.interact(rect, close_id, Sense::click()); - ui.expand_to_include_child(response.rect); + ui.expand_to_include_rect(response.rect); let stroke = ui.style().interact(&response).fg_stroke; ui.painter() diff --git a/egui/src/introspection.rs b/egui/src/introspection.rs index d3575a0dc..b6162cb28 100644 --- a/egui/src/introspection.rs +++ b/egui/src/introspection.rs @@ -30,7 +30,11 @@ impl Texture { if ui.hovered(rect) { show_tooltip(ui.ctx(), |ui| { - let pos = ui.input().mouse.pos.unwrap_or_else(|| ui.left_top()); + let pos = ui + .input() + .mouse + .pos + .unwrap_or_else(|| ui.min_rect().left_top()); let zoom_rect = ui.allocate_space(vec2(128.0, 128.0)); let u = remap_clamp(pos.x, rect.x_range(), 0.0..=tex_w); let v = remap_clamp(pos.y, rect.y_range(), 0.0..=tex_h); diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 5e846e8b3..f901f79b4 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -179,28 +179,24 @@ impl Ui { /// ## Sizes etc impl Ui { - /// Screen-space position of this Ui. - /// This may have moved from its original if a child overflowed to the left or up (rare). - pub fn left_top(&self) -> Pos2 { - // If a child doesn't fit in max_rect, we have effectively expanded: - self.max_rect.min - } - - /// Screen-space position of the current bottom right corner of this Ui. - /// This may move when we add children that overflow our desired rectangle bounds. - /// This position may be at infinity if the desired rect is infinite, - /// which happens when a parent widget says "be as big as you want to be". - pub fn right_bottom(&self) -> Pos2 { - // If a child doesn't fit in max_rect, we have effectively expanded: - self.max_rect.max - } - - /// Bounding box of all contained children + /// The current size of this Ui. + /// Bounding box of all contained child widgets. + /// No matter what, the final Ui will be at least this large. + /// This will grow as new widgets are added, but never shrink. pub fn min_rect(&self) -> Rect { self.min_rect } + /// Size of content; same as `min_rect().size()` + pub fn min_size(&self) -> Vec2 { + self.min_rect.size() + } + /// This is the soft max size of the Ui. + /// New widgets will *try* to fit within this rectangle. + /// For instance, text will wrap to fit within it. + /// If a widget doesn't fit within the `max_rect` then it will expand. + /// `max_rect()` is always at least as large as `min_rect()`. pub fn max_rect(&self) -> Rect { self.max_rect } @@ -214,91 +210,111 @@ impl Ui { /// If the desired rect is infinite ("be as big as you want") /// this will be bounded by `min_rect` instead. pub fn max_rect_finite(&self) -> Rect { - let mut right_bottom = self.min_rect.max; - if self.max_rect.max.x.is_finite() { - right_bottom.x = right_bottom.x.max(self.max_rect.max.x); + let mut result = self.max_rect; + if !result.min.x.is_finite() { + result.min.x = self.min_rect.min.x; + } + if !result.min.y.is_finite() { + result.min.y = self.min_rect.min.y; } - if self.max_rect.max.y.is_finite() { - right_bottom.y = right_bottom.y.max(self.max_rect.max.y); + if !result.max.x.is_finite() { + result.max.x = self.min_rect.max.x; } + if !result.max.y.is_finite() { + result.max.y = self.min_rect.max.y; + } + result + } - Rect::from_min_max(self.left_top(), right_bottom) + // ------------------------------------------------------------------------ + + /// Set the maximum size of the ui. + /// You won't be able to shrink it below the current minimum size. + pub fn set_max_size(&mut self, size: Vec2) { + self.set_max_width(size.x); + self.set_max_height(size.y); } - /// Set the width of the ui. - /// You won't be able to shrink it beyond its current child bounds. + /// Set the maximum width of the ui. + /// You won't be able to shrink it below the current minimum size. pub fn set_max_width(&mut self, width: f32) { - let min_width = self.min_rect.max.x - self.left_top().x; - let width = width.at_least(min_width); - self.max_rect.max.x = self.left_top().x + width; + if self.layout.dir() == Direction::Horizontal && self.layout.is_reversed() { + debug_assert_eq!(self.min_rect.max.x, self.max_rect.max.x); + self.max_rect.min.x = self.max_rect.max.x - width.at_least(self.min_rect.width()); + } else { + debug_assert_eq!(self.min_rect.min.x, self.max_rect.min.x); + self.max_rect.max.x = self.max_rect.min.x + width.at_least(self.min_rect.width()); + } } - /// Set the height of the ui. - /// You won't be able to shrink it beyond its current child bounds. + /// Set the maximum height of the ui. + /// You won't be able to shrink it below the current minimum size. pub fn set_max_height(&mut self, height: f32) { - let min_height = self.min_rect.max.y - self.left_top().y; - let height = height.at_least(min_height); - self.max_rect.max.y = self.left_top().y + height; + if self.layout.dir() == Direction::Vertical && self.layout.is_reversed() { + debug_assert_eq!(self.min_rect.max.y, self.max_rect.max.y); + self.max_rect.min.y = self.max_rect.max.y - height.at_least(self.min_rect.height()); + } else { + debug_assert_eq!(self.min_rect.min.y, self.max_rect.min.y); + self.max_rect.max.y = self.max_rect.min.y + height.at_least(self.min_rect.height()); + } } + // ------------------------------------------------------------------------ + + /// Set the minimum size of the ui. + /// This can't shrink the ui, only make it larger. pub fn set_min_size(&mut self, size: Vec2) { self.set_min_width(size.x); self.set_min_height(size.y); } + /// Set the minimum width of the ui. + /// This can't shrink the ui, only make it larger. pub fn set_min_width(&mut self, width: f32) { if self.layout.dir() == Direction::Horizontal && self.layout.is_reversed() { debug_assert_eq!(self.min_rect.max.x, self.max_rect.max.x); self.min_rect.min.x = self.min_rect.min.x.min(self.min_rect.max.x - width); - self.max_rect.min.x = self.max_rect.min.x.min(self.max_rect.max.x - width); } else { debug_assert_eq!(self.min_rect.min.x, self.max_rect.min.x); self.min_rect.max.x = self.min_rect.max.x.max(self.min_rect.min.x + width); - self.max_rect.max.x = self.max_rect.max.x.max(self.max_rect.min.x + width); } + self.max_rect = self.max_rect.union(self.min_rect); } + /// Set the minimum height of the ui. + /// This can't shrink the ui, only make it larger. pub fn set_min_height(&mut self, height: f32) { if self.layout.dir() == Direction::Vertical && self.layout.is_reversed() { debug_assert_eq!(self.min_rect.max.y, self.max_rect.max.y); self.min_rect.min.y = self.min_rect.min.y.min(self.min_rect.max.y - height); - self.max_rect.min.y = self.max_rect.min.y.min(self.max_rect.max.y - height); } else { debug_assert_eq!(self.min_rect.min.y, self.max_rect.min.y); self.min_rect.max.y = self.min_rect.max.y.max(self.min_rect.min.y + height); - self.max_rect.max.y = self.max_rect.max.y.max(self.max_rect.min.y + height); } + self.max_rect = self.max_rect.union(self.min_rect); } - /// Helper: shrinks the max/desired width to the current width, + // ------------------------------------------------------------------------ + + /// Helper: shrinks the max width to the current width, /// so further widgets will try not to be wider than previous widgets. /// Useful for normal vertical layouts. pub fn shrink_width_to_current(&mut self) { self.set_max_width(self.min_rect().width()) } - /// Helper: shrinks the max/desired height to the current height, + /// Helper: shrinks the max height to the current height, /// so further widgets will try not to be wider than previous widgets. pub fn shrink_height_to_current(&mut self) { - self.set_min_height(self.min_rect().height()) - } - - /// Size of content - pub fn min_size(&self) -> Vec2 { - self.min_rect.size() + self.set_max_height(self.min_rect().height()) } - /// Expand the bounding rect of this ui to include a child at the given rect. - pub fn expand_to_include_child(&mut self, rect: Rect) { + /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given rect. + pub fn expand_to_include_rect(&mut self, rect: Rect) { self.min_rect = self.min_rect.union(rect); self.max_rect = self.max_rect.union(rect); } - pub fn expand_to_size(&mut self, size: Vec2) { - self.min_rect.extend_with(self.left_top() + size); - self.max_rect.extend_with(self.left_top() + size); - } - // ------------------------------------------------------------------------ // Layout related measures: @@ -318,13 +334,6 @@ impl Ui { pub fn available_finite(&self) -> Rect { self.layout.available(self.cursor, self.max_rect_finite()) } - - // ------------------------------------------------------------------------ - - pub fn contains_mouse(&self, rect: Rect) -> bool { - self.ctx() - .contains_mouse(self.layer(), self.clip_rect(), rect) - } } /// # `Id` creation @@ -393,6 +402,11 @@ impl Ui { self.interact_hover(rect).hovered } + pub fn contains_mouse(&self, rect: Rect) -> bool { + self.ctx() + .contains_mouse(self.layer(), self.clip_rect(), rect) + } + // ------------------------------------------------------------------------ // Stuff that moves the cursor, i.e. allocates space in this ui! @@ -664,7 +678,7 @@ impl Ui { "You can only indent vertical layouts" ); let indent = vec2(self.style().spacing.indent, 0.0); - let child_rect = Rect::from_min_max(self.cursor + indent, self.right_bottom()); // TODO: wrong for reversed layouts + let child_rect = Rect::from_min_max(self.cursor + indent, self.max_rect.right_bottom()); // TODO: wrong for reversed layouts let mut child_ui = Ui { id: self.id.with(id_source), ..self.child_ui(child_rect, self.layout) @@ -788,8 +802,10 @@ impl Ui { let mut columns: Vec = (0..num_columns) .map(|col_idx| { let pos = self.cursor + vec2((col_idx as f32) * (column_width + spacing), 0.0); - let child_rect = - Rect::from_min_max(pos, pos2(pos.x + column_width, self.right_bottom().y)); + let child_rect = Rect::from_min_max( + pos, + pos2(pos.x + column_width, self.max_rect.right_bottom().y), + ); Self { id: self.make_child_id(&("column", col_idx)),