Fix: centered horizontal layouts should never overflow upwards

This commit is contained in:
Emil Ernerfeldt 2021-03-21 10:31:18 +01:00
parent cc5ad1505c
commit ec9f374d8c
3 changed files with 56 additions and 9 deletions

View file

@ -332,6 +332,8 @@ impl Layout {
/// ## Doing layout /// ## Doing layout
impl Layout { impl Layout {
pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect { pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect {
debug_assert!(size.x >= 0.0 && size.y >= 0.0);
debug_assert!(outer.is_non_negative());
self.align2().align_size_within_rect(size, outer) self.align2().align_size_within_rect(size, outer)
} }
@ -393,7 +395,25 @@ impl Layout {
/// Given the cursor in the region, how much space is available /// Given the cursor in the region, how much space is available
/// for the next widget? /// for the next widget?
fn available_from_cursor_max_rect(&self, cursor: Rect, max_rect: Rect) -> Rect { fn available_from_cursor_max_rect(&self, cursor: Rect, mut max_rect: Rect) -> Rect {
// NOTE: in normal top-down layout the cursor has moved below the current max_rect,
// but the available shouldn't be negative.
match self.main_dir {
Direction::LeftToRight => {
max_rect.max.x = max_rect.max.x.max(cursor.min.x);
}
Direction::RightToLeft => {
max_rect.min.x = max_rect.min.x.min(cursor.max.x);
}
Direction::TopDown => {
max_rect.max.y = max_rect.max.y.max(cursor.min.y);
}
Direction::BottomUp => {
max_rect.min.y = max_rect.min.y.min(cursor.max.y);
}
}
max_rect.intersect(cursor) max_rect.intersect(cursor)
} }
@ -402,6 +422,8 @@ impl Layout {
/// This is what you then pass to `advance_after_rects`. /// This is what you then pass to `advance_after_rects`.
/// Use `justify_and_align` to get the inner `widget_rect`. /// Use `justify_and_align` to get the inner `widget_rect`.
pub(crate) fn next_frame(&self, region: &Region, child_size: Vec2, spacing: Vec2) -> Rect { pub(crate) fn next_frame(&self, region: &Region, child_size: Vec2, spacing: Vec2) -> Rect {
debug_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
if self.main_wrap { if self.main_wrap {
let available_size = self.available_rect_before_wrap(region).size(); let available_size = self.available_rect_before_wrap(region).size();
@ -478,6 +500,8 @@ impl Layout {
} }
fn next_frame_ignore_wrap(&self, region: &Region, child_size: Vec2) -> Rect { fn next_frame_ignore_wrap(&self, region: &Region, child_size: Vec2) -> Rect {
debug_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_finite(region);
let mut frame_size = child_size; let mut frame_size = child_size;
@ -485,12 +509,12 @@ impl Layout {
if (self.is_vertical() && self.horizontal_align() == Align::Center) if (self.is_vertical() && self.horizontal_align() == Align::Center)
|| self.horizontal_justify() || self.horizontal_justify()
{ {
frame_size.x = frame_size.x.at_least(available_rect.width()); // fill full width frame_size.x = frame_size.x.max(available_rect.width()); // fill full width
} }
if (self.is_horizontal() && self.vertical_align() == Align::Center) if (self.is_horizontal() && self.vertical_align() == Align::Center)
|| self.vertical_justify() || self.vertical_justify()
{ {
frame_size.y = frame_size.y.at_least(available_rect.height()); // fill full height frame_size.y = frame_size.y.max(available_rect.height()); // fill full height
} }
let align2 = match self.main_dir { let align2 = match self.main_dir {
@ -500,11 +524,23 @@ impl Layout {
Direction::BottomUp => Align2([self.horizontal_align(), Align::BOTTOM]), Direction::BottomUp => Align2([self.horizontal_align(), Align::BOTTOM]),
}; };
align2.align_size_within_rect(frame_size, available_rect) let mut frame_rect = align2.align_size_within_rect(frame_size, available_rect);
if self.is_horizontal() && frame_rect.top() < region.cursor.top() {
// for horizontal layouts we always want to expand down,
// or we will overlap the row above.
// This is a bit hacky. Maybe we should do it for vertical layouts too.
frame_rect = frame_rect.translate(Vec2::Y * (region.cursor.top() - frame_rect.top()));
}
frame_rect
} }
/// Apply justify (fill width/height) and/or alignment after calling `next_space`. /// 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 { pub(crate) fn justify_and_align(&self, frame: Rect, mut child_size: Vec2) -> Rect {
debug_assert!(child_size.x >= 0.0 && child_size.y >= 0.0);
debug_assert!(frame.is_non_negative());
if self.horizontal_justify() { if self.horizontal_justify() {
child_size.x = child_size.x.at_least(frame.width()); // fill full width child_size.x = child_size.x.at_least(frame.width()); // fill full width
} }
@ -601,16 +637,16 @@ impl Layout {
match self.main_dir { match self.main_dir {
Direction::LeftToRight => { Direction::LeftToRight => {
cursor.min.x = widget_rect.right() + item_spacing.x; cursor.min.x = widget_rect.max.x + item_spacing.x;
} }
Direction::RightToLeft => { Direction::RightToLeft => {
cursor.max.x = widget_rect.left() - item_spacing.x; cursor.max.x = widget_rect.min.x - item_spacing.x;
} }
Direction::TopDown => { Direction::TopDown => {
cursor.min.y = widget_rect.bottom() + item_spacing.y; cursor.min.y = widget_rect.max.y + item_spacing.y;
} }
Direction::BottomUp => { Direction::BottomUp => {
cursor.max.y = widget_rect.top() - item_spacing.y; cursor.max.y = widget_rect.min.y - item_spacing.y;
} }
}; };
} }

View file

@ -287,10 +287,21 @@ impl Rect {
self.max.y..=self.min.y self.max.y..=self.min.y
} }
#[deprecated = "Use is_negative instead"]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.max.x < self.min.x || self.max.y < self.min.y self.max.x < self.min.x || self.max.y < self.min.y
} }
/// `max.x < min.x` or `max.y < min.y`.
pub fn is_negative(&self) -> bool {
self.max.x < self.min.x || self.max.y < self.min.y
}
/// `min.x <= max.x && min.y <= max.y`.
pub fn is_non_negative(&self) -> bool {
self.min.x <= self.max.x && self.min.y <= self.max.y
}
/// True if all members are also finite. /// True if all members are also finite.
pub fn is_finite(&self) -> bool { pub fn is_finite(&self) -> bool {
self.min.is_finite() && self.max.is_finite() self.min.is_finite() && self.max.is_finite()

View file

@ -597,7 +597,7 @@ impl Tessellator {
{ {
return; return;
} }
if rect.is_empty() { if rect.is_negative() {
return; return;
} }