#include "pch.h" #include "app.h" #include "layout.h" #include "node.h" namespace { using NodeChildren = std::vector>; using NodeChildIterator = NodeChildren::iterator; NodeChildIterator find_child_iterator(Node& parent, Node* child) { return std::find_if(parent.m_children.begin(), parent.m_children.end(), [child](const std::shared_ptr& candidate) { return candidate.get() == child; }); } template void attach_child(Node& parent, Node* child, int yoga_index, AttachChildrenFn&& attach_children) { if (child->m_parent) child->m_parent->remove_child(child); attach_children(child); child->m_parent = &parent; child->set_manager(parent.m_manager); child->m_destroyed = false; YGNodeInsertChild(parent.y_node, child->y_node, yoga_index); child->added(&parent); parent.on_child_added(child); } template void attach_child(Node& parent, const std::shared_ptr& child, int yoga_index, AttachChildrenFn&& attach_children) { if (child->m_parent) child->m_parent->remove_child(child.get()); attach_children(child); child->m_parent = &parent; child->set_manager(parent.m_manager); child->m_destroyed = false; YGNodeInsertChild(parent.y_node, child->y_node, yoga_index); child->added(&parent); parent.on_child_added(child.get()); } void detach_child(Node& parent, NodeChildIterator child_it) { Node* child = child_it->get(); child->removed(&parent); child->m_parent = nullptr; YGNodeRemoveChild(parent.y_node, child->y_node); parent.on_child_removed(child); parent.m_children.erase(child_it); if (parent.child_mouse_focus.get() == child) parent.child_mouse_focus = nullptr; } void detach_all_children(Node& parent) { for (auto& child : parent.m_children) { child->removed(&parent); child->m_parent = nullptr; YGNodeRemoveChild(parent.y_node, child->y_node); parent.on_child_removed(child.get()); } parent.m_children.clear(); parent.child_mouse_focus = nullptr; } void reorder_child(Node& parent, Node* child, int index) { int count = YGNodeGetChildCount(parent.y_node); index = glm::clamp(index, 0, count - 1); YGNodeRemoveChild(parent.y_node, child->y_node); YGNodeInsertChild(parent.y_node, child->y_node, index); auto child_it = find_child_iterator(parent, child); auto moved_child = *child_it; parent.m_children.erase(child_it); parent.m_children.insert(parent.m_children.begin() + index, moved_child); } } void Node::destroy() { //m_destroyed = true; //mouse_release(); //key_release(); //for (auto& c : m_children) // c->destroy(); //for (auto p = m_parent; p; p = p->m_parent) // if (p->child_mouse_focus.get() == this) // p->child_mouse_focus = nullptr; App::I->ui_task([&] { mouse_release(); key_release(); auto cp = m_children; for (auto& c : cp) c->destroy(); for (auto p = m_parent; p; p = p->m_parent) if (p->child_mouse_focus.get() == this) p->child_mouse_focus = nullptr; remove_all_children(); remove_from_parent(); }); } Node* Node::root() { Node* ret = this; while (ret->m_parent) ret = ret->m_parent; return ret; } void Node::set_manager(LayoutManager* manager) { m_manager = manager; for (auto& c : m_children) c->set_manager(manager); } bool Node::added_to_root() { auto r = root(); return r == m_manager->get(App::I->main_id); } void Node::handle_on_screen(bool old_visibility, bool new_visibility) { if (new_visibility == false) { for (auto& c : m_children) { if (c->m_on_screen) { c->handle_on_screen(true, false); c->m_on_screen = false; } } } } const Node* Node::init_template(const char* id) { Node* t = nullptr; App::I->ui_task([&] { auto hid = const_hash(id); Node* top = m_manager->get(hid); t = static_cast(top->m_children[0].get()); t->set_manager(m_manager); for (auto& c : t->m_children) { auto node = c->clone(); add_child(node); } YGNodeCopyStyle(y_node, t->y_node); t->clone_copy(this); }); return t; } bool Node::init_template_file(const std::string& path, const std::string& id) { bool ret = false; App::I->ui_task([&] { LayoutManager m; if (m.load(path.c_str())) { auto t = m.get_ref(id.c_str())->m_children[0]; t->set_manager(m_manager); for (auto& c : t->m_children) add_child(c->clone()); YGNodeCopyStyle(y_node, t->y_node); t->clone_copy(this); ret = true; } }); return ret; } std::shared_ptr Node::load_template(const std::string& filename, const std::string& name) const { LayoutManager m; std::shared_ptr ret; if (m.load(filename.c_str())) ret = m.get_ref(name.c_str())->m_children[0]->clone(); m.unload(); return ret; } std::shared_ptr Node::parse_template(const std::string& xml_string, const std::string& name) const { LayoutManager m; std::shared_ptr ret; if (m.parse(xml_string)) ret = m.get_ref(name.c_str())->m_children[0]->clone(); m.unload(); return ret; } void Node::add_child(Node* n) { App::I->ui_task([&] { attach_child(*this, n, YGNodeGetChildCount(y_node), [this](Node* child) { m_children.emplace_back(child); }); }); } void Node::add_child(Node* n, int index) { App::I->ui_task([&] { attach_child(*this, n, index, [this](Node* child) { // Preserve the current backing-vector behavior for raw-pointer inserts. m_children.emplace_back(child); }); }); } void Node::add_child(std::shared_ptr n) { App::I->ui_task([this, n] { attach_child(*this, n, YGNodeGetChildCount(y_node), [this](const std::shared_ptr& child) { m_children.push_back(child); }); }); } void Node::add_child(std::shared_ptr n, int index) { App::I->ui_task([&] { attach_child(*this, n, index, [this, index](const std::shared_ptr& child) { m_children.insert(m_children.begin() + index, child); }); }); } void Node::remove_from_parent() { if (m_parent) m_parent->remove_child(this); } void Node::remove_child(Node* n) { auto i = find_child_iterator(*this, n); if (i != m_children.end()) { App::I->ui_task([&] { detach_child(*this, i); }); } } void Node::remove_all_children() { App::I->ui_task([&] { detach_all_children(*this); }); } void Node::move_child(Node* n, int index) { App::I->ui_task([&] { reorder_child(*this, n, index); }); } void Node::move_child_front(Node* n) { int count = YGNodeGetChildCount(y_node); move_child(n, count - 1); } void Node::move_child_offset(Node* n, int offset) { int idx = get_child_index(n); move_child(n, idx + offset); } int Node::get_child_index(Node* n) { int count = YGNodeGetChildCount(y_node); for (int i = 0; i < count; i++) { if (YGNodeGetChild(y_node, i) == n->y_node) { return i; } } return -1; } Node* Node::get_child_at(int index) { auto n = YGNodeGetChild(y_node, index); for (auto& c : m_children) { if (c->y_node == n) return c.get(); } return nullptr; } glm::vec4 Node::get_children_rect() const { if (m_children.empty()) return glm::vec4(0); glm::vec4 ret = m_children[0]->m_clip_uncut; for (auto& c : m_children) { if (c->m_display) { ret = rect_union(ret, c->m_clip_uncut); auto r = c->get_children_rect(); if (r.w > 0 && r.z > 0) ret = rect_union(ret, r); } } return ret; } std::vector> Node::get_children_at_point(glm::vec2 point) const { std::vector> ret; for (const auto& c : m_children) { if (point_in_rect(point, c->m_clip)) { ret.push_back(c); auto c_ret = c->get_children_at_point(point); ret.insert(ret.end(), c_ret.begin(), c_ret.end()); } } return ret; } bool Node::is_child_recursive(Node* o) const { for (const auto& c : m_children) { if (c.get() == o || c->is_child_recursive(o)) return true; } return false; } bool Node::is_child(Node* o) const { for (const auto& c : m_children) { if (c.get() == o) return true; } return false; }