diff --git a/egui_datepicker/src/datepicker_popup.rs b/egui_datepicker/src/datepicker_popup.rs index 8a3d517f..eafc8b7f 100644 --- a/egui_datepicker/src/datepicker_popup.rs +++ b/egui_datepicker/src/datepicker_popup.rs @@ -53,356 +53,300 @@ impl<'a> DatePickerPopup<'a> { let weeks = month_data(popup_state.year, popup_state.month); let mut close = false; let height = 20.0; - GridBuilder::new(ui, Padding::new(2.0, 0.0)).vertical(|builder| { - builder - .rows( - Size::Absolute(height), - match (self.combo_boxes, self.arrows) { - (true, true) => 2, - (true, false) | (false, true) => 1, - (false, false) => 0, - }, - ) - .rows( - Size::Absolute(2.0 + (height + 2.0) * weeks.len() as f32), - if self.calendar { 1 } else { 0 }, - ) - .row(Size::Absolute(height)) - .build(|mut grid| { - if self.combo_boxes { - grid.horizontal_noclip(|builder| { - builder.columns(Size::Remainder, 3).build(|mut grid| { - grid.cell_noclip(|ui| { - ComboBox::from_id_source("date_picker_year") - .selected_text(format!("{}", popup_state.year)) - .show_ui(ui, |ui| { - 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| { + GridBuilder::new(ui, Padding::new(2.0, 0.0)) + .sizes( + Size::Absolute(height), + match (self.combo_boxes, self.arrows) { + (true, true) => 2, + (true, false) | (false, true) => 1, + (false, false) => 0, + }, + ) + .sizes( + Size::Absolute(2.0 + (height + 2.0) * weeks.len() as f32), + if self.calendar { 1 } else { 0 }, + ) + .size(Size::Absolute(height)) + .vertical(|mut grid| { + if self.combo_boxes { + grid.grid_noclip(|builder| { + builder.sizes(Size::Remainder, 3).horizontal(|mut grid| { + grid.cell_noclip(|ui| { + ComboBox::from_id_source("date_picker_year") + .selected_text(format!("{}", popup_state.year)) + .show_ui(ui, |ui| { + for year in today.year() - 5..today.year() + 10 { if ui - .button("<<<") - .on_hover_text("substract one year") - .clicked() + .selectable_value( + &mut popup_state.year, + year, + format!("{}", year), + ) + .changed() { - 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| { + } + }); + }); + 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 - .button("<<") - .on_hover_text("substract one month") - .clicked() + .selectable_value( + &mut popup_state.month, + 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() .data .insert_persisted(id, popup_state.clone()); } - }, - ); - }); - grid.cell(|ui| { - ui.with_layout( - Layout::top_down_justified(Align::Center), - |ui| { + } + }); + }); + 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 - .button("<") - .on_hover_text("substract one day") - .clicked() + .selectable_value( + &mut popup_state.day, + 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() .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.horizontal(|builder| { - builder.columns(Size::Remainder, 3).build(|mut grid| { - grid.empty(); + if self.arrows { + grid.grid(|builder| { + builder.sizes(Size::Remainder, 6).horizontal(|mut grid| { grid.cell(|ui| { ui.with_layout(Layout::top_down_justified(Align::Center), |ui| { - if ui.button("Abbrechen").clicked() { - close = true; + if ui + .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| { 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 ui + .button("<<") + .on_hover_text("substract one month") + .clicked() + { + 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().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 { popup_state.setup = false; diff --git a/egui_demo_lib/src/apps/demo/grid_demo.rs b/egui_demo_lib/src/apps/demo/grid_demo.rs index 10d3acaf..5782523f 100644 --- a/egui_demo_lib/src/apps/demo/grid_demo.rs +++ b/egui_demo_lib/src/apps/demo/grid_demo.rs @@ -25,80 +25,75 @@ impl super::Demo for GridDemo { impl super::View for GridDemo { fn ui(&mut self, ui: &mut egui::Ui) { - GridBuilder::new(ui, Padding::new(0.0, 5.0)).vertical(|builder| { - builder - .row(Size::Absolute(50.0)) - .row(Size::Remainder) - .row(Size::RelativeMinimum { - relative: 0.5, - minimum: 60.0, - }) - .build(|mut grid| { - grid.cell(|ui| { - ui.painter().rect_filled( - ui.available_rect_before_wrap(), - 0.0, - Color32::BLUE, - ); - }); - grid.horizontal(|builder| { - builder.columns(Size::Remainder, 2).build(|mut grid| { - grid.cell(|ui| { - 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, - ); - }); - }); - }); + GridBuilder::new(ui, Padding::new(0.0, 5.0)) + .size(Size::Absolute(50.0)) + .size(Size::Remainder) + .size(Size::RelativeMinimum { + relative: 0.5, + minimum: 60.0, + }) + .vertical(|mut grid| { + grid.cell(|ui| { + ui.painter() + .rect_filled(ui.available_rect_before_wrap(), 0.0, Color32::BLUE); + }); + grid.grid(|builder| { + builder.sizes(Size::Remainder, 2).horizontal(|mut grid| { + grid.cell(|ui| { + ui.painter().rect_filled( + ui.available_rect_before_wrap(), + 0.0, + Color32::RED, + ); }); - }); - grid.horizontal(|builder| { - 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.grid(|builder| { + builder.sizes(Size::Remainder, 3).vertical(|mut grid| { grid.empty(); grid.cell(|ui| { ui.painter().rect_filled( ui.available_rect_before_wrap(), 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, + ); + }); + }); + }); + }); } } diff --git a/egui_dynamic_grid/src/grid.rs b/egui_dynamic_grid/src/grid.rs index 29caea66..1635c543 100644 --- a/egui_dynamic_grid/src/grid.rs +++ b/egui_dynamic_grid/src/grid.rs @@ -1,26 +1,158 @@ -mod horizontal; -mod vertical; - -use crate::Padding; +use crate::{ + layout::{CellSize, Layout, LineDirection}, + sizing::Sizing, + Padding, Size, +}; use egui::Ui; -pub use horizontal::*; -pub use vertical::*; + +enum GridDirection { + Horizontal, + Vertical, +} pub struct GridBuilder<'a> { ui: &'a mut Ui, + sizing: Sizing, padding: Padding, } 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 { - Self { ui, padding } + let sizing = Sizing::new(); + + Self { + ui, + sizing, + padding, + } } - pub fn horizontal(self, horizontal_grid_builder: impl FnOnce(HorizontalGridBuilder)) { - horizontal_grid_builder(HorizontalGridBuilder::new(self.ui, self.padding)); + /// Add size hint for column/row + pub fn size(mut self, size: Size) -> Self { + self.sizing.add_size(size); + self } - pub fn vertical(self, vertical_grid_builder: impl FnOnce(VerticalGridBuilder)) { - vertical_grid_builder(VerticalGridBuilder::new(self.ui, self.padding)); + /// Add size hint for columns/rows [count] times + 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(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(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, +} + +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(); + } } } diff --git a/egui_dynamic_grid/src/grid/horizontal.rs b/egui_dynamic_grid/src/grid/horizontal.rs deleted file mode 100644 index 1aaa85b1..00000000 --- a/egui_dynamic_grid/src/grid/horizontal.rs +++ /dev/null @@ -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(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, -} - -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(); - } - } -} diff --git a/egui_dynamic_grid/src/grid/vertical.rs b/egui_dynamic_grid/src/grid/vertical.rs deleted file mode 100644 index 018af8f5..00000000 --- a/egui_dynamic_grid/src/grid/vertical.rs +++ /dev/null @@ -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(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, -} - -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(); - } - } -} diff --git a/egui_dynamic_grid/src/sizing.rs b/egui_dynamic_grid/src/sizing.rs index 8077483f..ec7c7dad 100644 --- a/egui_dynamic_grid/src/sizing.rs +++ b/egui_dynamic_grid/src/sizing.rs @@ -19,27 +19,20 @@ pub enum Size { } pub struct Sizing { - length: f32, - inner_padding: f32, sizes: Vec, } impl Sizing { - pub fn new(length: f32, inner_padding: f32) -> Self { - Self { - length, - inner_padding, - sizes: vec![], - } + pub fn new() -> Self { + Self { sizes: vec![] } } pub fn add_size(&mut self, size: Size) { self.sizes.push(size); } - pub fn into_lengths(self) -> Vec { + pub fn into_lengths(self, length: f32, inner_padding: f32) -> Vec { let mut remainders = 0; - let length = self.length; let sum_non_remainder = self .sizes .iter() @@ -61,7 +54,7 @@ impl Sizing { } }) .sum::() - + self.inner_padding * (self.sizes.len() + 1) as f32; + + inner_padding * (self.sizes.len() + 1) as f32; let avg_remainder_length = if remainders == 0 { 0.0 diff --git a/egui_dynamic_grid/src/table.rs b/egui_dynamic_grid/src/table.rs index 8195d470..8a35c1b7 100644 --- a/egui_dynamic_grid/src/table.rs +++ b/egui_dynamic_grid/src/table.rs @@ -20,10 +20,7 @@ pub struct TableBuilder<'a> { impl<'a> TableBuilder<'a> { pub fn new(ui: &'a mut Ui, padding: Padding) -> Self { - let sizing = Sizing::new( - ui.available_rect_before_wrap().width() - 2.0 * padding.outer, - padding.inner, - ); + let sizing = Sizing::new(); Self { ui, @@ -62,7 +59,10 @@ impl<'a> TableBuilder<'a> { /// Create a header row which always stays visible and at the top 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 mut layout = Layout::new(ui, self.padding.clone(), LineDirection::TopToBottom); @@ -92,7 +92,10 @@ impl<'a> TableBuilder<'a> { where 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 { ui: self.ui,