Files
panopainter/src/node.cpp
2019-09-17 13:57:37 +02:00

1411 lines
38 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"
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;
}
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)
{
}
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* m_template = nullptr;
App::I->ui_task([&]
{
auto hid = const_hash(id);
Node* top = m_manager->get(hid);
m_template = static_cast<Node*>(top->m_children[0].get());
for (auto& c : m_template->m_children)
{
auto node = c->clone();
add_child(node);
//node->init();
//node->create();
//node->loaded();
}
YGNodeCopyStyle(y_node, m_template->y_node);
m_template->clone_copy(this);
});
return m_template;
}
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->m_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->m_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([&]
{
if (n->m_parent)
n->m_parent->remove_child(n.get());
m_children.push_back(n);
n->m_parent = this;
n->m_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->m_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([&]
{
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)
{
App::I->ui_task([&]
{
int count = YGNodeGetChildCount(y_node);
move_child(n, count - 1);
});
}
void Node::move_child_offset(Node* n, int offset)
{
App::I->ui_task([&]
{
int count = YGNodeGetChildCount(y_node);
for (int i = 0; i < count; i++)
{
if (YGNodeGetChild(y_node, i) == n->y_node)
{
int new_index = glm::clamp<int>(i + offset, 0, count - 1);
YGNodeRemoveChild(y_node, n->y_node);
YGNodeInsertChild(y_node, n->y_node, new_index);
break;
}
}
});
}
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);
}
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()
{
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)
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)
return;
root()->current_key_capture = shared_from_this();
m_key_captured = true;
}
void Node::key_release()
{
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);
for (auto& c : m_children)
c->m_parent = this;
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;
m_manager = o.m_manager;
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;
}
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;
app_redraw();
}
void Node::SetWidthP(float value)
{
YGNodeStyleSetWidthPercent(y_node, value);
app_redraw();
}
void Node::SetHeight(float value)
{
YGNodeStyleSetHeight(y_node, value);
m_size.y = value;
app_redraw();
}
void Node::SetHeightP(float value)
{
YGNodeStyleSetHeightPercent(y_node, value);
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::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();
}
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, ceilf(width / zoom));
YGNodeStyleSetHeight(y_node, ceilf(height / zoom));
YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
m_proj = glm::ortho(0.f, ceilf(width / zoom), ceilf(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;
glm::vec2 parent_offset = m_parent ? m_parent->m_pos_offset_childred : glm::vec2(0.f);
m_pos = /*glm::floor*/(origin + glm::vec2(x, y) + m_pos_offset + parent_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->update_internal(m_pos, 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);
}
else
{
if (strchr(attr->Value(), '%'))
YGNodeStyleSetWidthPercent(y_node, attr->FloatValue());
else
YGNodeStyleSetWidth(y_node, attr->FloatValue());
}
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);
}
else
{
if (strchr(attr->Value(), '%'))
YGNodeStyleSetHeightPercent(y_node, attr->FloatValue());
else
YGNodeStyleSetHeight(y_node, attr->FloatValue());
}
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)
{
m_name = x_node->Name();
//LOG("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();
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;
}
}
kWidget child_id = (kWidget)const_hash(x_child->Name());
switch (child_id)
{
#define CASE(W,C) case W: { auto n = new C(); add_child(n); n->load_internal(x_child); break; }
CASE(kWidget::Node, Node);
CASE(kWidget::Border, NodeBorder);
CASE(kWidget::Image, NodeImage);
CASE(kWidget::ImageTexture, NodeImageTexture);
CASE(kWidget::Icon, NodeIcon);
CASE(kWidget::Text, NodeText);
CASE(kWidget::TextInput, NodeTextInput);
CASE(kWidget::Button, NodeButton);
CASE(kWidget::ButtonCustom, NodeButtonCustom);
CASE(kWidget::ComboBox, NodeComboBox);
CASE(kWidget::SliderH, NodeSliderH);
CASE(kWidget::SliderV, NodeSliderV);
CASE(kWidget::SliderHue, NodeSliderHue);
CASE(kWidget::PopupMenu, NodePopupMenu);
CASE(kWidget::Viewport, NodeViewport);
CASE(kWidget::CheckBox, NodeCheckBox);
CASE(kWidget::Layer, NodeLayer);
CASE(kWidget::PanelLayer, NodePanelLayer);
CASE(kWidget::PanelBrush, NodePanelBrush);
CASE(kWidget::PanelColor, NodePanelColor);
CASE(kWidget::PanelStroke, NodePanelStroke);
CASE(kWidget::PanelGrid, NodePanelGrid);
CASE(kWidget::PanelQuick, NodePanelQuick);
CASE(kWidget::ColorQuad, NodeColorQuad);
CASE(kWidget::StrokePreview, NodeStrokePreview);
CASE(kWidget::Canvas, NodeCanvas);
CASE(kWidget::Scroll, NodeScroll);
CASE(kWidget::DialogBrowse, NodeDialogBrowse);
CASE(kWidget::DialogBrowseItem, NodeDialogBrowseItem);
CASE(kWidget::DialogCloud, NodeDialogCloud);
CASE(kWidget::DialogCloudItem, NodeDialogCloudItem);
CASE(kWidget::ColorWheel, NodeColorWheel);
CASE(kWidget::ColorPicker, NodeColorPicker);
CASE(kWidget::About, NodeAbout);
CASE(kWidget::Changelog, NodeChangelog);
CASE(kWidget::UserManual, NodeUserManual);
CASE(kWidget::ToolBucket, NodeToolBucket);
#undef CASE
case kWidget::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);
break;
}
default:
{
LOG("instancing UNKNOWN node: %s", x_child->Name());
auto n = new Node();
add_child(n);
n->load_internal(x_child);
break;
}
}
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->m_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->m_manager = m_manager;
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;
}
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->m_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 */
}