Add support for reversed layouts
This commit is contained in:
parent
d4204f03c0
commit
cd1bbddaca
|
|
@ -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:"));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in New Issue