From 1f6c6882587deb84835f558a6998a693cc193424 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 13 May 2017 21:58:04 +0100 Subject: [PATCH] refactor layout.h into single file per Node* classes --- engine.vcxproj | 46 + engine.vcxproj.filters | 156 ++- engine/app.cpp | 2 + engine/app.h | 8 + engine/canvas.h | 4 +- engine/canvas_modes.cpp | 1 + engine/layout.cpp | 533 +------- engine/layout.h | 2136 -------------------------------- engine/node.cpp | 804 ++++++++++++ engine/node.h | 185 +++ engine/node_border.cpp | 83 ++ engine/node_border.h | 18 + engine/node_button.cpp | 120 ++ engine/node_button.h | 24 + engine/node_button_custom.cpp | 73 ++ engine/node_button_custom.h | 17 + engine/node_canvas.cpp | 204 +++ engine/node_canvas.h | 21 + engine/node_checkbox.cpp | 75 ++ engine/node_checkbox.h | 18 + engine/node_color_quad.cpp | 85 ++ engine/node_color_quad.h | 17 + engine/node_dialog_open.cpp | 41 + engine/node_dialog_open.h | 16 + engine/node_icon.cpp | 68 + engine/node_icon.h | 14 + engine/node_image.cpp | 96 ++ engine/node_image.h | 24 + engine/node_image_texture.cpp | 32 + engine/node_image_texture.h | 18 + engine/node_message_box.cpp | 21 + engine/node_message_box.h | 12 + engine/node_panel_brush.cpp | 121 ++ engine/node_panel_brush.h | 30 + engine/node_panel_color.cpp | 42 + engine/node_panel_color.h | 19 + engine/node_panel_layer.cpp | 221 ++++ engine/node_panel_layer.h | 59 + engine/node_panel_stroke.cpp | 52 + engine/node_panel_stroke.h | 27 + engine/node_popup_menu.cpp | 51 + engine/node_popup_menu.h | 11 + engine/node_settings.cpp | 26 + engine/node_settings.h | 13 + engine/node_slider.cpp | 146 +++ engine/node_slider.h | 39 + engine/node_stroke_preview.cpp | 135 ++ engine/node_stroke_preview.h | 26 + engine/node_text.cpp | 88 ++ engine/node_text.h | 21 + engine/node_text_input.cpp | 58 + engine/node_text_input.h | 14 + engine/node_viewport.cpp | 73 ++ engine/node_viewport.h | 23 + engine/shape.h | 8 + 55 files changed, 3600 insertions(+), 2675 deletions(-) create mode 100644 engine/node.cpp create mode 100644 engine/node.h create mode 100644 engine/node_border.cpp create mode 100644 engine/node_border.h create mode 100644 engine/node_button.cpp create mode 100644 engine/node_button.h create mode 100644 engine/node_button_custom.cpp create mode 100644 engine/node_button_custom.h create mode 100644 engine/node_canvas.cpp create mode 100644 engine/node_canvas.h create mode 100644 engine/node_checkbox.cpp create mode 100644 engine/node_checkbox.h create mode 100644 engine/node_color_quad.cpp create mode 100644 engine/node_color_quad.h create mode 100644 engine/node_dialog_open.cpp create mode 100644 engine/node_dialog_open.h create mode 100644 engine/node_icon.cpp create mode 100644 engine/node_icon.h create mode 100644 engine/node_image.cpp create mode 100644 engine/node_image.h create mode 100644 engine/node_image_texture.cpp create mode 100644 engine/node_image_texture.h create mode 100644 engine/node_message_box.cpp create mode 100644 engine/node_message_box.h create mode 100644 engine/node_panel_brush.cpp create mode 100644 engine/node_panel_brush.h create mode 100644 engine/node_panel_color.cpp create mode 100644 engine/node_panel_color.h create mode 100644 engine/node_panel_layer.cpp create mode 100644 engine/node_panel_layer.h create mode 100644 engine/node_panel_stroke.cpp create mode 100644 engine/node_panel_stroke.h create mode 100644 engine/node_popup_menu.cpp create mode 100644 engine/node_popup_menu.h create mode 100644 engine/node_settings.cpp create mode 100644 engine/node_settings.h create mode 100644 engine/node_slider.cpp create mode 100644 engine/node_slider.h create mode 100644 engine/node_stroke_preview.cpp create mode 100644 engine/node_stroke_preview.h create mode 100644 engine/node_text.cpp create mode 100644 engine/node_text.h create mode 100644 engine/node_text_input.cpp create mode 100644 engine/node_text_input.h create mode 100644 engine/node_viewport.cpp create mode 100644 engine/node_viewport.h diff --git a/engine.vcxproj b/engine.vcxproj index cde0bda..7c7b6fc 100644 --- a/engine.vcxproj +++ b/engine.vcxproj @@ -163,6 +163,29 @@ + + + + + + + + + + + + + + + + + + + + + + + Create Create @@ -207,6 +230,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine.vcxproj.filters b/engine.vcxproj.filters index 87a6cc2..2a31841 100644 --- a/engine.vcxproj.filters +++ b/engine.vcxproj.filters @@ -13,6 +13,12 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {600b8daa-4234-4c37-b4ba-c22cad7d1dc3} + + + {2c3e7ce5-d3f4-4731-b05f-5e288c7e6e63} + @@ -45,9 +51,6 @@ Source Files - - Source Files - Source Files @@ -81,6 +84,78 @@ Source Files + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + + + Source Files\ui + @@ -101,9 +176,6 @@ Header Files - - Header Files - Header Files @@ -140,5 +212,77 @@ Header Files + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + + + Header Files\ui + \ No newline at end of file diff --git a/engine/app.cpp b/engine/app.cpp index 86a4670..eba3f75 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "log.h" #include "app.h" +#include "node_icon.h" +#include "node_dialog_open.h" using namespace ui; diff --git a/engine/app.h b/engine/app.h index 5c1aaf0..6faf38c 100644 --- a/engine/app.h +++ b/engine/app.h @@ -6,6 +6,14 @@ #include "texture.h" #include "layout.h" #include "font.h" +#include "node_message_box.h" +#include "node_settings.h" +#include "node_popup_menu.h" +#include "node_panel_brush.h" +#include "node_panel_layer.h" +#include "node_panel_color.h" +#include "node_panel_stroke.h" +#include "node_canvas.h" class App { diff --git a/engine/canvas.h b/engine/canvas.h index 7d2119d..727f3fe 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -154,7 +154,7 @@ public: m_canvas->m_layers[m_layer_idx].m_rtt[i].bindTexture(); glm::vec2 box_sz = m_box[i].zw() - m_box[i].xy(); - glTexSubImage2D(GL_TEXTURE_2D, 0, m_box[i].x, m_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get()); + glTexSubImage2D(GL_TEXTURE_2D, 0, (int)m_box[i].x, (int)m_box[i].y, (int)box_sz.x, (int)box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get()); m_canvas->m_layers[m_layer_idx].m_rtt[i].unbindTexture(); } } @@ -163,7 +163,7 @@ public: size_t mem = 0; for (int i = 0; i < 6; i++) { - glm::vec2 sz = m_box[i].zw() - m_box[i].xy(); + glm::ivec2 sz = m_box[i].zw() - m_box[i].xy(); mem += sz.x * sz.y * 4 + sizeof(*this); } return mem; diff --git a/engine/canvas_modes.cpp b/engine/canvas_modes.cpp index 7428c28..d12c656 100644 --- a/engine/canvas_modes.cpp +++ b/engine/canvas_modes.cpp @@ -4,6 +4,7 @@ #include "layout.h" #include "canvas.h" #include "shader.h" +#include "node_canvas.h" NodeCanvas* CanvasMode::node; ui::Canvas* CanvasMode::canvas; diff --git a/engine/layout.cpp b/engine/layout.cpp index 64d8ca7..9b54f13 100644 --- a/engine/layout.cpp +++ b/engine/layout.cpp @@ -3,537 +3,8 @@ #include "layout.h" #include "util.h" #include "asset.h" - -using namespace ui; - -Plane NodeBorder::m_plane; -Plane NodeImage::m_plane; -Sampler NodeImage::m_sampler; -std::map NodeIcon::m_icons; - -kEventResult Node::on_event(Event* e) -{ - if (current_mouse_capture) - return current_mouse_capture->on_event(e); - - kEventResult ret = kEventResult::Available; - 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::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; - } - default: - if (handle_event(e) == kEventResult::Consumed) - return kEventResult::Consumed; - break; - } - return ret; -} - -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; -} - -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; - m_pos = 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, m_size + off_p + off_s); - m_clip = rect_intersection(m_clip_uncut, parent->m_clip); - } - else - { - 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; - default: - break; - } -} - -void Node::load_internal(const tinyxml2::XMLElement* x_node) -{ - m_name = x_node->Name(); - - 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); -#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(); -} - -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)); - } -} +#include "node.h" +#include "node_border.h" bool LayoutManager::load(const char* path) { diff --git a/engine/layout.h b/engine/layout.h index 55a70ce..04e9c1f 100644 --- a/engine/layout.h +++ b/engine/layout.h @@ -11,82 +11,6 @@ #include #include -enum class kAttribute : uint16_t -{ - id = const_hash("id"), - Width = const_hash("width"), - MinWidth = const_hash("min-width"), - MaxWidth = const_hash("max-width"), - Height = const_hash("height"), - MinHeight = const_hash("min-height"), - MaxHeight = const_hash("max-height"), - Divisions = const_hash("divisions"), - InnerRadius = const_hash("inner-radius"), - OuterRadius = const_hash("outer-radius"), - Grow = const_hash("grow"), - Shrink = const_hash("shrink"), - FlexDir = const_hash("dir"), - FlexWrap = const_hash("wrap"), - Padding = const_hash("pad"), - Margin = const_hash("margin"), - Color = const_hash("color"), - Thickness = const_hash("thickness"), - BorderColor = const_hash("border-color"), - Type = const_hash("type"), - Text = const_hash("text"), - FontFace = const_hash("font-face"), - FontSize = const_hash("font-size"), - Justify = const_hash("justify"), - Align = const_hash("align"), - Path = const_hash("path"), - Region = const_hash("region"), - Position = const_hash("position"), - Positioning = const_hash("positioning"), - FloodEvents = const_hash("flood-events"), - Icon = const_hash("icon"), - Selected = const_hash("selected"), - Template = const_hash("template"), - Value = const_hash("value"), - Range = const_hash("range"), -}; - -enum class kWidget : uint16_t -{ - Node = const_hash("node"), - Border = const_hash("border"), - Shape = const_hash("shape"), - Text = const_hash("text"), - TextInput = const_hash("text-input"), - Image = const_hash("image"), - ImageTexture = const_hash("image-texture"), - Icon = const_hash("icon"), - Button = const_hash("button"), - ButtonCustom = const_hash("button-custom"), - SliderH = const_hash("slider-h"), - SliderV = const_hash("slider-v"), - SliderHue = const_hash("slider-hue"), - PopupMenu = const_hash("popup-menu"), - Viewport = const_hash("viewport"), - Ref = const_hash("ref"), - CheckBox = const_hash("checkbox"), - Layer = const_hash("layer"), - PanelLayer = const_hash("panel-layer"), - PanelBrush = const_hash("panel-brush"), - PanelColor = const_hash("panel-color"), - PanelStroke = const_hash("panel-stroke"), - ColorQuad = const_hash("color-quad"), - StrokePreview = const_hash("stroke-preview"), - Canvas = const_hash("canvas"), -}; - -enum class kShapeType : uint16_t -{ - Quad = const_hash("rect"), - Poly = const_hash("poly"), - RoundRect = const_hash("round-rect"), - Slice9 = const_hash("slice9"), -}; - class LayoutManager { std::map> m_layouts; @@ -106,2063 +30,3 @@ public: void clear_context(); //Node& operator[](const char* ids) { return m_layouts[const_hash(ids)]; } }; - -class Node -{ - friend class LayoutManager; -public: - Node* parent{ nullptr }; - YGNodeRef y_node{ nullptr }; - class LayoutManager* m_manager; - uint16_t m_nodeID; - std::string m_nodeID_s; - std::vector> m_children; - Node* current_mouse_capture = nullptr; - Node* current_key_capture = nullptr; - bool m_mouse_captured = false; - bool m_key_captured = false; - - glm::mat4 m_proj; - glm::mat4 m_mvp; - bool m_mouse_inside = false; - bool m_flood_events = false; - bool m_destroyed = false; - - bool m_mouse_ignore = true; - float m_zoom = 1.f; - glm::vec2 m_scale{ 1.f }; - glm::vec2 m_pos; - glm::vec2 m_size; - glm::vec4 m_clip; - glm::vec4 m_clip_uncut; - std::string m_name; - bool m_display = true; - Node(const Node&) = delete; - Node& operator=(const Node&) = delete; - Node&& operator=(Node&& o) { return std::forward(o); } - 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() { y_node = YGNodeNew(); } - ~Node() - { - m_children.clear(); - if (y_node) - YGNodeFree(y_node); - } - - void SetWidth(float value) { YGNodeStyleSetWidth(y_node, value); } - void SetWidthP(float value) { YGNodeStyleSetWidthPercent(y_node, value); } - void SetHeight(float value) { YGNodeStyleSetHeight(y_node, value); } - void SetHeightP(float value) { YGNodeStyleSetHeightPercent(y_node, value); } - void SetSize(glm::vec2 value) { SetWidth(value.x); SetHeight(value.y); } - void SetSize(float w, float h) { SetWidth(w); SetHeight(h); } - - void 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); - } - void 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 SetPosition(float l, float t) - { - YGNodeStyleSetPosition(y_node, YGEdgeTop, t); - YGNodeStyleSetPosition(y_node, YGEdgeLeft, l); - } - void SetPosition(const glm::vec2 pos) - { - SetPosition(pos.x, pos.y); - } - - void SetFlexGrow(float value) { YGNodeStyleSetFlexGrow(y_node, value); } - void SetFlexShrink(float value) { YGNodeStyleSetFlexShrink(y_node, value); } - void SetFlexDir(YGFlexDirection value) { YGNodeStyleSetFlexDirection(y_node, value); } - void SetFlexWrap(YGWrap value) { YGNodeStyleSetFlexWrap(y_node, value); } - void SetJustify(YGJustify value) { YGNodeStyleSetJustifyContent(y_node, value); } - void SetAlign(YGAlign value) { YGNodeStyleSetAlignItems(y_node, value); } - void SetPositioning(YGPositionType value) { YGNodeStyleSetPositionType(y_node, value); } - void SetAspectRatio(float ar) { YGNodeStyleSetAspectRatio(y_node, ar); } - - glm::vec2 GetPosition() { return{ YGNodeLayoutGetLeft(y_node), YGNodeLayoutGetTop(y_node) }; } - float GetWidth() { return YGNodeLayoutGetWidth(y_node); } - float GetHeight() { return YGNodeLayoutGetHeight(y_node); } - glm::vec2 GetSize() { return { GetWidth(), GetHeight() }; } - - glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b) - { - // convert from [x,y,w,h] to [x1,y1,x2,y1] - a = glm::vec4(a.xy(), a.xy() + a.zw()); - b = glm::vec4(b.xy(), b.xy() + b.zw()); - auto o = glm::vec4(glm::max(a.xy(), b.xy()), glm::min(a.zw(), b.zw())); - o = glm::vec4(o.xy(), glm::max({ 0, 0 }, o.zw() - o.xy())); - return o; - } - - virtual void restore_context() - { - for (auto& c : m_children) - c->restore_context(); - }; - virtual void clear_context() - { - for (auto& c : m_children) - c->clear_context(); - } - void update(float width, float height, float zoom); - void update(); - void update_internal(const glm::vec2& origin, const glm::mat4& proj); - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr); - void load_internal(const tinyxml2::XMLElement* x_node); - virtual void draw() { } - Node* clone(); - virtual Node* clone_instantiate() const; - virtual void clone_copy(Node* dest) const; - virtual void clone_children(Node* dest) const; - virtual void clone_finalize(Node* dest) const { /* find controllers and stuff */ }; - void watch(std::function observer) - { - observer(this); - for (auto& c : m_children) - c->watch(observer); - } - void destroy() { m_destroyed = true; } - Node* root() - { - Node* ret = this; - while (ret->parent) - ret = ret->parent; - return ret; - } - - template T* find(const char* ids) - { - uint16_t id = const_hash(ids); - if (id == m_nodeID) - return static_cast(this); - for (auto& c : m_children) - if (auto found = c->find(ids)) - return static_cast(found); - return nullptr; - } - - virtual kEventResult on_event(Event* e); - virtual kEventResult handle_event(Event* e) { return kEventResult::Available; } - virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) { }; - virtual void create() { } - virtual void init() { } - virtual void loaded() { } - const Node* init_template(const char* id); - void add_child(Node* n); - void add_child(Node* n, int index); - void add_child(std::shared_ptr n); - void add_child(std::shared_ptr n, int index); - void remove_child(Node* n); - void remove_all_children(); - void move_child(Node* n, int index); - void move_child_offset(Node* n, int offset); - int get_child_index(Node* n); - void mouse_capture() { root()->current_mouse_capture = this; m_mouse_captured = true; } - void mouse_release() { root()->current_mouse_capture = nullptr; m_mouse_captured = false; } - void key_capture() { root()->current_key_capture = this; m_key_captured = true; } - void key_release() { root()->current_key_capture = nullptr; m_key_captured = false; } -}; - -class NodeBorder : public Node -{ -public: - static ui::Plane m_plane; - glm::vec4 m_color{ 0, 0, 0, 1 }; - glm::vec4 m_border_color{ 1, 1, 1, 1 }; - float m_thinkness{ 0 }; - NodeBorder() { m_mouse_ignore = false; } - static void static_init() - { - m_plane.create<1>(1, 1); - } - virtual Node* clone_instantiate() const override { return new NodeBorder(); } - virtual void clone_copy(Node* dest) const override - { - Node::clone_copy(dest); - NodeBorder* n = static_cast(dest); - n->m_color = m_color; - n->m_border_color = m_border_color; - n->m_thinkness = m_thinkness; - n->m_mouse_ignore = false; - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - Node::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Color: - { - glm::vec4 pad; - int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); - if (n == 1) - m_color = glm::vec4(pad.x, pad.x, pad.x, 1); - else - m_color = pad; - break; - } - case kAttribute::BorderColor: - { - glm::vec4 pad; - int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); - if (n == 1) - m_border_color = glm::vec4(pad.x); - else - m_border_color = pad; - break; - } - case kAttribute::Thickness: - m_thinkness = attr->FloatValue(); - break; - default: - break; - } - } - virtual void draw() override - { - using namespace ui; - ui::ShaderManager::use(kShader::Color); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - - if (m_color.a > 0.f) - { - m_color.a < 1.f ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); - m_plane.draw_fill(); - glDisable(GL_BLEND); - } - - if (m_thinkness > 0 && m_border_color.a > 0.f) - { - glLineWidth(m_thinkness); - ui::ShaderManager::u_vec4(kShaderUniform::Col, m_border_color); - m_border_color.a < 1.f ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - m_plane.draw_stroke(); - glDisable(GL_BLEND); - } - - } -}; - -class NodeText : public Node -{ -public: - TextMesh m_text_mesh; - std::string m_text; - std::string m_font; - glm::vec4 m_color{ 1, 1, 1, 1 }; - int m_font_size; - kFont font_id; - virtual Node* clone_instantiate() const override { return new NodeText(); } - virtual void clone_copy(Node* dest) const override - { - Node::clone_copy(dest); - NodeText* n = static_cast(dest); - n->m_text_mesh.create(); - n->m_text_mesh.update(font_id, m_text.c_str()); - n->m_text = m_text; - n->m_font = m_font; - n->m_color = m_color; - n->m_font_size = m_font_size; - n->font_id = font_id; - } - virtual void create() override - { - char font[64]; - sprintf(font, "%s-%d", m_font.c_str(), m_font_size); - font_id = (kFont)const_hash(font); - m_text_mesh.create(); - m_text_mesh.update(font_id, m_text.c_str()); - SetSize(m_text_mesh.bb); - } - virtual void restore_context() override - { - Node::restore_context(); - create(); - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - Node::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Text: - m_text = attr->Value(); - break; - case kAttribute::FontFace: - m_font = attr->Value(); - break; - case kAttribute::FontSize: - m_font_size = attr->IntValue(); - break; - case kAttribute::Color: - { - glm::vec4 pad; - int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); - if (n == 1) - m_color = glm::vec4(pad.x); - else - m_color = pad; - break; - } - default: - break; - } - } - void set_text(const char* s) - { - m_text = s; - m_text_mesh.update(font_id, s); - SetSize(m_text_mesh.bb); - } - virtual void draw() override - { - using namespace ui; - glm::mat4 pos = glm::translate(glm::vec3(glm::floor(m_pos), 0)); - m_mvp = m_proj * pos; - ui::ShaderManager::use(kShader::Font); - ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); - glEnable(GL_BLEND); - m_text_mesh.draw(); - glDisable(GL_BLEND); - } -}; - -class NodeImage : public Node -{ -public: - static ui::Plane m_plane; - static Sampler m_sampler; - bool m_use_atlas = false; - glm::vec4 m_region; - glm::vec2 m_off; - glm::vec2 m_sz; - std::string m_path; - uint16_t m_tex_id; - static void static_init() - { - m_plane.create<1>(1, 1); - m_sampler.create(); - } - virtual Node* clone_instantiate() const override { return new NodeImage(); } - virtual void clone_copy(Node* dest) const override - { - Node::clone_copy(dest); - NodeImage* n = static_cast(dest); - n->m_use_atlas = m_use_atlas; - n->m_region = m_region; - n->m_off = m_off; - n->m_sz = m_sz; - n->m_path = m_path; - n->m_tex_id = m_tex_id; - } - virtual void create() override - { - if (!m_path.empty() && TextureManager::load(m_path.c_str())) - { - //LOG("load image node %s", m_path.c_str()); - auto tex_sz = TextureManager::get(m_tex_id).size(); - m_off = m_region.xy / tex_sz; - m_sz = (m_region.zw - m_region.xy) / tex_sz; - } - } - virtual void restore_context() override - { - Node::restore_context(); - create(); - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - Node::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Path: - m_path = attr->Value(); - m_tex_id = const_hash(attr->Value()); - break; - case kAttribute::Region: - { - glm::vec4 v; - int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w); - if (n == 4) - { - m_region = v; - m_use_atlas = true; - } - break; - } - default: - break; - } - } - virtual void draw() override - { - using namespace ui; - TextureManager::get(m_tex_id).bind(); - m_sampler.bind(0); - glEnable(GL_BLEND); - if (m_use_atlas) - { - ui::ShaderManager::use(kShader::Atlas); - ui::ShaderManager::u_vec2(kShaderUniform::Tof, m_off); - ui::ShaderManager::u_vec2(kShaderUniform::Tsz, m_sz); - } - else - { - ui::ShaderManager::use(kShader::Texture); - } - ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - m_plane.draw_fill(); - m_sampler.unbind(); - TextureManager::get(m_tex_id).unbind(); - glDisable(GL_BLEND); - } -}; - -class NodeImageTexture : public Node -{ -public: - Texture2D tex; - virtual Node* clone_instantiate() const override { return new NodeImageTexture(); } - virtual void clone_copy(Node* dest) const override - { - Node::clone_copy(dest); - NodeImageTexture* n = static_cast(dest); - n->tex = tex; - } - // TODO: maybe we can save the texture data and restore later - //virtual void restore_context() override - //{ - // Node::restore_context(); - // create(); - //} - virtual void draw() override - { - using namespace ui; - tex.bind(); - NodeImage::m_sampler.bind(0); - glEnable(GL_BLEND); - ui::ShaderManager::use(kShader::Texture); - ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - NodeImage::m_plane.draw_fill(); - NodeImage::m_sampler.unbind(); - tex.unbind(); - glDisable(GL_BLEND); - } -}; - -class NodeButton : public Node -{ -public: - NodeBorder* m_border; - NodeText* m_text; - glm::vec4 color_normal{ .1, .1, .1, 1 }; - glm::vec4 color_hover{ .2, .2, .2, 1 }; - glm::vec4 color_down{ .3, .3, .3, 1 }; - std::function on_click; - virtual Node* clone_instantiate() const override { return new NodeButton(); } - virtual void clone_children(Node* dest) const override - { - Node::clone_children(dest); - NodeButton* n = static_cast(dest); - n->m_border = (NodeBorder*)n->m_children[0].get(); - n->m_text = (NodeText*)n->m_border->m_children[0].get(); - } - virtual void clone_copy(Node* dest) const override - { - Node::clone_copy(dest); - NodeButton* n = static_cast(dest); - //n->m_border = (NodeBorder*)m_border->clone(); - //n->m_text = (NodeText*)m_text->clone(); - n->color_normal = color_normal; - n->color_hover = color_hover; - n->color_down = color_down; - //n->on_click = on_click; - n->m_mouse_ignore = false; - } - virtual void init() override - { - m_border = new NodeBorder(); - m_text = new NodeText(); - add_child(m_border); - m_border->add_child(m_text); - m_border->init(); - m_border->m_color = color_normal; - m_text->init(); - m_text->m_font = "arial"; - m_text->m_font_size = 11; - m_border->SetAlign(YGAlignCenter); - m_border->SetJustify(YGJustifyCenter); - m_border->m_mouse_ignore = false; - m_mouse_ignore = false; - } - virtual void create() override - { - m_border->create(); - m_text->create(); - m_border->m_mouse_ignore = false; - m_mouse_ignore = false; - } - virtual void loaded() override - { - m_border->m_thinkness = 1; - m_border->m_border_color = glm::vec4(0, 0, 0, 1); - m_border->m_color = color_normal; - m_border->m_mouse_ignore = false; - m_mouse_ignore = false; - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - switch (ka) - { - case kAttribute::Padding: - case kAttribute::Width: - case kAttribute::Height: - case kAttribute::Color: - case kAttribute::Thickness: - case kAttribute::BorderColor: - m_border->parse_attributes(ka, attr); - break; - case kAttribute::Text: - case kAttribute::FontFace: - case kAttribute::FontSize: - m_text->parse_attributes(ka, attr); - break; - default: - Node::parse_attributes(ka, attr); - break; - } -// m_border->parse_attributes(ka, attr); -// m_text->parse_attributes(ka, attr); - } - void set_color(const glm::vec4& c) - { - color_normal = c; - m_border->m_color = color_normal; - } - virtual kEventResult handle_event(Event* e) override - { - Node::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseEnter: - m_border->m_color = color_hover; - break; - case kEventType::MouseLeave: - m_border->m_color = color_normal; - break; - case kEventType::MouseDownL: - m_border->m_color = color_down; - break; - case kEventType::MouseUpL: - m_border->m_color = color_normal; - if (m_mouse_inside && on_click != nullptr) - on_click(this); - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } -}; - -class NodeTextInput : public NodeBorder -{ -public: - NodeText* m_text; - std::string m_string; - virtual Node* clone_instantiate() const override { return new NodeTextInput(); } - virtual void init() override - { - init_controls(); - } - void init_controls() - { - m_text = new NodeText; - add_child(m_text); - m_text->m_font = "arial"; - m_text->m_font_size = 11; - m_text->m_text = "TextInput"; - m_text->create(); - } - virtual kEventResult handle_event(Event* e) override - { - KeyEvent* ke = (KeyEvent*)e; - switch (e->m_type) - { - case kEventType::MouseDownL: - break; - case kEventType::MouseUpL: - key_capture(); - break; - case kEventType::KeyDown: -// switch (ke->m_key) -// { -// case VK_BACK: -// m_string.erase(m_string.end() - 1); -// m_text->set_text(m_string.c_str()); -// break; -// default: -// break; -// } - break; - case kEventType::KeyChar: - if (ke->m_char >= 32 && ke->m_char < (32+96)) - { - m_string += (char)ke->m_key; - m_text->set_text(m_string.c_str()); - } - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } -}; - -class NodeMessageBox : public Node -{ -public: - Node* m_template; - NodeButton* btnOk; - virtual Node* clone_instantiate() const override { return new NodeMessageBox(); } - virtual void init() override - { - SetPosition(0, 0); - SetWidthP(100); - SetHeightP(100); - SetPositioning(YGPositionTypeAbsolute); - m_template = (*m_manager)[const_hash("message-box")]->m_children[0]->clone(); - add_child(m_template); - btnOk = m_template->find("btn-ok"); - btnOk->on_click = [&](Node*) { destroy(); }; - } -}; - -class NodePopupMenu : public Node -{ -public: - std::function on_select; - virtual Node* clone_instantiate() const override { return new NodePopupMenu(); } - virtual void init() override - { - m_flood_events = true; - SetPosition(0, 0); - SetWidth(100); - SetHeight(400); - SetPositioning(YGPositionTypeAbsolute); - m_mouse_ignore = false; - } - virtual kEventResult handle_event(Event* e) override - { - switch (e->m_type) - { - case kEventType::MouseDownL: - break; - case kEventType::MouseUpL: - if (!m_mouse_inside) - { - mouse_release(); - } - else - { - for (int i = 0; i < m_children.size(); i++) - { - if (m_children[i]->m_mouse_inside) - { - if (on_select) - on_select(this, i); - break; - } - } - mouse_release(); - } - destroy(); - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } -}; - -class NodeButtonCustom : public NodeBorder -{ -public: - glm::vec4 color_normal{ .2, .2, .2, 1 }; - glm::vec4 color_hover{ .3, .3, .3, 1 }; - glm::vec4 color_down{ .4, .4, .4, 1 }; - std::function on_click; - virtual Node* clone_instantiate() const override { return new NodeButtonCustom(); } - virtual void clone_copy(Node* dest) const override - { - NodeBorder::clone_copy(dest); - NodeButtonCustom* n = static_cast(dest); - n->color_normal = color_normal; - n->color_hover = color_hover; - n->color_down = color_down; - n->m_mouse_ignore = false; - n->m_color = color_normal; - } - virtual void loaded() override - { - NodeBorder::loaded(); - //m_thinkness = 1; - //m_border_color = glm::vec4(0, 0, 0, 1); - m_color = color_normal; - m_mouse_ignore = false; - } - void set_color(const glm::vec4& c) - { - color_normal = c; - m_color = color_normal; - } - virtual kEventResult handle_event(Event* e) override - { - NodeBorder::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseEnter: - m_color = color_hover; - break; - case kEventType::MouseLeave: - m_color = color_normal; - break; - case kEventType::MouseDownL: - m_color = color_down; - break; - case kEventType::MouseUpL: - m_color = m_mouse_inside ? color_hover : color_normal; - if (m_mouse_inside && on_click != nullptr) - on_click(this); - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - NodeBorder::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Color: - color_normal = m_color; - break; - default: - break; - } - } -}; - -class NodeSettings : public Node -{ - Node* m_template; - NodeButton* btnOk; -public: - virtual Node* clone_instantiate() const override { return new NodeButtonCustom(); } - virtual void init() override - { - SetPosition(0, 0); - SetWidthP(100); - SetHeightP(100); - SetPositioning(YGPositionTypeAbsolute); - m_template = (*m_manager)[const_hash("settings")]->m_children[0]->clone(); - add_child(m_template); - btnOk = m_template->find("btn-ok"); - btnOk->on_click = [&](Node*) { destroy(); }; - } - virtual kEventResult handle_event(Event* e) override - { - return kEventResult::Consumed; - } -}; - -class NodeIcon : public NodeImage -{ - static std::map m_icons; - std::string m_icon_name; -public: - static void static_init() - { - // spritesheet maker: https://draeton.github.io/stitches/ - // icons: http://www.famfamfam.com/lab/icons/silk/ - // regex css -> spritesheet.txt: \.([^{]+) {\s+width: (\d+)px;\s+height: (\d+)px;\s+.*: -(\d+)px -(\d+)px;\s+}\s+ - // to: "\1",\2,\3,\4,\5\n - Asset file; - if (!(file.open("data/spritesheet.txt") && file.read_all())) - return; - char* data = (char*)file.m_data; - int size = file.m_len; - static char name[256]; - int x, y, w, h; - char* s = strtok(data, "\n"); - auto i = strlen(s) + 1; - while (i < size && sscanf(s, "%s %d %d %d %d", name, &w, &h, &x, &y) == 5) - { - m_icons[name] = glm::vec4(x, y, x + w, y + h); - s = strtok(nullptr, "\n"); - i += strlen(s) + 1; - } - file.close(); - TextureManager::load("data/spritesheet.png"); - } - virtual Node* clone_instantiate() const override { return new NodeIcon(); } - virtual void clone_copy(Node* dest) const override - { - NodeImage::clone_copy(dest); - NodeIcon* n = static_cast(dest); - n->m_icon_name = m_icon_name; - } - virtual void create() override - { - m_region = m_icons[m_icon_name]; - m_path = "data/spritesheet.png"; - m_tex_id = const_hash(m_path.c_str()); - m_use_atlas = true; - NodeImage::create(); - auto tex_sz = TextureManager::get(m_tex_id).size(); - YGNodeStyleSetAspectRatio(y_node, tex_sz.x / tex_sz.y); - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - NodeImage::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Icon: - m_icon_name = attr->Value(); - break; - default: - break; - } - } -}; - -class NodeViewport : public Node -{ -public: - std::unique_ptr m_faces; - std::unique_ptr m_sampler; - uint16_t m_tex_id; - glm::vec2 drag_start; - glm::vec2 drag_end; - bool dragging = false; - float angle = 0.0f; - float angle_old; - - virtual void draw() override - { - using namespace ui; - glm::mat4 cam = glm::lookAt(glm::vec3(sinf(angle) * 10, 0, -10), glm::vec3(0, 0, 0), glm::vec3(0, -1, 0)); - glm::mat4 proj = glm::perspective(glm::radians(45.f), m_clip.z / m_clip.w, .1f, 100); - - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - - glClearColor(1, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - auto box = m_clip * root()->m_zoom; - glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w); - glViewport(c.x, c.y, c.z, c.w); - TextureManager::get(m_tex_id).bind(); - m_sampler->bind(0); - glEnable(GL_BLEND); - ui::ShaderManager::use(kShader::Texture); - ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * cam); - m_faces->draw_fill(); - m_sampler->unbind(); - TextureManager::get(m_tex_id).unbind(); - glDisable(GL_BLEND); - - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); - } - virtual Node* clone_instantiate() const override - { - return new NodeViewport; - } - virtual void create() override - { - m_faces = std::make_unique(); - m_faces->create<1>(10, 10); - m_sampler = std::make_unique(); - m_sampler->create(); - TextureManager::load("data/uvs.jpg"); - m_tex_id = const_hash("data/uvs.jpg"); - } - virtual kEventResult handle_event(Event* e) override - { - Node::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseDownL: - dragging = true; - drag_end = drag_start = ((MouseEvent*)e)->m_pos; - angle_old = angle; - break; - case kEventType::MouseUpL: - dragging = false; - break; - case kEventType::MouseMove: - if (dragging) - { - drag_end = ((MouseEvent*)e)->m_pos; - angle = angle_old + (drag_end - drag_start).x * .01f; - } - break; - default: - break; - } - return kEventResult::Consumed; - } -}; - -class NodeSliderH : public NodeBorder -{ - bool dragging = false; -public: - glm::vec2 m_mask{ 1, 0 }; - glm::vec2 m_value; - std::function on_value_changed; - virtual Node* clone_instantiate() const override { return new NodeSliderH(); } - virtual void clone_copy(Node* dest) const override - { - NodeBorder::clone_copy(dest); - NodeSliderH* n = static_cast(dest); - n->m_value = m_value; - } - virtual void init() override - { - SetPadding(1, 1, 1, 1); - SetWidthP(100); - SetHeightP(100); - m_color = glm::vec4(1); - } - virtual void draw() override - { - NodeBorder::draw(); - - using namespace ui; - auto sz = GetSize(); - glm::vec2 cur_size = sz * (1.f - m_mask) + m_mask * glm::vec2(10); - glm::mat4 scale = glm::scale(glm::vec3(cur_size, 1.f)); - glm::mat4 pos = glm::translate(glm::vec3(m_value * m_mask * sz + m_pos + sz * .5f * (1.f - m_mask), 0)); - auto mvp = m_proj * pos * scale; - - ui::ShaderManager::use(kShader::Color); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp); - ui::ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); - m_plane.draw_fill(); - } - void set_value(float value) - { - m_value = glm::vec2(value) * m_mask; - if (on_value_changed) - on_value_changed(this, glm::length(m_value)); - } - float get_value() - { - return glm::length(m_value); - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - NodeBorder::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Value: - m_value = glm::vec2(attr->FloatValue()); - break; - default: - break; - } - } - virtual kEventResult handle_event(Event* e) override - { - NodeBorder::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseDownL: - dragging = true; - mouse_capture(); - { - auto sz = GetSize(); - auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask; - m_value = pos / glm::max({ 1, 1 }, sz); - if (on_value_changed) - on_value_changed(this, glm::length(m_value)); - } - break; - case kEventType::MouseUpL: - mouse_release(); - dragging = false; - break; - case kEventType::MouseMove: - if (dragging) - { - auto sz = GetSize(); - auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask; - m_value = pos / glm::max({ 1, 1 }, sz); - if (on_value_changed) - on_value_changed(this, glm::length(m_value)); - } - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } -}; - -class NodeSliderV : public NodeSliderH -{ -public: - virtual Node* clone_instantiate() const override { return new NodeSliderV(); } - NodeSliderV() { m_mask = { 0, 1 }; } -}; - -class NodeSliderHue : public NodeSliderV -{ -public: - glm::vec4 m_color; - std::function on_hue_changed; - virtual Node* clone_instantiate() const override { return new NodeSliderHue(); } - virtual void clone_finalize(Node* dest) const override - { - NodeSliderV::clone_finalize(dest); - NodeSliderHue* n = static_cast(dest); - n->init_controls(); - } - virtual void init() override - { - NodeSliderV::init(); - init_controls(); - } - void init_controls() - { - on_value_changed = [this](Node*, float value) { - m_color = glm::vec4(convert_hsv2rgb({ value, 1, 1 }), 1); - if (on_hue_changed) - on_hue_changed(this, m_color); - }; - } - glm::vec4 get_hue() - { - return m_color; - } - virtual void draw() override - { - using namespace ui; - ui::ShaderManager::use(kShader::ColorHue); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - //ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); - m_plane.draw_fill(); - NodeBorder::m_color = glm::vec4(0); - NodeSliderH::draw(); - } -}; - -class NodeCheckBox : public Node -{ -public: - std::function on_value_changed; - NodeBorder* m_outer; - NodeBorder* m_inner; - bool checked = false; - virtual Node* clone_instantiate() const override { return new NodeCheckBox(); } - virtual void clone_children(Node* dest) const override - { - Node::clone_children(dest); - NodeCheckBox* n = static_cast(dest); - n->m_outer = (NodeBorder*)n->m_children[0].get(); - n->m_inner = (NodeBorder*)n->m_outer->m_children[0].get(); - n->m_mouse_ignore = false; - } - virtual void init() override - { - m_outer = new NodeBorder(); - m_inner = new NodeBorder(); - add_child(m_outer); - m_outer->add_child(m_inner); - m_outer->init(); - m_outer->m_color = { .3, .3, .3, 1 }; - m_outer->SetAlign(YGAlignCenter); - m_outer->SetJustify(YGJustifyCenter); - m_outer->SetPadding(5, 5, 5, 5); - m_outer->SetWidthP(100); - m_outer->SetHeightP(100); - m_outer->m_mouse_ignore = false; - m_inner->init(); - m_inner->SetWidthP(100); - m_inner->SetHeightP(100); - m_inner->m_border_color = glm::vec4(.8, .8, .8, 1); - m_inner->m_thinkness = 1; - m_inner->m_color = glm::vec4(.8, .8, .8, 1); - m_mouse_ignore = false; - } - virtual void create() override - { - m_outer->create(); - m_inner->create(); - } - virtual kEventResult handle_event(Event* e) override - { - Node::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseEnter: - break; - case kEventType::MouseLeave: - break; - case kEventType::MouseDownL: - break; - case kEventType::MouseUpL: - checked = !checked; - if (on_value_changed) - on_value_changed(this, checked); - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } - virtual void draw() override - { - m_inner->m_color = checked ? glm::vec4(.4, .4, .4, 1) : glm::vec4(.8, .8, .8, 1); - Node::draw(); - } -}; - -class NodeLayer : public NodeBorder -{ -public: - std::function on_selected; - std::function on_opacity_changed; - std::function on_visibility_changed; - bool m_selected = false; - glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1); - glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1); - glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1); - std::string m_label_text; - NodeText* m_label; - NodeCheckBox* m_visibility; - NodeSliderH* m_opacity; - virtual Node* clone_instantiate() const override { return new NodeLayer(); } - virtual void clone_children(Node* dest) const override - { - NodeBorder::clone_children(dest); - NodeLayer* n = static_cast(dest); - n->m_label = n->find("label"); - n->m_visibility = n->find("cb"); - } - virtual void clone_copy(Node* dest) const override - { - NodeBorder::clone_copy(dest); - NodeLayer* n = (NodeLayer*)dest; - n->m_selected = m_selected; - n->m_label_text = m_label_text; - } - virtual void init() override - { - const auto& m_template = (NodeBorder*)init_template("tpl-layer"); - m_color = m_template->m_color; - m_border_color = m_template->m_border_color; - m_thinkness = m_template->m_thinkness; - m_label = find("label"); - m_visibility = find("cb"); - m_opacity = find("sl-opacity"); - } - virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override - { - NodeBorder::parse_attributes(ka, attr); - switch (ka) - { - case kAttribute::Text: - m_label_text = attr->Value(); - break; - case kAttribute::Selected: - m_selected = attr->BoolValue(); - default: - break; - } - } - virtual void loaded() override - { - NodeBorder::loaded(); - if (!m_label_text.empty()) - m_label->set_text(m_label_text.c_str()); - m_opacity->on_value_changed = [this](Node*, float value) { - if (on_opacity_changed) - on_opacity_changed(this, value); - }; - m_visibility->on_value_changed = [this](Node*, bool checked) { - if (on_visibility_changed) - on_visibility_changed(this, checked); - }; - } - virtual kEventResult handle_event(Event* e) override - { - NodeBorder::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseEnter: - break; - case kEventType::MouseLeave: - break; - case kEventType::MouseDownL: - m_selected = true; - if (on_selected) - on_selected(this); - break; - case kEventType::MouseUpL: - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } - virtual void draw() override - { - auto c = m_selected ? m_color_selected : m_color_normal; - m_thinkness = m_selected ? 1.f : 0.f; - m_color = m_mouse_inside ? m_color_hover : c; - NodeBorder::draw(); - } - void set_name(const char* s) - { - m_label_text = s; - m_label->set_text(s); - } -}; - -class NodePanelLayer : public Node -{ - NodeButtonCustom* btn_add; - NodeButtonCustom* btn_remove; - NodeButtonCustom* btn_up; - NodeButtonCustom* btn_down; - int id_counter = 0; -public: - std::function on_layer_change; - std::function on_layer_opacity_changed; - std::function on_layer_visibility_changed; - std::function on_layer_delete; - std::function on_layer_add; - std::function on_layer_order; - NodeLayer* m_current_layer = nullptr; - std::vector m_layers; - NodeBorder* m_layers_container; - virtual Node* clone_instantiate() const override { return new NodePanelLayer(); } - virtual void init() override - { - LOG("NodePanelLayer::init"); - init_template("tpl-panel-layers"); - LOG("template initted"); - m_layers_container = find("layers-container"); - LOG("template container found"); -// for (int i = 0; i < 1; i++) -// { -// LOG("add layer"); -// add_layer(); -// } - LOG("find components"); -// m_current_layer = m_layers[0]; -// m_layers[0]->m_selected = true; - btn_add = find("btn-add"); - btn_remove = find("btn-remove"); - btn_up = find("btn-up"); - btn_down = find("btn-down"); - LOG("attach events"); - btn_add->on_click = [this](Node*) { - add_layer(); - if (on_layer_add) - on_layer_add(this); - }; - btn_remove->on_click = [this](Node*) { - if (m_layers.size() == 1) - return; // dont' delete the last layer - remove_layer(m_current_layer); - }; - btn_up->on_click = [this](Node*) { - int old_idx = m_layers_container->get_child_index(m_current_layer); - m_layers_container->move_child_offset(m_current_layer, -1); - int new_idx = m_layers_container->get_child_index(m_current_layer); - if (on_layer_order && old_idx != new_idx) - { - on_layer_order(this, old_idx, new_idx); - } - }; - btn_down->on_click = [this](Node*) { - int old_idx = m_layers_container->get_child_index(m_current_layer); - m_layers_container->move_child_offset(m_current_layer, +1); - int new_idx = m_layers_container->get_child_index(m_current_layer); - if (on_layer_order && old_idx != new_idx) - { - on_layer_order(this, old_idx, new_idx); - } - }; - LOG("done init"); - } - void add_layer() - { - static char s[64]; - sprintf(s, "Layer-%d", id_counter++); - add_layer(s); - } - void add_layer(const char* name) - { - NodeLayer* l = new NodeLayer; - m_layers_container->add_child(l); - l->init(); - l->create(); - l->loaded(); - l->set_name(name); - l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1); - l->on_opacity_changed = std::bind(&NodePanelLayer::handle_layer_opacity, this, std::placeholders::_1, std::placeholders::_2); - l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2); - m_layers.push_back(l); - } - void remove_layer(NodeLayer* layer) - { - auto it = std::find(m_layers.begin(), m_layers.end(), m_current_layer); - auto i = m_layers_container->get_child_index(m_current_layer); - m_layers_container->remove_child(m_current_layer); - m_layers.erase(it); - i = std::min(i, (int)m_layers.size() - 1); - m_current_layer = m_layers[i]; - m_current_layer->m_selected = true; - if (on_layer_delete) - on_layer_delete(this, (int)std::distance(m_layers.begin(), it)); - if (on_layer_change) - on_layer_change(this, -1, i); - } - void handle_layer_opacity(NodeLayer* target, float value) - { - if (on_layer_opacity_changed) - on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value); - } - void handle_layer_visibility(NodeLayer* target, bool visible) - { - if (on_layer_visibility_changed) - on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible); - } - void handle_layer_selected(NodeLayer* target) - { - if (m_current_layer) - m_current_layer->m_selected = false; - m_current_layer = target; - m_current_layer->m_selected = true; - if (on_layer_change) - on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer)); - } - void clear() - { - m_layers_container->remove_all_children(); - m_layers.clear(); - } -}; - -class NodeButtonBrush : public NodeButtonCustom -{ -public: - int m_brushID; - bool m_selected = false; - NodeImage* img; - virtual Node* clone_instantiate() const override { return new NodeButtonBrush(); } - virtual void init() override - { - init_template("tpl-brush-icon"); - color_hover = glm::vec4(.7, .7, .7, 1); - color_normal = glm::vec4(.3, .3, .3, 1); - m_color = color_normal; - img = (NodeImage*)m_children[0].get(); - } - void set_icon(const char* path) - { - img->m_path = path; - img->m_tex_id = const_hash(img->m_path.c_str()); - img->create(); - } - virtual void draw() override - { - m_color = m_mouse_inside ? color_hover : color_normal; - m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color; - NodeButtonCustom::draw(); - } -}; - -class NodePanelBrush : public Node -{ - std::vector m_brushes; - NodeButtonBrush* m_current = nullptr; - Node* m_container; -public: - std::function on_brush_changed; - virtual Node* clone_instantiate() const override { return new NodePanelLayer(); } - virtual void init() override - { - init_template("tpl-panel-brushes"); - //m_layers_container = find("layers-container"); - static auto icons = FindAllBrushes("data/Icons/"); - if ((m_container = find("brushes"))) - { - int count = 0; - for (auto& i : icons) - { - std::string path = "data/Icons/" + i; - NodeButtonBrush* brush = new NodeButtonBrush; - m_container->add_child(brush); - brush->init(); - brush->create(); - brush->loaded(); - brush->set_icon(path.c_str()); - brush->m_brushID = count++; - m_brushes.push_back(brush); - brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1); - } - } - } - void handle_click(Node* target) - { - if (target == m_current) - return; - if (m_current) - m_current->m_selected = false; - m_current = (NodeButtonBrush*)target; - m_current->m_selected = true; - if (on_brush_changed) - on_brush_changed(this, m_current->m_brushID); - } - std::vector FindAllBrushes(std::string folder) - { - std::vector names; - std::string search_path = folder + "*.png"; -#ifdef _WIN32 - WIN32_FIND_DATAA fd; - HANDLE hFind = ::FindFirstFileA(search_path.c_str(), &fd); - if (hFind != INVALID_HANDLE_VALUE) { - do { - // read all (real) files in current folder - // , delete '!' read other 2 default folder . and .. - if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - names.push_back(fd.cFileName); - } - } while (::FindNextFileA(hFind, &fd)); - ::FindClose(hFind); - } -#elif __ANDROID__ - //LOG("listing brushes"); - AAssetDir* dir = AAssetManager_openDir(Asset::m_am, "data/Icons"); - while (const char* name = AAssetDir_getNextFileName(dir)) - { - //LOG("asset: %s", name); - names.push_back(name); - } - AAssetDir_close(dir); -#else - DIR *dp; - struct dirent *ep; - dp = opendir(folder.c_str()); - - if (dp != NULL) - { - while ((ep = readdir(dp))) - if (ep->d_type != DT_DIR) - names.push_back(ep->d_name); - closedir(dp); - } - else - LOG("Couldn't open the directory: %s", folder.c_str()); -#endif - return names; - } - uint16_t get_texture_id(int index) const - { - return m_brushes[index]->img->m_tex_id; - } -}; - -class NodeColorQuad : public NodeBorder -{ - NodeBorder* m_picker; - bool dragging = false; -public: - glm::vec2 m_value; - std::function on_value_changed; - virtual Node* clone_instantiate() const override { return new NodeColorQuad(); } - virtual void clone_finalize(Node* dest) const override - { - auto n = (NodeColorQuad*)dest; - n->m_picker = (NodeBorder*)n->m_children[0].get(); - } - virtual void init() override - { - m_picker = new NodeBorder; - m_picker->SetSize({ 20, 20 }); - m_picker->SetPositioning(YGPositionTypeAbsolute); - m_picker->SetPosition(0, 0); - m_picker->m_thinkness = 1; - m_picker->m_color = glm::vec4(0); - add_child(m_picker); - } - void set_value(float x, float y) - { - auto sz = GetSize(); - auto pos = glm::clamp(glm::vec2(x, y) * sz, { 0, 0 }, sz); - m_picker->SetPosition(pos.x, pos.y); - m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 - if (on_value_changed) - on_value_changed(this, m_value); - } - virtual kEventResult handle_event(Event* e) override - { - NodeBorder::handle_event(e); - switch (e->m_type) - { - case kEventType::MouseDownL: - { - dragging = true; - mouse_capture(); - auto sz = GetSize(); - auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) - m_picker->GetSize() * .5f; - m_picker->SetPosition(pos.x, pos.y); - m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 - if (on_value_changed) - on_value_changed(this, m_value); - } - break; - case kEventType::MouseUpL: - mouse_release(); - dragging = false; - break; - case kEventType::MouseMove: - if (dragging) - { - auto sz = GetSize(); - auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) - m_picker->GetSize() * .5f; - m_picker->SetPosition(pos.x, pos.y); - m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 - if (on_value_changed) - on_value_changed(this, m_value); - } - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } - virtual void draw() override - { - m_picker->m_border_color = m_value.y > .5f ? glm::vec4(1) : glm::vec4(0, 0, 0, 1); - using namespace ui; - ui::ShaderManager::use(kShader::ColorQuad); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); - m_plane.draw_fill(); - } -}; - -class NodePanelColor : public Node -{ -public: - NodeColorQuad* m_quad; - NodeSliderHue* m_hue; - glm::vec4 m_base_color; - glm::vec4 m_color; - glm::vec2 m_cursor; - std::function on_color_changed; - virtual Node* clone_instantiate() const override { return new NodePanelColor(); } - virtual void clone_finalize(Node* dest) const override - { - NodePanelColor* n = static_cast(dest); - n->init_controls(); - } - virtual void init() override - { - init_template("tpl-panel-color"); - init_controls(); - } - void init_controls() - { - m_quad = find("quad"); - m_hue = find("hue"); - m_hue->on_hue_changed = [this](Node*, glm::vec4 hue_color) { - m_base_color = m_quad->m_color = hue_color; - auto x = glm::mix(m_base_color, glm::vec4(1, 1, 1, 1), m_cursor.x); - m_color = glm::mix(x, glm::vec4(0, 0, 0, 1), m_cursor.y); - if (on_color_changed) - on_color_changed(this, m_color); - }; - m_quad->on_value_changed = [this](Node*, glm::vec2 pos) - { - auto x = glm::mix(m_base_color, glm::vec4(1, 1, 1, 1), pos.x); - m_color = glm::mix(x, glm::vec4(0, 0, 0, 1), pos.y); - m_cursor = pos; - if (on_color_changed) - on_color_changed(this, m_color); - }; - m_hue->set_value(0); - } -}; - -class NodeStrokePreview : public NodeBorder -{ - RTT m_rtt; - Sampler m_sampler; - ui::BrushMesh m_mesh; -public: - ui::Brush m_brush; - ui::Stroke m_stroke; - std::vector m_bez_points; - virtual Node* clone_instantiate() const override { return new NodeStrokePreview(); } - virtual void clone_copy(Node* dest) const override - { - NodeBorder::clone_copy(dest); - } - virtual void clone_children(Node* dest) const override - { - // stop children cloning - } - virtual void clone_finalize(Node* dest) const override - { - NodeStrokePreview* n = (NodeStrokePreview*)dest; - n->init_controls(); - } - void init_controls() - { - m_mesh.create(); - m_sampler.create(); - TextureManager::load("data/Icons/Round-Hard.png"); - m_brush.m_tex_id = const_hash("data/Icons/Round-Hard.png"); - } - virtual void restore_context() override - { - NodeBorder::restore_context(); - init_controls(); - if (m_size.x > 0 && m_size.y > 0) - m_rtt.create(m_size.x, m_size.y); - draw_stroke(); - } - virtual void clear_context() override - { - NodeBorder::clear_context(); - m_rtt.destroy(); - } - void draw_stroke() - { - m_rtt.bindFramebuffer(); - { - using namespace ui; - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - - glClearColor(1, 1, 1, 1); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); - glEnable(GL_BLEND); - glm::mat4 proj = glm::ortho(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1); - - m_stroke.reset(); - m_stroke.start(m_brush); - auto samples = m_stroke.compute_samples(); - auto& tex = TextureManager::get(m_brush.m_tex_id); - tex.bind(); - m_sampler.bind(0); - - if (true) - { - m_mesh.shader.use(); - m_mesh.shader.u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); - m_mesh.shader.u_int(kShaderUniform::Tex, 0); - m_mesh.draw(samples, proj); - } -// else -// { -// ShaderManager::use("stroke"); -// ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); -// ShaderManager::u_int(kShaderUniform::Tex, 0); -// for (const auto& s : samples) -// { -// auto mvp = proj * -// glm::translate(glm::vec3(s.pos, 0)) * -// glm::scale(glm::vec3(s.size, s.size, 1)) * -// glm::eulerAngleZ(s.angle); -// -// ShaderManager::u_mat4(kShaderUniform::MVP, mvp); -// ShaderManager::u_float(kShaderUniform::Alpha, s.flow); -// m_plane.draw_fill(); -// } -// } - - m_sampler.unbind(); - tex.unbind(); - glDisable(GL_BLEND); - - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); - } - m_rtt.unbindFramebuffer(); - } - virtual void draw() override - { - using namespace ui; - ui::ShaderManager::use(kShader::Texture); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); - ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - m_rtt.bindTexture(); - m_sampler.bind(0); - m_plane.draw_fill(); - m_sampler.unbind(); - m_rtt.unbindTexture(); - } - virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override - { - float pad = 30.f; - float w = new_size.x; - float h = new_size.y; - std::vector kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; - m_stroke.start(m_brush); - for (int i = 0; i < 20; i++) - m_stroke.add_point(BezierCurve::Bezier2D(kp, i / 20.f), 1.f); - - m_rtt.destroy(); - m_rtt.create((int)new_size.x, (int)new_size.y); - draw_stroke(); - } -}; - -class NodePanelStroke : public Node -{ -public: - NodeStrokePreview* m_canvas; - NodeSliderH* m_tip_size; - NodeSliderH* m_tip_spacing; - NodeSliderH* m_tip_flow; - NodeSliderH* m_tip_opacity; - NodeSliderH* m_tip_angle; - NodeSliderH* m_jitter_scale; - NodeSliderH* m_jitter_angle; - NodeSliderH* m_jitter_spread; - NodeSliderH* m_jitter_flow; - std::function on_stroke_change; - virtual Node* clone_instantiate() const override { return new NodePanelStroke(); } - virtual void clone_finalize(Node* dest) const override - { - NodePanelColor* n = static_cast(dest); - n->init_controls(); - } - virtual void init() override - { - init_template("tpl-panel-stroke"); - init_controls(); - } - void init_controls() - { - m_canvas = find("canvas"); - - init_slider(m_tip_size, "tip-size", &ui::Brush::m_tip_size); - init_slider(m_tip_spacing, "tip-spacing", &ui::Brush::m_tip_spacing); - init_slider(m_tip_flow, "tip-flow", &ui::Brush::m_tip_flow); - init_slider(m_tip_opacity, "tip-opacity", &ui::Brush::m_tip_opacity); - init_slider(m_tip_angle, "tip-angle", &ui::Brush::m_tip_angle); - init_slider(m_jitter_scale, "jitter-scale", &ui::Brush::m_jitter_scale); - init_slider(m_jitter_angle, "jitter-angle", &ui::Brush::m_jitter_angle); - init_slider(m_jitter_spread, "jitter-spread", &ui::Brush::m_jitter_spread); - init_slider(m_jitter_flow, "jitter-flow", &ui::Brush::m_jitter_flow); - //m_canvas->draw_stroke(); - } - void init_slider(NodeSliderH*& slider, const char* id, float ui::Brush::* prop) - { - slider = find(id); - slider->on_value_changed = std::bind(&NodePanelStroke::handle_slide, - this, prop, std::placeholders::_1, std::placeholders::_2); - m_canvas->m_brush.*prop = slider->m_value.x; - } - void handle_slide(float ui::Brush::* prop, Node* target, float value) - { - m_canvas->m_brush.*prop = value; - m_canvas->draw_stroke(); - if (on_stroke_change) - on_stroke_change(this); - } -}; - -class NodeDialogOpen : public NodeBorder -{ -public: - NodeButton* btn_cancel; - NodeButton* btn_ok; - std::string data_path; - virtual Node* clone_instantiate() const override { return new NodeDialogOpen(); } - virtual void clone_finalize(Node* dest) const override - { - NodeDialogOpen* n = static_cast(dest); - n->init_controls(); - } - virtual void init() override - { - auto tpl = static_cast(init_template("dialog-open")); - m_color = tpl->m_color; - m_border_color = tpl->m_border_color;; - m_thinkness = tpl->m_thinkness;; - init_controls(); - } - void init_controls() - { - btn_ok = find("btn-ok"); - btn_cancel = find("btn-cancel"); - btn_cancel->on_click = [this](Node*) { - destroy(); - }; - } - virtual void loaded() override - { - ui::Image thumb = ui::Canvas::I->thumbnail_read(data_path); - auto image_tex = find("thumb-tex"); - image_tex->tex.create(thumb); - } -}; - -class NodeCanvas : public Node -{ -public: - std::string data_path; - std::unique_ptr m_canvas; - ui::Brush m_brush; - Sampler m_sampler; - ui::Plane m_face_plane; - ui::LineSegment m_line; - virtual Node* clone_instantiate() const override { return new NodeCanvas(); } - virtual void init() override - { - m_mouse_ignore = false; - m_canvas = std::make_unique(); - m_canvas->create(1024, 1024); - m_sampler.create(GL_NEAREST); - m_face_plane.create<1>(2, 2); - m_line.create(); - CanvasMode::node = this; - CanvasMode::canvas = m_canvas.get(); - for (int i = 0; i < (int)ui::Canvas::kCanvasMode::COUNT; i++) - for (auto m : ui::Canvas::modes[i]) - m->init(); - } - virtual void restore_context() override - { - Node::restore_context(); - m_canvas->create(1024, 1024); - m_sampler.create(GL_NEAREST); - m_face_plane.create<1>(2, 2); - m_canvas->snapshot_restore(); - CanvasMode::node = this; - CanvasMode::canvas = m_canvas.get(); - for (int i = 0; i < (int)ui::Canvas::kCanvasMode::COUNT; i++) - for (auto m : ui::Canvas::modes[i]) - m->init(); - } - virtual void clear_context() override - { - Node::clear_context(); - m_canvas->snapshot_save(data_path); - m_canvas->clear_context(); - // TODO: clear CanvasMode objects - } - virtual void draw() override - { - using namespace ui; - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - float zoom = root()->m_zoom; - auto box = m_clip * zoom; - glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w); - glViewport(c.x, c.y, c.z, c.w); - - //m_canvas->m_cam_rot = m_pan * 0.003f; - - glm::mat4 ortho_proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f); - glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.1f, 1000.f); - glm::mat4 camera = glm::eulerAngleXY(m_canvas->m_cam_rot.y, m_canvas->m_cam_rot.x) * - glm::translate(m_canvas->m_cam_pos); - - m_canvas->m_mv = camera; - m_canvas->m_proj = proj; - m_canvas->m_box = box; - -// auto plane_mvp = proj * camera * transform * -// glm::scale(glm::vec3(sz, 1)); - - - m_sampler.bind(0); - auto blend = glIsEnabled(GL_BLEND); - - glEnable(GL_BLEND); - //glEnable(GL_DEPTH_TEST); - glClear(GL_DEPTH_BUFFER_BIT); - for (int plane_index = 0; plane_index < 6; plane_index++) - { - auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_order.size())) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); - - ui::ShaderManager::use(kShader::Checkerboard); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); - m_face_plane.draw_fill(); - - ui::ShaderManager::use(kShader::TextureAlpha); - ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - for (auto layer_index : m_canvas->m_order) - { - int z = m_canvas->m_order.size() - layer_index; - auto plane_mvp_z = proj * camera * glm::scale(glm::vec3(z)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); - if (!(m_canvas->m_state == ui::Canvas::kCanvasMode::Erase && - m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)) - { - ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); - m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); - m_face_plane.draw_fill(); - m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture(); - } - if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) - { - ui::ShaderManager::u_float(kShaderUniform::Alpha, - m_canvas->m_current_stroke->m_brush.m_tip_opacity * m_canvas->m_layers[layer_index].m_opacity); - m_canvas->m_tmp[plane_index].bindTexture(); - m_face_plane.draw_fill(); - m_canvas->m_tmp[plane_index].unbindTexture(); - } - } - } - glDisable(GL_DEPTH_TEST); - - for (auto& mode : *m_canvas->m_mode) - mode->on_Draw(ortho_proj, proj, camera); - - // keep drawing the grids - if (m_canvas->m_state != ui::Canvas::kCanvasMode::Grid) - for (auto& mode : ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid]) - mode->on_Draw(ortho_proj, proj, camera); - - //ui::ShaderManager::use(kShader::Equirect); - //ui::ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(.5, .5, 1))); - //ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - //glBindTexture(GL_TEXTURE_CUBE_MAP, m_canvas->cube_id); - //m_face_plane.draw_fill(); - //glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - -// ui::ShaderManager::use(kShader::Color); -// ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); -// ui::ShaderManager::u_vec4(kShaderUniform::Col, { 1, 0, 0, 1 }); -// static glm::vec4 AB[4]{ {-.75, 0, -1, 1},{ -.75, 0, 1, 1 } }; -// m_line.update_vertices(AB); -// m_line.draw_stroke(); - - blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - m_sampler.unbind(); - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); - } - virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override - { - if (new_size.x > m_canvas->m_width) - { -// m_canvas->resize((int)new_size.x, (int)new_size.y); -// m_canvas->clear(); - } - } - virtual kEventResult handle_event(Event* e) override - { - Node::handle_event(e); - MouseEvent* me = static_cast(e); - KeyEvent* ke = static_cast(e); - GestureEvent* ge = static_cast(e); - auto loc = (me->m_pos - m_pos) * root()->m_zoom; - - switch (e->m_type) - { - case kEventType::MouseDownL: - case kEventType::MouseUpL: - case kEventType::MouseDownR: - case kEventType::MouseUpR: - case kEventType::MouseMove: - case kEventType::MouseScroll: - case kEventType::MouseCancel: - for (auto& mode : *m_canvas->m_mode) - mode->on_MouseEvent(me, loc); - break; - case kEventType::KeyDown: -// if (ke->m_key == kKey::KeyE) -// m_canvas->m_state = ui::Canvas::kPenState::Erase; -// if (ke->m_key == kKey::KeySpacebar) -// m_canvas->m_alpha_lock = true; -// if (ke->m_key == kKey::AndroidVolumeUp) -// m_zoom_canvas *= 0.9f; -// if (ke->m_key == kKey::AndroidVolumeDown) -// m_zoom_canvas *= 1.1f; - if (ke->m_key == kKey::AndroidBack) - if (!ActionManager::empty()) - ActionManager::undo(); - break; - case kEventType::KeyUp: -// if (ke->m_key == kKey::KeyE) -// m_canvas->m_state = ui::Canvas::kPenState::Draw; -// if (ke->m_key == kKey::KeySpacebar) -// m_canvas->m_alpha_lock = false; - break; - case kEventType::GestureStart: - case kEventType::GestureMove: - for (auto& mode : *m_canvas->m_mode) - mode->on_GestureEvent(ge); - break; - default: - return kEventResult::Available; - break; - } - return kEventResult::Consumed; - } -}; diff --git a/engine/node.cpp b/engine/node.cpp new file mode 100644 index 0000000..91fd0d8 --- /dev/null +++ b/engine/node.cpp @@ -0,0 +1,804 @@ +#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" + +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) +{ + if (current_mouse_capture) + return current_mouse_capture->on_event(e); + + kEventResult ret = kEventResult::Available; + 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::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; + } + 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; +} + +void Node::mouse_capture() +{ + root()->current_mouse_capture = this; m_mouse_captured = true; +} + +void Node::mouse_release() +{ + 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); +} + +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() }; +} + +glm::vec4 Node::rect_intersection(glm::vec4 a, glm::vec4 b) +{ + // convert from [x,y,w,h] to [x1,y1,x2,y1] + a = glm::vec4(a.xy(), a.xy() + a.zw()); + b = glm::vec4(b.xy(), b.xy() + b.zw()); + auto o = glm::vec4(glm::max(a.xy(), b.xy()), glm::min(a.zw(), b.zw())); + o = glm::vec4(o.xy(), glm::max({ 0, 0 }, o.zw() - o.xy())); + return o; +} + +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; + m_pos = 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, m_size + off_p + off_s); + m_clip = rect_intersection(m_clip_uncut, parent->m_clip); + } + else + { + 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; + default: + break; + } +} + +void Node::load_internal(const tinyxml2::XMLElement* x_node) +{ + m_name = x_node->Name(); + + 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); +#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 */ +} diff --git a/engine/node.h b/engine/node.h new file mode 100644 index 0000000..889194d --- /dev/null +++ b/engine/node.h @@ -0,0 +1,185 @@ +#pragma once +#include "event.h" + +enum class kAttribute : uint16_t +{ + id = const_hash("id"), + Width = const_hash("width"), + MinWidth = const_hash("min-width"), + MaxWidth = const_hash("max-width"), + Height = const_hash("height"), + MinHeight = const_hash("min-height"), + MaxHeight = const_hash("max-height"), + Divisions = const_hash("divisions"), + InnerRadius = const_hash("inner-radius"), + OuterRadius = const_hash("outer-radius"), + Grow = const_hash("grow"), + Shrink = const_hash("shrink"), + FlexDir = const_hash("dir"), + FlexWrap = const_hash("wrap"), + Padding = const_hash("pad"), + Margin = const_hash("margin"), + Color = const_hash("color"), + Thickness = const_hash("thickness"), + BorderColor = const_hash("border-color"), + Type = const_hash("type"), + Text = const_hash("text"), + FontFace = const_hash("font-face"), + FontSize = const_hash("font-size"), + Justify = const_hash("justify"), + Align = const_hash("align"), + Path = const_hash("path"), + Region = const_hash("region"), + Position = const_hash("position"), + Positioning = const_hash("positioning"), + FloodEvents = const_hash("flood-events"), + Icon = const_hash("icon"), + Selected = const_hash("selected"), + Template = const_hash("template"), + Value = const_hash("value"), + Range = const_hash("range"), +}; + +enum class kWidget : uint16_t +{ + Node = const_hash("node"), + Border = const_hash("border"), + Shape = const_hash("shape"), + Text = const_hash("text"), + TextInput = const_hash("text-input"), + Image = const_hash("image"), + ImageTexture = const_hash("image-texture"), + Icon = const_hash("icon"), + Button = const_hash("button"), + ButtonCustom = const_hash("button-custom"), + SliderH = const_hash("slider-h"), + SliderV = const_hash("slider-v"), + SliderHue = const_hash("slider-hue"), + PopupMenu = const_hash("popup-menu"), + Viewport = const_hash("viewport"), + Ref = const_hash("ref"), + CheckBox = const_hash("checkbox"), + Layer = const_hash("layer"), + PanelLayer = const_hash("panel-layer"), + PanelBrush = const_hash("panel-brush"), + PanelColor = const_hash("panel-color"), + PanelStroke = const_hash("panel-stroke"), + ColorQuad = const_hash("color-quad"), + StrokePreview = const_hash("stroke-preview"), + Canvas = const_hash("canvas"), +}; + +class Node +{ + friend class LayoutManager; +public: + Node* parent{ nullptr }; + YGNodeRef y_node{ nullptr }; + class LayoutManager* m_manager; + uint16_t m_nodeID; + std::string m_nodeID_s; + std::vector> m_children; + Node* current_mouse_capture = nullptr; + Node* current_key_capture = nullptr; + bool m_mouse_captured = false; + bool m_key_captured = false; + + glm::mat4 m_proj; + glm::mat4 m_mvp; + bool m_mouse_inside = false; + bool m_flood_events = false; + bool m_destroyed = false; + + bool m_mouse_ignore = true; + float m_zoom = 1.f; + glm::vec2 m_scale{ 1.f }; + glm::vec2 m_pos; + glm::vec2 m_size; + glm::vec4 m_clip; + glm::vec4 m_clip_uncut; + std::string m_name; + bool m_display = true; + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node&& operator=(Node&& o); + Node(Node&& o); + Node(); + ~Node(); + + void SetWidth(float value); + void SetWidthP(float value); + void SetHeight(float value); + void SetHeightP(float value); + void SetSize(glm::vec2 value); + void SetSize(float w, float h); + + void SetPadding(float t, float r, float b, float l); + void SetPosition(float l, float t, float r, float b); + void SetPosition(float l, float t); + void SetPosition(const glm::vec2 pos); + + void SetFlexGrow(float value); + void SetFlexShrink(float value); + void SetFlexDir(YGFlexDirection value); + void SetFlexWrap(YGWrap value); + void SetJustify(YGJustify value); + void SetAlign(YGAlign value); + void SetPositioning(YGPositionType value); + void SetAspectRatio(float ar); + + glm::vec2 GetPosition(); + float GetWidth(); + float GetHeight(); + glm::vec2 GetSize(); + + glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b); + + virtual void restore_context();; + virtual void clear_context(); + void update(float width, float height, float zoom); + void update(); + void update_internal(const glm::vec2& origin, const glm::mat4& proj); + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr); + void load_internal(const tinyxml2::XMLElement* x_node); + virtual void draw(); + Node* clone(); + virtual Node* clone_instantiate() const; + virtual void clone_copy(Node* dest) const; + virtual void clone_children(Node* dest) const; + virtual void clone_finalize(Node* dest) const;; + void watch(std::function observer); + void destroy(); + Node* root(); + + template T* find(const char* ids) + { + uint16_t id = const_hash(ids); + if (id == m_nodeID) + return static_cast(this); + for (auto& c : m_children) + if (auto found = c->find(ids)) + return static_cast(found); + return nullptr; + } + virtual kEventResult on_event(Event* e); + virtual kEventResult handle_event(Event* e); + virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size);; + virtual void create(); + virtual void init(); + virtual void loaded(); + const Node* init_template(const char* id); + void add_child(Node* n); + void add_child(Node* n, int index); + void add_child(std::shared_ptr n); + void add_child(std::shared_ptr n, int index); + void remove_child(Node* n); + void remove_all_children(); + void move_child(Node* n, int index); + void move_child_offset(Node* n, int offset); + int get_child_index(Node* n); + void mouse_capture(); + void mouse_release(); + void key_capture(); + void key_release(); +}; + diff --git a/engine/node_border.cpp b/engine/node_border.cpp new file mode 100644 index 0000000..659b509 --- /dev/null +++ b/engine/node_border.cpp @@ -0,0 +1,83 @@ +#include "pch.h" +#include "log.h" +#include "node_border.h" +#include "shader.h" + +ui::Plane NodeBorder::m_plane; + +NodeBorder::NodeBorder() +{ + m_mouse_ignore = false; +} +void NodeBorder::static_init() +{ + m_plane.create<1>(1, 1); +} +Node* NodeBorder::clone_instantiate() const +{ + return new NodeBorder(); +} +void NodeBorder::clone_copy(Node* dest) const +{ + Node::clone_copy(dest); + NodeBorder* n = static_cast(dest); + n->m_color = m_color; + n->m_border_color = m_border_color; + n->m_thinkness = m_thinkness; + n->m_mouse_ignore = false; +} +void NodeBorder::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + Node::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Color: + { + glm::vec4 pad; + int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); + if (n == 1) + m_color = glm::vec4(pad.x, pad.x, pad.x, 1); + else + m_color = pad; + break; + } + case kAttribute::BorderColor: + { + glm::vec4 pad; + int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); + if (n == 1) + m_border_color = glm::vec4(pad.x); + else + m_border_color = pad; + break; + } + case kAttribute::Thickness: + m_thinkness = attr->FloatValue(); + break; + default: + break; + } +} +void NodeBorder::draw() +{ + using namespace ui; + ui::ShaderManager::use(kShader::Color); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + + if (m_color.a > 0.f) + { + m_color.a < 1.f ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); + m_plane.draw_fill(); + glDisable(GL_BLEND); + } + + if (m_thinkness > 0 && m_border_color.a > 0.f) + { + glLineWidth(m_thinkness); + ui::ShaderManager::u_vec4(kShaderUniform::Col, m_border_color); + m_border_color.a < 1.f ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + m_plane.draw_stroke(); + glDisable(GL_BLEND); + } +} diff --git a/engine/node_border.h b/engine/node_border.h new file mode 100644 index 0000000..9a9440f --- /dev/null +++ b/engine/node_border.h @@ -0,0 +1,18 @@ +#pragma once +#include "node.h" +#include "shape.h" + +class NodeBorder : public Node +{ +public: + static ui::Plane m_plane; + glm::vec4 m_color{ 0, 0, 0, 1 }; + glm::vec4 m_border_color{ 1, 1, 1, 1 }; + float m_thinkness{ 0 }; + NodeBorder(); + static void static_init(); + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + virtual void draw() override; +}; diff --git a/engine/node_button.cpp b/engine/node_button.cpp new file mode 100644 index 0000000..24ccb65 --- /dev/null +++ b/engine/node_button.cpp @@ -0,0 +1,120 @@ +#include "pch.h" +#include "log.h" +#include "node_button.h" + +Node* NodeButton::clone_instantiate() const +{ + return new NodeButton(); +} + +void NodeButton::clone_children(Node* dest) const +{ + Node::clone_children(dest); + NodeButton* n = static_cast(dest); + n->m_border = (NodeBorder*)n->m_children[0].get(); + n->m_text = (NodeText*)n->m_border->m_children[0].get(); +} + +void NodeButton::clone_copy(Node* dest) const +{ + Node::clone_copy(dest); + NodeButton* n = static_cast(dest); + //n->m_border = (NodeBorder*)m_border->clone(); + //n->m_text = (NodeText*)m_text->clone(); + n->color_normal = color_normal; + n->color_hover = color_hover; + n->color_down = color_down; + //n->on_click = on_click; + n->m_mouse_ignore = false; +} + +void NodeButton::init() +{ + m_border = new NodeBorder(); + m_text = new NodeText(); + add_child(m_border); + m_border->add_child(m_text); + m_border->init(); + m_border->m_color = color_normal; + m_text->init(); + m_text->m_font = "arial"; + m_text->m_font_size = 11; + m_border->SetAlign(YGAlignCenter); + m_border->SetJustify(YGJustifyCenter); + m_border->m_mouse_ignore = false; + m_mouse_ignore = false; +} + +void NodeButton::create() +{ + m_border->create(); + m_text->create(); + m_border->m_mouse_ignore = false; + m_mouse_ignore = false; +} + +void NodeButton::loaded() +{ + m_border->m_thinkness = 1; + m_border->m_border_color = glm::vec4(0, 0, 0, 1); + m_border->m_color = color_normal; + m_border->m_mouse_ignore = false; + m_mouse_ignore = false; +} + +void NodeButton::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + switch (ka) + { + case kAttribute::Padding: + case kAttribute::Width: + case kAttribute::Height: + case kAttribute::Color: + case kAttribute::Thickness: + case kAttribute::BorderColor: + m_border->parse_attributes(ka, attr); + break; + case kAttribute::Text: + case kAttribute::FontFace: + case kAttribute::FontSize: + m_text->parse_attributes(ka, attr); + break; + default: + Node::parse_attributes(ka, attr); + break; + } + // m_border->parse_attributes(ka, attr); + // m_text->parse_attributes(ka, attr); +} + +void NodeButton::set_color(const glm::vec4& c) +{ + color_normal = c; + m_border->m_color = color_normal; +} + +kEventResult NodeButton::handle_event(Event* e) +{ + Node::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseEnter: + m_border->m_color = color_hover; + break; + case kEventType::MouseLeave: + m_border->m_color = color_normal; + break; + case kEventType::MouseDownL: + m_border->m_color = color_down; + break; + case kEventType::MouseUpL: + m_border->m_color = color_normal; + if (m_mouse_inside && on_click != nullptr) + on_click(this); + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} diff --git a/engine/node_button.h b/engine/node_button.h new file mode 100644 index 0000000..209a455 --- /dev/null +++ b/engine/node_button.h @@ -0,0 +1,24 @@ +#pragma once +#include "node.h" +#include "node_border.h" +#include "node_text.h" + +class NodeButton : public Node +{ +public: + NodeBorder* m_border; + NodeText* m_text; + glm::vec4 color_normal{ .1, .1, .1, 1 }; + glm::vec4 color_hover{ .2, .2, .2, 1 }; + glm::vec4 color_down{ .3, .3, .3, 1 }; + std::function on_click; + virtual Node* clone_instantiate() const override; + virtual void clone_children(Node* dest) const override; + virtual void clone_copy(Node* dest) const override; + virtual void init() override; + virtual void create() override; + virtual void loaded() override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + void set_color(const glm::vec4& c); + virtual kEventResult handle_event(Event* e) override; +}; diff --git a/engine/node_button_custom.cpp b/engine/node_button_custom.cpp new file mode 100644 index 0000000..3e02ba7 --- /dev/null +++ b/engine/node_button_custom.cpp @@ -0,0 +1,73 @@ +#include "pch.h" +#include "log.h" +#include "node_button_custom.h" + +Node* NodeButtonCustom::clone_instantiate() const +{ + return new NodeButtonCustom(); +} + +void NodeButtonCustom::clone_copy(Node* dest) const +{ + NodeBorder::clone_copy(dest); + NodeButtonCustom* n = static_cast(dest); + n->color_normal = color_normal; + n->color_hover = color_hover; + n->color_down = color_down; + n->m_mouse_ignore = false; + n->m_color = color_normal; +} + +void NodeButtonCustom::loaded() +{ + NodeBorder::loaded(); + //m_thinkness = 1; + //m_border_color = glm::vec4(0, 0, 0, 1); + m_color = color_normal; + m_mouse_ignore = false; +} + +void NodeButtonCustom::set_color(const glm::vec4& c) +{ + color_normal = c; + m_color = color_normal; +} + +kEventResult NodeButtonCustom::handle_event(Event* e) +{ + NodeBorder::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseEnter: + m_color = color_hover; + break; + case kEventType::MouseLeave: + m_color = color_normal; + break; + case kEventType::MouseDownL: + m_color = color_down; + break; + case kEventType::MouseUpL: + m_color = m_mouse_inside ? color_hover : color_normal; + if (m_mouse_inside && on_click != nullptr) + on_click(this); + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} + +void NodeButtonCustom::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + NodeBorder::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Color: + color_normal = m_color; + break; + default: + break; + } +} diff --git a/engine/node_button_custom.h b/engine/node_button_custom.h new file mode 100644 index 0000000..58c52cc --- /dev/null +++ b/engine/node_button_custom.h @@ -0,0 +1,17 @@ +#pragma once +#include "node_border.h" + +class NodeButtonCustom : public NodeBorder +{ +public: + glm::vec4 color_normal{ .2, .2, .2, 1 }; + glm::vec4 color_hover{ .3, .3, .3, 1 }; + glm::vec4 color_down{ .4, .4, .4, 1 }; + std::function on_click; + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void loaded() override; + void set_color(const glm::vec4& c); + virtual kEventResult handle_event(Event* e) override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; +}; diff --git a/engine/node_canvas.cpp b/engine/node_canvas.cpp new file mode 100644 index 0000000..5d44acb --- /dev/null +++ b/engine/node_canvas.cpp @@ -0,0 +1,204 @@ +#include "pch.h" +#include "log.h" +#include "node_canvas.h" + +Node* NodeCanvas::clone_instantiate() const +{ + return new NodeCanvas(); +} + +void NodeCanvas::init() +{ + m_mouse_ignore = false; + m_canvas = std::make_unique(); + m_canvas->create(1024, 1024); + m_sampler.create(GL_NEAREST); + m_face_plane.create<1>(2, 2); + m_line.create(); + CanvasMode::node = this; + CanvasMode::canvas = m_canvas.get(); + for (int i = 0; i < (int)ui::Canvas::kCanvasMode::COUNT; i++) + for (auto m : ui::Canvas::modes[i]) + m->init(); +} + +void NodeCanvas::restore_context() +{ + Node::restore_context(); + m_canvas->create(1024, 1024); + m_sampler.create(GL_NEAREST); + m_face_plane.create<1>(2, 2); + m_canvas->snapshot_restore(); + CanvasMode::node = this; + CanvasMode::canvas = m_canvas.get(); + for (int i = 0; i < (int)ui::Canvas::kCanvasMode::COUNT; i++) + for (auto m : ui::Canvas::modes[i]) + m->init(); +} + +void NodeCanvas::clear_context() +{ + Node::clear_context(); + m_canvas->snapshot_save(data_path); + m_canvas->clear_context(); + // TODO: clear CanvasMode objects +} + +void NodeCanvas::draw() +{ + using namespace ui; + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + float zoom = root()->m_zoom; + auto box = m_clip * zoom; + glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w); + glViewport(c.x, c.y, c.z, c.w); + + //m_canvas->m_cam_rot = m_pan * 0.003f; + + glm::mat4 ortho_proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f); + glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.1f, 1000.f); + glm::mat4 camera = glm::eulerAngleXY(m_canvas->m_cam_rot.y, m_canvas->m_cam_rot.x) * + glm::translate(m_canvas->m_cam_pos); + + m_canvas->m_mv = camera; + m_canvas->m_proj = proj; + m_canvas->m_box = box; + + // auto plane_mvp = proj * camera * transform * + // glm::scale(glm::vec3(sz, 1)); + + + m_sampler.bind(0); + auto blend = glIsEnabled(GL_BLEND); + + glEnable(GL_BLEND); + //glEnable(GL_DEPTH_TEST); + glClear(GL_DEPTH_BUFFER_BIT); + for (int plane_index = 0; plane_index < 6; plane_index++) + { + auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_order.size())) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); + + ui::ShaderManager::use(kShader::Checkerboard); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); + m_face_plane.draw_fill(); + + ui::ShaderManager::use(kShader::TextureAlpha); + ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + for (auto layer_index : m_canvas->m_order) + { + int z = m_canvas->m_order.size() - layer_index; + auto plane_mvp_z = proj * camera * glm::scale(glm::vec3(z)) * m_canvas->m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); + if (!(m_canvas->m_state == ui::Canvas::kCanvasMode::Erase && + m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)) + { + ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); + m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); + m_face_plane.draw_fill(); + m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture(); + } + if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index) + { + ui::ShaderManager::u_float(kShaderUniform::Alpha, + m_canvas->m_current_stroke->m_brush.m_tip_opacity * m_canvas->m_layers[layer_index].m_opacity); + m_canvas->m_tmp[plane_index].bindTexture(); + m_face_plane.draw_fill(); + m_canvas->m_tmp[plane_index].unbindTexture(); + } + } + } + glDisable(GL_DEPTH_TEST); + + for (auto& mode : *m_canvas->m_mode) + mode->on_Draw(ortho_proj, proj, camera); + + // keep drawing the grids + if (m_canvas->m_state != ui::Canvas::kCanvasMode::Grid) + for (auto& mode : ui::Canvas::modes[(int)ui::Canvas::kCanvasMode::Grid]) + mode->on_Draw(ortho_proj, proj, camera); + + //ui::ShaderManager::use(kShader::Equirect); + //ui::ShaderManager::u_mat4(kShaderUniform::MVP, glm::scale(glm::vec3(.5, .5, 1))); + //ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + //glBindTexture(GL_TEXTURE_CUBE_MAP, m_canvas->cube_id); + //m_face_plane.draw_fill(); + //glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + +// ui::ShaderManager::use(kShader::Color); +// ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera); +// ui::ShaderManager::u_vec4(kShaderUniform::Col, { 1, 0, 0, 1 }); +// static glm::vec4 AB[4]{ {-.75, 0, -1, 1},{ -.75, 0, 1, 1 } }; +// m_line.update_vertices(AB); +// m_line.draw_stroke(); + + blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + m_sampler.unbind(); + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); +} + +void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size) +{ + if (new_size.x > m_canvas->m_width) + { + // m_canvas->resize((int)new_size.x, (int)new_size.y); + // m_canvas->clear(); + } +} + +kEventResult NodeCanvas::handle_event(Event* e) +{ + Node::handle_event(e); + MouseEvent* me = static_cast(e); + KeyEvent* ke = static_cast(e); + GestureEvent* ge = static_cast(e); + auto loc = (me->m_pos - m_pos) * root()->m_zoom; + + switch (e->m_type) + { + case kEventType::MouseDownL: + case kEventType::MouseUpL: + case kEventType::MouseDownR: + case kEventType::MouseUpR: + case kEventType::MouseMove: + case kEventType::MouseScroll: + case kEventType::MouseCancel: + for (auto& mode : *m_canvas->m_mode) + mode->on_MouseEvent(me, loc); + break; + case kEventType::KeyDown: + // if (ke->m_key == kKey::KeyE) + // m_canvas->m_state = ui::Canvas::kPenState::Erase; + // if (ke->m_key == kKey::KeySpacebar) + // m_canvas->m_alpha_lock = true; + // if (ke->m_key == kKey::AndroidVolumeUp) + // m_zoom_canvas *= 0.9f; + // if (ke->m_key == kKey::AndroidVolumeDown) + // m_zoom_canvas *= 1.1f; + if (ke->m_key == kKey::AndroidBack) + if (!ActionManager::empty()) + ActionManager::undo(); + break; + case kEventType::KeyUp: + // if (ke->m_key == kKey::KeyE) + // m_canvas->m_state = ui::Canvas::kPenState::Draw; + // if (ke->m_key == kKey::KeySpacebar) + // m_canvas->m_alpha_lock = false; + break; + case kEventType::GestureStart: + case kEventType::GestureMove: + for (auto& mode : *m_canvas->m_mode) + mode->on_GestureEvent(ge); + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} diff --git a/engine/node_canvas.h b/engine/node_canvas.h new file mode 100644 index 0000000..0e9ab0f --- /dev/null +++ b/engine/node_canvas.h @@ -0,0 +1,21 @@ +#pragma once +#include "node.h" +#include "canvas.h" + +class NodeCanvas : public Node +{ +public: + std::string data_path; + std::unique_ptr m_canvas; + ui::Brush m_brush; + Sampler m_sampler; + ui::Plane m_face_plane; + ui::LineSegment m_line; + virtual Node* clone_instantiate() const override; + virtual void init() override; + virtual void restore_context() override; + virtual void clear_context() override; + virtual void draw() override; + virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override; + virtual kEventResult handle_event(Event* e) override; +}; diff --git a/engine/node_checkbox.cpp b/engine/node_checkbox.cpp new file mode 100644 index 0000000..b549c50 --- /dev/null +++ b/engine/node_checkbox.cpp @@ -0,0 +1,75 @@ +#include "pch.h" +#include "log.h" +#include "node_checkbox.h" + +Node* NodeCheckBox::clone_instantiate() const +{ + return new NodeCheckBox(); +} + +void NodeCheckBox::clone_children(Node* dest) const +{ + Node::clone_children(dest); + NodeCheckBox* n = static_cast(dest); + n->m_outer = (NodeBorder*)n->m_children[0].get(); + n->m_inner = (NodeBorder*)n->m_outer->m_children[0].get(); + n->m_mouse_ignore = false; +} + +void NodeCheckBox::init() +{ + m_outer = new NodeBorder(); + m_inner = new NodeBorder(); + add_child(m_outer); + m_outer->add_child(m_inner); + m_outer->init(); + m_outer->m_color = { .3, .3, .3, 1 }; + m_outer->SetAlign(YGAlignCenter); + m_outer->SetJustify(YGJustifyCenter); + m_outer->SetPadding(5, 5, 5, 5); + m_outer->SetWidthP(100); + m_outer->SetHeightP(100); + m_outer->m_mouse_ignore = false; + m_inner->init(); + m_inner->SetWidthP(100); + m_inner->SetHeightP(100); + m_inner->m_border_color = glm::vec4(.8, .8, .8, 1); + m_inner->m_thinkness = 1; + m_inner->m_color = glm::vec4(.8, .8, .8, 1); + m_mouse_ignore = false; +} + +void NodeCheckBox::create() +{ + m_outer->create(); + m_inner->create(); +} + +kEventResult NodeCheckBox::handle_event(Event* e) +{ + Node::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseEnter: + break; + case kEventType::MouseLeave: + break; + case kEventType::MouseDownL: + break; + case kEventType::MouseUpL: + checked = !checked; + if (on_value_changed) + on_value_changed(this, checked); + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} + +void NodeCheckBox::draw() +{ + m_inner->m_color = checked ? glm::vec4(.4, .4, .4, 1) : glm::vec4(.8, .8, .8, 1); + Node::draw(); +} diff --git a/engine/node_checkbox.h b/engine/node_checkbox.h new file mode 100644 index 0000000..c7e5fd1 --- /dev/null +++ b/engine/node_checkbox.h @@ -0,0 +1,18 @@ +#pragma once +#include "node.h" +#include "node_border.h" + +class NodeCheckBox : public Node +{ +public: + std::function on_value_changed; + NodeBorder* m_outer; + NodeBorder* m_inner; + bool checked = false; + virtual Node* clone_instantiate() const override; + virtual void clone_children(Node* dest) const override; + virtual void init() override; + virtual void create() override; + virtual kEventResult handle_event(Event* e) override; + virtual void draw() override; +}; diff --git a/engine/node_color_quad.cpp b/engine/node_color_quad.cpp new file mode 100644 index 0000000..01fab9e --- /dev/null +++ b/engine/node_color_quad.cpp @@ -0,0 +1,85 @@ +#include "pch.h" +#include "log.h" +#include "node_color_quad.h" +#include "shader.h" + +Node* NodeColorQuad::clone_instantiate() const +{ + return new NodeColorQuad(); +} + +void NodeColorQuad::clone_finalize(Node* dest) const +{ + auto n = (NodeColorQuad*)dest; + n->m_picker = (NodeBorder*)n->m_children[0].get(); +} + +void NodeColorQuad::init() +{ + m_picker = new NodeBorder; + m_picker->SetSize({ 20, 20 }); + m_picker->SetPositioning(YGPositionTypeAbsolute); + m_picker->SetPosition(0, 0); + m_picker->m_thinkness = 1; + m_picker->m_color = glm::vec4(0); + add_child(m_picker); +} + +void NodeColorQuad::set_value(float x, float y) +{ + auto sz = GetSize(); + auto pos = glm::clamp(glm::vec2(x, y) * sz, { 0, 0 }, sz); + m_picker->SetPosition(pos.x, pos.y); + m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 + if (on_value_changed) + on_value_changed(this, m_value); +} + +kEventResult NodeColorQuad::handle_event(Event* e) +{ + NodeBorder::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseDownL: + { + dragging = true; + mouse_capture(); + auto sz = GetSize(); + auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) - m_picker->GetSize() * .5f; + m_picker->SetPosition(pos.x, pos.y); + m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 + if (on_value_changed) + on_value_changed(this, m_value); + } + break; + case kEventType::MouseUpL: + mouse_release(); + dragging = false; + break; + case kEventType::MouseMove: + if (dragging) + { + auto sz = GetSize(); + auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) - m_picker->GetSize() * .5f; + m_picker->SetPosition(pos.x, pos.y); + m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 + if (on_value_changed) + on_value_changed(this, m_value); + } + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} + +void NodeColorQuad::draw() +{ + m_picker->m_border_color = m_value.y > .5f ? glm::vec4(1) : glm::vec4(0, 0, 0, 1); + using namespace ui; + ui::ShaderManager::use(kShader::ColorQuad); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); + m_plane.draw_fill(); +} diff --git a/engine/node_color_quad.h b/engine/node_color_quad.h new file mode 100644 index 0000000..e19e7fe --- /dev/null +++ b/engine/node_color_quad.h @@ -0,0 +1,17 @@ +#pragma once +#include "node_border.h" + +class NodeColorQuad : public NodeBorder +{ + NodeBorder* m_picker; + bool dragging = false; +public: + glm::vec2 m_value; + std::function on_value_changed; + virtual Node* clone_instantiate() const override; + virtual void clone_finalize(Node* dest) const override; + virtual void init() override; + void set_value(float x, float y); + virtual kEventResult handle_event(Event* e) override; + virtual void draw() override; +}; diff --git a/engine/node_dialog_open.cpp b/engine/node_dialog_open.cpp new file mode 100644 index 0000000..236234a --- /dev/null +++ b/engine/node_dialog_open.cpp @@ -0,0 +1,41 @@ +#include "pch.h" +#include "log.h" +#include "node_dialog_open.h" +#include "canvas.h" +#include "node_image_texture.h" + +Node* NodeDialogOpen::clone_instantiate() const +{ + return new NodeDialogOpen(); +} + +void NodeDialogOpen::clone_finalize(Node* dest) const +{ + NodeDialogOpen* n = static_cast(dest); + n->init_controls(); +} + +void NodeDialogOpen::init() +{ + auto tpl = static_cast(init_template("dialog-open")); + m_color = tpl->m_color; + m_border_color = tpl->m_border_color;; + m_thinkness = tpl->m_thinkness;; + init_controls(); +} + +void NodeDialogOpen::init_controls() +{ + btn_ok = find("btn-ok"); + btn_cancel = find("btn-cancel"); + btn_cancel->on_click = [this](Node*) { + destroy(); + }; +} + +void NodeDialogOpen::loaded() +{ + ui::Image thumb = ui::Canvas::I->thumbnail_read(data_path); + auto image_tex = find("thumb-tex"); + image_tex->tex.create(thumb); +} diff --git a/engine/node_dialog_open.h b/engine/node_dialog_open.h new file mode 100644 index 0000000..9a8f3b2 --- /dev/null +++ b/engine/node_dialog_open.h @@ -0,0 +1,16 @@ +#pragma once +#include "node_border.h" +#include "node_button.h" + +class NodeDialogOpen : public NodeBorder +{ +public: + NodeButton* btn_cancel; + NodeButton* btn_ok; + std::string data_path; + virtual Node* clone_instantiate() const override; + virtual void clone_finalize(Node* dest) const override; + virtual void init() override; + void init_controls(); + virtual void loaded() override; +}; diff --git a/engine/node_icon.cpp b/engine/node_icon.cpp new file mode 100644 index 0000000..9bb2ced --- /dev/null +++ b/engine/node_icon.cpp @@ -0,0 +1,68 @@ +#include "pch.h" +#include "log.h" +#include "node_icon.h" +#include "asset.h" +#include "texture.h" + +std::map NodeIcon::m_icons; + +void NodeIcon::static_init() +{ + // spritesheet maker: https://draeton.github.io/stitches/ + // icons: http://www.famfamfam.com/lab/icons/silk/ + // regex css -> spritesheet.txt: \.([^{]+) {\s+width: (\d+)px;\s+height: (\d+)px;\s+.*: -(\d+)px -(\d+)px;\s+}\s+ + // to: "\1",\2,\3,\4,\5\n + Asset file; + if (!(file.open("data/spritesheet.txt") && file.read_all())) + return; + char* data = (char*)file.m_data; + int size = file.m_len; + static char name[256]; + int x, y, w, h; + char* s = strtok(data, "\n"); + auto i = strlen(s) + 1; + while (i < size && sscanf(s, "%s %d %d %d %d", name, &w, &h, &x, &y) == 5) + { + m_icons[name] = glm::vec4(x, y, x + w, y + h); + s = strtok(nullptr, "\n"); + i += strlen(s) + 1; + } + file.close(); + TextureManager::load("data/spritesheet.png"); +} + +Node* NodeIcon::clone_instantiate() const +{ + return new NodeIcon(); +} + +void NodeIcon::clone_copy(Node* dest) const +{ + NodeImage::clone_copy(dest); + NodeIcon* n = static_cast(dest); + n->m_icon_name = m_icon_name; +} + +void NodeIcon::create() +{ + m_region = m_icons[m_icon_name]; + m_path = "data/spritesheet.png"; + m_tex_id = const_hash(m_path.c_str()); + m_use_atlas = true; + NodeImage::create(); + auto tex_sz = TextureManager::get(m_tex_id).size(); + YGNodeStyleSetAspectRatio(y_node, tex_sz.x / tex_sz.y); +} + +void NodeIcon::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + NodeImage::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Icon: + m_icon_name = attr->Value(); + break; + default: + break; + } +} diff --git a/engine/node_icon.h b/engine/node_icon.h new file mode 100644 index 0000000..392d95e --- /dev/null +++ b/engine/node_icon.h @@ -0,0 +1,14 @@ +#pragma once +#include "node_image.h" + +class NodeIcon : public NodeImage +{ + static std::map m_icons; + std::string m_icon_name; +public: + static void static_init(); + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void create() override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; +}; diff --git a/engine/node_image.cpp b/engine/node_image.cpp new file mode 100644 index 0000000..d599691 --- /dev/null +++ b/engine/node_image.cpp @@ -0,0 +1,96 @@ +#include "pch.h" +#include "log.h" +#include "node_image.h" +#include "shader.h" + +ui::Plane NodeImage::m_plane; +Sampler NodeImage::m_sampler; + +void NodeImage::static_init() +{ + m_plane.create<1>(1, 1); + m_sampler.create(); +} + +Node* NodeImage::clone_instantiate() const +{ + return new NodeImage(); +} + +void NodeImage::clone_copy(Node* dest) const +{ + Node::clone_copy(dest); + NodeImage* n = static_cast(dest); + n->m_use_atlas = m_use_atlas; + n->m_region = m_region; + n->m_off = m_off; + n->m_sz = m_sz; + n->m_path = m_path; + n->m_tex_id = m_tex_id; +} + +void NodeImage::create() +{ + if (!m_path.empty() && TextureManager::load(m_path.c_str())) + { + //LOG("load image node %s", m_path.c_str()); + auto tex_sz = TextureManager::get(m_tex_id).size(); + m_off = m_region.xy / tex_sz; + m_sz = (m_region.zw - m_region.xy) / tex_sz; + } +} + +void NodeImage::restore_context() +{ + Node::restore_context(); + create(); +} + +void NodeImage::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + Node::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Path: + m_path = attr->Value(); + m_tex_id = const_hash(attr->Value()); + break; + case kAttribute::Region: + { + glm::vec4 v; + int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w); + if (n == 4) + { + m_region = v; + m_use_atlas = true; + } + break; + } + default: + break; + } +} + +void NodeImage::draw() +{ + using namespace ui; + TextureManager::get(m_tex_id).bind(); + m_sampler.bind(0); + glEnable(GL_BLEND); + if (m_use_atlas) + { + ui::ShaderManager::use(kShader::Atlas); + ui::ShaderManager::u_vec2(kShaderUniform::Tof, m_off); + ui::ShaderManager::u_vec2(kShaderUniform::Tsz, m_sz); + } + else + { + ui::ShaderManager::use(kShader::Texture); + } + ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + m_plane.draw_fill(); + m_sampler.unbind(); + TextureManager::get(m_tex_id).unbind(); + glDisable(GL_BLEND); +} diff --git a/engine/node_image.h b/engine/node_image.h new file mode 100644 index 0000000..76ef180 --- /dev/null +++ b/engine/node_image.h @@ -0,0 +1,24 @@ +#pragma once +#include "node.h" +#include "shape.h" +#include "texture.h" + +class NodeImage : public Node +{ +public: + static ui::Plane m_plane; + static Sampler m_sampler; + bool m_use_atlas = false; + glm::vec4 m_region; + glm::vec2 m_off; + glm::vec2 m_sz; + std::string m_path; + uint16_t m_tex_id; + static void static_init(); + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void create() override; + virtual void restore_context() override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + virtual void draw() override; +}; diff --git a/engine/node_image_texture.cpp b/engine/node_image_texture.cpp new file mode 100644 index 0000000..840aaa0 --- /dev/null +++ b/engine/node_image_texture.cpp @@ -0,0 +1,32 @@ +#include "pch.h" +#include "log.h" +#include "node_image_texture.h" +#include "shader.h" +#include "node_image.h" + +Node* NodeImageTexture::clone_instantiate() const +{ + return new NodeImageTexture(); +} + +void NodeImageTexture::clone_copy(Node* dest) const +{ + Node::clone_copy(dest); + NodeImageTexture* n = static_cast(dest); + n->tex = tex; +} + +void NodeImageTexture::draw() +{ + using namespace ui; + tex.bind(); + NodeImage::m_sampler.bind(0); + glEnable(GL_BLEND); + ui::ShaderManager::use(kShader::Texture); + ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + NodeImage::m_plane.draw_fill(); + NodeImage::m_sampler.unbind(); + tex.unbind(); + glDisable(GL_BLEND); +} diff --git a/engine/node_image_texture.h b/engine/node_image_texture.h new file mode 100644 index 0000000..9058e26 --- /dev/null +++ b/engine/node_image_texture.h @@ -0,0 +1,18 @@ +#pragma once +#include "node.h" +#include "texture.h" + +class NodeImageTexture : public Node +{ +public: + Texture2D tex; + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + // TODO: maybe we can save the texture data and restore later + //virtual void restore_context() override + //{ + // Node::restore_context(); + // create(); + //} + virtual void draw() override; +}; diff --git a/engine/node_message_box.cpp b/engine/node_message_box.cpp new file mode 100644 index 0000000..06143ac --- /dev/null +++ b/engine/node_message_box.cpp @@ -0,0 +1,21 @@ +#include "pch.h" +#include "log.h" +#include "node_message_box.h" +#include "layout.h" + +Node* NodeMessageBox::clone_instantiate() const +{ + return new NodeMessageBox(); +} + +void NodeMessageBox::init() +{ + SetPosition(0, 0); + SetWidthP(100); + SetHeightP(100); + SetPositioning(YGPositionTypeAbsolute); + m_template = (*m_manager)[const_hash("message-box")]->m_children[0]->clone(); + add_child(m_template); + btnOk = m_template->find("btn-ok"); + btnOk->on_click = [&](Node*) { destroy(); }; +} diff --git a/engine/node_message_box.h b/engine/node_message_box.h new file mode 100644 index 0000000..78c3004 --- /dev/null +++ b/engine/node_message_box.h @@ -0,0 +1,12 @@ +#pragma once +#include "node.h" +#include "node_button.h" + +class NodeMessageBox : public Node +{ +public: + Node* m_template; + NodeButton* btnOk; + virtual Node* clone_instantiate() const override; + virtual void init() override; +}; diff --git a/engine/node_panel_brush.cpp b/engine/node_panel_brush.cpp new file mode 100644 index 0000000..48373f1 --- /dev/null +++ b/engine/node_panel_brush.cpp @@ -0,0 +1,121 @@ +#include "pch.h" +#include "log.h" +#include "node_panel_brush.h" + +Node* NodeButtonBrush::clone_instantiate() const +{ + return new NodeButtonBrush(); +} + +void NodeButtonBrush::init() +{ + init_template("tpl-brush-icon"); + color_hover = glm::vec4(.7, .7, .7, 1); + color_normal = glm::vec4(.3, .3, .3, 1); + m_color = color_normal; + img = (NodeImage*)m_children[0].get(); +} + +void NodeButtonBrush::set_icon(const char* path) +{ + img->m_path = path; + img->m_tex_id = const_hash(img->m_path.c_str()); + img->create(); +} + +void NodeButtonBrush::draw() +{ + m_color = m_mouse_inside ? color_hover : color_normal; + m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color; + NodeButtonCustom::draw(); +} + +Node* NodePanelBrush::clone_instantiate() const +{ + return new NodePanelBrush(); +} + +void NodePanelBrush::init() +{ + init_template("tpl-panel-brushes"); + //m_layers_container = find("layers-container"); + static auto icons = FindAllBrushes("data/Icons/"); + if ((m_container = find("brushes"))) + { + int count = 0; + for (auto& i : icons) + { + std::string path = "data/Icons/" + i; + NodeButtonBrush* brush = new NodeButtonBrush; + m_container->add_child(brush); + brush->init(); + brush->create(); + brush->loaded(); + brush->set_icon(path.c_str()); + brush->m_brushID = count++; + m_brushes.push_back(brush); + brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1); + } + } +} + +void NodePanelBrush::handle_click(Node* target) +{ + if (target == m_current) + return; + if (m_current) + m_current->m_selected = false; + m_current = (NodeButtonBrush*)target; + m_current->m_selected = true; + if (on_brush_changed) + on_brush_changed(this, m_current->m_brushID); +} + +std::vector NodePanelBrush::FindAllBrushes(std::string folder) +{ + std::vector names; + std::string search_path = folder + "*.png"; +#ifdef _WIN32 + WIN32_FIND_DATAA fd; + HANDLE hFind = ::FindFirstFileA(search_path.c_str(), &fd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + // read all (real) files in current folder + // , delete '!' read other 2 default folder . and .. + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + names.push_back(fd.cFileName); + } + } while (::FindNextFileA(hFind, &fd)); + ::FindClose(hFind); + } +#elif __ANDROID__ + //LOG("listing brushes"); + AAssetDir* dir = AAssetManager_openDir(Asset::m_am, "data/Icons"); + while (const char* name = AAssetDir_getNextFileName(dir)) + { + //LOG("asset: %s", name); + names.push_back(name); + } + AAssetDir_close(dir); +#else + DIR *dp; + struct dirent *ep; + dp = opendir(folder.c_str()); + + if (dp != NULL) + { + while ((ep = readdir(dp))) + if (ep->d_type != DT_DIR) + names.push_back(ep->d_name); + closedir(dp); + } + else + LOG("Couldn't open the directory: %s", folder.c_str()); +#endif + return names; +} + +uint16_t NodePanelBrush::get_texture_id(int index) const +{ + return m_brushes[index]->img->m_tex_id; +} diff --git a/engine/node_panel_brush.h b/engine/node_panel_brush.h new file mode 100644 index 0000000..558a8b7 --- /dev/null +++ b/engine/node_panel_brush.h @@ -0,0 +1,30 @@ +#pragma once +#include "node.h" +#include "node_button_custom.h" +#include "node_image.h" + +class NodeButtonBrush : public NodeButtonCustom +{ +public: + int m_brushID; + bool m_selected = false; + NodeImage* img; + virtual Node* clone_instantiate() const override; + virtual void init() override; + void set_icon(const char* path); + virtual void draw() override; +}; + +class NodePanelBrush : public Node +{ + std::vector m_brushes; + NodeButtonBrush* m_current = nullptr; + Node* m_container; +public: + std::function on_brush_changed; + virtual Node* clone_instantiate() const override; + virtual void init() override; + void handle_click(Node* target); + std::vector FindAllBrushes(std::string folder); + uint16_t get_texture_id(int index) const; +}; diff --git a/engine/node_panel_color.cpp b/engine/node_panel_color.cpp new file mode 100644 index 0000000..7766530 --- /dev/null +++ b/engine/node_panel_color.cpp @@ -0,0 +1,42 @@ +#include "pch.h" +#include "log.h" +#include "node_panel_color.h" + +Node* NodePanelColor::clone_instantiate() const +{ + return new NodePanelColor(); +} + +void NodePanelColor::clone_finalize(Node* dest) const +{ + NodePanelColor* n = static_cast(dest); + n->init_controls(); +} + +void NodePanelColor::init() +{ + init_template("tpl-panel-color"); + init_controls(); +} + +void NodePanelColor::init_controls() +{ + m_quad = find("quad"); + m_hue = find("hue"); + m_hue->on_hue_changed = [this](Node*, glm::vec4 hue_color) { + m_base_color = m_quad->m_color = hue_color; + auto x = glm::mix(m_base_color, glm::vec4(1, 1, 1, 1), m_cursor.x); + m_color = glm::mix(x, glm::vec4(0, 0, 0, 1), m_cursor.y); + if (on_color_changed) + on_color_changed(this, m_color); + }; + m_quad->on_value_changed = [this](Node*, glm::vec2 pos) + { + auto x = glm::mix(m_base_color, glm::vec4(1, 1, 1, 1), pos.x); + m_color = glm::mix(x, glm::vec4(0, 0, 0, 1), pos.y); + m_cursor = pos; + if (on_color_changed) + on_color_changed(this, m_color); + }; + m_hue->set_value(0); +} diff --git a/engine/node_panel_color.h b/engine/node_panel_color.h new file mode 100644 index 0000000..ea3ad88 --- /dev/null +++ b/engine/node_panel_color.h @@ -0,0 +1,19 @@ +#pragma once +#include "node.h" +#include "node_color_quad.h" +#include "node_slider.h" + +class NodePanelColor : public Node +{ +public: + NodeColorQuad* m_quad; + NodeSliderHue* m_hue; + glm::vec4 m_base_color; + glm::vec4 m_color; + glm::vec2 m_cursor; + std::function on_color_changed; + virtual Node* clone_instantiate() const override; + virtual void clone_finalize(Node* dest) const override; + virtual void init() override; + void init_controls(); +}; diff --git a/engine/node_panel_layer.cpp b/engine/node_panel_layer.cpp new file mode 100644 index 0000000..746306c --- /dev/null +++ b/engine/node_panel_layer.cpp @@ -0,0 +1,221 @@ +#include "pch.h" +#include "log.h" +#include "node_panel_layer.h" + +Node* NodeLayer::clone_instantiate() const +{ + return new NodeLayer(); +} + +void NodeLayer::clone_children(Node* dest) const +{ + NodeBorder::clone_children(dest); + NodeLayer* n = static_cast(dest); + n->m_label = n->find("label"); + n->m_visibility = n->find("cb"); +} + +void NodeLayer::clone_copy(Node* dest) const +{ + NodeBorder::clone_copy(dest); + NodeLayer* n = (NodeLayer*)dest; + n->m_selected = m_selected; + n->m_label_text = m_label_text; +} + +void NodeLayer::init() +{ + const auto& m_template = (NodeBorder*)init_template("tpl-layer"); + m_color = m_template->m_color; + m_border_color = m_template->m_border_color; + m_thinkness = m_template->m_thinkness; + m_label = find("label"); + m_visibility = find("cb"); + m_opacity = find("sl-opacity"); +} + +void NodeLayer::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + NodeBorder::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Text: + m_label_text = attr->Value(); + break; + case kAttribute::Selected: + m_selected = attr->BoolValue(); + default: + break; + } +} + +void NodeLayer::loaded() +{ + NodeBorder::loaded(); + if (!m_label_text.empty()) + m_label->set_text(m_label_text.c_str()); + m_opacity->on_value_changed = [this](Node*, float value) { + if (on_opacity_changed) + on_opacity_changed(this, value); + }; + m_visibility->on_value_changed = [this](Node*, bool checked) { + if (on_visibility_changed) + on_visibility_changed(this, checked); + }; +} + +kEventResult NodeLayer::handle_event(Event* e) +{ + NodeBorder::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseEnter: + break; + case kEventType::MouseLeave: + break; + case kEventType::MouseDownL: + m_selected = true; + if (on_selected) + on_selected(this); + break; + case kEventType::MouseUpL: + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} + +void NodeLayer::draw() +{ + auto c = m_selected ? m_color_selected : m_color_normal; + m_thinkness = m_selected ? 1.f : 0.f; + m_color = m_mouse_inside ? m_color_hover : c; + NodeBorder::draw(); +} + +void NodeLayer::set_name(const char* s) +{ + m_label_text = s; + m_label->set_text(s); +} +Node* NodePanelLayer::clone_instantiate() const +{ + return new NodePanelLayer(); +} + +void NodePanelLayer::init() +{ + LOG("NodePanelLayer::init"); + init_template("tpl-panel-layers"); + LOG("template initted"); + m_layers_container = find("layers-container"); + LOG("template container found"); + // for (int i = 0; i < 1; i++) + // { + // LOG("add layer"); + // add_layer(); + // } + LOG("find components"); + // m_current_layer = m_layers[0]; + // m_layers[0]->m_selected = true; + btn_add = find("btn-add"); + btn_remove = find("btn-remove"); + btn_up = find("btn-up"); + btn_down = find("btn-down"); + LOG("attach events"); + btn_add->on_click = [this](Node*) { + add_layer(); + if (on_layer_add) + on_layer_add(this); + }; + btn_remove->on_click = [this](Node*) { + if (m_layers.size() == 1) + return; // dont' delete the last layer + remove_layer(m_current_layer); + }; + btn_up->on_click = [this](Node*) { + int old_idx = m_layers_container->get_child_index(m_current_layer); + m_layers_container->move_child_offset(m_current_layer, -1); + int new_idx = m_layers_container->get_child_index(m_current_layer); + if (on_layer_order && old_idx != new_idx) + { + on_layer_order(this, old_idx, new_idx); + } + }; + btn_down->on_click = [this](Node*) { + int old_idx = m_layers_container->get_child_index(m_current_layer); + m_layers_container->move_child_offset(m_current_layer, +1); + int new_idx = m_layers_container->get_child_index(m_current_layer); + if (on_layer_order && old_idx != new_idx) + { + on_layer_order(this, old_idx, new_idx); + } + }; + LOG("done init"); +} + +void NodePanelLayer::add_layer(const char* name) +{ + NodeLayer* l = new NodeLayer; + m_layers_container->add_child(l); + l->init(); + l->create(); + l->loaded(); + l->set_name(name); + l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1); + l->on_opacity_changed = std::bind(&NodePanelLayer::handle_layer_opacity, this, std::placeholders::_1, std::placeholders::_2); + l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2); + m_layers.push_back(l); +} + +void NodePanelLayer::add_layer() +{ + static char s[64]; + sprintf(s, "Layer-%d", id_counter++); + add_layer(s); +} + +void NodePanelLayer::remove_layer(NodeLayer* layer) +{ + auto it = std::find(m_layers.begin(), m_layers.end(), m_current_layer); + auto i = m_layers_container->get_child_index(m_current_layer); + m_layers_container->remove_child(m_current_layer); + m_layers.erase(it); + i = std::min(i, (int)m_layers.size() - 1); + m_current_layer = m_layers[i]; + m_current_layer->m_selected = true; + if (on_layer_delete) + on_layer_delete(this, (int)std::distance(m_layers.begin(), it)); + if (on_layer_change) + on_layer_change(this, -1, i); +} + +void NodePanelLayer::handle_layer_opacity(NodeLayer* target, float value) +{ + if (on_layer_opacity_changed) + on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value); +} + +void NodePanelLayer::handle_layer_visibility(NodeLayer* target, bool visible) +{ + if (on_layer_visibility_changed) + on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible); +} + +void NodePanelLayer::handle_layer_selected(NodeLayer* target) +{ + if (m_current_layer) + m_current_layer->m_selected = false; + m_current_layer = target; + m_current_layer->m_selected = true; + if (on_layer_change) + on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer)); +} + +void NodePanelLayer::clear() +{ + m_layers_container->remove_all_children(); + m_layers.clear(); +} diff --git a/engine/node_panel_layer.h b/engine/node_panel_layer.h new file mode 100644 index 0000000..b940d29 --- /dev/null +++ b/engine/node_panel_layer.h @@ -0,0 +1,59 @@ +#pragma once +#include "node_border.h" +#include "node_text.h" +#include "node_checkbox.h" +#include "node_slider.h" +#include "node_button_custom.h" + +class NodeLayer : public NodeBorder +{ +public: + std::function on_selected; + std::function on_opacity_changed; + std::function on_visibility_changed; + bool m_selected = false; + glm::vec4 m_color_normal = glm::vec4(.4, .4, .4, 1); + glm::vec4 m_color_selected = glm::vec4(.3, .3, .3, 1); + glm::vec4 m_color_hover = glm::vec4(.5, .5, .5, 1); + std::string m_label_text; + NodeText* m_label; + NodeCheckBox* m_visibility; + NodeSliderH* m_opacity; + virtual Node* clone_instantiate() const override; + virtual void clone_children(Node* dest) const override; + virtual void clone_copy(Node* dest) const override; + virtual void init() override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + virtual void loaded() override; + virtual kEventResult handle_event(Event* e) override; + virtual void draw() override; + void set_name(const char* s); +}; + +class NodePanelLayer : public Node +{ + NodeButtonCustom* btn_add; + NodeButtonCustom* btn_remove; + NodeButtonCustom* btn_up; + NodeButtonCustom* btn_down; + int id_counter = 0; +public: + std::function on_layer_change; + std::function on_layer_opacity_changed; + std::function on_layer_visibility_changed; + std::function on_layer_delete; + std::function on_layer_add; + std::function on_layer_order; + NodeLayer* m_current_layer = nullptr; + std::vector m_layers; + NodeBorder* m_layers_container; + virtual Node* clone_instantiate() const override; + virtual void init() override; + void add_layer(); + void add_layer(const char* name); + void remove_layer(NodeLayer* layer); + void handle_layer_opacity(NodeLayer* target, float value); + void handle_layer_visibility(NodeLayer* target, bool visible); + void handle_layer_selected(NodeLayer* target); + void clear(); +}; diff --git a/engine/node_panel_stroke.cpp b/engine/node_panel_stroke.cpp new file mode 100644 index 0000000..2942909 --- /dev/null +++ b/engine/node_panel_stroke.cpp @@ -0,0 +1,52 @@ +#include "pch.h" +#include "log.h" +#include "node_panel_stroke.h" + +Node* NodePanelStroke::clone_instantiate() const +{ + return new NodePanelStroke(); +} + +void NodePanelStroke::clone_finalize(Node* dest) const +{ + NodePanelStroke* n = static_cast(dest); + n->init_controls(); +} + +void NodePanelStroke::init() +{ + init_template("tpl-panel-stroke"); + init_controls(); +} + +void NodePanelStroke::init_controls() +{ + m_canvas = find("canvas"); + + init_slider(m_tip_size, "tip-size", &ui::Brush::m_tip_size); + init_slider(m_tip_spacing, "tip-spacing", &ui::Brush::m_tip_spacing); + init_slider(m_tip_flow, "tip-flow", &ui::Brush::m_tip_flow); + init_slider(m_tip_opacity, "tip-opacity", &ui::Brush::m_tip_opacity); + init_slider(m_tip_angle, "tip-angle", &ui::Brush::m_tip_angle); + init_slider(m_jitter_scale, "jitter-scale", &ui::Brush::m_jitter_scale); + init_slider(m_jitter_angle, "jitter-angle", &ui::Brush::m_jitter_angle); + init_slider(m_jitter_spread, "jitter-spread", &ui::Brush::m_jitter_spread); + init_slider(m_jitter_flow, "jitter-flow", &ui::Brush::m_jitter_flow); + //m_canvas->draw_stroke(); +} + +void NodePanelStroke::init_slider(NodeSliderH*& slider, const char* id, float ui::Brush::* prop) +{ + slider = find(id); + slider->on_value_changed = std::bind(&NodePanelStroke::handle_slide, + this, prop, std::placeholders::_1, std::placeholders::_2); + m_canvas->m_brush.*prop = slider->m_value.x; +} + +void NodePanelStroke::handle_slide(float ui::Brush::* prop, Node* target, float value) +{ + m_canvas->m_brush.*prop = value; + m_canvas->draw_stroke(); + if (on_stroke_change) + on_stroke_change(this); +} diff --git a/engine/node_panel_stroke.h b/engine/node_panel_stroke.h new file mode 100644 index 0000000..5a759c7 --- /dev/null +++ b/engine/node_panel_stroke.h @@ -0,0 +1,27 @@ +#pragma once +#include "node.h" +#include "node_stroke_preview.h" +#include "node_slider.h" +#include "brush.h" + +class NodePanelStroke : public Node +{ +public: + NodeStrokePreview* m_canvas; + NodeSliderH* m_tip_size; + NodeSliderH* m_tip_spacing; + NodeSliderH* m_tip_flow; + NodeSliderH* m_tip_opacity; + NodeSliderH* m_tip_angle; + NodeSliderH* m_jitter_scale; + NodeSliderH* m_jitter_angle; + NodeSliderH* m_jitter_spread; + NodeSliderH* m_jitter_flow; + std::function on_stroke_change; + virtual Node* clone_instantiate() const override; + virtual void clone_finalize(Node* dest) const override; + virtual void init() override; + void init_controls(); + void init_slider(NodeSliderH*& slider, const char* id, float ui::Brush::* prop); + void handle_slide(float ui::Brush::* prop, Node* target, float value); +}; diff --git a/engine/node_popup_menu.cpp b/engine/node_popup_menu.cpp new file mode 100644 index 0000000..c27d4aa --- /dev/null +++ b/engine/node_popup_menu.cpp @@ -0,0 +1,51 @@ +#include "pch.h" +#include "log.h" +#include "node_popup_menu.h" + +Node* NodePopupMenu::clone_instantiate() const +{ + return new NodePopupMenu(); +} + +void NodePopupMenu::init() +{ + m_flood_events = true; + SetPosition(0, 0); + SetWidth(100); + SetHeight(400); + SetPositioning(YGPositionTypeAbsolute); + m_mouse_ignore = false; +} + +kEventResult NodePopupMenu::handle_event(Event* e) +{ + switch (e->m_type) + { + case kEventType::MouseDownL: + break; + case kEventType::MouseUpL: + if (!m_mouse_inside) + { + mouse_release(); + } + else + { + for (int i = 0; i < m_children.size(); i++) + { + if (m_children[i]->m_mouse_inside) + { + if (on_select) + on_select(this, i); + break; + } + } + mouse_release(); + } + destroy(); + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} diff --git a/engine/node_popup_menu.h b/engine/node_popup_menu.h new file mode 100644 index 0000000..c312b85 --- /dev/null +++ b/engine/node_popup_menu.h @@ -0,0 +1,11 @@ +#pragma once +#include "node.h" + +class NodePopupMenu : public Node +{ +public: + std::function on_select; + virtual Node* clone_instantiate() const override; + virtual void init() override; + virtual kEventResult handle_event(Event* e) override; +}; diff --git a/engine/node_settings.cpp b/engine/node_settings.cpp new file mode 100644 index 0000000..28f0599 --- /dev/null +++ b/engine/node_settings.cpp @@ -0,0 +1,26 @@ +#include "pch.h" +#include "log.h" +#include "node_settings.h" +#include "layout.h" + +Node* NodeSettings::clone_instantiate() const +{ + return new NodeSettings(); +} + +void NodeSettings::init() +{ + SetPosition(0, 0); + SetWidthP(100); + SetHeightP(100); + SetPositioning(YGPositionTypeAbsolute); + m_template = (*m_manager)[const_hash("settings")]->m_children[0]->clone(); + add_child(m_template); + btnOk = m_template->find("btn-ok"); + btnOk->on_click = [&](Node*) { destroy(); }; +} + +kEventResult NodeSettings::handle_event(Event* e) +{ + return kEventResult::Consumed; +} diff --git a/engine/node_settings.h b/engine/node_settings.h new file mode 100644 index 0000000..33f6039 --- /dev/null +++ b/engine/node_settings.h @@ -0,0 +1,13 @@ +#pragma once +#include "node.h" +#include "node_button.h" + +class NodeSettings : public Node +{ + Node* m_template; + NodeButton* btnOk; +public: + virtual Node* clone_instantiate() const override; + virtual void init() override; + virtual kEventResult handle_event(Event* e) override; +}; diff --git a/engine/node_slider.cpp b/engine/node_slider.cpp new file mode 100644 index 0000000..f9cba97 --- /dev/null +++ b/engine/node_slider.cpp @@ -0,0 +1,146 @@ +#include "pch.h" +#include "log.h" +#include "node_slider.h" +#include "shader.h" + +Node* NodeSliderH::clone_instantiate() const +{ + return new NodeSliderH(); +} + +void NodeSliderH::clone_copy(Node* dest) const +{ + NodeBorder::clone_copy(dest); + NodeSliderH* n = static_cast(dest); + n->m_value = m_value; +} + +void NodeSliderH::init() +{ + SetPadding(1, 1, 1, 1); + SetWidthP(100); + SetHeightP(100); + m_color = glm::vec4(1); +} + +void NodeSliderH::draw() +{ + NodeBorder::draw(); + + using namespace ui; + auto sz = GetSize(); + glm::vec2 cur_size = sz * (1.f - m_mask) + m_mask * glm::vec2(10); + glm::mat4 scale = glm::scale(glm::vec3(cur_size, 1.f)); + glm::mat4 pos = glm::translate(glm::vec3(m_value * m_mask * sz + m_pos + sz * .5f * (1.f - m_mask), 0)); + auto mvp = m_proj * pos * scale; + + ui::ShaderManager::use(kShader::Color); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp); + ui::ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); + m_plane.draw_fill(); +} + +void NodeSliderH::set_value(float value) +{ + m_value = glm::vec2(value) * m_mask; + if (on_value_changed) + on_value_changed(this, glm::length(m_value)); +} + +float NodeSliderH::get_value() +{ + return glm::length(m_value); +} + +void NodeSliderH::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + NodeBorder::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Value: + m_value = glm::vec2(attr->FloatValue()); + break; + default: + break; + } +} + +kEventResult NodeSliderH::handle_event(Event* e) +{ + NodeBorder::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseDownL: + dragging = true; + mouse_capture(); + { + auto sz = GetSize(); + auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask; + m_value = pos / glm::max({ 1, 1 }, sz); + if (on_value_changed) + on_value_changed(this, glm::length(m_value)); + } + break; + case kEventType::MouseUpL: + mouse_release(); + dragging = false; + break; + case kEventType::MouseMove: + if (dragging) + { + auto sz = GetSize(); + auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) * m_mask; + m_value = pos / glm::max({ 1, 1 }, sz); + if (on_value_changed) + on_value_changed(this, glm::length(m_value)); + } + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} + +Node* NodeSliderHue::clone_instantiate() const +{ + return new NodeSliderHue(); +} + +void NodeSliderHue::clone_finalize(Node* dest) const +{ + NodeSliderV::clone_finalize(dest); + NodeSliderHue* n = static_cast(dest); + n->init_controls(); +} + +void NodeSliderHue::init() +{ + NodeSliderV::init(); + init_controls(); +} + +void NodeSliderHue::init_controls() +{ + on_value_changed = [this](Node*, float value) { + m_color = glm::vec4(convert_hsv2rgb({ value, 1, 1 }), 1); + if (on_hue_changed) + on_hue_changed(this, m_color); + }; +} + +glm::vec4 NodeSliderHue::get_hue() +{ + return m_color; +} + +void NodeSliderHue::draw() +{ + using namespace ui; + ui::ShaderManager::use(kShader::ColorHue); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + //ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); + m_plane.draw_fill(); + NodeBorder::m_color = glm::vec4(0); + NodeSliderH::draw(); +} diff --git a/engine/node_slider.h b/engine/node_slider.h new file mode 100644 index 0000000..4597efe --- /dev/null +++ b/engine/node_slider.h @@ -0,0 +1,39 @@ +#pragma once +#include "node_border.h" + +class NodeSliderH : public NodeBorder +{ + bool dragging = false; +public: + glm::vec2 m_mask{ 1, 0 }; + glm::vec2 m_value; + std::function on_value_changed; + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void init() override; + virtual void draw() override; + void set_value(float value); + float get_value(); + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + virtual kEventResult handle_event(Event* e) override; +}; + +class NodeSliderV : public NodeSliderH +{ +public: + virtual Node* clone_instantiate() const override { return new NodeSliderV(); } + NodeSliderV() { m_mask = { 0, 1 }; } +}; + +class NodeSliderHue : public NodeSliderV +{ +public: + glm::vec4 m_color; + std::function on_hue_changed; + virtual Node* clone_instantiate() const override; + virtual void clone_finalize(Node* dest) const override; + virtual void init() override; + void init_controls(); + glm::vec4 get_hue(); + virtual void draw() override; +}; diff --git a/engine/node_stroke_preview.cpp b/engine/node_stroke_preview.cpp new file mode 100644 index 0000000..a1dee53 --- /dev/null +++ b/engine/node_stroke_preview.cpp @@ -0,0 +1,135 @@ +#include "pch.h" +#include "log.h" +#include "node_stroke_preview.h" +#include "texture.h" +#include "shader.h" +#include "bezier.h" + +Node* NodeStrokePreview::clone_instantiate() const +{ + return new NodeStrokePreview(); +} + +void NodeStrokePreview::clone_copy(Node* dest) const +{ + NodeBorder::clone_copy(dest); +} + +void NodeStrokePreview::clone_children(Node* dest) const +{ + // stop children cloning +} + +void NodeStrokePreview::clone_finalize(Node* dest) const +{ + NodeStrokePreview* n = (NodeStrokePreview*)dest; + n->init_controls(); +} + +void NodeStrokePreview::init_controls() +{ + m_mesh.create(); + m_sampler.create(); + TextureManager::load("data/Icons/Round-Hard.png"); + m_brush.m_tex_id = const_hash("data/Icons/Round-Hard.png"); +} + +void NodeStrokePreview::restore_context() +{ + NodeBorder::restore_context(); + init_controls(); + if (m_size.x > 0 && m_size.y > 0) + m_rtt.create(m_size.x, m_size.y); + draw_stroke(); +} + +void NodeStrokePreview::clear_context() +{ + NodeBorder::clear_context(); + m_rtt.destroy(); +} + +void NodeStrokePreview::draw_stroke() +{ + m_rtt.bindFramebuffer(); + { + using namespace ui; + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); + glEnable(GL_BLEND); + glm::mat4 proj = glm::ortho(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1); + + m_stroke.reset(); + m_stroke.start(m_brush); + auto samples = m_stroke.compute_samples(); + auto& tex = TextureManager::get(m_brush.m_tex_id); + tex.bind(); + m_sampler.bind(0); + + if (true) + { + m_mesh.shader.use(); + m_mesh.shader.u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); + m_mesh.shader.u_int(kShaderUniform::Tex, 0); + m_mesh.draw(samples, proj); + } + //else + //{ + // ShaderManager::use("stroke"); + // ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + // ShaderManager::u_int(kShaderUniform::Tex, 0); + // for (const auto& s : samples) + // { + // auto mvp = proj * + // glm::translate(glm::vec3(s.pos, 0)) * + // glm::scale(glm::vec3(s.size, s.size, 1)) * + // glm::eulerAngleZ(s.angle); + // ShaderManager::u_mat4(kShaderUniform::MVP, mvp); + // ShaderManager::u_float(kShaderUniform::Alpha, s.flow); + // m_plane.draw_fill(); + // } + //} + + m_sampler.unbind(); + tex.unbind(); + glDisable(GL_BLEND); + + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); + } + m_rtt.unbindFramebuffer(); +} + +void NodeStrokePreview::draw() +{ + using namespace ui; + ui::ShaderManager::use(kShader::Texture); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + m_rtt.bindTexture(); + m_sampler.bind(0); + m_plane.draw_fill(); + m_sampler.unbind(); + m_rtt.unbindTexture(); +} + +void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size) +{ + float pad = 30.f; + float w = new_size.x; + float h = new_size.y; + std::vector kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; + m_stroke.start(m_brush); + for (int i = 0; i < 20; i++) + m_stroke.add_point(BezierCurve::Bezier2D(kp, i / 20.f), 1.f); + + m_rtt.destroy(); + m_rtt.create((int)new_size.x, (int)new_size.y); + draw_stroke(); +} diff --git a/engine/node_stroke_preview.h b/engine/node_stroke_preview.h new file mode 100644 index 0000000..7a1d625 --- /dev/null +++ b/engine/node_stroke_preview.h @@ -0,0 +1,26 @@ +#pragma once +#include "node_border.h" +#include "rtt.h" +#include "brush.h" +#include "texture.h" + +class NodeStrokePreview : public NodeBorder +{ + RTT m_rtt; + Sampler m_sampler; + ui::BrushMesh m_mesh; +public: + ui::Brush m_brush; + ui::Stroke m_stroke; + std::vector m_bez_points; + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void clone_children(Node* dest) const override; + virtual void clone_finalize(Node* dest) const override; + void init_controls(); + virtual void restore_context() override; + virtual void clear_context() override; + void draw_stroke(); + virtual void draw() override; + virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override; +}; diff --git a/engine/node_text.cpp b/engine/node_text.cpp new file mode 100644 index 0000000..fbfef2f --- /dev/null +++ b/engine/node_text.cpp @@ -0,0 +1,88 @@ +#include "pch.h" +#include "log.h" +#include "node_text.h" +#include "shader.h" + +Node* NodeText::clone_instantiate() const +{ + return new NodeText(); +} + +void NodeText::clone_copy(Node* dest) const +{ + Node::clone_copy(dest); + NodeText* n = static_cast(dest); + n->m_text_mesh.create(); + n->m_text_mesh.update(font_id, m_text.c_str()); + n->m_text = m_text; + n->m_font = m_font; + n->m_color = m_color; + n->m_font_size = m_font_size; + n->font_id = font_id; +} + +void NodeText::create() +{ + char font[64]; + sprintf(font, "%s-%d", m_font.c_str(), m_font_size); + font_id = (kFont)const_hash(font); + m_text_mesh.create(); + m_text_mesh.update(font_id, m_text.c_str()); + SetSize(m_text_mesh.bb); +} + +void NodeText::restore_context() +{ + Node::restore_context(); + create(); +} + +void NodeText::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + Node::parse_attributes(ka, attr); + switch (ka) + { + case kAttribute::Text: + m_text = attr->Value(); + break; + case kAttribute::FontFace: + m_font = attr->Value(); + break; + case kAttribute::FontSize: + m_font_size = attr->IntValue(); + break; + case kAttribute::Color: + { + glm::vec4 pad; + int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); + if (n == 1) + m_color = glm::vec4(pad.x); + else + m_color = pad; + break; + } + default: + break; + } +} + +void NodeText::set_text(const char* s) +{ + m_text = s; + m_text_mesh.update(font_id, s); + SetSize(m_text_mesh.bb); +} + +void NodeText::draw() +{ + using namespace ui; + glm::mat4 pos = glm::translate(glm::vec3(glm::floor(m_pos), 0)); + m_mvp = m_proj * pos; + ui::ShaderManager::use(kShader::Font); + ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); + ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color); + glEnable(GL_BLEND); + m_text_mesh.draw(); + glDisable(GL_BLEND); +} diff --git a/engine/node_text.h b/engine/node_text.h new file mode 100644 index 0000000..47cefc1 --- /dev/null +++ b/engine/node_text.h @@ -0,0 +1,21 @@ +#pragma once +#include "node.h" +#include "font.h" + +class NodeText : public Node +{ +public: + TextMesh m_text_mesh; + std::string m_text; + std::string m_font; + glm::vec4 m_color{ 1, 1, 1, 1 }; + int m_font_size; + kFont font_id; + virtual Node* clone_instantiate() const override; + virtual void clone_copy(Node* dest) const override; + virtual void create() override; + virtual void restore_context() override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + void set_text(const char* s); + virtual void draw() override; +}; diff --git a/engine/node_text_input.cpp b/engine/node_text_input.cpp new file mode 100644 index 0000000..8245fd4 --- /dev/null +++ b/engine/node_text_input.cpp @@ -0,0 +1,58 @@ +#include "pch.h" +#include "log.h" +#include "node_text_input.h" + +Node* NodeTextInput::clone_instantiate() const +{ + return new NodeTextInput(); +} + +void NodeTextInput::init() +{ + init_controls(); +} + +void NodeTextInput::init_controls() +{ + m_text = new NodeText; + add_child(m_text); + m_text->m_font = "arial"; + m_text->m_font_size = 11; + m_text->m_text = "TextInput"; + m_text->create(); +} + +kEventResult NodeTextInput::handle_event(Event* e) +{ + KeyEvent* ke = (KeyEvent*)e; + switch (e->m_type) + { + case kEventType::MouseDownL: + break; + case kEventType::MouseUpL: + key_capture(); + break; + case kEventType::KeyDown: + //switch (ke->m_key) + //{ + //case VK_BACK: + // m_string.erase(m_string.end() - 1); + // m_text->set_text(m_string.c_str()); + // break; + //default: + // break; + //} + break; + case kEventType::KeyChar: + if (ke->m_char >= 32 && ke->m_char < (32 + 96)) + { + m_string += (char)ke->m_key; + m_text->set_text(m_string.c_str()); + } + break; + default: + return kEventResult::Available; + break; + } + return kEventResult::Consumed; +} diff --git a/engine/node_text_input.h b/engine/node_text_input.h new file mode 100644 index 0000000..c34ac0a --- /dev/null +++ b/engine/node_text_input.h @@ -0,0 +1,14 @@ +#pragma once +#include "node_border.h" +#include "node_text.h" + +class NodeTextInput : public NodeBorder +{ +public: + NodeText* m_text; + std::string m_string; + virtual Node* clone_instantiate() const override; + virtual void init() override; + void init_controls(); + virtual kEventResult handle_event(Event* e) override; +}; diff --git a/engine/node_viewport.cpp b/engine/node_viewport.cpp new file mode 100644 index 0000000..ee1e5f7 --- /dev/null +++ b/engine/node_viewport.cpp @@ -0,0 +1,73 @@ +#include "pch.h" +#include "log.h" +#include "node_viewport.h" +#include "shader.h" + +void NodeViewport::draw() +{ + using namespace ui; + glm::mat4 cam = glm::lookAt(glm::vec3(sinf(angle) * 10, 0, -10), glm::vec3(0, 0, 0), glm::vec3(0, -1, 0)); + glm::mat4 proj = glm::perspective(glm::radians(45.f), m_clip.z / m_clip.w, .1f, 100); + + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + + glClearColor(1, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + auto box = m_clip * root()->m_zoom; + glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w); + glViewport(c.x, c.y, c.z, c.w); + TextureManager::get(m_tex_id).bind(); + m_sampler->bind(0); + glEnable(GL_BLEND); + ui::ShaderManager::use(kShader::Texture); + ui::ShaderManager::u_int(kShaderUniform::Tex, 0); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * cam); + m_faces->draw_fill(); + m_sampler->unbind(); + TextureManager::get(m_tex_id).unbind(); + glDisable(GL_BLEND); + + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); +} +Node* NodeViewport::clone_instantiate() const +{ + return new NodeViewport; +} +void NodeViewport::create() +{ + m_faces = std::make_unique(); + m_faces->create<1>(10, 10); + m_sampler = std::make_unique(); + m_sampler->create(); + TextureManager::load("data/uvs.jpg"); + m_tex_id = const_hash("data/uvs.jpg"); +} +kEventResult NodeViewport::handle_event(Event* e) +{ + Node::handle_event(e); + switch (e->m_type) + { + case kEventType::MouseDownL: + dragging = true; + drag_end = drag_start = ((MouseEvent*)e)->m_pos; + angle_old = angle; + break; + case kEventType::MouseUpL: + dragging = false; + break; + case kEventType::MouseMove: + if (dragging) + { + drag_end = ((MouseEvent*)e)->m_pos; + angle = angle_old + (drag_end - drag_start).x * .01f; + } + break; + default: + break; + } + return kEventResult::Consumed; +} diff --git a/engine/node_viewport.h b/engine/node_viewport.h new file mode 100644 index 0000000..49badb5 --- /dev/null +++ b/engine/node_viewport.h @@ -0,0 +1,23 @@ +#pragma once +#include "node.h" +#include "shape.h" +#include "texture.h" +#include "event.h" + +class NodeViewport : public Node +{ +public: + std::unique_ptr m_faces; + std::unique_ptr m_sampler; + uint16_t m_tex_id; + glm::vec2 drag_start; + glm::vec2 drag_end; + bool dragging = false; + float angle = 0.0f; + float angle_old; + + virtual void draw() override; + virtual Node* clone_instantiate() const override; + virtual void create() override; + virtual kEventResult handle_event(Event* e) override; +}; diff --git a/engine/shape.h b/engine/shape.h index e6f1b69..238e65c 100644 --- a/engine/shape.h +++ b/engine/shape.h @@ -1,5 +1,13 @@ #pragma once +enum class kShapeType : uint16_t +{ + Quad = const_hash("rect"), + Poly = const_hash("poly"), + RoundRect = const_hash("round-rect"), + Slice9 = const_hash("slice9"), +}; + namespace ui { class Shape