#pragma once #include "shape.hpp" #include "util.hpp" #include "shader.hpp" enum class kAttribute : uint16_t { 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"), }; enum class kWidget : uint16_t { Border = const_hash("border"), }; class Widget { public: glm::mat4 mvp; glm::vec4 clip; 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 Node { const Node* parent{ nullptr }; YGNodeRef y_node{ nullptr }; struct stat m_file_info { 0 }; std::string m_path; 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_path = std::move(o.m_path); 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; m_file_info = o.m_file_info; 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(const char* path); void load_internal(const tinyxml2::XMLElement* x_node); void reload(); 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; } };