diff --git a/emigui/src/examples/app.rs b/emigui/src/examples/app.rs index 3558af6d..16c9dea8 100644 --- a/emigui/src/examples/app.rs +++ b/emigui/src/examples/app.rs @@ -446,6 +446,7 @@ use crate::layout::*; struct LayoutExample { dir: Direction, align: Option, // None == jusitifed + reversed: bool, } impl Default for LayoutExample { @@ -453,6 +454,7 @@ impl Default for LayoutExample { Self { dir: Direction::Vertical, align: Some(Align::Center), + reversed: false, } } } @@ -465,7 +467,12 @@ impl LayoutExample { } pub fn contents_ui(&mut self, ui: &mut Ui) { - ui.set_layout(Layout::from_dir_align(self.dir, self.align)); + let layout = Layout::from_dir_align(self.dir, self.align); + if self.reversed { + ui.set_layout(layout.reverse()); + } else { + ui.set_layout(layout); + } ui.add(label!("Available space: {:?}", ui.available().size())); if ui.add(Button::new("Reset")).clicked { @@ -485,6 +492,8 @@ impl LayoutExample { } } + ui.add(Checkbox::new(&mut self.reversed, "Reversed")); + ui.add(Separator::new()); ui.add(label!("Align:")); diff --git a/emigui/src/layout.rs b/emigui/src/layout.rs index fb6fa09b..76befccb 100644 --- a/emigui/src/layout.rs +++ b/emigui/src/layout.rs @@ -64,6 +64,9 @@ pub struct Layout { /// For horizontal layouts: put things to top, center or bottom? /// None means justified, which means full width (vertical layout) or height (horizontal layouts). align: Option, + + /// Lay out things in reversed order, i.e. from the right or bottom-up. + reversed: bool, } impl Default for Layout { @@ -71,6 +74,7 @@ impl Default for Layout { Self { dir: Direction::Vertical, align: Some(Align::Min), + reversed: false, } } } @@ -78,13 +82,18 @@ impl Default for Layout { impl Layout { /// None align means justified, e.g. fill full width/height. pub fn from_dir_align(dir: Direction, align: Option) -> Self { - Self { dir, align } + Self { + dir, + align, + reversed: false, + } } pub fn vertical(align: Align) -> Self { Self { dir: Direction::Vertical, align: Some(align), + reversed: false, } } @@ -92,19 +101,47 @@ impl Layout { Self { dir: Direction::Horizontal, align: Some(align), + reversed: false, } } /// Full-width layout. /// Nice for menues etc where each button is full width. pub fn justified(dir: Direction) -> Self { - Self { dir, align: None } + Self { + dir, + align: None, + reversed: false, + } + } + + #[must_use] + pub fn reverse(self) -> Self { + Self { + dir: self.dir, + align: self.align, + reversed: !self.reversed, + } } pub fn dir(&self) -> Direction { self.dir } + pub fn is_reversed(&self) -> bool { + self.reversed + } + + /// Given the cursor in the region, how much space is available + /// for the next widget? + pub fn available(&self, cursor: Pos2, rect: Rect) -> Rect { + if self.reversed { + Rect::from_min_max(rect.min, cursor) + } else { + Rect::from_min_max(cursor, rect.max) + } + } + /// Reserve this much space and move the cursor. /// Returns where to put the widget. /// @@ -126,15 +163,12 @@ impl Layout { ) -> Rect { let available_size = available_size.max(child_size); - let mut child_pos = *cursor; + let mut child_move = Vec2::default(); + let mut cursor_change = Vec2::default(); + if self.dir == Direction::Horizontal { if let Some(align) = self.align { - if align != Align::Min { - debug_assert!(available_size.y.is_finite()); - debug_assert!(child_size.y.is_finite()); - } - - child_pos.y += match align { + child_move.y += match align { Align::Min => 0.0, Align::Center => 0.5 * (available_size.y - child_size.y), Align::Max => available_size.y - child_size.y, @@ -144,16 +178,11 @@ impl Layout { child_size.y = child_size.y.max(available_size.y); } - cursor.x += child_size.x; - cursor.x += style.item_spacing.x; // Where to put next thing, if there is a next thing + cursor_change.x += child_size.x; + cursor_change.x += style.item_spacing.x; // Where to put next thing, if there is a next thing } else { if let Some(align) = self.align { - if align != Align::Min { - debug_assert!(available_size.y.is_finite()); - debug_assert!(child_size.y.is_finite()); - } - - child_pos.x += match align { + child_move.x += match align { Align::Min => 0.0, Align::Center => 0.5 * (available_size.x - child_size.x), Align::Max => available_size.x - child_size.x, @@ -162,10 +191,31 @@ impl Layout { // justified: fill full width child_size.x = child_size.x.max(available_size.x); }; - cursor.y += child_size.y; - cursor.y += style.item_spacing.y; // Where to put next thing, if there is a next thing + cursor_change.y += child_size.y; + cursor_change.y += style.item_spacing.y; // Where to put next thing, if there is a next thing } - Rect::from_min_size(child_pos, child_size) + if self.is_reversed() { + // reverse: cursor starts at bottom right corner of new widget. + + let child_pos = if self.dir == Direction::Horizontal { + pos2( + cursor.x - child_size.x, + cursor.y - available_size.y + child_move.y, + ) + } else { + pos2( + cursor.x - available_size.x + child_move.x, + cursor.y - child_size.y, + ) + }; + // let child_pos = *cursor - child_move - child_size; + *cursor -= cursor_change; + Rect::from_min_size(child_pos, child_size) + } else { + let child_pos = *cursor + child_move; + *cursor += cursor_change; + Rect::from_min_size(child_pos, child_size) + } } } diff --git a/emigui/src/ui.rs b/emigui/src/ui.rs index 55086f90..79659ccb 100644 --- a/emigui/src/ui.rs +++ b/emigui/src/ui.rs @@ -164,7 +164,17 @@ impl Ui { self.desired_rect.max.max(self.child_bounds.max) } - pub fn finite_bottom_right(&self) -> Pos2 { + /// Position and current size of the ui. + /// The size is the maximum of the origional (minimum/desired) size and + /// the size of the containted children. + pub fn rect(&self) -> Rect { + Rect::from_min_max(self.top_left(), self.bottom_right()) + } + + /// This is like `rect()`, but will never be infinite. + /// If the desired rect is infinite ("be as big as you want") + /// this will be bounded by child bounds. + pub fn rect_finite(&self) -> Rect { let mut bottom_right = self.child_bounds.max; if self.desired_rect.max.x.is_finite() { bottom_right.x = bottom_right.x.max(self.desired_rect.max.x); @@ -172,14 +182,8 @@ impl Ui { if self.desired_rect.max.y.is_finite() { bottom_right.y = bottom_right.y.max(self.desired_rect.max.y); } - bottom_right - } - /// Position and current size of the ui. - /// The size is the maximum of the origional (minimum/desired) size and - /// the size of the containted children. - pub fn rect(&self) -> Rect { - Rect::from_min_max(self.top_left(), self.bottom_right()) + Rect::from_min_max(self.top_left(), bottom_right) } /// Set the width of the ui. @@ -232,14 +236,14 @@ impl Ui { /// An infinite rectangle should be interpred as "as much as you want". /// In most layouts the next widget will be put in the top left corner of this `Rect`. pub fn available(&self) -> Rect { - Rect::from_min_max(self.cursor, self.bottom_right()) + self.layout.available(self.cursor, self.rect()) } /// This is like `available()`, but will never be infinite. /// Use this for components that want to grow without bounds (but shouldn't). /// In most layouts the next widget will be put in the top left corner of this `Rect`. pub fn available_finite(&self) -> Rect { - Rect::from_min_max(self.cursor, self.finite_bottom_right()) + self.layout.available(self.cursor, self.rect_finite()) } pub fn layout(&self) -> &Layout { @@ -249,6 +253,11 @@ impl Ui { // TODO: remove pub fn set_layout(&mut self, layout: Layout) { self.layout = layout; + + // TODO: remove this HACK: + if layout.is_reversed() { + self.cursor = self.rect_finite().max; + } } // ------------------------------------------------------------------------