diff --git a/emigui/src/containers/collapsing_header.rs b/emigui/src/containers/collapsing_header.rs index edf89d25..72216f90 100644 --- a/emigui/src/containers/collapsing_header.rs +++ b/emigui/src/containers/collapsing_header.rs @@ -61,7 +61,11 @@ impl CollapsingHeader { let text_pos = ui.cursor() + vec2(ui.style().indent, 0.0); let (title, text_size) = label.layout(text_pos, ui); let text_max_x = text_pos.x + text_size.x; - let desired_width = ui.available_space_min().x.max(text_max_x - ui.cursor().x); + let desired_width = ui + .available_finite() + .size() + .x + .max(text_max_x - ui.cursor().x); let interact = ui.reserve_space( vec2( @@ -115,15 +119,21 @@ impl CollapsingHeader { if animate { ui.indent(id, |child_ui| { let max_height = if state.open { - let full_height = state.open_height.unwrap_or(1000.0); - remap(time_since_toggle, 0.0..=animation_time, 0.0..=full_height) + if let Some(full_height) = state.open_height { + remap(time_since_toggle, 0.0..=animation_time, 0.0..=full_height) + } else { + // First frame of expansion. + // We don't know full height yet, but we will next frame. + // Just use a placehodler value that shows some movement: + 10.0 + } } else { let full_height = state.open_height.unwrap_or_default(); remap_clamp(time_since_toggle, 0.0..=animation_time, full_height..=0.0) }; let mut clip_rect = child_ui.clip_rect(); - clip_rect.max.y = clip_rect.max.y.min(child_ui.cursor().y + max_height); + clip_rect.max.y = clip_rect.max.y.min(child_ui.rect().top() + max_height); child_ui.set_clip_rect(clip_rect); let top_left = child_ui.top_left(); diff --git a/emigui/src/containers/frame.rs b/emigui/src/containers/frame.rs index d4d6e276..b2d21bd0 100644 --- a/emigui/src/containers/frame.rs +++ b/emigui/src/containers/frame.rs @@ -58,18 +58,14 @@ impl Frame { outline, } = self; - let outer_pos = ui.cursor(); - let inner_rect = - Rect::from_min_size(outer_pos + margin, ui.available_space() - 2.0 * margin); + let outer_rect = ui.available(); + let inner_rect = outer_rect.expand2(-margin); let where_to_put_background = ui.paint_list_len(); let mut child_ui = ui.child_ui(inner_rect); add_contents(&mut child_ui); - let inner_size = child_ui.bounding_size(); - let inner_size = inner_size.ceil(); // TODO: round to pixel - - let outer_rect = Rect::from_min_size(outer_pos, margin + inner_size + margin); + let outer_rect = Rect::from_min_max(outer_rect.min, child_ui.child_bounds().max + margin); ui.insert_paint_cmd( where_to_put_background, @@ -81,7 +77,7 @@ impl Frame { }, ); - ui.expand_to_include_child(child_ui.child_bounds().expand2(margin)); - // TODO: move up cursor? + ui.expand_to_include_child(outer_rect); + // TODO: move cursor in parent ui } } diff --git a/emigui/src/containers/menu.rs b/emigui/src/containers/menu.rs index 2a559c3a..0709dddf 100644 --- a/emigui/src/containers/menu.rs +++ b/emigui/src/containers/menu.rs @@ -31,7 +31,7 @@ pub fn bar(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) { ui.set_style(style); // Take full width and fixed height: - ui.expand_to_size(vec2(ui.available_width(), ui.style().menu_bar.height)); + ui.expand_to_size(vec2(ui.available().width(), ui.style().menu_bar.height)); add_contents(ui) }) }) diff --git a/emigui/src/containers/resize.rs b/emigui/src/containers/resize.rs index b3121955..c6003fc4 100644 --- a/emigui/src/containers/resize.rs +++ b/emigui/src/containers/resize.rs @@ -143,9 +143,9 @@ impl Resize { // TODO: a common trait for Things that follow this pattern impl Resize { pub fn show(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) { - let id = ui.make_child_id("scroll"); - self.min_size = self.min_size.min(ui.available_space()); - self.max_size = self.max_size.min(ui.available_space()); + let id = ui.make_child_id("resize"); + self.min_size = self.min_size.min(ui.available().size()); + self.max_size = self.max_size.min(ui.available().size()); self.max_size = self.max_size.max(self.min_size); let (is_new, mut state) = match ui.memory().resize.get(&id) { diff --git a/emigui/src/containers/scroll_area.rs b/emigui/src/containers/scroll_area.rs index 2d3f9b78..e66cf25a 100644 --- a/emigui/src/containers/scroll_area.rs +++ b/emigui/src/containers/scroll_area.rs @@ -69,8 +69,8 @@ impl ScrollArea { }; let outer_size = vec2( - outer_ui.available_width(), - outer_ui.available_height().min(self.max_height), + outer_ui.available().width(), + outer_ui.available().height().min(self.max_height), ); let inner_size = outer_size - vec2(current_scroll_bar_width, 0.0); diff --git a/emigui/src/examples/app.rs b/emigui/src/examples/app.rs index b0ba5ab1..1983ea8d 100644 --- a/emigui/src/examples/app.rs +++ b/emigui/src/examples/app.rs @@ -354,7 +354,7 @@ impl Painting { ui.add_custom_contents(vec2(f32::INFINITY, 200.0), |ui| { let canvas_corner = ui.cursor(); - let interact = ui.reserve_space(ui.available_space(), Some(ui.id())); + let interact = ui.reserve_space(ui.available().size(), Some(ui.id())); ui.set_clip_rect(ui.clip_rect().intersect(interact.rect)); // Make sure we don't paint out of bounds if self.lines.is_empty() { diff --git a/emigui/src/examples/fractal_clock.rs b/emigui/src/examples/fractal_clock.rs index de3f2413..d73ba0e1 100644 --- a/emigui/src/examples/fractal_clock.rs +++ b/emigui/src/examples/fractal_clock.rs @@ -123,7 +123,7 @@ impl FractalClock { Hand::from_length_angle(0.5, angle_from_period(12.0 * 60.0 * 60.0)), ]; - let rect = ui.available_rect_min(); + let rect = ui.available_finite(); let scale = self.zoom * rect.width().min(rect.height()); let mut paint_line = |points: [Pos2; 2], color: Color, width: f32| { diff --git a/emigui/src/math.rs b/emigui/src/math.rs index a8e638ea..c587cd6b 100644 --- a/emigui/src/math.rs +++ b/emigui/src/math.rs @@ -382,13 +382,13 @@ impl Rect { /// Expand by this much in each direction, keeping the center #[must_use] pub fn expand(self, amnt: f32) -> Self { - Rect::from_center_size(self.center(), self.size() + 2.0 * vec2(amnt, amnt)) + self.expand2(Vec2::splat(amnt)) } /// Expand by this much in each direction, keeping the center #[must_use] pub fn expand2(self, amnt: Vec2) -> Self { - Rect::from_center_size(self.center(), self.size() + 2.0 * amnt) + Rect::from_min_max(self.min - amnt, self.max + amnt) } #[must_use] diff --git a/emigui/src/texture_atlas.rs b/emigui/src/texture_atlas.rs index 4707dafd..22e37bfa 100644 --- a/emigui/src/texture_atlas.rs +++ b/emigui/src/texture_atlas.rs @@ -100,8 +100,8 @@ impl Texture { self.height )); let mut size = vec2(self.width as f32, self.height as f32); - if size.x > ui.available_width() { - size *= ui.available_width() / size.x; + if size.x > ui.available().width() { + size *= ui.available().width() / size.x; } let interact = ui.reserve_space(size, None); let rect = interact.rect; diff --git a/emigui/src/ui.rs b/emigui/src/ui.rs index 9fffcc74..196cdfbf 100644 --- a/emigui/src/ui.rs +++ b/emigui/src/ui.rs @@ -231,33 +231,19 @@ impl Ui { // ------------------------------------------------------------------------ // Layout related measures: - /// A zero should be intepreted as "as little as possible". - /// An infinite value should be intereted as "as much as you want" - pub fn available_width(&self) -> f32 { - self.available_space().x - } - - /// A zero should be intepreted as "as little as possible". - /// An infinite value should be intereted as "as much as you want" - pub fn available_height(&self) -> f32 { - self.available_space().y - } - + /// The available space at the moment, given the current cursor. /// This how much more space we can take up without overflowing our parent. - /// Shrinks as cursor increments. - /// A zero size should be intepreted as "as little as possible". - /// An infinite size should be intereted as "as much as you want" - pub fn available_space(&self) -> Vec2 { - self.bottom_right() - self.cursor + /// Shrinks as widgets allocate space and the cursor moves. + /// A small rectangle should be intepreted as "as little as possible". + /// An infinite rectangle should be interpred as "as much as you want" + pub fn available(&self) -> Rect { + Rect::from_min_max(self.cursor, self.bottom_right()) } - /// Use this for components that want to grow witout bounds. - pub fn available_space_min(&self) -> Vec2 { - self.finite_bottom_right() - self.cursor - } - - pub fn available_rect_min(&self) -> Rect { - Rect::from_min_size(self.cursor, self.available_space_min()) + /// This is like `available()`, but will never be infinite. + /// Use this for components that want to grow without bounds (but shouldn't). + pub fn available_finite(&self) -> Rect { + Rect::from_min_max(self.cursor, self.finite_bottom_right()) } pub fn direction(&self) -> Direction { @@ -344,7 +330,7 @@ impl Ui { /// # How sizes are negotiated /// Each widget should have a *minimum desired size* and a *desired size*. /// When asking for space, ask AT LEAST for you minimum, and don't ask for more than you need. - /// If you want to fill the space, ask about `available_space()` and use that. + /// If you want to fill the space, ask about `available().size()` and use that. /// /// You may get MORE space than you asked for, for instance /// for `Justified` aligned layouts, like in menus. @@ -355,8 +341,8 @@ impl Ui { self.cursor = self.round_pos_to_pixels(self.cursor); // For debug rendering - let too_wide = child_size.x > self.available_width(); - let too_high = child_size.x > self.available_height(); + let too_wide = child_size.x > self.available().width(); + let too_high = child_size.x > self.available().height(); let rect = self.reserve_space_impl(child_size); @@ -371,40 +357,19 @@ impl Ui { let color = color::srgba(200, 0, 0, 255); let width = 2.5; + let mut paint_line_seg = + |a, b| self.add_paint_cmd(PaintCmd::line_segment([a, b], color, width)); + if too_wide { - self.add_paint_cmd(PaintCmd::line_segment( - [rect.left_top(), rect.left_bottom()], - color, - width, - )); - self.add_paint_cmd(PaintCmd::line_segment( - [rect.left_center(), rect.right_center()], - color, - width, - )); - self.add_paint_cmd(PaintCmd::line_segment( - [rect.right_top(), rect.right_bottom()], - color, - width, - )); + paint_line_seg(rect.left_top(), rect.left_bottom()); + paint_line_seg(rect.left_center(), rect.right_center()); + paint_line_seg(rect.right_top(), rect.right_bottom()); } if too_high { - self.add_paint_cmd(PaintCmd::line_segment( - [rect.left_top(), rect.right_top()], - color, - width, - )); - self.add_paint_cmd(PaintCmd::line_segment( - [rect.center_top(), rect.center_bottom()], - color, - width, - )); - self.add_paint_cmd(PaintCmd::line_segment( - [rect.left_bottom(), rect.right_bottom()], - color, - width, - )); + paint_line_seg(rect.left_top(), rect.right_top()); + paint_line_seg(rect.center_top(), rect.center_bottom()); + paint_line_seg(rect.left_bottom(), rect.right_bottom()); } } @@ -419,12 +384,12 @@ impl Ui { if self.dir == Direction::Horizontal { child_pos.y += match self.align { Align::Min | Align::Justified => 0.0, - Align::Center => 0.5 * (self.available_height() - child_size.y), - Align::Max => self.available_height() - child_size.y, + Align::Center => 0.5 * (self.available().height() - child_size.y), + Align::Max => self.available().height() - child_size.y, }; - if self.align == Align::Justified && self.available_height().is_finite() { + if self.align == Align::Justified && self.available().height().is_finite() { // Fill full height - child_size.y = child_size.y.max(self.available_height()); + child_size.y = child_size.y.max(self.available().height()); } self.child_bounds.extend_with(self.cursor + child_size); self.cursor.x += child_size.x; @@ -432,12 +397,12 @@ impl Ui { } else { child_pos.x += match self.align { Align::Min | Align::Justified => 0.0, - Align::Center => 0.5 * (self.available_width() - child_size.x), - Align::Max => self.available_width() - child_size.x, + Align::Center => 0.5 * (self.available().width() - child_size.x), + Align::Max => self.available().width() - child_size.x, }; - if self.align == Align::Justified && self.available_width().is_finite() { + if self.align == Align::Justified && self.available().width().is_finite() { // Fill full width - child_size.x = child_size.x.max(self.available_width()); + child_size.x = child_size.x.max(self.available().width()); } self.child_bounds.extend_with(self.cursor + child_size); self.cursor.y += child_size.y; @@ -490,6 +455,18 @@ impl Ui { self.ctx.debug_text(pos, text); } + pub fn debug_rect(&mut self, rect: Rect, text: &str) { + self.add_paint_cmd(PaintCmd::Rect { + corner_radius: 0.0, + fill_color: None, + outline: Some(Outline::new(1.0, color::RED)), + rect, + }); + let align = (Align::Min, Align::Min); + let text_style = TextStyle::Monospace; + self.floating_text(rect.min, text, text_style, align, Some(color::RED)); + } + /// Show some text anywhere in the ui. /// To center the text at the given position, use `align: (Center, Center)`. /// If you want to draw text floating on top of everything, @@ -565,7 +542,7 @@ impl Ui { /// After `add_contents` is called the contents of `bounding_size` /// will decide how much space will be used in the parent ui. pub fn add_custom_contents(&mut self, size: Vec2, add_contents: impl FnOnce(&mut Ui)) { - let size = size.min(self.available_space()); + let size = size.min(self.available().size()); let child_rect = Rect::from_min_size(self.cursor, size); let mut child_ui = Ui { ..self.child_ui(child_rect) @@ -597,7 +574,7 @@ impl Ui { // draw a grey line on the left to mark the indented section let line_start = child_rect.min - indent * 0.5; let line_start = line_start.round(); // TODO: round to pixel instead - let line_end = pos2(line_start.x, line_start.y + size.y - 8.0); + let line_end = pos2(line_start.x, line_start.y + size.y - 2.0); self.add_paint_cmd(PaintCmd::line_segment( [line_start, line_end], gray(150, 255), @@ -623,16 +600,16 @@ impl Ui { pub fn column(&mut self, column_position: Align, mut width: f32) -> Ui { let x = match column_position { Align::Min => 0.0, - Align::Center => self.available_width() / 2.0 - width / 2.0, - Align::Max => self.available_width() - width, + Align::Center => self.available().width() / 2.0 - width / 2.0, + Align::Max => self.available().width() - width, Align::Justified => { - width = self.available_width(); + width = self.available().width(); 0.0 } }; self.child_ui(Rect::from_min_size( self.cursor + vec2(x, 0.0), - vec2(width, self.available_height()), + vec2(width, self.available().height()), )) } @@ -678,7 +655,7 @@ impl Ui { // TODO: ensure there is space let spacing = self.style.item_spacing.x; let total_spacing = spacing * (num_columns as f32 - 1.0); - let column_width = (self.available_width() - total_spacing) / (num_columns as f32); + let column_width = (self.available().width() - total_spacing) / (num_columns as f32); let mut columns: Vec = (0..num_columns) .map(|col_idx| { @@ -707,7 +684,7 @@ impl Ui { max_height = size.y.max(max_height); } - let size = vec2(self.available_width().max(sum_width), max_height); + let size = vec2(self.available().width().max(sum_width), max_height); self.reserve_space(size, None); result } diff --git a/emigui/src/widgets.rs b/emigui/src/widgets.rs index f699c034..d6fd0240 100644 --- a/emigui/src/widgets.rs +++ b/emigui/src/widgets.rs @@ -149,7 +149,7 @@ impl Widget for Hyperlink { let font = &ui.fonts()[text_style]; let line_spacing = font.line_spacing(); // TODO: underline - let (text, text_size) = font.layout_multiline(&self.text, ui.available_width()); + let (text, text_size) = font.layout_multiline(&self.text, ui.available().width()); let interact = ui.reserve_space(text_size, Some(id)); if interact.hovered { ui.ctx().output().cursor_icon = CursorIcon::PointingHand; @@ -215,7 +215,7 @@ impl Widget for Button { let id = ui.make_position_id(); let text_style = TextStyle::Button; let font = &ui.fonts()[text_style]; - let (text, text_size) = font.layout_multiline(&self.text, ui.available_width()); + let (text, text_size) = font.layout_multiline(&self.text, ui.available().width()); let padding = ui.style().button_padding; let mut size = text_size + 2.0 * padding; size.y = size.y.max(ui.style().clickable_diameter); @@ -341,7 +341,7 @@ impl Widget for RadioButton { let id = ui.make_position_id(); let text_style = TextStyle::Button; let font = &ui.fonts()[text_style]; - let (text, text_size) = font.layout_multiline(&self.text, ui.available_width()); + let (text, text_size) = font.layout_multiline(&self.text, ui.available().width()); let interact = ui.reserve_space( ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0) @@ -422,7 +422,7 @@ impl Separator { impl Widget for Separator { fn ui(self, ui: &mut Ui) -> GuiResponse { - let available_space = ui.available_space_min(); + let available_space = ui.available_finite().size(); let extra = self.extra; let (points, interact) = match ui.direction() { diff --git a/emigui/src/widgets/slider.rs b/emigui/src/widgets/slider.rs index 75c791b2..78a6c2e5 100644 --- a/emigui/src/widgets/slider.rs +++ b/emigui/src/widgets/slider.rs @@ -116,7 +116,7 @@ impl<'a> Widget for Slider<'a> { let slider_sans_text = Slider { text: None, ..self }; if text_on_top { - // let (text, text_size) = font.layout_multiline(&full_text, ui.available_width()); + // let (text, text_size) = font.layout_multiline(&full_text, ui.available().width()); let (text, text_size) = font.layout_single_line(&full_text); let pos = ui.reserve_space(text_size, None).rect.min; ui.add_text(pos, text_style, text, text_color); @@ -144,7 +144,7 @@ impl<'a> Widget for Slider<'a> { let interact = ui.reserve_space( Vec2 { - x: ui.available_width(), + x: ui.available().width(), y: height, }, Some(id), diff --git a/emigui/src/widgets/text_edit.rs b/emigui/src/widgets/text_edit.rs index 00889b34..4950c6b9 100644 --- a/emigui/src/widgets/text_edit.rs +++ b/emigui/src/widgets/text_edit.rs @@ -40,8 +40,8 @@ impl<'t> Widget for TextEdit<'t> { let font = &ui.fonts()[self.text_style]; let line_spacing = font.line_spacing(); - let (text, text_size) = font.layout_multiline(self.text.as_str(), ui.available_width()); - let desired_size = text_size.max(vec2(ui.available_width(), line_spacing)); + let (text, text_size) = font.layout_multiline(self.text.as_str(), ui.available().width()); + let desired_size = text_size.max(vec2(ui.available().width(), line_spacing)); let interact = ui.reserve_space(desired_size, Some(id)); if interact.clicked { diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index 1298070d..ab1e1b1d 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -110,7 +110,7 @@ fn main() { ctx.begin_frame(raw_input.clone()); // TODO: avoid clone let mut ui = ctx.fullscreen_ui(); example_app.ui(&mut ui); - let mut ui = ui.centered_column(ui.available_width().min(480.0)); + let mut ui = ui.centered_column(ui.available().width().min(480.0)); ui.set_align(Align::Min); ui.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading)); if ui.add(Button::new("Quit")).clicked { diff --git a/example_wasm/src/lib.rs b/example_wasm/src/lib.rs index 7995ff51..6ca3d6d4 100644 --- a/example_wasm/src/lib.rs +++ b/example_wasm/src/lib.rs @@ -42,7 +42,7 @@ impl State { let mut ui = self.ctx.fullscreen_ui(); self.example_app.ui(&mut ui); - let mut ui = ui.centered_column(ui.available_width().min(480.0)); + let mut ui = ui.centered_column(ui.available().width().min(480.0)); ui.set_align(Align::Min); ui.add(label!("Emigui!").text_style(TextStyle::Heading)); ui.add_label("Emigui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");