#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 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(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(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(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(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(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(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(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([&] { 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 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 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(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& 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> 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; } 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(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 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 */ }