#include #include #include namespace psemek::ui::impl { namespace { static constexpr box_layout::size_policy default_policy = box_layout::minimized{}; template size_constraints compute_size_constraints(std::vector const & size_policies, std::vector const & children_size_constraints, float padding) { size_constraints minimized = size_constraints::max(); size_constraints weight_unit = minimized; float weight_sum = 0.f; for (std::size_t i = 0; i < std::min(size_policies.size(), children_size_constraints.size()); ++i) { if (std::get_if(&size_policies[i])) { minimized = sum_along(minimized, children_size_constraints[i], Dimension); } else if (auto weight = std::get_if(&size_policies[i])) { weight_unit = intersect(weight_unit, scale(children_size_constraints[i], 1.f / weight->value, Dimension)); weight_sum += weight->value; } } auto result = std::move(minimized); if (weight_sum > 0.f) result = sum_along(result, scale(weight_unit, weight_sum, Dimension), Dimension); if (size_policies.size() > 0) { geom::vector shift_delta{0.f, 0.f}; shift_delta[Dimension] = padding * (size_policies.size() - 1); result = shift(std::move(result), shift_delta); } return result; } template std::vector allocate(float total_size, std::vector> const & size_policies, util::span const> children, float padding) { std::vector result(std::min(size_policies.size(), children.size()), 0.f); if (!size_policies.empty()) total_size -= padding * (result.size() - 1); float total_weight = 0.f; // First, allocate minimized elements // Also compute the total weight for weighted elements for (std::size_t i = 0; i < result.size(); ++i) { if (!children[i]) continue; auto const policy = size_policies[i] ? *size_policies[i] : default_policy; if (std::get_if(&policy)) { result[i] = min(*children[i]->size_constraints(), Dimension); 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 < result.size(); ++i) { auto const policy = size_policies[i] ? *size_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() : padding_(0.f) , size_policies_(std::vector>{}) , children_size_constraints_() , size_constraints_( react::map( [](auto const & size_policies, auto const & children_size_constraints, auto const & padding){ return compute_size_constraints(size_policies, children_size_constraints, padding); }, react::join(react::map(react::unpack_with_default(default_policy), react::join(size_policies_))), react::join(react::map(react::unpack, children_size_constraints_)), react::join(padding_) ) ) {} template void box_layout_base::reshape(geom::box const & new_shape) { container::reshape(new_shape); if (children().empty()) return; auto padding = **padding_; auto sizes = allocate(new_shape[Dimension].length(), **size_policies_, children(), padding); 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 += padding; } } template react::value box_layout_base::size_constraints() const { return size_constraints_; } template void box_layout_base::set_children(std::vector> children) { container::set_children(std::move(children)); std::vector> children_size_constraints; for (auto const & child : this->children()) { if (child) children_size_constraints.push_back(child->size_constraints()); else children_size_constraints.push_back(size_constraints::max()); } children_size_constraints_.set(std::move(children_size_constraints)); } template std::vector> box_layout_base::release_children() { auto result = container::release_children(); children_size_constraints_.set({}); return result; } template void box_layout_base::update(typename detail::box_layout_type::type const & box_layout) { size_policies_.set(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)); padding_.set(box_layout.padding); } template struct box_layout_base<0>; template struct box_layout_base<1>; }