#pragma once #include "shape.hpp" #include "util.hpp" namespace att { 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"), }; enum class kWidget : uint16_t { Border = const_hash("border"), }; struct AttributeBase { kAttribute id; }; typedef std::map> AttrubutesMap; template struct Attribute : public AttributeBase { static const kAttribute static_id = T; V value; Attribute() : value{ 0 } { id = static_id; } Attribute(V v) : value(v) { id = static_id; } }; struct kAttributeMap { kAttribute value; const char* name; }; static constexpr kAttributeMap map[] = { { kAttribute::Width, "width" }, { kAttribute::MinWidth, "min-width" }, { kAttribute::MaxWidth, "max-width" }, { kAttribute::Height, "height" }, { kAttribute::MinHeight, "min-height" }, { kAttribute::MaxHeight, "max-height" }, { kAttribute::Divisions, "divisions" }, { kAttribute::InnerRadius, "inner-radius" }, { kAttribute::OuterRadius, "outer-radius" }, { kAttribute::Grow, "grow" }, { kAttribute::Shrink, "shrink" }, { kAttribute::FlexDir, "dir" }, { kAttribute::FlexWrap, "wrap" }, { kAttribute::Padding, "pad" }, { kAttribute::Margin, "margin" }, { kAttribute::Color, "color" }, }; constexpr const char* string(const kAttribute value, int i = 0) { // no check on size, value MUST be found return (map[i].value == value) ? map[i].name : string(value, i + 1); } #define DECLARE_ATTRIBUTE(N,T) \ struct N : public Attribute \ { using Attribute::Attribute; }; DECLARE_ATTRIBUTE(Width, float); DECLARE_ATTRIBUTE(MinWidth, float); DECLARE_ATTRIBUTE(MaxWidth, float); DECLARE_ATTRIBUTE(Height, float); DECLARE_ATTRIBUTE(MinHeight, float); DECLARE_ATTRIBUTE(MaxHeight, float); DECLARE_ATTRIBUTE(Divisions, int); DECLARE_ATTRIBUTE(InnerRadius, float); DECLARE_ATTRIBUTE(OuterRadius, float); DECLARE_ATTRIBUTE(Grow, float); DECLARE_ATTRIBUTE(Shrink, float); DECLARE_ATTRIBUTE(FlexDir, int); DECLARE_ATTRIBUTE(FlexWrap, int); DECLARE_ATTRIBUTE(Padding, glm::vec4); DECLARE_ATTRIBUTE(Margin, glm::vec4); DECLARE_ATTRIBUTE(Color, glm::vec4); #undef DECLARE_ATTRIBUTE } class Widget { public: glm::mat4 mvp; glm::vec4 clip; virtual void draw() { }; }; class WidgetBorder : public Widget { public: static Plane* m_plane; virtual void draw() override { m_plane->draw_fill(); } }; 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; glm::vec4 color; 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_children = std::move(o.m_children); for (auto& c : m_children) c.parent = this; parent = o.parent; y_node = o.y_node; color = o.color; 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(att::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; } };