211 lines
5.1 KiB
C++
211 lines
5.1 KiB
C++
#include <psemek/ui/grid_layout.hpp>
|
|
|
|
#include <numeric>
|
|
|
|
namespace psemek::ui
|
|
{
|
|
|
|
element::children_range grid_layout::children() const
|
|
{
|
|
return children_range{children_range_.data(), children_range_.data() + children_range_.size()};
|
|
}
|
|
|
|
bool grid_layout::add_child(std::shared_ptr<element> c)
|
|
{
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
{
|
|
for (std::size_t j = 0; j < column_count(); ++j)
|
|
{
|
|
if (!get(i, j))
|
|
{
|
|
set(i, j, std::move(c));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool grid_layout::has_child(element * c) const
|
|
{
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
for (std::size_t j = 0; j < column_count(); ++j)
|
|
if (get(i, j) == c) return true;
|
|
return false;
|
|
}
|
|
|
|
std::shared_ptr<element> grid_layout::remove_child(element * c)
|
|
{
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
{
|
|
for (std::size_t j = 0; j < column_count(); ++j)
|
|
{
|
|
if (get(i, j) == c)
|
|
{
|
|
return remove(i, j);
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void grid_layout::set_row_count(std::size_t count)
|
|
{
|
|
set_size(count, std::max<std::size_t>(1, column_count()));
|
|
}
|
|
|
|
void grid_layout::set_column_count(std::size_t count)
|
|
{
|
|
set_size(std::max<std::size_t>(1, row_count()), count);
|
|
}
|
|
|
|
void grid_layout::set_size(std::size_t rows, std::size_t columns)
|
|
{
|
|
std::size_t const old_row_count = row_count();
|
|
std::size_t const old_column_count = column_count();
|
|
|
|
children_.resize({rows, columns});
|
|
|
|
children_range_.resize(rows * columns);
|
|
for (std::size_t i = 0; i < rows; ++i)
|
|
{
|
|
for (std::size_t j = 0; j < columns; ++j)
|
|
{
|
|
children_range_[i * columns + j] = children_(i, j).get();
|
|
}
|
|
}
|
|
|
|
row_weight_.resize(rows);
|
|
for (std::size_t i = old_row_count; i < rows; ++i)
|
|
row_weight_[i] = 1.f;
|
|
|
|
column_weight_.resize(columns);
|
|
for (std::size_t i = old_column_count; i < columns; ++i)
|
|
column_weight_[i] = 1.f;
|
|
|
|
post_reshape();
|
|
}
|
|
|
|
void grid_layout::set_row_weight(std::size_t i, float w)
|
|
{
|
|
row_weight_.at(i) = w;
|
|
post_reshape();
|
|
}
|
|
|
|
void grid_layout::set_column_weight(std::size_t i, float w)
|
|
{
|
|
column_weight_.at(i) = w;
|
|
post_reshape();
|
|
}
|
|
|
|
element * grid_layout::get(std::size_t i, std::size_t j) const
|
|
{
|
|
return children_(i, j).get();
|
|
}
|
|
|
|
std::shared_ptr<element> grid_layout::set(std::size_t i, std::size_t j, std::shared_ptr<element> c)
|
|
{
|
|
auto old = std::move(children_(i, j));
|
|
if (old) old->set_parent(nullptr);
|
|
if (c) c->set_parent(this);
|
|
children_range_[i * column_count() + j] = c.get();
|
|
children_(i, j) = std::move(c);
|
|
post_reshape();
|
|
return old;
|
|
}
|
|
|
|
std::shared_ptr<element> grid_layout::remove(std::size_t i, std::size_t j)
|
|
{
|
|
return set(i, j, nullptr);
|
|
}
|
|
|
|
geom::box<float, 2> grid_layout::size_constraints() const
|
|
{
|
|
auto st = merged_style();
|
|
if (!st) return element::size_constraints();
|
|
|
|
static float const inf = std::numeric_limits<float>::infinity();
|
|
|
|
std::vector<geom::interval<float>> row_size(row_count(), {0.f, inf});
|
|
std::vector<geom::interval<float>> column_size(column_count(), {0.f, inf});
|
|
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
{
|
|
for (std::size_t j = 0; j < column_count(); ++j)
|
|
{
|
|
if (!get(i, j)) continue;
|
|
|
|
auto sc = get(i, j)->size_constraints();
|
|
row_size[i] &= sc[1];
|
|
column_size[j] &= sc[0];
|
|
}
|
|
}
|
|
|
|
geom::box<float, 2> result{{{0.f, 0.f}, {0.f, 0.f}}};
|
|
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
{
|
|
result[1].min += row_size[i].min;
|
|
result[1].max += row_size[i].max;
|
|
}
|
|
|
|
for (std::size_t i = 0; i < column_count(); ++i)
|
|
{
|
|
result[0].min += column_size[i].min;
|
|
result[0].max += column_size[i].max;
|
|
}
|
|
|
|
result[1] += (row_count() + 1) * (*st->outer_margin);
|
|
result[0] += (column_count() + 1) * (*st->outer_margin);
|
|
|
|
return result;
|
|
}
|
|
|
|
void grid_layout::reshape(geom::box<float, 2> const & bbox)
|
|
{
|
|
auto st = merged_style();
|
|
if (!st) return;
|
|
|
|
float const margin = *st->outer_margin;
|
|
|
|
float const available_width = std::max(0.f, bbox[0].length() - margin * (1 + column_count()));
|
|
float const available_height = std::max(0.f, bbox[1].length() - margin * (1 + row_count()));
|
|
|
|
float const row_weight_sum = std::accumulate(row_weight_.begin(), row_weight_.end(), 0.f);
|
|
float const column_weight_sum = std::accumulate(column_weight_.begin(), column_weight_.end(), 0.f);
|
|
|
|
std::vector<geom::interval<float>> row_shape(row_count());
|
|
std::vector<geom::interval<float>> column_shape(column_count());
|
|
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
{
|
|
float const row_height = available_height * row_weight_[i] / row_weight_sum;
|
|
row_shape[i] = {margin, margin + row_height};
|
|
if (i == 0)
|
|
row_shape[i] += bbox[1].min;
|
|
else
|
|
row_shape[i] += row_shape[i - 1].max;
|
|
}
|
|
|
|
for (std::size_t i = 0; i < column_count(); ++i)
|
|
{
|
|
float const column_width = available_width * column_weight_[i] / column_weight_sum;
|
|
column_shape[i] = {margin, margin + column_width};
|
|
if (i == 0)
|
|
column_shape[i] += bbox[0].min;
|
|
else
|
|
column_shape[i] += column_shape[i - 1].max;
|
|
}
|
|
|
|
for (std::size_t i = 0; i < row_count(); ++i)
|
|
{
|
|
for (std::size_t j = 0; j < column_count(); ++j)
|
|
{
|
|
if (!get(i, j)) continue;
|
|
|
|
get(i, j)->reshape({{column_shape[j], row_shape[i]}});
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|