#include #include #include namespace psemek::ui::impl { namespace { static constexpr box_layout::size_policy default_policy = box_layout::weight{}; template std::vector allocate(float total_size, float other_dimension_size, std::vector> const & policies, util::span const> children) { std::vector result(policies.size(), 0.f); float total_weight = 0.f; // First, allocate minimized & fixed elements // Also compute the total weight for weighted elements for (std::size_t i = 0; i < policies.size(); ++i) { auto const policy = policies[i] ? *policies[i] : default_policy; if (std::get_if(&policy)) { result[i] = children[i]->size_constraints(Dimension, other_dimension_size).min; total_size -= result[i]; } else if (auto fixed = std::get_if(&policy)) { result[i] = fixed->value; total_size -= result[i]; } else if (auto weight = std::get_if(&policy)) { total_weight += weight->value; } } // Next, allocate weighted elements for (std::size_t i = 0; i < policies.size(); ++i) { auto const policy = policies[i] ? *policies[i] : default_policy; if (auto weight = std::get_if(&policy)) { result[i] = total_size * weight->value / total_weight; } } return result; } } template box_layout_base::box_layout_base(react::value margin) : margin_(margin) {} template void box_layout_base::reshape(geom::box const & new_shape) { container::reshape(new_shape); if (children().empty()) return; float const margin = *margin_; auto sizes = allocate(new_shape[Dimension].length() - (children().size() - 1) * margin, new_shape[Dimension ^ 1].length(), *size_policies_, children()); float pen = new_shape[Dimension].min; for (std::size_t i = 0; i < children().size(); ++i) { if (!children()[i]) continue; geom::box child_shape; child_shape[Dimension] = {pen, pen + sizes[i]}; child_shape[Dimension ^ 1] = new_shape[Dimension ^ 1]; children()[i]->reshape(child_shape); pen += sizes[i]; pen += margin; } } template geom::interval box_layout_base::size_constraints(int dimension, float other_dimension_size) const { if (children().empty()) return container::size_constraints(dimension, other_dimension_size); float const margin = *margin_; if (dimension == Dimension) { auto result = geom::interval{0.f, 0.f}; auto const & policies = *size_policies_; auto weighted_unit_constraints = geom::interval{0.f, infinity}; float weight_sum = 0.f; for (std::size_t i = 0; i < children().size(); ++i) { auto const & child = children()[i]; auto const policy = policies[i] ? *policies[i] : default_policy; if (auto fixed = std::get_if(&policy)) result += fixed->value; else if (std::get_if(&policy)) { if (child) { auto child_constraints = child->size_constraints(dimension, other_dimension_size); result.min += child_constraints.min; result.max += child_constraints.max; } else { result.max = infinity; } } else if (auto weight = std::get_if(&policy)) { if (child) { auto child_constraints = child->size_constraints(dimension, other_dimension_size); child_constraints.min /= weight->value; child_constraints.max /= weight->value; weighted_unit_constraints &= child_constraints; } weight_sum += weight->value; } } // Prevent NaN in result.max (inf * 0) if (weight_sum > 0.f) { result.min += weighted_unit_constraints.min * weight_sum; result.max += weighted_unit_constraints.max * weight_sum; } result += (children().size() - 1) * margin; return result; } else { float const margin = *margin_; auto sizes = allocate(other_dimension_size - (children().size() - 1) * margin, shape()[1].length(), *size_policies_, children()); auto result = container::size_constraints(dimension, other_dimension_size); for (std::size_t i = 0; i < children().size(); ++i) { if (!children()[i]) continue; result &= children()[i]->size_constraints(dimension, sizes[i]); } return result; } } template void box_layout_base::update(typename detail::box_layout_type::type const & box_layout) { size_policies_ = react::map([](auto const & children){ std::vector> size_policies; size_policies.reserve(children.size()); for (auto const & child : children) size_policies.push_back(child.policy); return size_policies; }, box_layout.children); } template struct box_layout_base<0>; template struct box_layout_base<1>; }