Rename grid to strip.

Easier documentation/naming of cell direction.
Add doc comments for public structs.
This commit is contained in:
René Rössler 2022-03-15 10:09:19 +01:00
parent 42b6ed6100
commit 6d4e10cf91
10 changed files with 135 additions and 136 deletions

View file

@ -23,13 +23,13 @@ impl Default for Demos {
Box::new(super::dancing_strings::DancingStrings::default()), Box::new(super::dancing_strings::DancingStrings::default()),
Box::new(super::drag_and_drop::DragAndDropDemo::default()), Box::new(super::drag_and_drop::DragAndDropDemo::default()),
Box::new(super::font_book::FontBook::default()), Box::new(super::font_book::FontBook::default()),
Box::new(super::grid_demo::GridDemo::default()),
Box::new(super::MiscDemoWindow::default()), Box::new(super::MiscDemoWindow::default()),
Box::new(super::multi_touch::MultiTouch::default()), Box::new(super::multi_touch::MultiTouch::default()),
Box::new(super::painting::Painting::default()), Box::new(super::painting::Painting::default()),
Box::new(super::plot_demo::PlotDemo::default()), Box::new(super::plot_demo::PlotDemo::default()),
Box::new(super::scrolling::Scrolling::default()), Box::new(super::scrolling::Scrolling::default()),
Box::new(super::sliders::Sliders::default()), Box::new(super::sliders::Sliders::default()),
Box::new(super::strip_demo::StripDemo::default()),
Box::new(super::table_demo::TableDemo::default()), Box::new(super::table_demo::TableDemo::default()),
Box::new(super::text_edit::TextEdit::default()), Box::new(super::text_edit::TextEdit::default()),
Box::new(super::widget_gallery::WidgetGallery::default()), Box::new(super::widget_gallery::WidgetGallery::default()),

View file

@ -12,7 +12,6 @@ pub mod dancing_strings;
pub mod demo_app_windows; pub mod demo_app_windows;
pub mod drag_and_drop; pub mod drag_and_drop;
pub mod font_book; pub mod font_book;
pub mod grid_demo;
pub mod layout_test; pub mod layout_test;
pub mod misc_demo_window; pub mod misc_demo_window;
pub mod multi_touch; pub mod multi_touch;
@ -22,6 +21,7 @@ pub mod password;
pub mod plot_demo; pub mod plot_demo;
pub mod scrolling; pub mod scrolling;
pub mod sliders; pub mod sliders;
pub mod strip_demo;
pub mod table_demo; pub mod table_demo;
pub mod tests; pub mod tests;
pub mod text_edit; pub mod text_edit;

View file

@ -1,14 +1,14 @@
use egui::Color32; use egui::Color32;
use egui_extras::{GridBuilder, Size}; use egui_extras::{Size, StripBuilder};
/// Shows off a table with dynamic layout /// Shows off a table with dynamic layout
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Default)] #[derive(Default)]
pub struct GridDemo {} pub struct StripDemo {}
impl super::Demo for GridDemo { impl super::Demo for StripDemo {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"Grid Demo" "Strip Demo"
} }
fn show(&mut self, ctx: &egui::Context, open: &mut bool) { fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
@ -23,9 +23,9 @@ impl super::Demo for GridDemo {
} }
} }
impl super::View for GridDemo { impl super::View for StripDemo {
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
GridBuilder::new(ui) StripBuilder::new(ui)
.size(Size::Absolute(50.0)) .size(Size::Absolute(50.0))
.size(Size::Remainder) .size(Size::Remainder)
.size(Size::RelativeMinimum { .size(Size::RelativeMinimum {
@ -33,15 +33,15 @@ impl super::View for GridDemo {
minimum: 60.0, minimum: 60.0,
}) })
.size(Size::Absolute(10.0)) .size(Size::Absolute(10.0))
.vertical(|mut grid| { .vertical(|mut strip| {
grid.cell(|ui| { strip.cell(|ui| {
ui.painter() ui.painter()
.rect_filled(ui.available_rect_before_wrap(), 0.0, Color32::BLUE); .rect_filled(ui.available_rect_before_wrap(), 0.0, Color32::BLUE);
ui.label("Full width and 50px height"); ui.label("Full width and 50px height");
}); });
grid.grid(|builder| { strip.strip(|builder| {
builder.sizes(Size::Remainder, 2).horizontal(|mut grid| { builder.sizes(Size::Remainder, 2).horizontal(|mut strip| {
grid.cell(|ui| { strip.cell(|ui| {
ui.painter().rect_filled( ui.painter().rect_filled(
ui.available_rect_before_wrap(), ui.available_rect_before_wrap(),
0.0, 0.0,
@ -49,10 +49,10 @@ impl super::View for GridDemo {
); );
ui.label("remaining height and 50% of the width"); ui.label("remaining height and 50% of the width");
}); });
grid.grid(|builder| { strip.strip(|builder| {
builder.sizes(Size::Remainder, 3).vertical(|mut grid| { builder.sizes(Size::Remainder, 3).vertical(|mut strip| {
grid.empty(); strip.empty();
grid.cell(|ui| { strip.cell(|ui| {
ui.painter().rect_filled( ui.painter().rect_filled(
ui.available_rect_before_wrap(), ui.available_rect_before_wrap(),
0.0, 0.0,
@ -64,22 +64,22 @@ impl super::View for GridDemo {
}); });
}); });
}); });
grid.grid(|builder| { strip.strip(|builder| {
builder builder
.size(Size::Remainder) .size(Size::Remainder)
.size(Size::Absolute(60.0)) .size(Size::Absolute(60.0))
.size(Size::Remainder) .size(Size::Remainder)
.size(Size::Absolute(70.0)) .size(Size::Absolute(70.0))
.horizontal(|mut grid| { .horizontal(|mut strip| {
grid.empty(); strip.empty();
grid.grid(|builder| { strip.strip(|builder| {
builder builder
.size(Size::Remainder) .size(Size::Remainder)
.size(Size::Absolute(60.0)) .size(Size::Absolute(60.0))
.size(Size::Remainder) .size(Size::Remainder)
.vertical(|mut grid| { .vertical(|mut strip| {
grid.empty(); strip.empty();
grid.cell(|ui| { strip.cell(|ui| {
ui.painter().rect_filled( ui.painter().rect_filled(
ui.available_rect_before_wrap(), ui.available_rect_before_wrap(),
0.0, 0.0,
@ -89,8 +89,8 @@ impl super::View for GridDemo {
}); });
}); });
}); });
grid.empty(); strip.empty();
grid.cell(|ui| { strip.cell(|ui| {
ui.painter().rect_filled( ui.painter().rect_filled(
ui.available_rect_before_wrap(), ui.available_rect_before_wrap(),
0.0, 0.0,
@ -100,7 +100,7 @@ impl super::View for GridDemo {
}); });
}); });
}); });
grid.cell(|ui| { strip.cell(|ui| {
ui.vertical_centered(|ui| { ui.vertical_centered(|ui| {
ui.add(crate::__egui_github_link_file!()); ui.add(crate::__egui_github_link_file!());
}); });

View file

@ -1,5 +1,5 @@
use egui::{Label, Vec2}; use egui::{Label, Vec2};
use egui_extras::{GridBuilder, Size, TableBuilder}; use egui_extras::{Size, StripBuilder, TableBuilder};
/// Shows off a table with dynamic layout /// Shows off a table with dynamic layout
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
@ -31,7 +31,7 @@ impl super::View for TableDemo {
// The table is inside a grid as its container would otherwise grow slowly as it takes all available height // The table is inside a grid as its container would otherwise grow slowly as it takes all available height
ui.spacing_mut().item_spacing = Vec2::splat(4.0); ui.spacing_mut().item_spacing = Vec2::splat(4.0);
GridBuilder::new(ui) StripBuilder::new(ui)
.size(Size::Remainder) .size(Size::Remainder)
.size(Size::Absolute(10.0)) .size(Size::Absolute(10.0))
.vertical(|mut grid| { .vertical(|mut grid| {

View file

@ -1,5 +1,5 @@
use super::{button::DatePickerButtonState, month_data}; use super::{button::DatePickerButtonState, month_data};
use crate::{GridBuilder, Size, TableBuilder}; use crate::{Size, StripBuilder, TableBuilder};
use chrono::{Date, Datelike, NaiveDate, Utc, Weekday}; use chrono::{Date, Datelike, NaiveDate, Utc, Weekday};
use egui::{Align, Button, Color32, ComboBox, Direction, Id, Label, Layout, RichText, Ui, Vec2}; use egui::{Align, Button, Color32, ComboBox, Direction, Id, Label, Layout, RichText, Ui, Vec2};
@ -54,7 +54,7 @@ impl<'a> DatePickerPopup<'a> {
let height = 20.0; let height = 20.0;
let spacing = 2.0; let spacing = 2.0;
ui.spacing_mut().item_spacing = Vec2::splat(spacing); ui.spacing_mut().item_spacing = Vec2::splat(spacing);
GridBuilder::new(ui) StripBuilder::new(ui)
.sizes( .sizes(
Size::Absolute(height), Size::Absolute(height),
match (self.combo_boxes, self.arrows) { match (self.combo_boxes, self.arrows) {
@ -68,11 +68,11 @@ impl<'a> DatePickerPopup<'a> {
if self.calendar { 1 } else { 0 }, if self.calendar { 1 } else { 0 },
) )
.size(Size::Absolute(height)) .size(Size::Absolute(height))
.vertical(|mut grid| { .vertical(|mut strip| {
if self.combo_boxes { if self.combo_boxes {
grid.grid_noclip(|builder| { strip.strip_noclip(|builder| {
builder.sizes(Size::Remainder, 3).horizontal(|mut grid| { builder.sizes(Size::Remainder, 3).horizontal(|mut strip| {
grid.cell(|ui| { strip.cell(|ui| {
ComboBox::from_id_source("date_picker_year") ComboBox::from_id_source("date_picker_year")
.selected_text(popup_state.year.to_string()) .selected_text(popup_state.year.to_string())
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
@ -92,7 +92,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.cell(|ui| {
ComboBox::from_id_source("date_picker_month") ComboBox::from_id_source("date_picker_month")
.selected_text(popup_state.month.to_string()) .selected_text(popup_state.month.to_string())
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
@ -112,7 +112,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.cell(|ui| {
ComboBox::from_id_source("date_picker_day") ComboBox::from_id_source("date_picker_day")
.selected_text(popup_state.day.to_string()) .selected_text(popup_state.day.to_string())
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
@ -137,9 +137,9 @@ impl<'a> DatePickerPopup<'a> {
} }
if self.arrows { if self.arrows {
grid.grid(|builder| { strip.strip(|builder| {
builder.sizes(Size::Remainder, 6).horizontal(|mut grid| { builder.sizes(Size::Remainder, 6).horizontal(|mut strip| {
grid.cell(|ui| { strip.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| { ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui if ui
.button("<<<") .button("<<<")
@ -153,7 +153,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| { ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui if ui
.button("<<") .button("<<")
@ -171,7 +171,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.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("<").on_hover_text("substract one day").clicked() { if ui.button("<").on_hover_text("substract one day").clicked() {
popup_state.day -= 1; popup_state.day -= 1;
@ -187,7 +187,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.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(">").on_hover_text("add one day").clicked() { if ui.button(">").on_hover_text("add one day").clicked() {
popup_state.day += 1; popup_state.day += 1;
@ -203,7 +203,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.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(">>").on_hover_text("add one month").clicked() { if ui.button(">>").on_hover_text("add one month").clicked() {
popup_state.month += 1; popup_state.month += 1;
@ -217,7 +217,7 @@ impl<'a> DatePickerPopup<'a> {
} }
}); });
}); });
grid.cell(|ui| { strip.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(">>>").on_hover_text("add one year").clicked() { if ui.button(">>>").on_hover_text("add one year").clicked() {
popup_state.year += 1; popup_state.year += 1;
@ -232,7 +232,7 @@ impl<'a> DatePickerPopup<'a> {
} }
if self.calendar { if self.calendar {
grid.cell(|ui| { strip.cell(|ui| {
ui.spacing_mut().item_spacing = Vec2::new(1.0, 2.0); ui.spacing_mut().item_spacing = Vec2::new(1.0, 2.0);
TableBuilder::new(ui) TableBuilder::new(ui)
.scroll(false) .scroll(false)
@ -321,17 +321,17 @@ impl<'a> DatePickerPopup<'a> {
}); });
} }
grid.grid(|builder| { strip.strip(|builder| {
builder.sizes(Size::Remainder, 3).horizontal(|mut grid| { builder.sizes(Size::Remainder, 3).horizontal(|mut strip| {
grid.empty(); strip.empty();
grid.cell(|ui| { strip.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("Cancel").clicked() { if ui.button("Cancel").clicked() {
close = true; close = true;
} }
}); });
}); });
grid.cell(|ui| { strip.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("Save").clicked() { if ui.button("Save").clicked() {
*self.selection = Date::from_utc( *self.selection = Date::from_utc(

View file

@ -8,35 +8,33 @@ pub(crate) enum CellSize {
Remainder, Remainder,
} }
/// Cells are positioned in two dimensions /// Cells are positioned in two dimensions, cells go in one direction and form lines.
/// ///
/// In a grid there's only one line which goes into the orthogonal direction of the grid: /// In a strip there's only one line which goes in the direction of the strip:
/// ///
/// In a horizontal grid, a `[Layout]` with vertical `[LineDirection]` is used. /// In a horizontal strip, a `[Layout]` with horizontal `[CellDirection]` is used.
/// Its cells go from left to right inside this `[Layout]`. /// Its cells go from left to right inside this `[Layout]`.
/// ///
/// In a table there's a `[Layout]` for each table row with a horizontal `[LineDirection]`. /// In a table there's a `[Layout]` for each table row with a horizonal `[CellDirection]`.
/// Its cells go from left to right. /// Its cells go from left to right. And the lines go from top to bottom.
pub(crate) enum LineDirection { pub(crate) enum CellDirection {
/// Cells go from top to bottom on each line
/// Lines go from left to right
Horizontal,
/// Cells go from left to right /// Cells go from left to right
/// Lines go from top to bottom Horizontal,
/// Cells go fromtop to bottom
Vertical, Vertical,
} }
/// Positions cells in `[LineDirection]` and starts a new line on `[Layout::end_line]` /// Positions cells in `[CellDirection]` and starts a new line on `[Layout::end_line]`
pub struct Layout<'l> { pub struct Layout<'l> {
ui: &'l mut Ui, ui: &'l mut Ui,
direction: LineDirection, direction: CellDirection,
rect: Rect, rect: Rect,
pos: Pos2, pos: Pos2,
max: Pos2, max: Pos2,
} }
impl<'l> Layout<'l> { impl<'l> Layout<'l> {
pub(crate) fn new(ui: &'l mut Ui, direction: LineDirection) -> Self { pub(crate) fn new(ui: &'l mut Ui, direction: CellDirection) -> Self {
let rect = ui.available_rect_before_wrap(); let rect = ui.available_rect_before_wrap();
let pos = rect.left_top(); let pos = rect.left_top();
@ -71,12 +69,12 @@ impl<'l> Layout<'l> {
fn set_pos(&mut self, rect: Rect) { fn set_pos(&mut self, rect: Rect) {
match self.direction { match self.direction {
LineDirection::Horizontal => { CellDirection::Horizontal => {
self.pos.y = rect.bottom() + self.ui.spacing().item_spacing.y;
}
LineDirection::Vertical => {
self.pos.x = rect.right() + self.ui.spacing().item_spacing.x; self.pos.x = rect.right() + self.ui.spacing().item_spacing.x;
} }
CellDirection::Vertical => {
self.pos.y = rect.bottom() + self.ui.spacing().item_spacing.y;
}
} }
self.max.x = self self.max.x = self
@ -130,14 +128,14 @@ impl<'l> Layout<'l> {
/// only needed for layouts with multiple lines, like Table /// only needed for layouts with multiple lines, like Table
pub fn end_line(&mut self) { pub fn end_line(&mut self) {
match self.direction { match self.direction {
LineDirection::Horizontal => { CellDirection::Horizontal => {
self.pos.x = self.max.x;
self.pos.y = self.rect.top();
}
LineDirection::Vertical => {
self.pos.y = self.max.y; self.pos.y = self.max.y;
self.pos.x = self.rect.left(); self.pos.x = self.rect.left();
} }
CellDirection::Vertical => {
self.pos.x = self.max.x;
self.pos.y = self.rect.top();
}
} }
} }

View file

@ -85,17 +85,17 @@
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
mod datepicker; mod datepicker;
mod grid;
pub mod image; pub mod image;
mod layout; mod layout;
mod sizing; mod sizing;
mod strip;
mod table; mod table;
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub use crate::datepicker::DatePickerButton; pub use crate::datepicker::DatePickerButton;
pub use crate::grid::*;
pub use crate::image::RetainedImage; pub use crate::image::RetainedImage;
pub(crate) use crate::layout::Layout; pub(crate) use crate::layout::Layout;
pub use crate::sizing::Size; pub use crate::sizing::Size;
pub use crate::strip::*;
pub use crate::table::*; pub use crate::table::*;

View file

@ -1,4 +1,4 @@
/// Size hint for table column/grid cell /// Size hint for table column/strip cell
#[derive(Clone, Debug, Copy)] #[derive(Clone, Debug, Copy)]
pub enum Size { pub enum Size {
/// Absolute size in points /// Absolute size in points

View file

@ -1,50 +1,42 @@
use crate::{ use crate::{
layout::{CellSize, Layout, LineDirection}, layout::{CellDirection, CellSize, Layout},
sizing::Sizing, sizing::Sizing,
Size, Size,
}; };
use egui::{Response, Ui}; use egui::{Response, Ui};
/// The direction in which cells are positioned in the grid. /// Builder for creating a new [`Strip`].
/// pub struct StripBuilder<'a> {
/// In a horizontal grid cells are positions from left to right.
/// In a vertical grid cells are positions from top to bottom.
enum GridDirection {
Horizontal,
Vertical,
}
pub struct GridBuilder<'a> {
ui: &'a mut Ui, ui: &'a mut Ui,
sizing: Sizing, sizing: Sizing,
} }
impl<'a> GridBuilder<'a> { impl<'a> StripBuilder<'a> {
/// Create new grid builder. /// Create new strip builder.
/// ///
/// In contrast to normal egui behavior, cells do *not* grow with its children! /// In contrast to normal egui behavior, strip cells do *not* grow with its children!
/// ///
/// After adding size hints with `[Self::column]`/`[Self::columns]` the grid can be build with `[Self::horizontal]`/`[Self::vertical]`. /// After adding size hints with `[Self::column]`/`[Self::columns]` the strip can be build with `[Self::horizontal]`/`[Self::vertical]`.
/// ///
/// ### Example /// ### Example
/// ``` /// ```
/// # egui::__run_test_ui(|ui| { /// # egui::__run_test_ui(|ui| {
/// use egui_extras::{GridBuilder, Size}; /// use egui_extras::{StripBuilder, Size};
/// GridBuilder::new(ui) /// StripBuilder::new(ui)
/// .size(Size::RemainderMinimum(100.0)) /// .size(Size::RemainderMinimum(100.0))
/// .size(Size::Absolute(40.0)) /// .size(Size::Absolute(40.0))
/// .vertical(|mut grid| { /// .vertical(|mut strip| {
/// grid.grid(|builder| { /// strip.strip(|builder| {
/// builder.sizes(Size::Remainder, 2).horizontal(|mut grid| { /// builder.sizes(Size::Remainder, 2).horizontal(|mut strip| {
/// grid.cell(|ui| { /// strip.cell(|ui| {
/// ui.label("Top Left"); /// ui.label("Top Left");
/// }); /// });
/// grid.cell(|ui| { /// strip.cell(|ui| {
/// ui.label("Top Right"); /// ui.label("Top Right");
/// }); /// });
/// }); /// });
/// }); /// });
/// grid.cell(|ui| { /// strip.cell(|ui| {
/// ui.label("Fixed"); /// ui.label("Fixed");
/// }); /// });
/// }); /// });
@ -62,7 +54,7 @@ impl<'a> GridBuilder<'a> {
self self
} }
/// Add size hint for columns/rows [count] times /// Add size hint for columns/rows `count` times
pub fn sizes(mut self, size: Size, count: usize) -> Self { pub fn sizes(mut self, size: Size, count: usize) -> Self {
for _ in 0..count { for _ in 0..count {
self.sizing.add(size); self.sizing.add(size);
@ -70,63 +62,65 @@ impl<'a> GridBuilder<'a> {
self self
} }
/// Build horizontal grid: Cells are positions from left to right. /// Build horizontal strip: Cells are positions from left to right.
/// Takes the available horizontal width, so there can't be anything right of the grid or the container will grow slowly! /// Takes the available horizontal width, so there can't be anything right of the strip or the container will grow slowly!
/// ///
/// Returns a `[egui::Response]` for hover events. /// Returns a `[egui::Response]` for hover events.
pub fn horizontal<F>(self, grid: F) -> Response pub fn horizontal<F>(self, strip: F) -> Response
where where
F: for<'b> FnOnce(Grid<'a, 'b>), F: for<'b> FnOnce(Strip<'a, 'b>),
{ {
let widths = self.sizing.into_lengths( let widths = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().width() - self.ui.spacing().item_spacing.x, self.ui.available_rect_before_wrap().width() - self.ui.spacing().item_spacing.x,
self.ui.spacing().item_spacing.x, self.ui.spacing().item_spacing.x,
); );
let mut layout = Layout::new(self.ui, LineDirection::Vertical); let mut layout = Layout::new(self.ui, CellDirection::Horizontal);
grid(Grid { strip(Strip {
layout: &mut layout, layout: &mut layout,
direction: GridDirection::Horizontal, direction: CellDirection::Horizontal,
sizes: widths, sizes: widths,
}); });
layout.set_rect() layout.set_rect()
} }
/// Build vertical grid: Cells are positions from top to bottom. /// Build vertical strip: Cells are positions from top to bottom.
/// Takes the full available vertical height, so there can't be anything below of the grid or the container will grow slowly! /// Takes the full available vertical height, so there can't be anything below of the strip or the container will grow slowly!
/// ///
/// Returns a `[egui::Response]` for hover events. /// Returns a `[egui::Response]` for hover events.
pub fn vertical<F>(self, grid: F) -> Response pub fn vertical<F>(self, strip: F) -> Response
where where
F: for<'b> FnOnce(Grid<'a, 'b>), F: for<'b> FnOnce(Strip<'a, 'b>),
{ {
let heights = self.sizing.into_lengths( let heights = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().height() - self.ui.spacing().item_spacing.y, self.ui.available_rect_before_wrap().height() - self.ui.spacing().item_spacing.y,
self.ui.spacing().item_spacing.y, self.ui.spacing().item_spacing.y,
); );
let mut layout = Layout::new(self.ui, LineDirection::Horizontal); let mut layout = Layout::new(self.ui, CellDirection::Vertical);
grid(Grid { strip(Strip {
layout: &mut layout, layout: &mut layout,
direction: GridDirection::Vertical, direction: CellDirection::Vertical,
sizes: heights, sizes: heights,
}); });
layout.set_rect() layout.set_rect()
} }
} }
pub struct Grid<'a, 'b> { /// A Strip of cells which go in one direction. Each cell has a fixed size.
/// In contrast to normal egui behavior, strip cells do *not* grow with its children!
pub struct Strip<'a, 'b> {
layout: &'b mut Layout<'a>, layout: &'b mut Layout<'a>,
direction: GridDirection, direction: CellDirection,
sizes: Vec<f32>, sizes: Vec<f32>,
} }
impl<'a, 'b> Grid<'a, 'b> { impl<'a, 'b> Strip<'a, 'b> {
fn next_cell_size(&mut self) -> (CellSize, CellSize) { fn next_cell_size(&mut self) -> (CellSize, CellSize) {
match self.direction { match self.direction {
GridDirection::Horizontal => ( CellDirection::Horizontal => (
CellSize::Absolute(self.sizes.remove(0)), CellSize::Absolute(self.sizes.remove(0)),
CellSize::Remainder, CellSize::Remainder,
), ),
GridDirection::Vertical => ( CellDirection::Vertical => (
CellSize::Remainder, CellSize::Remainder,
CellSize::Absolute(self.sizes.remove(0)), CellSize::Absolute(self.sizes.remove(0)),
), ),
@ -137,7 +131,7 @@ impl<'a, 'b> Grid<'a, 'b> {
pub fn empty(&mut self) { pub fn empty(&mut self) {
assert!( assert!(
!self.sizes.is_empty(), !self.sizes.is_empty(),
"Tried using more grid cells then available." "Tried using more strip cells then available."
); );
let (width, height) = self.next_cell_size(); let (width, height) = self.next_cell_size();
@ -147,7 +141,7 @@ impl<'a, 'b> Grid<'a, 'b> {
fn _cell(&mut self, clip: bool, add_contents: impl FnOnce(&mut Ui)) { fn _cell(&mut self, clip: bool, add_contents: impl FnOnce(&mut Ui)) {
assert!( assert!(
!self.sizes.is_empty(), !self.sizes.is_empty(),
"Tried using more grid cells then available." "Tried using more strip cells then available."
); );
let (width, height) = self.next_cell_size(); let (width, height) = self.next_cell_size();
@ -164,23 +158,23 @@ impl<'a, 'b> Grid<'a, 'b> {
self._cell(true, add_contents); self._cell(true, add_contents);
} }
fn _grid(&mut self, clip: bool, grid_builder: impl FnOnce(GridBuilder<'_>)) { fn _strip(&mut self, clip: bool, strip_builder: impl FnOnce(StripBuilder<'_>)) {
self._cell(clip, |ui| { self._cell(clip, |ui| {
grid_builder(GridBuilder::new(ui)); strip_builder(StripBuilder::new(ui));
}); });
} }
/// Add grid as cell /// Add strip as cell
pub fn grid(&mut self, grid_builder: impl FnOnce(GridBuilder<'_>)) { pub fn strip(&mut self, strip_builder: impl FnOnce(StripBuilder<'_>)) {
self._grid(false, grid_builder); self._strip(false, strip_builder);
} }
/// Add grid as cell, content is clipped /// Add strip as cell, content is clipped
pub fn grid_noclip(&mut self, grid_builder: impl FnOnce(GridBuilder<'_>)) { pub fn strip_noclip(&mut self, strip_builder: impl FnOnce(StripBuilder<'_>)) {
self._grid(true, grid_builder); self._strip(true, strip_builder);
} }
} }
impl<'a, 'b> Drop for Grid<'a, 'b> { impl<'a, 'b> Drop for Strip<'a, 'b> {
fn drop(&mut self) { fn drop(&mut self) {
while !self.sizes.is_empty() { while !self.sizes.is_empty() {
self.empty(); self.empty();

View file

@ -1,10 +1,10 @@
//! Table view with (optional) fixed header and scrolling body. //! Table view with (optional) fixed header and scrolling body.
//! Cell widths are precalculated with given size hints so we can have tables like this: //! Cell widths are precalculated with given size hints so we can have tables like this:
//! | fixed size | all available space/minimum | 30% of available width | fixed size | //! | fixed size | all available space/minimum | 30% of available width | fixed size |
//! Takes all available height, so if you want something below the table, put it in a grid. //! Takes all available height, so if you want something below the table, put it in a strip.
use crate::{ use crate::{
layout::{CellSize, LineDirection}, layout::{CellDirection, CellSize},
sizing::Sizing, sizing::Sizing,
Layout, Size, Layout, Size,
}; };
@ -12,6 +12,7 @@ use crate::{
use egui::{Response, Ui}; use egui::{Response, Ui};
use std::cmp; use std::cmp;
/// Builder for creating a new [`Table`].
pub struct TableBuilder<'a> { pub struct TableBuilder<'a> {
ui: &'a mut Ui, ui: &'a mut Ui,
sizing: Sizing, sizing: Sizing,
@ -27,7 +28,7 @@ impl<'a> TableBuilder<'a> {
/// | fixed size | all available space/minimum | 30% of available width | fixed size | /// | fixed size | all available space/minimum | 30% of available width | fixed size |
/// ///
/// In contrast to normal egui behavior, columns/rows do *not* grow with its children! /// In contrast to normal egui behavior, columns/rows do *not* grow with its children!
/// Takes all available height, so if you want something below the table, put it in a grid. /// Takes all available height, so if you want something below the table, put it in a strip.
/// ///
/// ### Example /// ### Example
/// ``` /// ```
@ -85,7 +86,7 @@ impl<'a> TableBuilder<'a> {
self self
} }
/// Add size hint for column [count] times /// Add size hint for column `count` times
pub fn columns(mut self, size: Size, count: usize) -> Self { pub fn columns(mut self, size: Size, count: usize) -> Self {
for _ in 0..count { for _ in 0..count {
self.sizing.add(size); self.sizing.add(size);
@ -107,7 +108,7 @@ impl<'a> TableBuilder<'a> {
); );
let ui = self.ui; let ui = self.ui;
{ {
let mut layout = Layout::new(ui, LineDirection::Vertical); let mut layout = Layout::new(ui, CellDirection::Horizontal);
{ {
let row = TableRow { let row = TableRow {
layout: &mut layout, layout: &mut layout,
@ -149,6 +150,8 @@ impl<'a> TableBuilder<'a> {
} }
} }
/// Table struct which can construct a [`TableBody`].
/// Is created by [`TableBuilder`] by either calling `body` or after creating a header row with `header`.
pub struct Table<'a> { pub struct Table<'a> {
ui: &'a mut Ui, ui: &'a mut Ui,
widths: Vec<f32>, widths: Vec<f32>,
@ -169,7 +172,7 @@ impl<'a> Table<'a> {
let end_y = ui.available_rect_before_wrap().bottom(); let end_y = ui.available_rect_before_wrap().bottom();
egui::ScrollArea::new([false, self.scroll]).show(ui, move |ui| { egui::ScrollArea::new([false, self.scroll]).show(ui, move |ui| {
let layout = Layout::new(ui, LineDirection::Vertical); let layout = Layout::new(ui, CellDirection::Horizontal);
body(TableBody { body(TableBody {
layout, layout,
@ -183,6 +186,8 @@ impl<'a> Table<'a> {
} }
} }
/// The body of a table.
/// Is created by calling `body` on a [`Table`] (after adding a header row) or [`TableBuilder`] (without a header row).
pub struct TableBody<'a> { pub struct TableBody<'a> {
layout: Layout<'a>, layout: Layout<'a>,
widths: Vec<f32>, widths: Vec<f32>,
@ -265,6 +270,8 @@ impl<'a> Drop for TableBody<'a> {
} }
} }
/// The row of a table.
/// Is created by [`TableRow`] for each created [`TableBody::row`] or each visible row in rows created by calling [`TableBody::rows`].
pub struct TableRow<'a, 'b> { pub struct TableRow<'a, 'b> {
layout: &'b mut Layout<'a>, layout: &'b mut Layout<'a>,
widths: Vec<f32>, widths: Vec<f32>,