combine vertical/horizontal variant to remove duplicate code

This commit is contained in:
René Rössler 2022-01-09 21:16:14 +01:00
parent a346bcf8a3
commit 7dec7054fb
7 changed files with 477 additions and 725 deletions

View file

@ -53,356 +53,300 @@ impl<'a> DatePickerPopup<'a> {
let weeks = month_data(popup_state.year, popup_state.month); let weeks = month_data(popup_state.year, popup_state.month);
let mut close = false; let mut close = false;
let height = 20.0; let height = 20.0;
GridBuilder::new(ui, Padding::new(2.0, 0.0)).vertical(|builder| { GridBuilder::new(ui, Padding::new(2.0, 0.0))
builder .sizes(
.rows( Size::Absolute(height),
Size::Absolute(height), match (self.combo_boxes, self.arrows) {
match (self.combo_boxes, self.arrows) { (true, true) => 2,
(true, true) => 2, (true, false) | (false, true) => 1,
(true, false) | (false, true) => 1, (false, false) => 0,
(false, false) => 0, },
}, )
) .sizes(
.rows( Size::Absolute(2.0 + (height + 2.0) * weeks.len() as f32),
Size::Absolute(2.0 + (height + 2.0) * weeks.len() as f32), if self.calendar { 1 } else { 0 },
if self.calendar { 1 } else { 0 }, )
) .size(Size::Absolute(height))
.row(Size::Absolute(height)) .vertical(|mut grid| {
.build(|mut grid| { if self.combo_boxes {
if self.combo_boxes { grid.grid_noclip(|builder| {
grid.horizontal_noclip(|builder| { builder.sizes(Size::Remainder, 3).horizontal(|mut grid| {
builder.columns(Size::Remainder, 3).build(|mut grid| { grid.cell_noclip(|ui| {
grid.cell_noclip(|ui| { ComboBox::from_id_source("date_picker_year")
ComboBox::from_id_source("date_picker_year") .selected_text(format!("{}", popup_state.year))
.selected_text(format!("{}", popup_state.year)) .show_ui(ui, |ui| {
.show_ui(ui, |ui| { for year in today.year() - 5..today.year() + 10 {
for year in today.year() - 5..today.year() + 10 {
if ui
.selectable_value(
&mut popup_state.year,
year,
format!("{}", year),
)
.changed()
{
ui.memory()
.data
.insert_persisted(id, popup_state.clone());
}
}
});
});
grid.cell_noclip(|ui| {
ComboBox::from_id_source("date_picker_month")
.selected_text(format!("{}", popup_state.month))
.show_ui(ui, |ui| {
for month in 1..=12 {
if ui
.selectable_value(
&mut popup_state.month,
month,
format!("{}", month),
)
.changed()
{
ui.memory()
.data
.insert_persisted(id, popup_state.clone());
}
}
});
});
grid.cell_noclip(|ui| {
ComboBox::from_id_source("date_picker_day")
.selected_text(format!("{}", popup_state.day))
.show_ui(ui, |ui| {
for day in 1..=popup_state.last_day_of_month() {
if ui
.selectable_value(
&mut popup_state.day,
day,
format!("{}", day),
)
.changed()
{
ui.memory()
.data
.insert_persisted(id, popup_state.clone());
}
}
});
});
})
});
}
if self.arrows {
grid.horizontal(|builder| {
builder.columns(Size::Remainder, 6).build(|mut grid| {
grid.cell(|ui| {
ui.with_layout(
Layout::top_down_justified(Align::Center),
|ui| {
if ui if ui
.button("<<<") .selectable_value(
.on_hover_text("substract one year") &mut popup_state.year,
.clicked() year,
format!("{}", year),
)
.changed()
{ {
popup_state.year -= 1;
popup_state.day = popup_state
.day
.min(popup_state.last_day_of_month());
ui.memory() ui.memory()
.data .data
.insert_persisted(id, popup_state.clone()); .insert_persisted(id, popup_state.clone());
} }
}, }
); });
}); });
grid.cell(|ui| { grid.cell_noclip(|ui| {
ui.with_layout( ComboBox::from_id_source("date_picker_month")
Layout::top_down_justified(Align::Center), .selected_text(format!("{}", popup_state.month))
|ui| { .show_ui(ui, |ui| {
for month in 1..=12 {
if ui if ui
.button("<<") .selectable_value(
.on_hover_text("substract one month") &mut popup_state.month,
.clicked() month,
format!("{}", month),
)
.changed()
{ {
popup_state.month -= 1;
if popup_state.month == 0 {
popup_state.month = 12;
popup_state.year -= 1;
}
popup_state.day = popup_state
.day
.min(popup_state.last_day_of_month());
ui.memory() ui.memory()
.data .data
.insert_persisted(id, popup_state.clone()); .insert_persisted(id, popup_state.clone());
} }
}, }
); });
}); });
grid.cell(|ui| { grid.cell_noclip(|ui| {
ui.with_layout( ComboBox::from_id_source("date_picker_day")
Layout::top_down_justified(Align::Center), .selected_text(format!("{}", popup_state.day))
|ui| { .show_ui(ui, |ui| {
for day in 1..=popup_state.last_day_of_month() {
if ui if ui
.button("<") .selectable_value(
.on_hover_text("substract one day") &mut popup_state.day,
.clicked() day,
format!("{}", day),
)
.changed()
{ {
popup_state.day -= 1;
if popup_state.day == 0 {
popup_state.month -= 1;
if popup_state.month == 0 {
popup_state.year -= 1;
popup_state.month = 12;
}
popup_state.day =
popup_state.last_day_of_month();
}
ui.memory() ui.memory()
.data .data
.insert_persisted(id, popup_state.clone()); .insert_persisted(id, popup_state.clone());
} }
}, }
); });
}); });
grid.cell(|ui| { })
ui.with_layout( });
Layout::top_down_justified(Align::Center), }
|ui| {
if ui.button(">").on_hover_text("add one day").clicked()
{
popup_state.day += 1;
if popup_state.day > popup_state.last_day_of_month()
{
popup_state.day = 1;
popup_state.month += 1;
if popup_state.month > 12 {
popup_state.month = 1;
popup_state.year += 1;
}
}
ui.memory()
.data
.insert_persisted(id, popup_state.clone());
}
},
);
});
grid.cell(|ui| {
ui.with_layout(
Layout::top_down_justified(Align::Center),
|ui| {
if ui
.button(">>")
.on_hover_text("add one month")
.clicked()
{
popup_state.month += 1;
if popup_state.month > 12 {
popup_state.month = 1;
popup_state.year += 1;
}
popup_state.day = popup_state
.day
.min(popup_state.last_day_of_month());
ui.memory()
.data
.insert_persisted(id, popup_state.clone());
}
},
);
});
grid.cell(|ui| {
ui.with_layout(
Layout::top_down_justified(Align::Center),
|ui| {
if ui
.button(">>>")
.on_hover_text("add one year")
.clicked()
{
popup_state.year += 1;
popup_state.day = popup_state
.day
.min(popup_state.last_day_of_month());
ui.memory()
.data
.insert_persisted(id, popup_state.clone());
}
},
);
});
})
});
}
if self.calendar { if self.arrows {
grid.cell(|ui| { grid.grid(|builder| {
TableBuilder::new(ui, Padding::new(2.0, 0.0)) builder.sizes(Size::Remainder, 6).horizontal(|mut grid| {
.scroll(false)
.columns(Size::Remainder, if self.calendar_week { 8 } else { 7 })
.header(height, |mut header| {
if self.calendar_week {
header.col(|ui| {
ui.with_layout(
Layout::centered_and_justified(Direction::TopDown),
|ui| {
ui.add(Label::new("Week"));
},
);
});
}
//TODO: Locale
for name in ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] {
header.col(|ui| {
ui.with_layout(
Layout::centered_and_justified(Direction::TopDown),
|ui| {
ui.add(Label::new(name));
},
);
});
}
})
.body(|mut body| {
for week in weeks {
body.row(height, |mut row| {
if self.calendar_week {
row.col(|ui| {
ui.add(Label::new(format!("{}", week.number)));
});
}
for day in week.days {
row.col(|ui| {
ui.with_layout(
Layout::top_down_justified(Align::Center),
|ui| {
//TODO: Colors from egui style
let fill_color = if popup_state.year
== day.year()
&& popup_state.month == day.month()
&& popup_state.day == day.day()
{
Color32::DARK_BLUE
} else if day.weekday() == Weekday::Sat
|| day.weekday() == Weekday::Sun
{
Color32::DARK_RED
} else {
Color32::BLACK
};
let text_color = if day == today {
Color32::RED
} else if day.month()
== popup_state.month
{
Color32::WHITE
} else {
Color32::from_gray(80)
};
let button = Button::new(
RichText::new(format!(
"{}",
day.day()
))
.color(text_color),
)
.fill(fill_color);
if ui.add(button).clicked() {
popup_state.year = day.year();
popup_state.month = day.month();
popup_state.day = day.day();
ui.memory().data.insert_persisted(
id,
popup_state.clone(),
);
}
},
);
});
}
});
}
});
});
}
grid.horizontal(|builder| {
builder.columns(Size::Remainder, 3).build(|mut grid| {
grid.empty();
grid.cell(|ui| { grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| { ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button("Abbrechen").clicked() { if ui
close = true; .button("<<<")
.on_hover_text("substract one year")
.clicked()
{
popup_state.year -= 1;
popup_state.day =
popup_state.day.min(popup_state.last_day_of_month());
ui.memory().data.insert_persisted(id, popup_state.clone());
} }
}); });
}); });
grid.cell(|ui| { grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| { ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button("Speichern").clicked() { if ui
*self.selection = Date::from_utc( .button("<<")
NaiveDate::from_ymd( .on_hover_text("substract one month")
popup_state.year, .clicked()
popup_state.month, {
popup_state.day, popup_state.month -= 1;
), if popup_state.month == 0 {
Utc, popup_state.month = 12;
); popup_state.year -= 1;
close = true; }
popup_state.day =
popup_state.day.min(popup_state.last_day_of_month());
ui.memory().data.insert_persisted(id, popup_state.clone());
}
});
});
grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button("<").on_hover_text("substract one day").clicked() {
popup_state.day -= 1;
if popup_state.day == 0 {
popup_state.month -= 1;
if popup_state.month == 0 {
popup_state.year -= 1;
popup_state.month = 12;
}
popup_state.day = popup_state.last_day_of_month();
}
ui.memory().data.insert_persisted(id, popup_state.clone());
}
});
});
grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button(">").on_hover_text("add one day").clicked() {
popup_state.day += 1;
if popup_state.day > popup_state.last_day_of_month() {
popup_state.day = 1;
popup_state.month += 1;
if popup_state.month > 12 {
popup_state.month = 1;
popup_state.year += 1;
}
}
ui.memory().data.insert_persisted(id, popup_state.clone());
}
});
});
grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button(">>").on_hover_text("add one month").clicked() {
popup_state.month += 1;
if popup_state.month > 12 {
popup_state.month = 1;
popup_state.year += 1;
}
popup_state.day =
popup_state.day.min(popup_state.last_day_of_month());
ui.memory().data.insert_persisted(id, popup_state.clone());
}
});
});
grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button(">>>").on_hover_text("add one year").clicked() {
popup_state.year += 1;
popup_state.day =
popup_state.day.min(popup_state.last_day_of_month());
ui.memory().data.insert_persisted(id, popup_state.clone());
} }
}); });
}); });
}) })
}); });
}
if self.calendar {
grid.cell(|ui| {
TableBuilder::new(ui, Padding::new(2.0, 0.0))
.scroll(false)
.columns(Size::Remainder, if self.calendar_week { 8 } else { 7 })
.header(height, |mut header| {
if self.calendar_week {
header.col(|ui| {
ui.with_layout(
Layout::centered_and_justified(Direction::TopDown),
|ui| {
ui.add(Label::new("Week"));
},
);
});
}
//TODO: Locale
for name in ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"] {
header.col(|ui| {
ui.with_layout(
Layout::centered_and_justified(Direction::TopDown),
|ui| {
ui.add(Label::new(name));
},
);
});
}
})
.body(|mut body| {
for week in weeks {
body.row(height, |mut row| {
if self.calendar_week {
row.col(|ui| {
ui.add(Label::new(format!("{}", week.number)));
});
}
for day in week.days {
row.col(|ui| {
ui.with_layout(
Layout::top_down_justified(Align::Center),
|ui| {
//TODO: Colors from egui style
let fill_color = if popup_state.year
== day.year()
&& popup_state.month == day.month()
&& popup_state.day == day.day()
{
Color32::DARK_BLUE
} else if day.weekday() == Weekday::Sat
|| day.weekday() == Weekday::Sun
{
Color32::DARK_RED
} else {
Color32::BLACK
};
let text_color = if day == today {
Color32::RED
} else if day.month() == popup_state.month {
Color32::WHITE
} else {
Color32::from_gray(80)
};
let button = Button::new(
RichText::new(format!("{}", day.day()))
.color(text_color),
)
.fill(fill_color);
if ui.add(button).clicked() {
popup_state.year = day.year();
popup_state.month = day.month();
popup_state.day = day.day();
ui.memory().data.insert_persisted(
id,
popup_state.clone(),
);
}
},
);
});
}
});
}
});
});
}
grid.grid(|builder| {
builder.sizes(Size::Remainder, 3).horizontal(|mut grid| {
grid.empty();
grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button("Abbrechen").clicked() {
close = true;
}
});
});
grid.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button("Speichern").clicked() {
*self.selection = Date::from_utc(
NaiveDate::from_ymd(
popup_state.year,
popup_state.month,
popup_state.day,
),
Utc,
);
close = true;
}
});
});
})
}); });
}); });
if close { if close {
popup_state.setup = false; popup_state.setup = false;

View file

@ -25,80 +25,75 @@ impl super::Demo for GridDemo {
impl super::View for GridDemo { impl super::View for GridDemo {
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
GridBuilder::new(ui, Padding::new(0.0, 5.0)).vertical(|builder| { GridBuilder::new(ui, Padding::new(0.0, 5.0))
builder .size(Size::Absolute(50.0))
.row(Size::Absolute(50.0)) .size(Size::Remainder)
.row(Size::Remainder) .size(Size::RelativeMinimum {
.row(Size::RelativeMinimum { relative: 0.5,
relative: 0.5, minimum: 60.0,
minimum: 60.0, })
}) .vertical(|mut grid| {
.build(|mut grid| { grid.cell(|ui| {
grid.cell(|ui| { ui.painter()
ui.painter().rect_filled( .rect_filled(ui.available_rect_before_wrap(), 0.0, Color32::BLUE);
ui.available_rect_before_wrap(), });
0.0, grid.grid(|builder| {
Color32::BLUE, builder.sizes(Size::Remainder, 2).horizontal(|mut grid| {
); grid.cell(|ui| {
}); ui.painter().rect_filled(
grid.horizontal(|builder| { ui.available_rect_before_wrap(),
builder.columns(Size::Remainder, 2).build(|mut grid| { 0.0,
grid.cell(|ui| { Color32::RED,
ui.painter().rect_filled( );
ui.available_rect_before_wrap(),
0.0,
Color32::RED,
);
});
grid.vertical(|builder| {
builder.rows(Size::Remainder, 3).build(|mut grid| {
grid.empty();
grid.cell(|ui| {
ui.painter().rect_filled(
ui.available_rect_before_wrap(),
0.0,
Color32::YELLOW,
);
});
});
});
}); });
}); grid.grid(|builder| {
grid.horizontal(|builder| { builder.sizes(Size::Remainder, 3).vertical(|mut grid| {
builder
.column(Size::Remainder)
.column(Size::Absolute(50.0))
.column(Size::Remainder)
.column(Size::Absolute(70.0))
.build(|mut grid| {
grid.empty();
grid.vertical(|builder| {
builder
.row(Size::Remainder)
.row(Size::Absolute(50.0))
.row(Size::Remainder)
.build(|mut grid| {
grid.empty();
grid.cell(|ui| {
ui.painter().rect_filled(
ui.available_rect_before_wrap(),
0.0,
Color32::GOLD,
);
});
});
});
grid.empty(); grid.empty();
grid.cell(|ui| { grid.cell(|ui| {
ui.painter().rect_filled( ui.painter().rect_filled(
ui.available_rect_before_wrap(), ui.available_rect_before_wrap(),
0.0, 0.0,
Color32::GREEN, Color32::YELLOW,
); );
}); });
}); });
});
}); });
}); });
}); grid.grid(|builder| {
builder
.size(Size::Remainder)
.size(Size::Absolute(50.0))
.size(Size::Remainder)
.size(Size::Absolute(70.0))
.horizontal(|mut grid| {
grid.empty();
grid.grid(|builder| {
builder
.size(Size::Remainder)
.size(Size::Absolute(50.0))
.size(Size::Remainder)
.vertical(|mut grid| {
grid.empty();
grid.cell(|ui| {
ui.painter().rect_filled(
ui.available_rect_before_wrap(),
0.0,
Color32::GOLD,
);
});
});
});
grid.empty();
grid.cell(|ui| {
ui.painter().rect_filled(
ui.available_rect_before_wrap(),
0.0,
Color32::GREEN,
);
});
});
});
});
} }
} }

View file

@ -1,26 +1,158 @@
mod horizontal; use crate::{
mod vertical; layout::{CellSize, Layout, LineDirection},
sizing::Sizing,
use crate::Padding; Padding, Size,
};
use egui::Ui; use egui::Ui;
pub use horizontal::*;
pub use vertical::*; enum GridDirection {
Horizontal,
Vertical,
}
pub struct GridBuilder<'a> { pub struct GridBuilder<'a> {
ui: &'a mut Ui, ui: &'a mut Ui,
sizing: Sizing,
padding: Padding, padding: Padding,
} }
impl<'a> GridBuilder<'a> { impl<'a> GridBuilder<'a> {
/// Create new grid builder
/// After adding size hints with [Self::column]/[Self::columns] the grid can be build with [Self::horizontal]/[Self::vertical]
pub fn new(ui: &'a mut Ui, padding: Padding) -> Self { pub fn new(ui: &'a mut Ui, padding: Padding) -> Self {
Self { ui, padding } let sizing = Sizing::new();
Self {
ui,
sizing,
padding,
}
} }
pub fn horizontal(self, horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder)) { /// Add size hint for column/row
horizontal_grid_builder(HorizontalGridBuilder::new(self.ui, self.padding)); pub fn size(mut self, size: Size) -> Self {
self.sizing.add_size(size);
self
} }
pub fn vertical(self, vertical_grid_builder: impl FnOnce(VerticalGridBuilder)) { /// Add size hint for columns/rows [count] times
vertical_grid_builder(VerticalGridBuilder::new(self.ui, self.padding)); pub fn sizes(mut self, size: Size, count: usize) -> Self {
for _ in 0..count {
self.sizing.add_size(size.clone());
}
self
}
/// Build horizontal grid
pub fn horizontal<F>(self, grid: F)
where
F: for<'b> FnOnce(Grid<'a, 'b>),
{
let widths = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().width() - 2.0 * self.padding.outer,
self.padding.inner,
);
let mut layout = Layout::new(self.ui, self.padding.clone(), LineDirection::TopToBottom);
grid(Grid {
layout: &mut layout,
direction: GridDirection::Horizontal,
padding: self.padding.clone(),
widths,
});
}
/// Build vertical grid
pub fn vertical<F>(self, grid: F)
where
F: for<'b> FnOnce(Grid<'a, 'b>),
{
let widths = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().height() - 2.0 * self.padding.outer,
self.padding.inner,
);
let mut layout = Layout::new(self.ui, self.padding.clone(), LineDirection::LeftToRight);
grid(Grid {
layout: &mut layout,
direction: GridDirection::Vertical,
padding: self.padding.clone(),
widths,
});
}
}
pub struct Grid<'a, 'b> {
layout: &'b mut Layout<'a>,
direction: GridDirection,
padding: Padding,
widths: Vec<f32>,
}
impl<'a, 'b> Grid<'a, 'b> {
fn size(&mut self) -> (CellSize, CellSize) {
match self.direction {
GridDirection::Horizontal => (
CellSize::Absolute(self.widths.remove(0)),
CellSize::Remainder,
),
GridDirection::Vertical => (
CellSize::Remainder,
CellSize::Absolute(self.widths.remove(0)),
),
}
}
/// Add empty cell
pub fn empty(&mut self) {
assert!(
!self.widths.is_empty(),
"Tried using more grid cells then available."
);
let (width, height) = self.size();
self.layout.empty(width, height);
}
pub fn _cell(&mut self, clip: bool, add_contents: impl FnOnce(&mut Ui)) {
assert!(
!self.widths.is_empty(),
"Tried using more grid cells then available."
);
let (width, height) = self.size();
self.layout.add(width, height, clip, add_contents);
}
/// Add cell, content is clipped
pub fn cell(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self._cell(true, add_contents);
}
/// Add cell, content is not clipped
pub fn cell_noclip(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self._cell(false, add_contents);
}
pub fn _grid(&mut self, clip: bool, grid_builder: impl FnOnce(GridBuilder)) {
let padding = self.padding.clone();
self._cell(clip, |ui| {
grid_builder(GridBuilder::new(ui, padding));
});
}
/// Add grid as cell, content is clipped
pub fn grid(&mut self, grid_builder: impl FnOnce(GridBuilder)) {
self._grid(true, grid_builder)
}
/// Add grid as cell, content is not clipped
pub fn grid_noclip(&mut self, grid_builder: impl FnOnce(GridBuilder)) {
self._grid(false, grid_builder)
}
}
impl<'a, 'b> Drop for Grid<'a, 'b> {
fn drop(&mut self) {
while !self.widths.is_empty() {
self.empty();
}
} }
} }

View file

@ -1,157 +0,0 @@
use crate::{
layout::{CellSize, LineDirection},
sizing::Sizing,
Layout, Padding, Size,
};
use egui::Ui;
use super::VerticalGridBuilder;
pub struct HorizontalGridBuilder<'a> {
ui: &'a mut Ui,
padding: Padding,
sizing: Sizing,
}
impl<'a> HorizontalGridBuilder<'a> {
/// Create new grid builder for horizontal grid
/// After adding size hints with [Self::column]/[Self::columns] the grid can be build with [Self::build]
pub(crate) fn new(ui: &'a mut Ui, padding: Padding) -> Self {
let layouter = Sizing::new(
ui.available_rect_before_wrap().width() - 2.0 * padding.outer,
padding.inner,
);
Self {
ui,
padding,
sizing: layouter,
}
}
/// Add size hint for column
pub fn column(mut self, size: Size) -> Self {
self.sizing.add_size(size);
self
}
/// Add size hint for columns [count] times
pub fn columns(mut self, size: Size, count: usize) -> Self {
for _ in 0..count {
self.sizing.add_size(size.clone());
}
self
}
/// Build grid
pub fn build<F>(self, horizontal_grid: F)
where
F: for<'b> FnOnce(HorizontalGrid<'a, 'b>),
{
let widths = self.sizing.into_lengths();
let mut layout = Layout::new(self.ui, self.padding.clone(), LineDirection::TopToBottom);
let grid = HorizontalGrid {
layout: &mut layout,
padding: self.padding.clone(),
widths,
};
horizontal_grid(grid);
}
}
pub struct HorizontalGrid<'a, 'b> {
layout: &'b mut Layout<'a>,
padding: Padding,
widths: Vec<f32>,
}
impl<'a, 'b> HorizontalGrid<'a, 'b> {
/// Add empty cell
pub fn empty(&mut self) {
assert!(
!self.widths.is_empty(),
"Tried using more grid cells then available."
);
self.layout.empty(
CellSize::Absolute(self.widths.remove(0)),
CellSize::Remainder,
);
}
pub fn _cell(&mut self, clip: bool, add_contents: impl FnOnce(&mut Ui)) {
assert!(
!self.widths.is_empty(),
"Tried using more grid cells then available."
);
self.layout.add(
CellSize::Absolute(self.widths.remove(0)),
CellSize::Remainder,
clip,
add_contents,
);
}
/// Add cell, content is clipped
pub fn cell(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self._cell(true, add_contents);
}
/// Add cell, content is not clipped
pub fn cell_noclip(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self._cell(false, add_contents);
}
pub fn _horizontal(
&mut self,
clip: bool,
horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder),
) {
let padding = self.padding.clone();
self._cell(clip, |ui| {
horizontal_grid_builder(HorizontalGridBuilder::new(ui, padding));
});
}
/// Add horizontal grid as cell, content is clipped
pub fn horizontal(&mut self, horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder)) {
self._horizontal(true, horizontal_grid_builder)
}
/// Add horizontal grid as cell, content is not clipped
pub fn horizontal_noclip(
&mut self,
horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder),
) {
self._horizontal(false, horizontal_grid_builder)
}
pub fn _vertical(
&mut self,
clip: bool,
vertical_grid_builder: impl FnOnce(VerticalGridBuilder),
) {
let padding = self.padding.clone();
self._cell(clip, |ui| {
vertical_grid_builder(VerticalGridBuilder::new(ui, padding));
});
}
/// Add vertical grid as cell, content is clipped
pub fn vertical(&mut self, vertical_grid_builder: impl FnOnce(VerticalGridBuilder)) {
self._vertical(true, vertical_grid_builder);
}
/// Add vertical grid as cell, content is not clipped
pub fn vertical_noclip(&mut self, vertical_grid_builder: impl FnOnce(VerticalGridBuilder)) {
self._vertical(false, vertical_grid_builder);
}
}
impl<'a, 'b> Drop for HorizontalGrid<'a, 'b> {
fn drop(&mut self) {
while !self.widths.is_empty() {
self.empty();
}
}
}

View file

@ -1,158 +0,0 @@
use crate::{layout::CellSize, sizing::Sizing, Layout, Padding, Size};
use egui::Ui;
use super::HorizontalGridBuilder;
pub struct VerticalGridBuilder<'a> {
ui: &'a mut Ui,
padding: Padding,
sizing: Sizing,
}
impl<'a> VerticalGridBuilder<'a> {
/// Create new grid builder for vertical grid
/// After adding size hints with [Self::row]/[Self::rows] the grid can be build with [Self::build]
pub(crate) fn new(ui: &'a mut Ui, padding: Padding) -> Self {
let layouter = Sizing::new(
ui.available_rect_before_wrap().height() - 2.0 * padding.outer,
padding.inner,
);
Self {
ui,
padding,
sizing: layouter,
}
}
/// Add size hint for row
pub fn row(mut self, size: Size) -> Self {
self.sizing.add_size(size);
self
}
/// Add size hint for rows [count] times
pub fn rows(mut self, size: Size, count: usize) -> Self {
for _ in 0..count {
self.sizing.add_size(size.clone());
}
self
}
/// Build grid
pub fn build<F>(self, vertical_grid: F)
where
F: for<'b> FnOnce(VerticalGrid<'a, 'b>),
{
let heights = self.sizing.into_lengths();
let mut layout = Layout::new(
self.ui,
self.padding.clone(),
crate::layout::LineDirection::LeftToRight,
);
let grid = VerticalGrid {
layout: &mut layout,
padding: self.padding.clone(),
heights,
};
vertical_grid(grid);
}
}
pub struct VerticalGrid<'a, 'b> {
layout: &'b mut Layout<'a>,
padding: Padding,
heights: Vec<f32>,
}
impl<'a, 'b> VerticalGrid<'a, 'b> {
/// Add empty cell
pub fn empty(&mut self) {
assert!(
!self.heights.is_empty(),
"Tried using more grid cells then available."
);
self.layout.empty(
CellSize::Remainder,
CellSize::Absolute(self.heights.remove(0)),
);
}
pub fn _cell(&mut self, clip: bool, add_contents: impl FnOnce(&mut Ui)) {
assert!(
!self.heights.is_empty(),
"Tried using more grid cells then available."
);
self.layout.add(
CellSize::Remainder,
CellSize::Absolute(self.heights.remove(0)),
clip,
add_contents,
);
}
/// Add cell, content is clipped
pub fn cell(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self._cell(true, add_contents);
}
/// Add cell, content is not clipped
pub fn cell_noclip(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self._cell(false, add_contents);
}
pub fn _horizontal(
&mut self,
clip: bool,
horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder),
) {
let padding = self.padding.clone();
self._cell(clip, |ui| {
horizontal_grid_builder(HorizontalGridBuilder::new(ui, padding));
});
}
/// Add horizontal grid as cell, content is clipped
pub fn horizontal(&mut self, horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder)) {
self._horizontal(true, horizontal_grid_builder)
}
/// Add horizontal grid as cell, content is not clipped
pub fn horizontal_noclip(
&mut self,
horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder),
) {
self._horizontal(false, horizontal_grid_builder)
}
pub fn _vertical(
&mut self,
clip: bool,
vertical_grid_builder: impl FnOnce(VerticalGridBuilder),
) {
let padding = self.padding.clone();
self._cell(clip, |ui| {
vertical_grid_builder(VerticalGridBuilder::new(ui, padding));
});
}
/// Add vertical grid as cell, content is clipped
pub fn vertical(&mut self, vertical_grid_builder: impl FnOnce(VerticalGridBuilder)) {
self._vertical(true, vertical_grid_builder);
}
/// Add vertical grid as cell, content is not clipped
pub fn vertical_noclip(&mut self, vertical_grid_builder: impl FnOnce(VerticalGridBuilder)) {
self._vertical(false, vertical_grid_builder);
}
}
impl<'a, 'b> Drop for VerticalGrid<'a, 'b> {
fn drop(&mut self) {
while !self.heights.is_empty() {
self.empty();
}
}
}

View file

@ -19,27 +19,20 @@ pub enum Size {
} }
pub struct Sizing { pub struct Sizing {
length: f32,
inner_padding: f32,
sizes: Vec<Size>, sizes: Vec<Size>,
} }
impl Sizing { impl Sizing {
pub fn new(length: f32, inner_padding: f32) -> Self { pub fn new() -> Self {
Self { Self { sizes: vec![] }
length,
inner_padding,
sizes: vec![],
}
} }
pub fn add_size(&mut self, size: Size) { pub fn add_size(&mut self, size: Size) {
self.sizes.push(size); self.sizes.push(size);
} }
pub fn into_lengths(self) -> Vec<f32> { pub fn into_lengths(self, length: f32, inner_padding: f32) -> Vec<f32> {
let mut remainders = 0; let mut remainders = 0;
let length = self.length;
let sum_non_remainder = self let sum_non_remainder = self
.sizes .sizes
.iter() .iter()
@ -61,7 +54,7 @@ impl Sizing {
} }
}) })
.sum::<f32>() .sum::<f32>()
+ self.inner_padding * (self.sizes.len() + 1) as f32; + inner_padding * (self.sizes.len() + 1) as f32;
let avg_remainder_length = if remainders == 0 { let avg_remainder_length = if remainders == 0 {
0.0 0.0

View file

@ -20,10 +20,7 @@ pub struct TableBuilder<'a> {
impl<'a> TableBuilder<'a> { impl<'a> TableBuilder<'a> {
pub fn new(ui: &'a mut Ui, padding: Padding) -> Self { pub fn new(ui: &'a mut Ui, padding: Padding) -> Self {
let sizing = Sizing::new( let sizing = Sizing::new();
ui.available_rect_before_wrap().width() - 2.0 * padding.outer,
padding.inner,
);
Self { Self {
ui, ui,
@ -62,7 +59,10 @@ impl<'a> TableBuilder<'a> {
/// Create a header row which always stays visible and at the top /// Create a header row which always stays visible and at the top
pub fn header(self, height: f32, header: impl FnOnce(TableRow<'_, '_>)) -> Table<'a> { pub fn header(self, height: f32, header: impl FnOnce(TableRow<'_, '_>)) -> Table<'a> {
let widths = self.sizing.into_lengths(); let widths = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().width() - 2.0 * self.padding.outer,
self.padding.inner,
);
let ui = self.ui; let ui = self.ui;
{ {
let mut layout = Layout::new(ui, self.padding.clone(), LineDirection::TopToBottom); let mut layout = Layout::new(ui, self.padding.clone(), LineDirection::TopToBottom);
@ -92,7 +92,10 @@ impl<'a> TableBuilder<'a> {
where where
F: for<'b> FnOnce(TableBody<'b>), F: for<'b> FnOnce(TableBody<'b>),
{ {
let widths = self.sizing.into_lengths(); let widths = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().width() - 2.0 * self.padding.outer,
self.padding.inner,
);
Table { Table {
ui: self.ui, ui: self.ui,