#include namespace psemek::ui { screen::screen() : container_(this) {} element::children_range screen::children() const { return container_.children(); } bool screen::add_child(std::shared_ptr c, x_policy x, y_policy y) { auto i = container_.add(std::move(c)); if (i >= policies_.size()) policies_.resize(i + 1); policies_[i] = {x, y}; post_reshape(); return true; } bool screen::add_child(std::shared_ptr c) { return add_child(std::move(c), x_policy::floating, y_policy::floating); } bool screen::has_child(element * c) const { return static_cast(container_.find(c)); } std::shared_ptr screen::remove_child(element * c) { return container_.remove(c); } bool screen::move_to_top(element * c) { if (auto idx = container_.find(c)) { auto e = container_.remove(*idx); auto new_idx = container_.size(); container_.add(e, new_idx); policies_.resize(container_.size()); policies_[new_idx] = policies_[*idx]; return true; } return false; } void screen::reshape(math::box const & bbox) { shape_.box = bbox; auto const m = bbox.center(); auto cs = children(); for (std::size_t i = 0; i < cs.size(); ++i) { auto c = cs[i]; if (!c) continue; auto sc = c->size_constraints(); math::box cbox = c->shape().bbox(); auto update_width = [&](float width) { width = std::min(bbox[0].length(), width); switch (policies_[i].x) { case x_policy::left: cbox[0] = {bbox[0].min, bbox[0].min + width}; break; case x_policy::center: cbox[0] = {m[0] - width / 2.f, m[0] + width / 2.f}; break; case x_policy::right: cbox[0] = {bbox[0].max - width, bbox[0].max}; break; case x_policy::fill: cbox[0] = bbox[0]; break; case x_policy::floating: cbox[0] = math::expand(cbox[0], (width - cbox[0].length()) / 2.f); if (cbox[0].min < bbox[0].min) cbox[0] += (bbox[0].min - cbox[0].min); else if (cbox[0].max > bbox[0].max) cbox[0] -= (cbox[0].max - bbox[0].max); break; } }; auto update_height = [&](float height) { height = std::min(bbox[1].length(), height); switch (policies_[i].y) { case y_policy::top: cbox[1] = {bbox[1].min, bbox[1].min + height}; break; case y_policy::center: cbox[1] = {m[1] - height / 2.f, m[1] + height / 2.f}; break; case y_policy::bottom: cbox[1] = {bbox[1].max - height, bbox[1].max}; break; case y_policy::fill: cbox[1] = bbox[1]; break; case y_policy::floating: cbox[1] = math::expand(cbox[1], (height - cbox[1].length()) / 2.f); if (cbox[1].min < bbox[1].min) cbox[1] += (bbox[1].min - cbox[1].min); else if (cbox[1].max > bbox[1].max) cbox[1] -= (cbox[1].max - bbox[1].max); break; } }; if (width_first()) { update_width(sc[0].min); auto hc = c->height_constraints(cbox[0].length()); update_height(hc.min); } else { update_height(sc[1].min); auto wc = c->width_constraints(cbox[1].length()); update_width(wc.min); } c->reshape(cbox); } } math::box screen::size_constraints() const { math::box result = element::size_constraints(); for (auto c : children()) if (c) result &= c->size_constraints(); return result; } screen::~screen() { release_children(); } }