wrapping
This commit is contained in:
parent
7fb3c66d0b
commit
ea9133a92d
2 changed files with 155 additions and 62 deletions
|
@ -306,31 +306,57 @@ use crate::layout::*;
|
||||||
struct LayoutDemo {
|
struct LayoutDemo {
|
||||||
// Identical to contents of `egui::Layout`
|
// Identical to contents of `egui::Layout`
|
||||||
main_dir: Direction,
|
main_dir: Direction,
|
||||||
|
main_wrap: bool,
|
||||||
cross_align: Align,
|
cross_align: Align,
|
||||||
cross_justify: bool,
|
cross_justify: bool,
|
||||||
|
|
||||||
|
// Extra for testing wrapping:
|
||||||
|
wrap_column_width: f32,
|
||||||
|
wrap_row_height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LayoutDemo {
|
impl Default for LayoutDemo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
main_dir: Direction::TopDown,
|
main_dir: Direction::TopDown,
|
||||||
|
main_wrap: false,
|
||||||
cross_align: Align::Min,
|
cross_align: Align::Min,
|
||||||
cross_justify: false,
|
cross_justify: false,
|
||||||
|
wrap_column_width: 150.0,
|
||||||
|
wrap_row_height: 40.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutDemo {
|
impl LayoutDemo {
|
||||||
fn layout(&self) -> Layout {
|
fn layout(&self) -> Layout {
|
||||||
Layout::from_parts(self.main_dir, self.cross_align, self.cross_justify)
|
Layout::from_main_dir_and_cross_align(self.main_dir, self.cross_align)
|
||||||
|
.with_main_wrap(self.main_wrap)
|
||||||
|
.with_cross_justify(self.cross_justify)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut Ui) {
|
pub fn ui(&mut self, ui: &mut Ui) {
|
||||||
|
self.content_ui(ui);
|
||||||
Resize::default()
|
Resize::default()
|
||||||
.default_size([200.0, 100.0])
|
.default_size([200.0, 100.0])
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.with_layout(self.layout(), |ui| self.content_ui(ui))
|
if self.main_wrap {
|
||||||
|
if self.main_dir.is_horizontal() {
|
||||||
|
ui.allocate_ui_min(
|
||||||
|
vec2(ui.available_finite().width(), self.wrap_row_height),
|
||||||
|
|ui| ui.with_layout(self.layout(), |ui| self.demo_ui(ui)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ui.allocate_ui_min(
|
||||||
|
vec2(self.wrap_column_width, ui.available_finite().height()),
|
||||||
|
|ui| ui.with_layout(self.layout(), |ui| self.demo_ui(ui)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ui.with_layout(self.layout(), |ui| self.demo_ui(ui));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
ui.label("Resize to see effect");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content_ui(&mut self, ui: &mut Ui) {
|
pub fn content_ui(&mut self, ui: &mut Ui) {
|
||||||
|
@ -339,30 +365,46 @@ impl LayoutDemo {
|
||||||
*self = Default::default();
|
*self = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Main Direction:");
|
||||||
|
for &dir in &[
|
||||||
|
Direction::LeftToRight,
|
||||||
|
Direction::RightToLeft,
|
||||||
|
Direction::TopDown,
|
||||||
|
Direction::BottomUp,
|
||||||
|
] {
|
||||||
|
ui.radio_value(&mut self.main_dir, dir, format!("{:?}", dir));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ui.label("Main Direction:");
|
ui.checkbox(&mut self.main_wrap, "Main wrap")
|
||||||
for &dir in &[
|
.on_hover_text("Wrap when next widget doesn't fit the current row/column");
|
||||||
Direction::LeftToRight,
|
|
||||||
Direction::RightToLeft,
|
if self.main_wrap {
|
||||||
Direction::TopDown,
|
if self.main_dir.is_horizontal() {
|
||||||
Direction::BottomUp,
|
ui.add(Slider::f32(&mut self.wrap_row_height, 0.0..=200.0).text("Row height"));
|
||||||
] {
|
} else {
|
||||||
ui.radio_value(&mut self.main_dir, dir, format!("{:?}", dir));
|
ui.add(Slider::f32(&mut self.wrap_column_width, 0.0..=200.0).text("Column width"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Cross Align:");
|
||||||
ui.label("Cross Align:");
|
for &align in &[Align::Min, Align::Center, Align::Max] {
|
||||||
for &align in &[Align::Min, Align::Center, Align::Max] {
|
ui.radio_value(&mut self.cross_align, align, format!("{:?}", align));
|
||||||
ui.radio_value(&mut self.cross_align, align, format!("{:?}", align));
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.checkbox(&mut self.cross_justify, "Cross Justified")
|
ui.checkbox(&mut self.cross_justify, "Cross Justified")
|
||||||
.on_hover_text("Try to fill full width/height (e.g. buttons)");
|
.on_hover_text("Try to fill full width/height (e.g. buttons)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn demo_ui(&mut self, ui: &mut Ui) {
|
||||||
|
ui.heading("Effect:");
|
||||||
|
for i in 0..7 {
|
||||||
|
let _ = ui.button(format!("Button {}", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -100,6 +100,11 @@ pub struct Layout {
|
||||||
/// Main axis direction
|
/// Main axis direction
|
||||||
main_dir: Direction,
|
main_dir: Direction,
|
||||||
|
|
||||||
|
/// If true, wrap around when reading the end of the main direction.
|
||||||
|
/// For instance, for `main_dir == Direction::LeftToRight` this will
|
||||||
|
/// wrap to a new row when we reach the right side of the `max_rect`.
|
||||||
|
main_wrap: bool,
|
||||||
|
|
||||||
/// How to align things on the cross axis.
|
/// How to align things on the cross axis.
|
||||||
/// For vertical layouts: put things to left, center or right?
|
/// For vertical layouts: put things to left, center or right?
|
||||||
/// For horizontal layouts: put things to top, center or bottom?
|
/// For horizontal layouts: put things to top, center or bottom?
|
||||||
|
@ -117,6 +122,7 @@ impl Default for Layout {
|
||||||
// This is a very euro-centric default.
|
// This is a very euro-centric default.
|
||||||
Self {
|
Self {
|
||||||
main_dir: Direction::TopDown,
|
main_dir: Direction::TopDown,
|
||||||
|
main_wrap: false,
|
||||||
cross_align: Align::left(),
|
cross_align: Align::left(),
|
||||||
cross_justify: false,
|
cross_justify: false,
|
||||||
}
|
}
|
||||||
|
@ -124,12 +130,48 @@ impl Default for Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
/// None align means justified, e.g. fill full width/height.
|
pub(crate) fn from_main_dir_and_cross_align(main_dir: Direction, cross_align: Align) -> Self {
|
||||||
pub(crate) fn from_parts(main_dir: Direction, cross_align: Align, cross_justify: bool) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
main_dir,
|
main_dir,
|
||||||
|
main_wrap: false,
|
||||||
cross_align,
|
cross_align,
|
||||||
cross_justify,
|
cross_justify: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left_to_right() -> Self {
|
||||||
|
Self {
|
||||||
|
main_dir: Direction::LeftToRight,
|
||||||
|
main_wrap: false,
|
||||||
|
cross_align: Align::Center,
|
||||||
|
cross_justify: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right_to_left() -> Self {
|
||||||
|
Self {
|
||||||
|
main_dir: Direction::RightToLeft,
|
||||||
|
main_wrap: false,
|
||||||
|
cross_align: Align::Center,
|
||||||
|
cross_justify: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top_down(cross_align: Align) -> Self {
|
||||||
|
Self {
|
||||||
|
main_dir: Direction::TopDown,
|
||||||
|
main_wrap: false,
|
||||||
|
cross_align,
|
||||||
|
cross_justify: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bottom_up(cross_align: Align) -> Self {
|
||||||
|
Self {
|
||||||
|
main_dir: Direction::BottomUp,
|
||||||
|
main_wrap: false,
|
||||||
|
cross_align,
|
||||||
|
cross_justify: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,36 +185,8 @@ impl Layout {
|
||||||
Self::left_to_right().with_cross_align(cross_align)
|
Self::left_to_right().with_cross_align(cross_align)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left_to_right() -> Self {
|
pub fn with_main_wrap(self, main_wrap: bool) -> Self {
|
||||||
Self {
|
Self { main_wrap, ..self }
|
||||||
main_dir: Direction::LeftToRight,
|
|
||||||
cross_align: Align::Center,
|
|
||||||
cross_justify: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn right_to_left() -> Self {
|
|
||||||
Self {
|
|
||||||
main_dir: Direction::RightToLeft,
|
|
||||||
cross_align: Align::Center,
|
|
||||||
cross_justify: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn top_down(cross_align: Align) -> Self {
|
|
||||||
Self {
|
|
||||||
main_dir: Direction::TopDown,
|
|
||||||
cross_align,
|
|
||||||
cross_justify: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bottom_up(cross_align: Align) -> Self {
|
|
||||||
Self {
|
|
||||||
main_dir: Direction::BottomUp,
|
|
||||||
cross_align,
|
|
||||||
cross_justify: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_cross_align(self, cross_align: Align) -> Self {
|
pub fn with_cross_align(self, cross_align: Align) -> Self {
|
||||||
|
@ -317,6 +331,43 @@ impl Layout {
|
||||||
/// You may get LESS space than you asked for if the current layout won't fit what you asked for.
|
/// You may get LESS space than you asked for if the current layout won't fit what you asked for.
|
||||||
#[allow(clippy::collapsible_if)]
|
#[allow(clippy::collapsible_if)]
|
||||||
pub fn next_space(self, region: &Region, minimum_child_size: Vec2) -> Rect {
|
pub fn next_space(self, region: &Region, minimum_child_size: Vec2) -> Rect {
|
||||||
|
let mut cursor = region.cursor;
|
||||||
|
|
||||||
|
if self.main_wrap {
|
||||||
|
let available_size = self.available_finite(region).size();
|
||||||
|
// TODO: spacing?
|
||||||
|
match self.main_dir {
|
||||||
|
Direction::LeftToRight => {
|
||||||
|
if available_size.x < minimum_child_size.x && region.max_rect.left() < cursor.x
|
||||||
|
{
|
||||||
|
// New row
|
||||||
|
cursor = pos2(region.max_rect.left(), region.max_rect.bottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Direction::RightToLeft => {
|
||||||
|
if available_size.x < minimum_child_size.x && cursor.x < region.max_rect.right()
|
||||||
|
{
|
||||||
|
// New row
|
||||||
|
cursor = pos2(region.max_rect.right(), region.max_rect.bottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Direction::TopDown => {
|
||||||
|
if available_size.y < minimum_child_size.y && region.max_rect.top() < cursor.y {
|
||||||
|
// New column
|
||||||
|
cursor = pos2(region.max_rect.right(), region.max_rect.top());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Direction::BottomUp => {
|
||||||
|
if available_size.y < minimum_child_size.y
|
||||||
|
&& cursor.y < region.max_rect.bottom()
|
||||||
|
{
|
||||||
|
// New column
|
||||||
|
cursor = pos2(region.max_rect.right(), region.max_rect.bottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let available_size = self.available_finite(region).size();
|
let available_size = self.available_finite(region).size();
|
||||||
let available_size = available_size.at_least(minimum_child_size);
|
let available_size = available_size.at_least(minimum_child_size);
|
||||||
|
|
||||||
|
@ -348,10 +399,10 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
let child_pos = match self.main_dir {
|
let child_pos = match self.main_dir {
|
||||||
Direction::LeftToRight => region.cursor + child_move,
|
Direction::LeftToRight => cursor + child_move,
|
||||||
Direction::RightToLeft => region.cursor + child_move + vec2(-child_size.x, 0.0),
|
Direction::RightToLeft => cursor + child_move + vec2(-child_size.x, 0.0),
|
||||||
Direction::TopDown => region.cursor + child_move,
|
Direction::TopDown => cursor + child_move,
|
||||||
Direction::BottomUp => region.cursor + child_move + vec2(0.0, -child_size.y),
|
Direction::BottomUp => cursor + child_move + vec2(0.0, -child_size.y),
|
||||||
};
|
};
|
||||||
|
|
||||||
Rect::from_min_size(child_pos, child_size)
|
Rect::from_min_size(child_pos, child_size)
|
||||||
|
@ -378,12 +429,12 @@ impl Layout {
|
||||||
|
|
||||||
/// Advance cursor after a widget was added to a specific rectangle.
|
/// Advance cursor after a widget was added to a specific rectangle.
|
||||||
pub fn advance_after_rect(self, region: &mut Region, rect: Rect, item_spacing: Vec2) {
|
pub fn advance_after_rect(self, region: &mut Region, rect: Rect, item_spacing: Vec2) {
|
||||||
match self.main_dir {
|
region.cursor = match self.main_dir {
|
||||||
Direction::LeftToRight => region.cursor.x = rect.right() + item_spacing.x,
|
Direction::LeftToRight => pos2(rect.right() + item_spacing.x, rect.top()),
|
||||||
Direction::RightToLeft => region.cursor.x = rect.left() - item_spacing.x,
|
Direction::RightToLeft => pos2(rect.left() - item_spacing.x, rect.top()),
|
||||||
Direction::TopDown => region.cursor.y = rect.bottom() + item_spacing.y,
|
Direction::TopDown => pos2(rect.left(), rect.bottom() + item_spacing.y),
|
||||||
Direction::BottomUp => region.cursor.y = rect.top() - item_spacing.y,
|
Direction::BottomUp => pos2(rect.left(), rect.top() - item_spacing.y),
|
||||||
}
|
};
|
||||||
region.expand_to_include_rect(rect);
|
region.expand_to_include_rect(rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue