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::drag_and_drop::DragAndDropDemo::default()),
Box::new(super::font_book::FontBook::default()),
Box::new(super::grid_demo::GridDemo::default()),
Box::new(super::MiscDemoWindow::default()),
Box::new(super::multi_touch::MultiTouch::default()),
Box::new(super::painting::Painting::default()),
Box::new(super::plot_demo::PlotDemo::default()),
Box::new(super::scrolling::Scrolling::default()),
Box::new(super::sliders::Sliders::default()),
Box::new(super::strip_demo::StripDemo::default()),
Box::new(super::table_demo::TableDemo::default()),
Box::new(super::text_edit::TextEdit::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 drag_and_drop;
pub mod font_book;
pub mod grid_demo;
pub mod layout_test;
pub mod misc_demo_window;
pub mod multi_touch;
@ -22,6 +21,7 @@ pub mod password;
pub mod plot_demo;
pub mod scrolling;
pub mod sliders;
pub mod strip_demo;
pub mod table_demo;
pub mod tests;
pub mod text_edit;

View file

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

View file

@ -1,5 +1,5 @@
use egui::{Label, Vec2};
use egui_extras::{GridBuilder, Size, TableBuilder};
use egui_extras::{Size, StripBuilder, TableBuilder};
/// Shows off a table with dynamic layout
#[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
ui.spacing_mut().item_spacing = Vec2::splat(4.0);
GridBuilder::new(ui)
StripBuilder::new(ui)
.size(Size::Remainder)
.size(Size::Absolute(10.0))
.vertical(|mut grid| {

View file

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

View file

@ -8,35 +8,33 @@ pub(crate) enum CellSize {
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]`.
///
/// In a table there's a `[Layout]` for each table row with a horizontal `[LineDirection]`.
/// Its cells go from left to right.
pub(crate) enum LineDirection {
/// Cells go from top to bottom on each line
/// Lines go from left to right
Horizontal,
/// In a table there's a `[Layout]` for each table row with a horizonal `[CellDirection]`.
/// Its cells go from left to right. And the lines go from top to bottom.
pub(crate) enum CellDirection {
/// Cells go from left to right
/// Lines go from top to bottom
Horizontal,
/// Cells go fromtop to bottom
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> {
ui: &'l mut Ui,
direction: LineDirection,
direction: CellDirection,
rect: Rect,
pos: Pos2,
max: Pos2,
}
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 pos = rect.left_top();
@ -71,12 +69,12 @@ impl<'l> Layout<'l> {
fn set_pos(&mut self, rect: Rect) {
match self.direction {
LineDirection::Horizontal => {
self.pos.y = rect.bottom() + self.ui.spacing().item_spacing.y;
}
LineDirection::Vertical => {
CellDirection::Horizontal => {
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
@ -130,14 +128,14 @@ impl<'l> Layout<'l> {
/// only needed for layouts with multiple lines, like Table
pub fn end_line(&mut self) {
match self.direction {
LineDirection::Horizontal => {
self.pos.x = self.max.x;
self.pos.y = self.rect.top();
}
LineDirection::Vertical => {
CellDirection::Horizontal => {
self.pos.y = self.max.y;
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")]
mod datepicker;
mod grid;
pub mod image;
mod layout;
mod sizing;
mod strip;
mod table;
#[cfg(feature = "chrono")]
pub use crate::datepicker::DatePickerButton;
pub use crate::grid::*;
pub use crate::image::RetainedImage;
pub(crate) use crate::layout::Layout;
pub use crate::sizing::Size;
pub use crate::strip::*;
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)]
pub enum Size {
/// Absolute size in points

View file

@ -1,50 +1,42 @@
use crate::{
layout::{CellSize, Layout, LineDirection},
layout::{CellDirection, CellSize, Layout},
sizing::Sizing,
Size,
};
use egui::{Response, Ui};
/// The direction in which cells are positioned in the grid.
///
/// 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> {
/// Builder for creating a new [`Strip`].
pub struct StripBuilder<'a> {
ui: &'a mut Ui,
sizing: Sizing,
}
impl<'a> GridBuilder<'a> {
/// Create new grid builder.
impl<'a> StripBuilder<'a> {
/// 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
/// ```
/// # egui::__run_test_ui(|ui| {
/// use egui_extras::{GridBuilder, Size};
/// GridBuilder::new(ui)
/// use egui_extras::{StripBuilder, Size};
/// StripBuilder::new(ui)
/// .size(Size::RemainderMinimum(100.0))
/// .size(Size::Absolute(40.0))
/// .vertical(|mut grid| {
/// grid.grid(|builder| {
/// builder.sizes(Size::Remainder, 2).horizontal(|mut grid| {
/// grid.cell(|ui| {
/// .vertical(|mut strip| {
/// strip.strip(|builder| {
/// builder.sizes(Size::Remainder, 2).horizontal(|mut strip| {
/// strip.cell(|ui| {
/// ui.label("Top Left");
/// });
/// grid.cell(|ui| {
/// strip.cell(|ui| {
/// ui.label("Top Right");
/// });
/// });
/// });
/// grid.cell(|ui| {
/// strip.cell(|ui| {
/// ui.label("Fixed");
/// });
/// });
@ -62,7 +54,7 @@ impl<'a> GridBuilder<'a> {
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 {
for _ in 0..count {
self.sizing.add(size);
@ -70,63 +62,65 @@ impl<'a> GridBuilder<'a> {
self
}
/// Build horizontal grid: 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!
/// Build horizontal strip: Cells are positions from left to right.
/// 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.
pub fn horizontal<F>(self, grid: F) -> Response
pub fn horizontal<F>(self, strip: F) -> Response
where
F: for<'b> FnOnce(Grid<'a, 'b>),
F: for<'b> FnOnce(Strip<'a, 'b>),
{
let widths = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().width() - self.ui.spacing().item_spacing.x,
self.ui.spacing().item_spacing.x,
);
let mut layout = Layout::new(self.ui, LineDirection::Vertical);
grid(Grid {
let mut layout = Layout::new(self.ui, CellDirection::Horizontal);
strip(Strip {
layout: &mut layout,
direction: GridDirection::Horizontal,
direction: CellDirection::Horizontal,
sizes: widths,
});
layout.set_rect()
}
/// Build vertical grid: 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!
/// 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 strip or the container will grow slowly!
///
/// Returns a `[egui::Response]` for hover events.
pub fn vertical<F>(self, grid: F) -> Response
pub fn vertical<F>(self, strip: F) -> Response
where
F: for<'b> FnOnce(Grid<'a, 'b>),
F: for<'b> FnOnce(Strip<'a, 'b>),
{
let heights = self.sizing.into_lengths(
self.ui.available_rect_before_wrap().height() - self.ui.spacing().item_spacing.y,
self.ui.spacing().item_spacing.y,
);
let mut layout = Layout::new(self.ui, LineDirection::Horizontal);
grid(Grid {
let mut layout = Layout::new(self.ui, CellDirection::Vertical);
strip(Strip {
layout: &mut layout,
direction: GridDirection::Vertical,
direction: CellDirection::Vertical,
sizes: heights,
});
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>,
direction: GridDirection,
direction: CellDirection,
sizes: Vec<f32>,
}
impl<'a, 'b> Grid<'a, 'b> {
impl<'a, 'b> Strip<'a, 'b> {
fn next_cell_size(&mut self) -> (CellSize, CellSize) {
match self.direction {
GridDirection::Horizontal => (
CellDirection::Horizontal => (
CellSize::Absolute(self.sizes.remove(0)),
CellSize::Remainder,
),
GridDirection::Vertical => (
CellDirection::Vertical => (
CellSize::Remainder,
CellSize::Absolute(self.sizes.remove(0)),
),
@ -137,7 +131,7 @@ impl<'a, 'b> Grid<'a, 'b> {
pub fn empty(&mut self) {
assert!(
!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();
@ -147,7 +141,7 @@ impl<'a, 'b> Grid<'a, 'b> {
fn _cell(&mut self, clip: bool, add_contents: impl FnOnce(&mut Ui)) {
assert!(
!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();
@ -164,23 +158,23 @@ impl<'a, 'b> Grid<'a, 'b> {
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| {
grid_builder(GridBuilder::new(ui));
strip_builder(StripBuilder::new(ui));
});
}
/// Add grid as cell
pub fn grid(&mut self, grid_builder: impl FnOnce(GridBuilder<'_>)) {
self._grid(false, grid_builder);
/// Add strip as cell
pub fn strip(&mut self, strip_builder: impl FnOnce(StripBuilder<'_>)) {
self._strip(false, strip_builder);
}
/// Add grid as cell, content is clipped
pub fn grid_noclip(&mut self, grid_builder: impl FnOnce(GridBuilder<'_>)) {
self._grid(true, grid_builder);
/// Add strip as cell, content is clipped
pub fn strip_noclip(&mut self, strip_builder: impl FnOnce(StripBuilder<'_>)) {
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) {
while !self.sizes.is_empty() {
self.empty();

View file

@ -1,10 +1,10 @@
//! Table view with (optional) fixed header and scrolling body.
//! 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 |
//! 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::{
layout::{CellSize, LineDirection},
layout::{CellDirection, CellSize},
sizing::Sizing,
Layout, Size,
};
@ -12,6 +12,7 @@ use crate::{
use egui::{Response, Ui};
use std::cmp;
/// Builder for creating a new [`Table`].
pub struct TableBuilder<'a> {
ui: &'a mut Ui,
sizing: Sizing,
@ -27,7 +28,7 @@ impl<'a> TableBuilder<'a> {
/// | 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!
/// 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
/// ```
@ -85,7 +86,7 @@ impl<'a> TableBuilder<'a> {
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 {
for _ in 0..count {
self.sizing.add(size);
@ -107,7 +108,7 @@ impl<'a> TableBuilder<'a> {
);
let ui = self.ui;
{
let mut layout = Layout::new(ui, LineDirection::Vertical);
let mut layout = Layout::new(ui, CellDirection::Horizontal);
{
let row = TableRow {
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> {
ui: &'a mut Ui,
widths: Vec<f32>,
@ -169,7 +172,7 @@ impl<'a> Table<'a> {
let end_y = ui.available_rect_before_wrap().bottom();
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 {
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> {
layout: Layout<'a>,
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> {
layout: &'b mut Layout<'a>,
widths: Vec<f32>,