Browse Source

All `Ui`:s must now have a finite `max_rect`

Deprecated `max_rect_finite`, `available_size_before_wrap_finite`
and `available_rect_before_wrap_finite`.
pull/687/head
Emil Ernerfeldt 3 years ago
parent
commit
9def6ef6df
  1. 5
      CHANGELOG.md
  2. 13
      egui/src/containers/area.rs
  3. 2
      egui/src/containers/collapsing_header.rs
  4. 6
      egui/src/containers/panel.rs
  5. 10
      egui/src/grid.rs
  6. 50
      egui/src/layout.rs
  7. 66
      egui/src/menu.rs
  8. 13
      egui/src/placer.rs
  9. 20
      egui/src/ui.rs
  10. 4
      egui/src/widgets/plot/mod.rs
  11. 4
      egui/src/widgets/progress_bar.rs
  12. 2
      egui/src/widgets/separator.rs
  13. 10
      egui_demo_lib/src/apps/demo/layout_test.rs
  14. 2
      egui_demo_lib/src/apps/demo/multi_touch.rs
  15. 2
      egui_demo_lib/src/apps/demo/painting.rs
  16. 2
      egui_demo_lib/src/apps/fractal_clock.rs
  17. 2
      egui_demo_lib/src/easy_mark/easy_mark_viewer.rs
  18. 2
      egui_demo_lib/src/frame_history.rs

5
CHANGELOG.md

@ -7,10 +7,13 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md) and [
## Unreleased
### Added ⭐
* Add horizontal scrolling support to `ScrollArea` and `Window` (opt-in).
### Changed 🔧
* All `Ui`:s must now have a finite `max_rect`.
* Deprecated: `max_rect_finite`, `available_size_before_wrap_finite` and `available_rect_before_wrap_finite`.
## 0.14.2 - 2021-08-28 - Window resize fix

13
egui/src/containers/area.rs

@ -274,7 +274,18 @@ impl Prepared {
}
pub(crate) fn content_ui(&self, ctx: &CtxRef) -> Ui {
let max_rect = Rect::from_min_size(self.state.pos, Vec2::INFINITY);
let max_rect = if ctx.available_rect().contains(self.state.pos) {
Rect::from_min_max(self.state.pos, ctx.available_rect().max)
} else {
Rect::from_min_max(
self.state.pos,
ctx.input()
.screen_rect()
.max
.max(self.state.pos + Vec2::splat(32.0)),
)
};
let shadow_radius = ctx.style().visuals.window_shadow.extrusion; // hacky
let bounds = self.drag_bounds.unwrap_or_else(|| ctx.input().screen_rect);

2
egui/src/containers/collapsing_header.rs

@ -266,7 +266,7 @@ impl CollapsingHeader {
let id = ui.make_persistent_id(id_source);
let button_padding = ui.spacing().button_padding;
let available = ui.available_rect_before_wrap_finite();
let available = ui.available_rect_before_wrap();
let text_pos = available.min + vec2(ui.spacing().indent, 0.0);
let galley = label.layout_width(ui, available.right() - text_pos.x);
let text_max_x = text_pos.x + galley.size.x;

6
egui/src/containers/panel.rs

@ -212,7 +212,7 @@ impl SidePanel {
panel_ui.expand_to_include_rect(panel_rect);
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
let inner_response = frame.show(&mut panel_ui, |ui| {
ui.set_min_height(ui.max_rect_finite().height()); // Make sure the frame fills the full height
ui.set_min_height(ui.max_rect().height()); // Make sure the frame fills the full height
ui.set_min_width(*width_range.start());
add_contents(ui)
});
@ -473,7 +473,7 @@ impl TopBottomPanel {
panel_ui.expand_to_include_rect(panel_rect);
let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style()));
let inner_response = frame.show(&mut panel_ui, |ui| {
ui.set_min_width(ui.max_rect_finite().width()); // Make the frame fill full width
ui.set_min_width(ui.max_rect().width()); // Make the frame fill full width
ui.set_min_height(*height_range.start());
add_contents(ui)
});
@ -588,7 +588,7 @@ impl CentralPanel {
) -> InnerResponse<R> {
let Self { frame } = self;
let panel_rect = ui.available_rect_before_wrap_finite();
let panel_rect = ui.available_rect_before_wrap();
let mut panel_ui = ui.child_ui(panel_rect, Layout::top_down(Align::Min));
let frame = frame.unwrap_or_else(|| Frame::central_panel(ui.style()));

10
egui/src/grid.rs

@ -109,11 +109,6 @@ impl GridLayout {
}
pub(crate) fn available_rect(&self, region: &Region) -> Rect {
// required for putting CollapsingHeader in anything but the last column:
self.available_rect_finite(region)
}
pub(crate) fn available_rect_finite(&self, region: &Region) -> Rect {
let is_last_column = Some(self.col + 1) == self.num_columns;
let width = if is_last_column {
@ -135,7 +130,7 @@ impl GridLayout {
let available = region.max_rect.intersect(region.cursor);
let height = region.max_rect_finite().max.y - available.top();
let height = region.max_rect.max.y - available.top();
let height = height
.at_least(self.min_cell_size.y)
.at_most(self.max_cell_size.y);
@ -351,7 +346,8 @@ impl Grid {
// If somebody wants to wrap more things inside a cell,
// then we should pick a default layout that matches that alignment,
// which we do here:
ui.allocate_ui_at_rect(ui.cursor(), |ui| {
let max_rect = ui.cursor().intersect(ui.max_rect());
ui.allocate_ui_at_rect(max_rect, |ui| {
ui.horizontal(|ui| {
let id = ui.make_persistent_id(id_source);
let grid = GridLayout {

50
egui/src/layout.rs

@ -43,26 +43,6 @@ pub(crate) struct Region {
}
impl Region {
/// This is like `max_rect`, but will never be infinite.
/// 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 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 !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
}
/// 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);
@ -340,8 +320,8 @@ impl Layout {
/// ## Doing layout
impl Layout {
pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect {
crate::egui_assert!(size.x >= 0.0 && size.y >= 0.0);
crate::egui_assert!(!outer.is_negative());
egui_assert!(size.x >= 0.0 && size.y >= 0.0);
egui_assert!(!outer.is_negative());
self.align2().align_size_within_rect(size, outer)
}
@ -367,7 +347,8 @@ impl Layout {
}
pub(crate) fn region_from_max_rect(&self, max_rect: Rect) -> Region {
crate::egui_assert!(!max_rect.any_nan());
egui_assert!(!max_rect.any_nan());
egui_assert!(max_rect.is_finite());
let mut region = Region {
min_rect: Rect::NOTHING, // temporary
max_rect,
@ -382,10 +363,6 @@ impl Layout {
self.available_from_cursor_max_rect(region.cursor, region.max_rect)
}
pub(crate) fn available_rect_before_wrap_finite(&self, region: &Region) -> Rect {
self.available_from_cursor_max_rect(region.cursor, region.max_rect_finite())
}
/// Amount of space available for a widget.
/// For wrapping layouts, this is the maximum (after wrap).
pub(crate) fn available_size(&self, r: &Region) -> Vec2 {
@ -406,6 +383,7 @@ impl Layout {
fn available_from_cursor_max_rect(&self, cursor: Rect, max_rect: Rect) -> Rect {
egui_assert!(!cursor.any_nan());
egui_assert!(!max_rect.any_nan());
egui_assert!(max_rect.is_finite());
// NOTE: in normal top-down layout the cursor has moved below the current max_rect,
// but the available shouldn't be negative.
@ -470,7 +448,7 @@ impl Layout {
/// Use `justify_and_align` to get the inner `widget_rect`.
pub(crate) fn next_frame(&self, region: &Region, child_size: Vec2, spacing: Vec2) -> Rect {
region.sanity_check();
crate::egui_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
egui_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
if self.main_wrap {
let available_size = self.available_rect_before_wrap(region).size();
@ -550,9 +528,9 @@ impl Layout {
fn next_frame_ignore_wrap(&self, region: &Region, child_size: Vec2) -> Rect {
region.sanity_check();
crate::egui_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
egui_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
let available_rect = self.available_rect_before_wrap_finite(region);
let available_rect = self.available_rect_before_wrap(region);
let mut frame_size = child_size;
@ -591,8 +569,8 @@ impl Layout {
/// Apply justify (fill width/height) and/or alignment after calling `next_space`.
pub(crate) fn justify_and_align(&self, frame: Rect, mut child_size: Vec2) -> Rect {
crate::egui_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
crate::egui_assert!(!frame.is_negative());
egui_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
egui_assert!(!frame.is_negative());
if self.horizontal_justify() {
child_size.x = child_size.x.at_least(frame.width()); // fill full width
@ -610,10 +588,10 @@ impl Layout {
) -> Rect {
let frame = self.next_frame_ignore_wrap(region, size);
let rect = self.align_size_within_rect(size, frame);
crate::egui_assert!(!rect.any_nan());
crate::egui_assert!(!rect.is_negative());
crate::egui_assert!((rect.width() - size.x).abs() < 1.0 || size.x == f32::INFINITY);
crate::egui_assert!((rect.height() - size.y).abs() < 1.0 || size.y == f32::INFINITY);
egui_assert!(!rect.any_nan());
egui_assert!(!rect.is_negative());
egui_assert!((rect.width() - size.x).abs() < 1.0 || size.x == f32::INFINITY);
egui_assert!((rect.height() - size.y).abs() < 1.0 || size.y == f32::INFINITY);
rect
}

66
egui/src/menu.rs

@ -70,6 +70,40 @@ pub fn menu<R>(
menu_impl(ui, title, Box::new(add_contents))
}
pub(crate) fn menu_ui<'c, R>(
ctx: &CtxRef,
menu_id: impl std::hash::Hash,
pos: Pos2,
mut style: Style,
add_contents: impl FnOnce(&mut Ui) -> R + 'c,
) -> InnerResponse<R> {
let area = Area::new(menu_id)
.order(Order::Foreground)
.fixed_pos(pos)
.interactable(false)
.drag_bounds(Rect::EVERYTHING);
let frame = Frame::menu(&style);
area.show(ctx, |ui| {
frame
.show(ui, |ui| {
const DEFAULT_MENU_WIDTH: f32 = 150.0; // TODO: add to ui.spacing
ui.set_max_width(DEFAULT_MENU_WIDTH);
// style.visuals.widgets.active.bg_fill = Color32::TRANSPARENT;
style.visuals.widgets.active.bg_stroke = Stroke::none();
// style.visuals.widgets.hovered.bg_fill = Color32::TRANSPARENT;
style.visuals.widgets.hovered.bg_stroke = Stroke::none();
style.visuals.widgets.inactive.bg_fill = Color32::TRANSPARENT;
style.visuals.widgets.inactive.bg_stroke = Stroke::none();
ui.set_style(style);
ui.with_layout(Layout::top_down_justified(Align::LEFT), add_contents)
.inner
})
.inner
})
}
#[allow(clippy::needless_pass_by_value)]
fn menu_impl<'c, R>(
ui: &mut Ui,
@ -103,30 +137,14 @@ fn menu_impl<'c, R>(
let inner = if bar_state.open_menu == Some(menu_id) || ui.ctx().memory().everything_is_visible()
{
let area = Area::new(menu_id)
.order(Order::Foreground)
.fixed_pos(button_response.rect.left_bottom());
let frame = Frame::menu(ui.style());
let inner = area
.show(ui.ctx(), |ui| {
frame
.show(ui, |ui| {
let mut style = (**ui.style()).clone();
style.spacing.button_padding = vec2(2.0, 0.0);
// style.visuals.widgets.active.bg_fill = Color32::TRANSPARENT;
style.visuals.widgets.active.bg_stroke = Stroke::none();
// style.visuals.widgets.hovered.bg_fill = Color32::TRANSPARENT;
style.visuals.widgets.hovered.bg_stroke = Stroke::none();
style.visuals.widgets.inactive.bg_fill = Color32::TRANSPARENT;
style.visuals.widgets.inactive.bg_stroke = Stroke::none();
ui.set_style(style);
ui.with_layout(Layout::top_down_justified(Align::LEFT), add_contents)
.inner
})
.inner
})
.inner;
let inner = menu_ui(
ui.ctx(),
menu_id,
button_response.rect.left_bottom(),
ui.style().as_ref().clone(),
add_contents,
)
.inner;
// TODO: this prevents sub-menus in menus. We should fix that.
if ui.input().key_pressed(Key::Escape) || button_response.clicked_elsewhere() {

13
egui/src/placer.rs

@ -58,11 +58,6 @@ impl Placer {
self.region.max_rect
}
#[inline(always)]
pub(crate) fn max_rect_finite(&self) -> Rect {
self.region.max_rect_finite()
}
#[inline(always)]
pub(crate) fn force_set_min_rect(&mut self, min_rect: Rect) {
self.region.min_rect = min_rect;
@ -96,14 +91,6 @@ impl Placer {
}
}
pub(crate) fn available_rect_before_wrap_finite(&self) -> Rect {
if let Some(grid) = &self.grid {
grid.available_rect_finite(&self.region)
} else {
self.layout.available_rect_before_wrap_finite(&self.region)
}
}
/// Amount of space available for a widget.
/// For wrapping layouts, this is the maximum (after wrap).
pub(crate) fn available_size(&self) -> Vec2 {

20
egui/src/ui.rs

@ -367,10 +367,9 @@ impl Ui {
self.placer.max_rect()
}
/// This is like `max_rect()`, but will never be infinite.
/// This can be useful for widgets that expand to fit the available space.
#[deprecated = "Use .max_rect() instead"]
pub fn max_rect_finite(&self) -> Rect {
self.placer.max_rect_finite()
self.max_rect()
}
/// Used for animation, kind of hacky
@ -501,22 +500,18 @@ impl Ui {
self.placer.available_rect_before_wrap().size()
}
/// This is like `available_size_before_wrap()`, but will never be infinite.
/// This can be useful for widgets that expand to fit the available space.
/// In most layouts the next widget will be put in the top left corner of this `Rect`.
#[deprecated = "Use .available_size_before_wrap() instead"]
pub fn available_size_before_wrap_finite(&self) -> Vec2 {
self.placer.available_rect_before_wrap_finite().size()
self.available_size_before_wrap()
}
pub fn available_rect_before_wrap(&self) -> Rect {
self.placer.available_rect_before_wrap()
}
/// This is like `available_rect_before_wrap()`, but will never be infinite.
/// This can be useful for widgets that expand to fit the available space.
/// In most layouts the next widget will be put in the top left corner of this `Rect`.
#[deprecated = "Use .available_rect_before_wrap() instead"]
pub fn available_rect_before_wrap_finite(&self) -> Rect {
self.placer.available_rect_before_wrap_finite()
self.available_rect_before_wrap()
}
}
@ -812,6 +807,7 @@ impl Ui {
max_rect: Rect,
add_contents: impl FnOnce(&mut Self) -> R,
) -> InnerResponse<R> {
egui_assert!(max_rect.is_finite());
let mut child_ui = self.child_ui(max_rect, *self.layout());
let ret = add_contents(&mut child_ui);
let final_child_rect = child_ui.min_rect();
@ -1649,7 +1645,7 @@ impl Ui {
/// Shows the given text where the next widget is to be placed
/// if when [`Context::set_debug_on_hover`] has been turned on and the mouse is hovering the Ui.
pub fn trace_location(&self, text: impl ToString) {
let rect = self.max_rect_finite();
let rect = self.max_rect();
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
self.placer
.debug_paint_cursor(&self.ctx().debug_painter(), text);

4
egui/src/widgets/plot/mod.rs

@ -382,7 +382,7 @@ impl Widget for Plot {
if let (Some(height), Some(aspect)) = (height, view_aspect) {
height * aspect
} else {
ui.available_size_before_wrap_finite().x
ui.available_size_before_wrap().x
}
})
.at_least(min_size.x);
@ -392,7 +392,7 @@ impl Widget for Plot {
if let Some(aspect) = view_aspect {
width / aspect
} else {
ui.available_size_before_wrap_finite().y
ui.available_size_before_wrap().y
}
})
.at_least(min_size.y);

4
egui/src/widgets/progress_bar.rs

@ -67,8 +67,8 @@ impl Widget for ProgressBar {
ui.ctx().request_repaint();
}
let desired_width = desired_width
.unwrap_or_else(|| ui.available_size_before_wrap_finite().x.at_least(96.0));
let desired_width =
desired_width.unwrap_or_else(|| ui.available_size_before_wrap().x.at_least(96.0));
let height = ui.spacing().interact_size.y;
let (outer_rect, response) =
ui.allocate_exact_size(vec2(desired_width, height), Sense::hover());

2
egui/src/widgets/separator.rs

@ -59,7 +59,7 @@ impl Widget for Separator {
let is_horizontal_line = is_horizontal_line
.unwrap_or_else(|| ui.is_grid() || !ui.layout().main_dir().is_horizontal());
let available_space = ui.available_size_before_wrap_finite();
let available_space = ui.available_size_before_wrap();
let size = if is_horizontal_line {
vec2(available_space.x, spacing)

10
egui_demo_lib/src/apps/demo/layout_test.rs

@ -53,18 +53,12 @@ impl super::View for LayoutTest {
if self.main_wrap {
if self.main_dir.is_horizontal() {
ui.allocate_ui(
vec2(
ui.available_size_before_wrap_finite().x,
self.wrap_row_height,
),
vec2(ui.available_size_before_wrap().x, self.wrap_row_height),
|ui| ui.with_layout(self.layout(), demo_ui),
);
} else {
ui.allocate_ui(
vec2(
self.wrap_column_width,
ui.available_size_before_wrap_finite().y,
),
vec2(self.wrap_column_width, ui.available_size_before_wrap().y),
|ui| ui.with_layout(self.layout(), demo_ui),
);
}

2
egui_demo_lib/src/apps/demo/multi_touch.rs

@ -63,7 +63,7 @@ impl super::View for MultiTouch {
// set up the drawing canvas with normalized coordinates:
let (response, painter) =
ui.allocate_painter(ui.available_size_before_wrap_finite(), Sense::drag());
ui.allocate_painter(ui.available_size_before_wrap(), Sense::drag());
// normalize painter coordinates to ±1 units in each direction with [0,0] in the center:
let painter_proportions = response.rect.square_proportions();
let to_screen = RectTransform::from_to(

2
egui_demo_lib/src/apps/demo/painting.rs

@ -31,7 +31,7 @@ impl Painting {
pub fn ui_content(&mut self, ui: &mut Ui) -> egui::Response {
let (mut response, painter) =
ui.allocate_painter(ui.available_size_before_wrap_finite(), Sense::drag());
ui.allocate_painter(ui.available_size_before_wrap(), Sense::drag());
let to_screen = emath::RectTransform::from_to(
Rect::from_min_size(Pos2::ZERO, response.rect.square_proportions()),

2
egui_demo_lib/src/apps/fractal_clock.rs

@ -54,7 +54,7 @@ impl FractalClock {
let painter = Painter::new(
ui.ctx().clone(),
ui.layer_id(),
ui.available_rect_before_wrap_finite(),
ui.available_rect_before_wrap(),
);
self.paint(&painter);
// Make sure we allocate what we used (everything)

2
egui_demo_lib/src/easy_mark/easy_mark_viewer.rs

@ -68,7 +68,7 @@ pub fn item_ui(ui: &mut Ui, item: easy_mark::Item<'_>) {
let where_to_put_background = ui.painter().add(Shape::Noop);
let mut rect = ui.monospace(code).rect;
rect = rect.expand(1.0); // looks better
rect.max.x = ui.max_rect_finite().max.x;
rect.max.x = ui.max_rect().max.x;
let code_bg_color = ui.visuals().code_bg_color;
ui.painter().set(
where_to_put_background,

2
egui_demo_lib/src/frame_history.rs

@ -64,7 +64,7 @@ impl FrameHistory {
// TODO: we should not use `slider_width` as default graph width.
let height = ui.spacing().slider_width;
let size = vec2(ui.available_size_before_wrap_finite().x, height);
let size = vec2(ui.available_size_before_wrap().x, height);
let (rect, response) = ui.allocate_at_least(size, Sense::hover());
let style = ui.style().noninteractive();

Loading…
Cancel
Save