Files
panopainter/src/node.cpp
2019-12-01 22:50:44 +01:00

1552 lines
41 KiB
C++

#include "pch.h"
#include "app.h"
#include "log.h"
#include "node.h"
#include "layout.h"
#include "util.h"
#include "asset.h"
#include "node_border.h"
#include "node_image.h"
#include "node_image_texture.h"
#include "node_icon.h"
#include "node_text.h"
#include "node_text_input.h"
#include "node_button.h"
#include "node_button_custom.h"
#include "node_slider.h"
#include "node_popup_menu.h"
#include "node_viewport.h"
#include "node_checkbox.h"
#include "node_panel_layer.h"
#include "node_panel_brush.h"
#include "node_panel_color.h"
#include "node_panel_stroke.h"
#include "node_color_quad.h"
#include "node_stroke_preview.h"
#include "node_canvas.h"
#include "node_scroll.h"
#include "node_dialog_browse.h"
#include "node_dialog_cloud.h"
#include "node_combobox.h"
#include "node_colorwheel.h"
#include "node_dialog_picker.h"
#include "node_panel_grid.h"
#include "node_about.h"
#include "node_changelog.h"
#include "node_usermanual.h"
#include "node_panel_quick.h"
#include "node_tool_bucket.h"
#include "node_panel_animation.h"
#include "node_metadata.h"
void Node::app_redraw()
{
App::I->redraw = true;
App::I->render_cv.notify_all();
}
void Node::watch(std::function<bool(Node*)> observer)
{
bool cont = observer(this);
if (cont)
{
for (auto& c : m_children)
{
//if (!glm::any(glm::lessThanEqual(zw(c->m_clip), { 0, 0 })))
c->watch(observer);
}
}
}
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);
}
kEventResult Node::on_event(Event* e)
{
kEventResult ret = kEventResult::Available;
if (e->m_cat == kEventCategory::KeyEvent && current_key_capture)
return current_key_capture->on_event(e);
if (current_mouse_capture && current_mouse_capture.get() != this)
{
if (e->m_cat == kEventCategory::MouseEvent &&
child_mouse_focus != current_mouse_capture &&
is_child(current_mouse_capture.get()))
{
MouseEvent* me = static_cast<MouseEvent*>(e);
if (child_mouse_focus)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseUnfocus;
child_mouse_focus->handle_event(&e2);
child_mouse_focus->m_mouse_focus = false;
}
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseFocus;
current_mouse_capture->handle_event(&e2);
child_mouse_focus = current_mouse_capture;
child_mouse_focus->m_mouse_focus = true;
}
return current_mouse_capture->on_event(e);
}
bool skip_children = false;
// skip mouse events if outside
if (e->m_cat == kEventCategory::MouseEvent)
{
MouseEvent* me = static_cast<MouseEvent*>(e);
skip_children |= !m_mouse_inside && !point_in_rect(me->m_pos, m_clip);
}
skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) &&
(m_mouse_captured) && (root()->current_mouse_capture.get() == this) && m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed
if (!m_display || glm::any(glm::lessThanEqual(zw(m_clip), { 0, 0 })))
return kEventResult::Available;
if (!skip_children)
{
// make a copy because any handler can change the children vector
auto children_copy = m_children;
for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it)
{
if ((*it)->on_event(e) == kEventResult::Consumed)
{
if (m_flood_events)
{
ret = kEventResult::Consumed;
}
else
{
if (e->m_cat == kEventCategory::MouseEvent && child_mouse_focus.get() != it->get())
{
MouseEvent* me = static_cast<MouseEvent*>(e);
if (child_mouse_focus && !child_mouse_focus->m_destroyed)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseUnfocus;
child_mouse_focus->handle_event(&e2);
child_mouse_focus->m_mouse_focus = false;
}
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseFocus;
(*it)->handle_event(&e2);
if (!(*it)->m_destroyed)
{
child_mouse_focus = *it;
child_mouse_focus->m_mouse_focus = true;
}
//else
//{
// child_mouse_focus = nullptr;
//}
}
ret = kEventResult::Consumed;
break;
}
}
}
if (ret == kEventResult::Consumed)
{
if (e->m_cat == kEventCategory::MouseEvent)
{
MouseEvent* me = static_cast<MouseEvent*>(e);
bool old_inside = m_mouse_inside;
m_mouse_inside = point_in_rect(me->m_pos, m_clip);
if (old_inside == false && m_mouse_inside == true)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseEnter;
handle_event(&e2);
}
if (old_inside == true && m_mouse_inside == false)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseLeave;
handle_event(&e2);
}
}
return kEventResult::Consumed;
}
}
switch (e->m_cat)
{
case kEventCategory::MouseEvent:
{
if (m_mouse_ignore)
break;
MouseEvent* me = static_cast<MouseEvent*>(e);
bool inside = point_in_rect(me->m_pos, m_clip);
bool inside_old = m_mouse_inside;
m_mouse_inside = inside;
switch (e->m_type)
{
case kEventType::MouseScroll:
case kEventType::MouseDownL:
case kEventType::MouseDownR:
case kEventType::MouseUpL:
case kEventType::MouseUpR:
if ((inside || m_mouse_captured) && ((handle_event(e) == kEventResult::Consumed) || m_force_mouse_capture))
return kEventResult::Consumed;
break;
case kEventType::MouseMove:
if (inside_old == false && inside == true)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseEnter;
handle_event(&e2);
}
if (inside || m_mouse_captured)
{
ret = handle_event(e);
if (m_force_mouse_capture)
ret = kEventResult::Consumed;
}
if (inside_old == true && inside == false)
{
MouseEvent e2 = *me;
e2.m_type = kEventType::MouseLeave;
handle_event(&e2);
}
break;
default:
if (handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
break;
}
case kEventCategory::GestureEvent:
{
if (m_mouse_ignore)
break;
GestureEvent* ge = static_cast<GestureEvent*>(e);
bool inside = point_in_rect(ge->m_pos, m_clip);
//bool inside_old = m_mouse_inside;
m_mouse_inside = inside;
if ((inside || m_mouse_captured) && handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
default:
if (handle_event(e) == kEventResult::Consumed)
return kEventResult::Consumed;
break;
}
return ret;
}
kEventResult Node::handle_event(Event* e)
{
return kEventResult::Available;
}
void Node::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
{
}
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;
}
}
}
}
void Node::handle_parent_resize(glm::vec2 old_size, glm::vec2 new_size)
{
}
void Node::create()
{
}
void Node::init()
{
}
void Node::loaded()
{
}
void Node::added(Node* parent)
{
for (auto& c : m_children)
c->added(this);
}
void Node::removed(Node* parent)
{
for (auto p = m_parent; p; p = p->m_parent)
if (p->child_mouse_focus.get() == this)
p->child_mouse_focus = nullptr;
}
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([&]
{
if (n->m_parent)
n->m_parent->remove_child(n);
m_children.emplace_back(n);
n->m_parent = this;
n->set_manager(m_manager);
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node));
n->added(this);
on_child_added(n);
});
}
void Node::add_child(Node* n, int index)
{
App::I->ui_task([&]
{
if (n->m_parent)
n->m_parent->remove_child(n);
m_children.emplace_back(n);
n->m_parent = this;
n->set_manager(m_manager);
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, index);
n->added(this);
on_child_added(n);
});
}
void Node::add_child(std::shared_ptr<Node> n)
{
App::I->ui_task([this,n]
{
if (n->m_parent)
n->m_parent->remove_child(n.get());
m_children.push_back(n);
n->m_parent = this;
n->set_manager(m_manager);
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node));
n->added(this);
on_child_added(n.get());
});
}
void Node::add_child(std::shared_ptr<Node> n, int index)
{
App::I->ui_task([&]
{
if (n->m_parent)
n->m_parent->remove_child(n.get());
m_children.insert(m_children.begin() + index, n);
n->m_parent = this;
n->set_manager(m_manager);
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, index);
n->added(this);
on_child_added(n.get());
});
}
void Node::remove_from_parent()
{
if (m_parent)
m_parent->remove_child(this);
}
void Node::remove_child(Node* n)
{
auto i = std::find_if(m_children.begin(), m_children.end(), [=](auto& ptr) { return ptr.get() == n; });
if (i != m_children.end())
{
App::I->ui_task([&]
{
n->removed(this);
n->m_parent = nullptr;
YGNodeRemoveChild(y_node, n->y_node);
on_child_removed(n);
m_children.erase(i);
if (child_mouse_focus.get() == n)
child_mouse_focus = nullptr;
});
}
}
void Node::remove_all_children()
{
App::I->ui_task([&]
{
for (auto& n : m_children)
{
n->removed(this);
n->m_parent = nullptr;
YGNodeRemoveChild(y_node, n->y_node);
on_child_removed(n.get());
}
m_children.clear();
child_mouse_focus = nullptr;
});
}
void Node::move_child(Node* n, int index)
{
App::I->ui_task([&]
{
int count = YGNodeGetChildCount(y_node);
index = glm::clamp<int>(index, 0, count - 1);
YGNodeRemoveChild(y_node, n->y_node);
YGNodeInsertChild(y_node, n->y_node, index);
auto it = std::find_if(m_children.begin(), m_children.end(),
[n](const std::shared_ptr<Node>& o) { return o.get() == n; });
auto tmp = *it; // copy the ptr before removing it
m_children.erase(it);
m_children.insert(m_children.begin() + index, tmp);
});
}
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;
}
void Node::mouse_capture()
{
if (!m_parent || !m_manager)
return;
auto root = m_manager->get_ref("main");
if (!root) return;
auto& c = root->current_mouse_capture;
auto& s = root->m_capture_stack;
// already owner of capture
if (c.get() == this || std::find_if(s.begin(), s.end(),
[this](const auto& a) { return a.get() == this; }) != s.end())
return;
if (c)
{
if (c->is_child_recursive(this))
{
// save on stack
s.push_back(c);
}
else
{
// cancel previous owner
MouseEvent e;
e.m_type = kEventType::MouseCancel;
c->handle_event(&e);
// TODO: only delete nodes on a different tree,
// so preserve direct parents of this
// also clear the whole stack
//s.clear();
s.push_back(c);
}
}
// make current
c = shared_from_this();
m_mouse_captured = true;
}
void Node::mouse_release()
{
if (!m_parent || !m_manager)
return;
auto root = m_manager->get_ref("main");
if (!root) return;
auto& c = root->current_mouse_capture;
auto& s = root->m_capture_stack;
s.erase(std::remove_if(s.begin(), s.end(),
[this](const auto& a) { return a.get() == this; }), s.end());
if (c.get() == this)
{
if (s.empty())
{
c = nullptr;
}
else
{
c = s.back();
s.pop_back();
}
}
m_mouse_captured = false;
}
void Node::key_capture()
{
if (!m_parent || !m_manager)
return;
auto root = m_manager->get_ref("main");
if (!root) return;
root->current_key_capture = shared_from_this();
m_key_captured = true;
}
void Node::key_release()
{
if (!m_parent || !m_manager)
return;
auto root = m_manager->get_ref("main");
if (!root) return;
if (root->current_key_capture.get() == this)
root->current_key_capture = nullptr;
m_key_captured = false;
}
Node&& Node::operator=(Node&& o)
{
return std::forward<Node>(o);
}
Node::Node()
{
y_node = YGNodeNew();
YGNodeSetContext(y_node, this);
y_placeholder = nullptr;
}
Node::Node(Node&& o)
{
m_name = std::move(o.m_name);
m_nodeID_s = std::move(o.m_nodeID_s);
m_children = std::move(o.m_children);
m_nodeID = o.m_nodeID;
m_display = o.m_display;
m_parent = o.m_parent;
y_node = o.y_node;
m_pos = o.m_pos;
m_size = o.m_size;
m_clip = o.m_clip;
m_zoom = o.m_zoom;
m_mouse_ignore = o.m_mouse_ignore;
o.y_node = nullptr;
o.m_parent = nullptr;
set_manager(o.m_manager);
for (auto& c : m_children)
c->m_parent = this;
current_mouse_capture = o.current_mouse_capture;
current_key_capture = o.current_key_capture;
m_mouse_captured = o.m_mouse_captured;
m_key_captured = o.m_key_captured;
m_proj = o.m_proj;
m_mvp = o.m_mvp;
m_mouse_inside = o.m_mouse_inside;
m_flood_events = o.m_flood_events;
m_force_mouse_capture = o.m_force_mouse_capture;
m_capture_children = o.m_capture_children;
m_destroyed = false;// o.m_destroyed;
m_scale = o.m_scale;
m_pos_origin = o.m_pos_origin;
m_pos_offset = o.m_pos_offset;
m_pos_offset_childred = o.m_pos_offset_childred;
m_clip_uncut = o.m_clip_uncut;
auto_width = o.auto_width;
auto_height = o.auto_height;
}
Node::~Node()
{
m_children.clear();
if (y_node)
YGNodeFree(y_node);
if (y_placeholder)
YGNodeFree(y_placeholder);
}
void Node::SetWidth(float value)
{
YGNodeStyleSetWidth(y_node, value);
m_size.x = value;
auto_width = value == YGUndefined;
app_redraw();
}
void Node::SetWidthP(float value)
{
YGNodeStyleSetWidthPercent(y_node, value);
auto_width = value == YGUndefined;
app_redraw();
}
void Node::SetHeight(float value)
{
YGNodeStyleSetHeight(y_node, value);
m_size.y = value;
auto_height = value == YGUndefined;
app_redraw();
}
void Node::SetHeightP(float value)
{
YGNodeStyleSetHeightPercent(y_node, value);
auto_height = value == YGUndefined;
app_redraw();
}
void Node::SetSize(float w, float h)
{
SetWidth(w); SetHeight(h);
m_size = {w, h};
app_redraw();
}
void Node::SetSize(glm::vec2 value)
{
SetWidth(value.x); SetHeight(value.y);
m_size = value;
app_redraw();
}
void Node::SetMinSize(float w, float h)
{
SetMinWidth(w);
SetMinHeight(h);
}
void Node::SetMinSize(glm::vec2 value)
{
SetMinWidth(value.x);
SetMinHeight(value.y);
}
void Node::SetMaxSize(float w, float h)
{
SetMaxWidth(w);
SetMaxHeight(h);
}
void Node::SetMaxSize(glm::vec2 value)
{
SetMaxWidth(value.x);
SetMaxHeight(value.y);
}
void Node::SetMaxWidth(float value)
{
YGNodeStyleSetMaxWidth(y_node, value);
app_redraw();
}
void Node::SetMaxWidthP(float value)
{
YGNodeStyleSetMaxWidthPercent(y_node, value);
app_redraw();
}
void Node::SetMaxHeight(float value)
{
YGNodeStyleSetMaxHeight(y_node, value);
app_redraw();
}
void Node::SetMaxHeightP(float value)
{
YGNodeStyleSetMaxHeightPercent(y_node, value);
app_redraw();
}
void Node::SetMinWidth(float value)
{
YGNodeStyleSetMinWidth(y_node, value);
app_redraw();
}
void Node::SetMinWidthP(float value)
{
YGNodeStyleSetMinWidthPercent(y_node, value);
app_redraw();
}
void Node::SetMinHeight(float value)
{
YGNodeStyleSetMinHeight(y_node, value);
app_redraw();
}
void Node::SetMinHeightP(float value)
{
YGNodeStyleSetMinHeightPercent(y_node, value);
app_redraw();
}
void Node::SetPadding(float t, float r, float b, float l)
{
YGNodeStyleSetPadding(y_node, YGEdgeTop, t);
YGNodeStyleSetPadding(y_node, YGEdgeRight, r);
YGNodeStyleSetPadding(y_node, YGEdgeBottom, b);
YGNodeStyleSetPadding(y_node, YGEdgeLeft, l);
app_redraw();
}
glm::vec4 Node::GetPadding() const
{
float t = YGNodeLayoutGetPadding(y_node, YGEdgeTop);
float r = YGNodeLayoutGetPadding(y_node, YGEdgeRight);
float b = YGNodeLayoutGetPadding(y_node, YGEdgeBottom);
float l = YGNodeLayoutGetPadding(y_node, YGEdgeLeft);
return{ t, r, b, l };
}
void Node::SetMargin(float t, float r, float b, float l)
{
YGNodeStyleSetMargin(y_node, YGEdgeTop, t);
YGNodeStyleSetMargin(y_node, YGEdgeRight, r);
YGNodeStyleSetMargin(y_node, YGEdgeBottom, b);
YGNodeStyleSetMargin(y_node, YGEdgeLeft, l);
app_redraw();
}
glm::vec4 Node::GetMargin() const
{
float t = YGNodeLayoutGetMargin(y_node, YGEdgeTop);
float r = YGNodeLayoutGetMargin(y_node, YGEdgeRight);
float b = YGNodeLayoutGetMargin(y_node, YGEdgeBottom);
float l = YGNodeLayoutGetMargin(y_node, YGEdgeLeft);
return{ t, r, b, l };
}
void Node::SetPosition(const glm::vec2 pos)
{
SetPosition(pos.x, pos.y);
m_pos = pos;
app_redraw();
}
void Node::SetPosition(float l, float t)
{
YGNodeStyleSetPosition(y_node, YGEdgeTop, t);
YGNodeStyleSetPosition(y_node, YGEdgeLeft, l);
m_pos = {l, t};
app_redraw();
}
void Node::SetPosition(float l, float t, float r, float b)
{
YGNodeStyleSetPosition(y_node, YGEdgeTop, t);
YGNodeStyleSetPosition(y_node, YGEdgeRight, r);
YGNodeStyleSetPosition(y_node, YGEdgeBottom, b);
YGNodeStyleSetPosition(y_node, YGEdgeLeft, l);
app_redraw();
}
void Node::SetFlexGrow(float value)
{
YGNodeStyleSetFlexGrow(y_node, value);
app_redraw();
}
void Node::SetFlexShrink(float value)
{
YGNodeStyleSetFlexShrink(y_node, value);
app_redraw();
}
void Node::SetFlexDir(YGFlexDirection value)
{
YGNodeStyleSetFlexDirection(y_node, value);
app_redraw();
}
void Node::SetFlexWrap(YGWrap value)
{
YGNodeStyleSetFlexWrap(y_node, value);
app_redraw();
}
void Node::SetJustify(YGJustify value)
{
YGNodeStyleSetJustifyContent(y_node, value);
app_redraw();
}
void Node::SetAlign(YGAlign value)
{
YGNodeStyleSetAlignItems(y_node, value);
app_redraw();
}
void Node::SetPositioning(YGPositionType value)
{
YGNodeStyleSetPositionType(y_node, value);
app_redraw();
}
void Node::SetAspectRatio(float ar)
{
YGNodeStyleSetAspectRatio(y_node, ar);
app_redraw();
}
void Node::SetRTL(YGDirection dir)
{
YGNodeStyleSetDirection(y_node, dir);
app_redraw();
}
bool Node::GetVisibility()
{
return m_display;
}
void Node::SetVisibility(bool visible)
{
App::I->ui_task([&]
{
if (m_display && !visible)
{
// hide
int idx = m_parent->get_child_index(this);
YGNodeRemoveChild(m_parent->y_node, y_node);
y_placeholder = YGNodeNew();
YGNodeInsertChild(m_parent->y_node, y_placeholder, idx);
}
else if (!m_display && visible)
{
int count = YGNodeGetChildCount(m_parent->y_node);
for (int i = 0; i < count; i++)
{
if (YGNodeGetChild(m_parent->y_node, i) == y_placeholder)
{
YGNodeRemoveChild(m_parent->y_node, y_placeholder);
YGNodeInsertChild(m_parent->y_node, y_node, i);
YGNodeFree(y_placeholder);
y_placeholder = nullptr;
break;
}
}
}
});
m_display = visible;
app_redraw();
}
void Node::ToggleVisibility()
{
SetVisibility(!m_display);
}
glm::vec2 Node::GetPosition()
{
return { YGNodeLayoutGetLeft(y_node), YGNodeLayoutGetTop(y_node) };
}
float Node::GetWidth()
{
return YGNodeLayoutGetWidth(y_node);
}
float Node::GetHeight()
{
return YGNodeLayoutGetHeight(y_node);
}
glm::vec2 Node::GetSize()
{
return{ GetWidth(), GetHeight() };
}
YGDirection Node::GetRTL()
{
return YGNodeStyleGetDirection(y_node);
}
void Node::restore_context()
{
for (auto& c : m_children)
c->restore_context();
}
void Node::clear_context()
{
for (auto& c : m_children)
c->clear_context();
}
void Node::update(float width, float height, float zoom)
{
m_zoom = zoom;
YGNodeStyleSetWidth(y_node, (width / zoom));
YGNodeStyleSetHeight(y_node, (height / zoom));
YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
m_proj = glm::ortho(0.f, (width / zoom), (height / zoom), 0.f, -1.f, 1.f);
update_internal({ 0, 0 }, m_proj, zoom);
}
void Node::update()
{
YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
update_internal({ 0, 0 }, m_proj, m_zoom);
}
void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj, float zoom)
{
float x = YGNodeLayoutGetLeft(y_node);
float y = YGNodeLayoutGetTop(y_node);
float w = YGNodeLayoutGetWidth(y_node);
float h = YGNodeLayoutGetHeight(y_node);
auto old_size = m_size;
if (YGNodeStyleGetWidth(y_node).unit == YGUnit::YGUnitAuto)
w -= m_pos_offset.x;
//if (YGNodeStyleGetHeight(y_node).unit == YGUnit::YGUnitAuto)
// h -= m_pos_offset.y;
m_pos = /*glm::floor*/(origin + glm::vec2(x, y) + m_pos_offset);
m_pos_origin = /*glm::floor*/(origin + glm::vec2(x, y));
m_size = /*glm::floor*/(glm::vec2(w, h));
if (m_parent)
{
// correct the padding clip
// should not clip the padded area
// useful to draw decorations
float pt = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeTop);
float pr = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeRight);
float pb = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeBottom);
float pl = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeLeft);
glm::vec2 off_p(pl, pt);
glm::vec2 off_s(pr, pb);
glm::vec4 pclip = { xy(m_parent->m_clip) + off_p, zw(m_parent->m_clip) - (off_s + off_p) };
m_clip_uncut = glm::vec4(m_pos - glm::vec2(1), m_size + glm::vec2(2));
m_clip = rect_intersection(m_clip_uncut, m_parent->m_clip);
}
else
{
m_clip_uncut = m_clip = glm::vec4(m_pos, m_size);
}
glm::mat4 pivot = glm::translate(glm::vec3(.5f, .5f, 0.f));
glm::mat4 scale = glm::scale(glm::vec3(m_size, 1.f));
glm::mat4 prescale = glm::scale(glm::vec3(m_scale, 1.f));
glm::mat4 pos = glm::translate(glm::vec3(m_pos, 0));
m_mvp = proj * pos * scale * pivot * prescale;
m_proj = proj;
if (!glm::any(glm::isnan(m_size)) && m_size != old_size || m_zoom != zoom)
{
m_zoom = zoom;
handle_resize(old_size, m_size, zoom);
for (auto& c : m_children)
c->handle_parent_resize(old_size, m_size);
}
for (auto& c : m_children)
{
c->m_pos_offset = m_pos_offset + m_pos_offset_childred;
c->update_internal(m_pos_origin, proj, zoom);
}
m_children.erase(std::remove_if(m_children.begin(), m_children.end(),
[](const auto& n) { return n->m_destroyed; }), m_children.end());
}
void Node::tick(float dt)
{
for (auto& c : m_children)
c->tick(dt);
on_tick(dt);
}
void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
switch (ka)
{
case kAttribute::id:
m_nodeID_s = attr->Value();
m_nodeID = const_hash(attr->Value());
break;
case kAttribute::Width:
if (strcmp(attr->Value(), "auto") == 0)
{
YGNodeStyleSetWidth(y_node, YGUndefined);
YGNodeStyleSetWidthPercent(y_node, YGUndefined);
auto_width = true;
}
else
{
if (strchr(attr->Value(), '%'))
YGNodeStyleSetWidthPercent(y_node, attr->FloatValue());
else
YGNodeStyleSetWidth(y_node, attr->FloatValue());
auto_width = false;
}
break;
case kAttribute::MinWidth:
YGNodeStyleSetMinWidth(y_node, attr->FloatValue());
break;
case kAttribute::MaxWidth:
YGNodeStyleSetMaxWidth(y_node, attr->FloatValue());
break;
case kAttribute::Height:
if (strcmp(attr->Value(), "auto") == 0)
{
YGNodeStyleSetHeight(y_node, YGUndefined);
YGNodeStyleSetHeightPercent(y_node, YGUndefined);
auto_height = true;
}
else
{
if (strchr(attr->Value(), '%'))
YGNodeStyleSetHeightPercent(y_node, attr->FloatValue());
else
YGNodeStyleSetHeight(y_node, attr->FloatValue());
auto_height = false;
}
break;
case kAttribute::MinHeight:
YGNodeStyleSetMinHeight(y_node, attr->FloatValue());
break;
case kAttribute::MaxHeight:
YGNodeStyleSetMaxHeight(y_node, attr->FloatValue());
break;
case kAttribute::Grow:
YGNodeStyleSetFlexGrow(y_node, attr->FloatValue());
break;
case kAttribute::Shrink:
YGNodeStyleSetFlexShrink(y_node, attr->FloatValue());
break;
case kAttribute::FlexDir:
{
YGFlexDirection dir = YGFlexDirectionRow;
if (strcmp("col", attr->Value()) == 0)
dir = YGFlexDirectionColumn;
else if (strcmp("col-reverse", attr->Value()) == 0)
dir = YGFlexDirectionColumnReverse;
else if (strcmp("row", attr->Value()) == 0)
dir = YGFlexDirectionRow;
else if (strcmp("row-reverse", attr->Value()) == 0)
dir = YGFlexDirectionRowReverse;
YGNodeStyleSetFlexDirection(y_node, dir);
break;
}
case kAttribute::FlexWrap:
YGNodeStyleSetFlexWrap(y_node, attr->IntValue() ? YGWrapWrap : YGWrapNoWrap);
break;
case kAttribute::Justify:
{
YGJustify v = YGJustifyFlexStart;
if (strcmp("center", attr->Value()) == 0)
v = YGJustifyCenter;
else if (strcmp("flex-start", attr->Value()) == 0)
v = YGJustifyFlexStart;
else if (strcmp("flex-end", attr->Value()) == 0)
v = YGJustifyFlexEnd;
else if (strcmp("space-around", attr->Value()) == 0)
v = YGJustifySpaceAround;
else if (strcmp("space-between", attr->Value()) == 0)
v = YGJustifySpaceBetween;
YGNodeStyleSetJustifyContent(y_node, v);
break;
}
case kAttribute::Align:
{
YGAlign v = YGAlignStretch;
if (strcmp("stretch", attr->Value()) == 0)
v = YGAlignStretch;
else if (strcmp("flex-start", attr->Value()) == 0)
v = YGAlignFlexStart;
else if (strcmp("flex-end", attr->Value()) == 0)
v = YGAlignFlexEnd;
else if (strcmp("center", attr->Value()) == 0)
v = YGAlignCenter;
YGNodeStyleSetAlignItems(y_node, v);
break;
}
case kAttribute::Positioning:
{
YGPositionType v = YGPositionTypeRelative;
if (strcmp("relative", attr->Value()) == 0)
v = YGPositionTypeRelative;
else if (strcmp("absolute", attr->Value()) == 0)
v = YGPositionTypeAbsolute;
YGNodeStyleSetPositionType(y_node, v);
break;
}
case kAttribute::Position:
{
glm::vec4 v;
int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w);
if (n == 2)
{
YGNodeStyleSetPosition(y_node, YGEdgeLeft, v.x);
YGNodeStyleSetPosition(y_node, YGEdgeTop, v.y);
}
else
{
YGNodeStyleSetPadding(y_node, YGEdgeLeft, v.x);
YGNodeStyleSetPadding(y_node, YGEdgeTop, v.y);
YGNodeStyleSetPadding(y_node, YGEdgeRight, v.z);
YGNodeStyleSetPadding(y_node, YGEdgeBottom, v.w);
}
break;
}
case kAttribute::Padding:
{
glm::vec4 pad;
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
if (n == 1)
{
YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x);
YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.x);
YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.x);
YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.x);
}
else
{
YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x);
YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.y);
YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.z);
YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.w);
}
break;
}
case kAttribute::Margin:
{
glm::vec4 pad;
int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w);
if (n == 1)
{
YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x);
YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.x);
YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.x);
YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.x);
}
else
{
YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x);
YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.y);
YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.z);
YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.w);
}
break;
}
case kAttribute::FloodEvents:
m_flood_events = attr->BoolValue();
break;
case kAttribute::MouseCapture:
m_force_mouse_capture = attr->BoolValue();
break;
case kAttribute::AspectRatio:
YGNodeStyleSetAspectRatio(y_node, attr->FloatValue());
break;
case kAttribute::RTL:
if (strcmp("rtl", attr->Value()) == 0)
SetRTL(YGDirectionRTL);
else if (strcmp("ltr", attr->Value()) == 0)
SetRTL(YGDirectionLTR);
else if (strcmp("inherit", attr->Value()) == 0)
SetRTL(YGDirectionInherit);
else
{
LOG("Attribute %s for RTL unrecognized", attr->Value());
}
default:
break;
}
}
void Node::load_internal(const tinyxml2::XMLElement* x_node, bool skip_children /*= false*/)
{
m_name = x_node->Name();
//LOG("load_internal node %s", m_name.c_str());
init();
auto attr = x_node->FirstAttribute();
while (attr)
{
parse_attributes((kAttribute)const_hash(attr->Name()), attr);
attr = attr->Next();
}
create();
if (skip_children)
{
loaded();
return;
}
auto x_child = x_node->FirstChildElement();
while (x_child)
{
if (auto os = x_child->Attribute("os"))
{
auto osv = split(os, ',');
if (std::find(osv.begin(), osv.end(), PP_OS) == osv.end())
{
LOG("Element %s not for this os(%s), skipping", x_child->Name(), PP_OS)
x_child = x_child->NextSiblingElement();
continue;
}
}
std::string node_name = x_child->Name();
if (node_name == "ref")
{
auto ids = x_child->Attribute("id");
auto id = const_hash(ids);
auto& ref = (*m_manager)[id]->m_children[0];
auto n = ref->clone();
n->m_nodeID_s = ids;
n->m_nodeID = id;
add_child(n);
}
else if (node_name == "text")
{
auto n = new NodeText();
add_child(n);
n->load_internal(x_child, true);
std::string text;
auto node = x_child->FirstChild();
while (node)
{
if (auto e = node->ToElement())
{
if (strcmp(e->Name(), "br") == 0)
text.append("\n");
}
else if (auto t = node->ToText())
{
text.append(t->Value());
}
node = node->NextSibling();
}
if (!text.empty())
n->set_text(text);
}
#define CASE(W,C) else if (node_name == W) { auto n = new C(); add_child(n); n->load_internal(x_child); }
CASE("node", Node)
CASE("border", NodeBorder)
CASE("image", NodeImage)
CASE("image-texture", NodeImageTexture)
CASE("icon", NodeIcon)
CASE("text-input", NodeTextInput)
CASE("button", NodeButton)
CASE("button-custom", NodeButtonCustom)
CASE("combobox", NodeComboBox)
CASE("slider-h", NodeSliderH)
CASE("slider-v", NodeSliderV)
CASE("slider-hue", NodeSliderHue)
CASE("popup-menu", NodePopupMenu)
CASE("viewport", NodeViewport)
CASE("checkbox", NodeCheckBox)
CASE("layer", NodeLayer)
CASE("panel-layer", NodePanelLayer)
CASE("panel-brush", NodePanelBrush)
CASE("panel-color", NodePanelColor)
CASE("panel-stroke", NodePanelStroke)
CASE("panel-grid", NodePanelGrid)
CASE("panel-quick", NodePanelQuick)
CASE("dialog-browse", NodeDialogBrowse)
CASE("dialog-browse-item", NodeDialogBrowseItem)
CASE("dialog-cloud", NodeDialogCloud)
CASE("dialog-cloud-item", NodeDialogCloudItem)
CASE("color-picker", NodeColorPicker)
CASE("about", NodeAbout)
CASE("changelog", NodeChangelog)
CASE("usermanual", NodeUserManual)
CASE("tool-bucket", NodeToolBucket)
CASE("timeline", NodeAnimationTimeline)
CASE("stroke-preview", NodeStrokePreview)
CASE("canvas", NodeCanvas)
CASE("scroll", NodeScroll)
CASE("metadata", NodeMetadata)
CASE("panel-quick", NodePanelQuick)
CASE("colorwheel", NodeColorWheel)
CASE("color-quad", NodeColorQuad)
#undef CASE
else
{
LOG("instancing UNKNOWN node: %s", x_child->Name());
auto n = new Node();
add_child(n);
n->load_internal(x_child);
}
x_child = x_child->NextSiblingElement();
}
loaded();
}
void Node::draw()
{
}
Node* Node::clone_instantiate() const
{
return new Node();
}
void Node::clone_copy(Node* dest) const
{
YGNodeCopyStyle(dest->y_node, y_node);
dest->set_manager(m_manager);
dest->m_name = m_name;
dest->m_nodeID_s = m_nodeID_s;
dest->m_nodeID = m_nodeID;
dest->m_display = m_display;
dest->m_pos = m_pos;
dest->m_size = m_size;
dest->m_clip = m_clip;
dest->m_flood_events = m_flood_events;
dest->m_force_mouse_capture = m_force_mouse_capture;
dest->current_mouse_capture = current_mouse_capture;
dest->current_key_capture = current_key_capture;
dest->m_mouse_captured = m_mouse_captured;
dest->m_key_captured = m_key_captured;
dest->m_proj = m_proj;
dest->m_mvp = m_mvp;
dest->m_mouse_inside = m_mouse_inside;
dest->m_capture_children = m_capture_children;
dest->m_destroyed = false;// m_destroyed;
dest->m_scale = m_scale;
dest->m_pos_origin = m_pos_origin;
dest->m_pos_offset = m_pos_offset;
dest->m_pos_offset_childred = m_pos_offset_childred;
dest->m_clip_uncut = m_clip_uncut;
dest->auto_width = auto_width;
dest->auto_height = auto_height;
}
void Node::clone_children(Node* dest) const
{
for (auto& c : m_children)
{
std::shared_ptr<Node> cn = c->clone();
dest->m_children.push_back(cn);
cn->m_parent = dest;
cn->set_manager(dest->m_manager);
cn->loaded();
YGNodeInsertChild(dest->y_node, cn->y_node, YGNodeGetChildCount(dest->y_node));
}
}
void Node::clone_finalize(Node* dest) const
{
/* find controllers and stuff */
}