Extract app runtime shell, canvas render shell, and node tree services

This commit is contained in:
2026-06-16 20:36:47 +02:00
parent a05afb24f3
commit c25af6f493
10 changed files with 1450 additions and 1251 deletions

View File

@@ -0,0 +1,377 @@
#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;
}