#pragma once #include "shape.h" #include "util.h" #include "shader.h" enum class kAttribute : uint16_t { id = const_hash("id"), Width = const_hash("width"), MinWidth = const_hash("max-width"), MaxWidth = const_hash("min-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"), }; enum class kWidget : uint16_t { Border = const_hash("border"), Shape = const_hash("shape"), }; enum class kShapeType : uint16_t { Quad = const_hash("rect"), Poly = const_hash("poly"), RoundRect = const_hash("round-rect"), Slice9 = const_hash("slice9"), }; class Widget { public: glm::mat4 mvp; glm::vec4 clip; virtual void create() { } virtual void draw() { } virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) { } }; class WidgetBorder : public Widget { public: static Plane m_plane; glm::vec4 m_color; glm::vec4 m_border_color; float m_thinkness{ 1 }; virtual void draw() override { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_vec4(kShaderUniform::Col, m_color); m_plane.draw_fill(); glLineWidth(m_thinkness); ShaderManager::u_vec4(kShaderUniform::Col, m_border_color); m_plane.draw_stroke(); } virtual void parse_attributes(kAttribute id, const tinyxml2::XMLAttribute* attr) { switch (id) { 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; } 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; } } }; class WidgetShape : public Widget { public: std::unique_ptr m_shape; kShapeType m_type; glm::vec4 m_color; glm::vec4 m_border_color; float m_thinkness{ 1 }; float m_radius{ .5f }; virtual void create() override { switch (m_type) { case kShapeType::Quad: m_shape = std::make_unique(); ((Plane*)m_shape.get())->create<1>(1, 1); break; case kShapeType::Poly: m_shape = std::make_unique(); ((Circle*)m_shape.get())->create<5>(m_radius); break; case kShapeType::RoundRect: m_shape = std::make_unique(); ((Rounded*)m_shape.get())->create<3>(1, 1, .1f); break; case kShapeType::Slice9: m_shape = std::make_unique(); ((Slice9*)m_shape.get())->create(1, 1, .1f, .1f); break; default: break; } } virtual void draw() override { ShaderManager::use(kShader::Color); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_vec4(kShaderUniform::Col, m_color); m_shape->draw_fill(); glLineWidth(m_thinkness); ShaderManager::u_vec4(kShaderUniform::Col, m_border_color); m_shape->draw_stroke(); } virtual void parse_attributes(kAttribute id, const tinyxml2::XMLAttribute* attr) { switch (id) { 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; } 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; case kAttribute::Type: m_type = (kShapeType)const_hash(attr->Value()); break; } } }; class Node { friend class LayoutManager; const Node* parent{ nullptr }; YGNodeRef y_node{ nullptr }; public: std::vector m_children; std::unique_ptr m_widget; glm::vec2 m_pos; glm::vec2 m_size; glm::vec4 m_clip; std::string m_name; 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_widget = std::move(o.m_widget); m_children = std::move(o.m_children); for (auto& c : m_children) c.parent = this; parent = o.parent; y_node = o.y_node; m_pos = o.m_pos; m_size = o.m_size; m_clip = o.m_clip; 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 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 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); } 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; } void update(float width, float height); void update_internal(const glm::vec2& origin, const glm::mat4& proj); void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr); void load_internal(const tinyxml2::XMLElement* x_node); void draw(); class iterator { std::stack m_nodes; Node* m_current; public: iterator(Node* root) { m_current = root; if (root) m_nodes.push(root); } iterator& operator++() { m_nodes.pop(); for (auto& c : m_current->m_children) m_nodes.push(&c); m_current = m_nodes.size() ? m_nodes.top() : nullptr; return *this; } Node& operator*() { return *m_current; } Node* operator->() { return m_current; } bool operator==(const iterator& rhs) { return m_current == rhs.m_current; } bool operator!=(const iterator& rhs) { return m_current != rhs.m_current; } }; iterator begin() { return this; } iterator end() { return nullptr; } }; class LayoutManager { std::map m_layouts; std::string m_path; struct stat m_file_info { 0 }; public: bool load(const char* path); bool reload(); Node& operator[](uint16_t id) { return m_layouts[id]; } Node& operator[](const char* ids) { return m_layouts[const_hash(ids)]; } };