#include "pch.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" void Node::watch(std::function observer) { observer(this); for (auto& c : m_children) c->watch(observer); } void Node::destroy() { m_destroyed = true; } Node* Node::root() { Node* ret = this; while (ret->parent) ret = ret->parent; return ret; } kEventResult Node::on_event(Event* e) { kEventResult ret = kEventResult::Available; if (current_mouse_capture) return current_mouse_capture->on_event(e); bool skip_children = false; skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) && (m_mouse_captured) && (root()->current_mouse_capture == this) && m_capture_children; if (!skip_children) { for (auto it = m_children.rbegin(); it != m_children.rend(); ++it) { if ((*it)->on_event(e) == kEventResult::Consumed) { if (m_flood_events) { ret = kEventResult::Consumed; } else { return kEventResult::Consumed; } } } if (ret == kEventResult::Consumed) return ret; } 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) 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 (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) { } void Node::create() { } void Node::init() { } void Node::loaded() { } const Node* Node::init_template(const char* id) { const auto& m_template = static_cast((*m_manager)[const_hash(id)]->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); return m_template; } void Node::add_child(Node* n) { m_children.emplace_back(n); n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node)); } void Node::add_child(Node* n, int index) { m_children.emplace_back(n); n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, index); } void Node::add_child(std::shared_ptr n) { m_children.push_back(n); n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node)); } void Node::add_child(std::shared_ptr n, int index) { m_children.push_back(n); n->parent = this; n->m_manager = m_manager; YGNodeInsertChild(y_node, n->y_node, index); } 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()) { YGNodeRemoveChild(y_node, n->y_node); m_children.erase(i); } } void Node::remove_all_children() { for (auto& n : m_children) YGNodeRemoveChild(y_node, n->y_node); m_children.clear(); } void Node::move_child(Node* n, int index) { YGNodeRemoveChild(y_node, n->y_node); YGNodeInsertChild(y_node, n->y_node, index); } void Node::move_child_offset(Node* n, int offset) { 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(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) ret = rect_union(ret, c->m_clip_uncut); return ret; } void Node::mouse_capture() { // already owner of capture if (root()->current_mouse_capture == this) return; // cancel previous owner if (auto n = root()->current_mouse_capture) { MouseEvent e; e.m_type = kEventType::MouseCancel; n->handle_event(&e); } // make current root()->current_mouse_capture = this; m_mouse_captured = true; } void Node::mouse_release() { if (root()->current_mouse_capture == this) root()->current_mouse_capture = nullptr; m_mouse_captured = false; } void Node::key_capture() { root()->current_key_capture = 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(o); } Node::Node() { y_node = YGNodeNew(); } 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->parent = this; m_nodeID = o.m_nodeID; m_display = o.m_display; parent = o.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.parent = nullptr; } Node::~Node() { m_children.clear(); if (y_node) YGNodeFree(y_node); } void Node::SetWidth(float value) { YGNodeStyleSetWidth(y_node, value); } void Node::SetWidthP(float value) { YGNodeStyleSetWidthPercent(y_node, value); } void Node::SetHeight(float value) { YGNodeStyleSetHeight(y_node, value); } void Node::SetHeightP(float value) { YGNodeStyleSetHeightPercent(y_node, value); } void Node::SetSize(float w, float h) { SetWidth(w); SetHeight(h); } void Node::SetSize(glm::vec2 value) { SetWidth(value.x); SetHeight(value.y); } 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); } 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::SetPosition(const glm::vec2 pos) { SetPosition(pos.x, pos.y); } void Node::SetPosition(float l, float t) { YGNodeStyleSetPosition(y_node, YGEdgeTop, t); YGNodeStyleSetPosition(y_node, YGEdgeLeft, l); } 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); } void Node::SetFlexGrow(float value) { YGNodeStyleSetFlexGrow(y_node, value); } void Node::SetFlexShrink(float value) { YGNodeStyleSetFlexShrink(y_node, value); } void Node::SetFlexDir(YGFlexDirection value) { YGNodeStyleSetFlexDirection(y_node, value); } void Node::SetFlexWrap(YGWrap value) { YGNodeStyleSetFlexWrap(y_node, value); } void Node::SetJustify(YGJustify value) { YGNodeStyleSetJustifyContent(y_node, value); } void Node::SetAlign(YGAlign value) { YGNodeStyleSetAlignItems(y_node, value); } void Node::SetPositioning(YGPositionType value) { YGNodeStyleSetPositionType(y_node, value); } void Node::SetAspectRatio(float ar) { YGNodeStyleSetAspectRatio(y_node, ar); } 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() }; } 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); } void Node::update() { YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR); update_internal({ 0, 0 }, m_proj); } void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj) { 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 = parent ? 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 (parent) { // correct the padding clip // should not clip the padded area // useful to draw decorations float pt = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeTop); float pr = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeRight); float pb = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeBottom); float pl = 0;//YGNodeLayoutGetPadding(parent->y_node, YGEdgeLeft); glm::vec2 off_p(pl, pt); glm::vec2 off_s(pr, pb); m_clip_uncut = glm::vec4(m_pos - off_p - glm::vec2(1), m_size + off_p + off_s + glm::vec2(2)); m_clip = rect_intersection(m_clip_uncut, 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; for (int i = 0; i < m_children.size(); i++) { if (m_children[i]->m_destroyed) { remove_child(m_children[i].get()); i--; } } if (m_size != old_size) handle_resize(old_size, m_size); for (auto& c : m_children) c->update_internal(m_pos, proj); } 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->IntValue() > 0; break; case kAttribute::AspectRatio: YGNodeStyleSetAspectRatio(y_node, attr->FloatValue()); break; 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) { 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::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::ColorQuad, NodeColorQuad); CASE(kWidget::StrokePreview, NodeStrokePreview); CASE(kWidget::Canvas, NodeCanvas); CASE(kWidget::Scroll, NodeScroll); CASE(kWidget::DialogBrowse, NodeDialogBrowse); CASE(kWidget::DialogBrowseItem, NodeDialogBrowseItem); #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() { Node* n = clone_instantiate(); clone_copy(n); clone_children(n); clone_finalize(n); return n; } 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; } void Node::clone_children(Node* dest) const { for (auto& c : m_children) { Node* cn = c->clone(); dest->m_children.emplace_back(cn); cn->parent = dest; cn->m_manager = dest->m_manager; YGNodeInsertChild(dest->y_node, cn->y_node, YGNodeGetChildCount(dest->y_node)); } } void Node::clone_finalize(Node* dest) const { /* find controllers and stuff */ }