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