Commit aa77bfd0 authored by Florian Blasius's avatar Florian Blasius 🤘

grid simplification

parent 58c273fc
......@@ -74,170 +74,85 @@ impl GridLayout {
(y, height)
}
fn calculate_column_width(
fn calculate_block_size(
&self,
child: Entity,
column: Column,
grid_column: usize,
column_widths: &mut BTreeMap<usize, f64>,
margin: Thickness,
) {
if column.width != ColumnWidth::Auto {
return;
}
let child_width = self.children_sizes.borrow().get(&child).unwrap().0;
if let Some(width) = column_widths.get(&grid_column) {
if *width < child_width + margin.left() + margin.right() {
column_widths.insert(grid_column, child_width + margin.left() + margin.right());
}
} else {
column_widths.insert(grid_column, child_width + margin.left() + margin.right());
}
}
fn calculate_row_height(
&self,
child: Entity,
row: Row,
grid_row: usize,
row_heights: &mut BTreeMap<usize, f64>,
block: Block,
grid_block: usize,
block_sizes: &mut BTreeMap<usize, f64>,
margin: Thickness,
) {
if row.height != RowHeight::Auto {
if block.size != BlockSize::Auto {
return;
}
let child_size = self.children_sizes.borrow().get(&child).unwrap().0;
let child_height = self.children_sizes.borrow().get(&child).unwrap().1;
if let Some(height) = row_heights.get(&grid_row) {
if *height < child_height + margin.top() + margin.bottom() {
row_heights.insert(grid_row, child_height + margin.top() + margin.bottom());
if let Some(size) = block_sizes.get(&grid_block) {
if *size < child_size + margin.left() + margin.right() {
block_sizes.insert(grid_block, child_size + margin.left() + margin.right());
}
} else {
row_heights.insert(grid_row, child_height + margin.top() + margin.bottom());
block_sizes.insert(grid_block, child_size + margin.left() + margin.right());
}
}
fn calculate_columns(
fn calculate_blocks(
&self,
size: (f64, f64),
columns_cache: &mut Vec<(f64, f64)>,
columns: &mut Columns,
column_widths: &BTreeMap<usize, f64>,
blocks_cache: &mut Vec<(f64, f64)>,
blocks: &mut Blocks,
block_sizes: &BTreeMap<usize, f64>,
) {
if !columns.is_empty() {
// sets auto columns width to the width of the largest child
for (grid_column, width) in column_widths {
if let Some(column) = columns.get_mut(*grid_column) {
column.set_current_width(*width);
if !blocks.is_empty() {
// sets auto blocks size to the size of the largest child
for (grid_block, size) in block_sizes {
if let Some(block) = blocks.get_mut(*grid_block) {
block.set_current_size(*size);
}
}
// sets the width of columns with fixed width
for column in columns.iter_mut() {
if let ColumnWidth::Width(width) = column.width {
column.set_current_width(width);
// sets the size of blocks with fixed size
for block in blocks.iter_mut() {
if let BlockSize::Size(size) = block.size {
block.set_current_size(size);
}
}
// calculates the width of the stretch columns
let used_width: f64 = columns
// calculates the size of the stretch blocks
let used_size: f64 = blocks
.iter()
.filter(|column| column.width != ColumnWidth::Stretch)
.map(|column| column.current_width())
.filter(|block| block.size != BlockSize::Stretch)
.map(|block| block.current_size())
.sum();
let stretch_width = ((size.0 - used_width)
/ columns
let stretch_size = ((size.0 - used_size)
/ blocks
.iter()
.filter(|column| column.width == ColumnWidth::Stretch)
.filter(|block| block.size == BlockSize::Stretch)
.count() as f64)
.trunc();
columns
blocks
.iter_mut()
.filter(|column| column.width == ColumnWidth::Stretch)
.for_each(|column| column.set_current_width(stretch_width));
let mut column_sum = 0.0;
columns_cache.reserve(columns.len());
for col in columns.iter() {
columns_cache.push((column_sum, col.current_width()));
column_sum += col.current_width();
}
// fix rounding gab
if size.0 - column_sum > 0.0 {
if let Some(last_column) = columns
.iter_mut()
.rev()
.find(|column| column.width == ColumnWidth::Stretch)
{
last_column
.set_current_width(last_column.current_width() + size.0 - column_sum);
}
}
}
}
fn calculate_rows(
&self,
size: (f64, f64),
rows_cache: &mut Vec<(f64, f64)>,
rows: &mut Rows,
row_heights: &BTreeMap<usize, f64>,
) {
if !rows.is_empty() {
// sets auto rows height to the height of the largest child
for (grid_row, height) in row_heights {
if let Some(row) = rows.get_mut(*grid_row) {
row.set_current_height(*height);
}
}
// sets the height of rows with fixed height
for row in rows.iter_mut() {
if let RowHeight::Height(height) = row.height {
row.set_current_height(height);
}
}
// calculates the height of the stretch rows
let used_height: f64 = rows
.iter()
.filter(|row| row.height != RowHeight::Stretch)
.map(|row| row.current_height())
.sum();
let stretch_height = ((size.1 - used_height)
/ rows
.iter()
.filter(|row| row.height == RowHeight::Stretch)
.count() as f64)
.trunc();
rows.iter_mut()
.filter(|row| row.height == RowHeight::Stretch)
.for_each(|row| row.set_current_height(stretch_height));
.filter(|block| block.size == BlockSize::Stretch)
.for_each(|block| block.set_current_size(stretch_size));
let mut row_sum = 0.0;
let mut block_sum = 0.0;
rows_cache.reserve(rows.len());
for row in rows.iter() {
rows_cache.push((row_sum, row.current_height()));
row_sum += row.current_height();
blocks_cache.reserve(blocks.len());
for col in blocks.iter() {
blocks_cache.push((block_sum, col.current_size()));
block_sum += col.current_size();
}
// fix rounding gab
if size.1 - row_sum > 0.0 {
if let Some(last_row) = rows
if size.0 - block_sum > 0.0 {
if let Some(last_block) = blocks
.iter_mut()
.rev()
.find(|row| row.height == RowHeight::Stretch)
.find(|block| block.size == BlockSize::Stretch)
{
last_row.set_current_height(last_row.current_height() + size.1 - row_sum);
last_block.set_current_size(last_block.current_size() + size.0 - block_sum);
}
}
}
......@@ -384,9 +299,9 @@ impl Layout for GridLayout {
let margin: Thickness = component(ecm, entity, "margin");
if let Ok(grid_column) = ecm.component_store().get::<usize>("column", child) {
if let Ok(columns) = ecm.component_store().get::<Columns>("columns", entity) {
if let Ok(columns) = ecm.component_store().get::<Blocks>("columns", entity) {
if let Some(column) = columns.get(*grid_column) {
self.calculate_column_width(
self.calculate_block_size(
child,
*column,
*grid_column,
......@@ -401,23 +316,23 @@ impl Layout for GridLayout {
let grid_row = *grid_row;
// calculate row_height for each row and insert into row_heights
if let Ok(rows) = ecm.component_store().get::<Rows>("rows", entity) {
if let Ok(rows) = ecm.component_store().get::<Blocks>("rows", entity) {
if let Some(row) = rows.get(grid_row) {
self.calculate_row_height(child, *row, grid_row, &mut row_heights, margin);
self.calculate_block_size(child, *row, grid_row, &mut row_heights, margin);
}
}
}
}
if let Ok(columns) = ecm
.component_store_mut()
.get_mut::<Columns>("columns", entity)
.get_mut::<Blocks>("columns", entity)
{
self.calculate_columns(size, &mut columns_cache, columns, &column_widths);
self.calculate_blocks(size, &mut columns_cache, columns, &column_widths);
}
// take row_heights and calculate rows_cache
if let Ok(rows) = ecm.component_store_mut().get_mut::<Rows>("rows", entity) {
self.calculate_rows(size, &mut rows_cache, rows, &row_heights);
if let Ok(rows) = ecm.component_store_mut().get_mut::<Blocks>("rows", entity) {
self.calculate_blocks(size, &mut rows_cache, rows, &row_heights);
}
if let Some(bounds) = component_try_mut::<Rectangle>(ecm, entity, "bounds") {
......@@ -436,7 +351,7 @@ impl Layout for GridLayout {
let mut available_size = size;
let has_columns =
if let Ok(columns) = ecm.component_store().get::<Columns>("columns", entity) {
if let Ok(columns) = ecm.component_store().get::<Blocks>("columns", entity) {
!columns.is_empty()
} else {
false
......@@ -464,7 +379,7 @@ impl Layout for GridLayout {
available_size.0 = size.0;
}
let has_rows = if let Ok(rows) = ecm.component_store().get::<Rows>("rows", entity) {
let has_rows = if let Ok(rows) = ecm.component_store().get::<Blocks>("rows", entity) {
!rows.is_empty()
} else {
false
......
use std::slice::{Iter, IterMut};
static BLOCK_SEPARATOR: &str = ",";
/// Used to build a block (block | row), specifying additional details.
#[derive(Default)]
pub struct BlockBuilder {
size: BlockSize,
min_size: f64,
max_size: f64,
}
impl BlockBuilder {
/// Creates a new `BlockBuilder` with default values.
pub fn new() -> Self {
BlockBuilder::default()
}
/// Inserts a new size.
pub fn size(mut self, size: BlockSize) -> Self {
self.size = size;
self
}
/// Inserts a new min size.
pub fn min_size(mut self, min_size: f64) -> Self {
self.min_size = min_size;
self
}
/// Inserts a new max size.
pub fn max_size(mut self, max_size: f64) -> Self {
self.max_size = max_size;
self
}
/// Builds the block.
pub fn build(self) -> Block {
Block {
size: self.size,
min_size: self.min_size,
max_size: self.max_size,
current_size: 0.0,
}
}
}
/// Used to define the block of the `Grid`.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct Block {
pub size: BlockSize,
pub min_size: f64,
pub max_size: f64,
current_size: f64,
}
impl Block {
/// Creates a new `BlockBuilder` object with default values.
#[inline]
pub fn create() -> BlockBuilder {
BlockBuilder::new()
}
/// Gets the block size.
pub fn size(&self) -> BlockSize {
self.size
}
/// Gets the current size.
pub fn current_size(&self) -> f64 {
self.current_size
}
/// Sets the current size.
pub fn set_current_size(&mut self, size: f64) {
self.current_size = if self.min_size == 0.0 && self.max_size == 0.0 && size > 0.0 {
size
} else if size < self.min_size && self.min_size > 0.0 {
self.min_size
} else if size > self.max_size && self.max_size > 0.0 {
self.max_size
} else {
size
};
}
}
impl From<&str> for Block {
fn from(t: &str) -> Self {
if let Ok(size) = t.parse::<f64>() {
return Block::create().size(BlockSize::Size(size)).build();
}
match t {
"Auto" | "auto" => Block::create().size(BlockSize::Auto).build(),
_ => Block::create().size(BlockSize::Stretch).build(),
}
}
}
impl From<f64> for Block {
fn from(t: f64) -> Self {
Block::create().size(BlockSize::Size(t)).build()
}
}
impl From<i32> for Block {
fn from(t: i32) -> Self {
Block::create().size(BlockSize::Size(t.into())).build()
}
}
/// Used to define the size of a grid block.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BlockSize {
/// Block is measured by the largest child.
Auto,
/// Block expands to the rest available size.
Stretch,
/// Defines a fixed size for the block.
Size(f64),
}
impl Default for BlockSize {
fn default() -> Self {
BlockSize::Stretch
}
}
/// Used to build a blocks, specifying additional details.
#[derive(Default)]
pub struct BlocksBuilder {
blocks: Vec<Block>,
}
/// Used to build a blocks, specifying additional details.
impl BlocksBuilder {
/// Creates a new `BlocksBuilder` with default values.
pub fn new() -> Self {
BlocksBuilder::default()
}
/// Inserts a new block.
pub fn push<C: Into<Block>>(mut self, block: C) -> Self {
self.blocks.push(block.into());
self
}
/// Inserts a new block.
#[inline(always)]
#[deprecated = "Use push instead"]
pub fn block<C: Into<Block>>(self, block: C) -> Self {
self.push(block)
}
/// Inserts a list of blocks.
pub fn blocks<R: Into<Block> + Clone>(mut self, blocks: &[R]) -> Self {
for block in blocks.to_vec() {
self.blocks.push(block.into());
}
self
}
/// Inserts the given block as often as given.
pub fn repeat<R: Into<Block> + Copy>(mut self, block: R, count: usize) -> Self {
for _ in 0..count {
self.blocks.push(block.into())
}
self
}
/// Builds the blocks.
pub fn build(self) -> Blocks {
Blocks(self.blocks)
}
}
impl From<BlocksBuilder> for Blocks {
fn from(builder: BlocksBuilder) -> Self {
builder.build()
}
}
/// Helper struct used inside of the blocks Property.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Blocks(pub Vec<Block>);
impl Blocks {
/// Creates a new `BlocksBuilder` object with default values.
#[inline]
pub fn create() -> BlocksBuilder {
BlocksBuilder::new()
}
/// Returns the number of elements in the blocks list, also referred to as its 'length'.
pub fn len(&self) -> usize {
self.0.len()
}
/// Is the block empty?
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Returns a reference to an block.
pub fn get(&self, block: usize) -> Option<&Block> {
self.0.get(block)
}
/// Returns a mutable reference to an block.
pub fn get_mut(&mut self, block: usize) -> Option<&mut Block> {
self.0.get_mut(block)
}
/// Returns an iterator over the slice.
pub fn iter(&self) -> Iter<Block> {
self.0.iter()
}
/// Returns a mutable iterator over the slice.
pub fn iter_mut(&mut self) -> IterMut<Block> {
self.0.iter_mut()
}
}
impl From<&str> for Blocks {
fn from(blocks: &str) -> Self {
let blocks = blocks.replace(" ", "");
let mut block_builder = BlocksBuilder::new();
for block in blocks.split(BLOCK_SEPARATOR) {
block_builder = block_builder.push(Block::from(block));
}
block_builder.build()
}
}
impl From<String> for Blocks {
fn from(blocks: String) -> Self {
Self::from(blocks.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
const ERROR: f64 = f64::EPSILON;
#[test]
fn test_size() {
let size = BlockSize::Size(64.0);
let builder = BlockBuilder::new();
let block = builder.size(size).build();
assert_eq!(block.size, size);
}
#[test]
fn test_min_size() {
let min_size = 64.0;
let builder = BlockBuilder::new();
let block = builder.min_size(min_size).build();
assert!((block.min_size - min_size).abs() < ERROR);
}
#[test]
fn test_max_size() {
let max_size = 64.0;
let builder = BlockBuilder::new();
let block = builder.max_size(max_size).build();
assert!((block.max_size - max_size) < ERROR);
}
#[test]
fn test_set_current_size() {
let out_one_size = 10.0;
let out_two_size = 66.0;
let in_size = 33.0;
let min_size = 14.0;
let max_size = 64.0;
let builder = BlockBuilder::new();
let mut block = builder.min_size(min_size).max_size(max_size).build();
block.set_current_size(out_one_size);
assert!((block.current_size() - min_size).abs() < ERROR);
block.set_current_size(out_two_size);
assert!((block.current_size() - max_size).abs() < ERROR);
block.set_current_size(in_size);
assert!((block.current_size() - min_size).abs() < ERROR);
}
#[test]
fn test_block() {
let builder = BlocksBuilder::new();
let blocks = builder.build();
assert_eq!(blocks.len(), 0);
let builder = BlocksBuilder::new();
let blocks = builder
.push(Block::create().build())
.push(Block::create().build())
.build();
assert_eq!(blocks.len(), 2);
}
#[test]
fn test_block_size_into() {
let block: Block = "Auto".into();
assert_eq!(block.size(), BlockSize::Auto);