Add support for reversed layouts

This commit is contained in:
Emil Ernerfeldt 2020-05-13 22:24:32 +02:00
parent d4204f03c0
commit cd1bbddaca
3 changed files with 99 additions and 31 deletions

View File

@ -446,6 +446,7 @@ use crate::layout::*;
struct LayoutExample {
dir: Direction,
align: Option<Align>, // 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:"));

View File

@ -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<Align>,
/// 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<Align>) -> 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)
}
}
}

View File

@ -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;
}
}
// ------------------------------------------------------------------------