378 lines
8.9 KiB
C++
378 lines
8.9 KiB
C++
#include "pch.h"
|
|
#include "app.h"
|
|
#include "layout.h"
|
|
#include "node.h"
|
|
|
|
namespace
|
|
{
|
|
using NodeChildren = std::vector<std::shared_ptr<Node>>;
|
|
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<Node>& candidate) { return candidate.get() == child; });
|
|
}
|
|
|
|
template <typename AttachChildrenFn>
|
|
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 <typename AttachChildrenFn>
|
|
void attach_child(Node& parent, const std::shared_ptr<Node>& 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<int>(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<Node*>(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> Node::load_template(const std::string& filename, const std::string& name) const
|
|
{
|
|
LayoutManager m;
|
|
std::shared_ptr<Node> 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> Node::parse_template(const std::string& xml_string, const std::string& name) const
|
|
{
|
|
LayoutManager m;
|
|
std::shared_ptr<Node> 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<Node> n)
|
|
{
|
|
App::I->ui_task([this, n]
|
|
{
|
|
attach_child(*this, n, YGNodeGetChildCount(y_node),
|
|
[this](const std::shared_ptr<Node>& child)
|
|
{
|
|
m_children.push_back(child);
|
|
});
|
|
});
|
|
}
|
|
|
|
void Node::add_child(std::shared_ptr<Node> n, int index)
|
|
{
|
|
App::I->ui_task([&]
|
|
{
|
|
attach_child(*this, n, index,
|
|
[this, index](const std::shared_ptr<Node>& 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<std::shared_ptr<Node>> Node::get_children_at_point(glm::vec2 point) const
|
|
{
|
|
std::vector<std::shared_ptr<Node>> 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;
|
|
}
|