Files
panopainter/engine/layout.h

489 lines
14 KiB
C++

#pragma once
#include "shape.h"
#include "util.h"
#include "shader.h"
#include "font.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"),
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"),
};
enum class kWidget : uint16_t
{
Border = const_hash("border"),
Shape = const_hash("shape"),
Text = const_hash("text"),
Image = const_hash("image"),
Ref = const_hash("ref"),
};
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::mat4 proj;
glm::mat4 scale;
glm::mat4 pos;
glm::vec4 clip;
virtual std::unique_ptr<Widget> clone() = 0;
virtual void create() { }
virtual void draw() { }
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) { }
virtual void update() { }
};
class WidgetRef : public Widget
{
public:
uint16_t id;
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 };
static void init()
{
m_plane.create<1>(1, 1);
}
virtual std::unique_ptr<Widget> clone() override
{
auto ret = std::make_unique<WidgetBorder>();
*ret = *this;
return std::move(ret);
}
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) override
{
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;
default:
break;
}
}
};
class WidgetShape : public Widget
{
public:
std::unique_ptr<Shape> m_shape;
kShapeType m_type;
glm::vec4 m_color;
glm::vec4 m_border_color;
float m_thinkness{ 1 };
float m_radius{ .5f };
virtual std::unique_ptr<Widget> clone() override
{
auto ret = std::make_unique<WidgetShape>();
//*ret = *this;
return std::move(ret);
}
virtual void create() override
{
switch (m_type)
{
case kShapeType::Quad:
m_shape = std::make_unique<Plane>();
((Plane*)m_shape.get())->create<1>(1, 1);
break;
case kShapeType::Poly:
m_shape = std::make_unique<Circle>();
((Circle*)m_shape.get())->create<5>(m_radius);
break;
case kShapeType::RoundRect:
m_shape = std::make_unique<Rounded>();
((Rounded*)m_shape.get())->create<3>(1, 1, .1f);
break;
case kShapeType::Slice9:
m_shape = std::make_unique<Slice9>();
((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) override
{
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;
default:
break;
}
}
};
class WidgetText : public Widget
{
public:
TextMesh m_text_mesh;
std::string m_text;
std::string m_font;
glm::vec4 m_color{ 1, 1, 1, 1 };
int m_size;
virtual std::unique_ptr<Widget> clone() override
{
auto ret = std::make_unique<WidgetText>();
*ret = *this;
return std::move(ret);
}
virtual void create() override
{
char font[64];
sprintf(font, "%s-%d", m_font.c_str(), m_size);
kFont font_id = (kFont)const_hash(font);
m_text_mesh.create();
m_text_mesh.update(font_id, m_text.c_str());
}
virtual void draw() override
{
ShaderManager::use(kShader::Font);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
ShaderManager::u_vec4(kShaderUniform::Col, m_color);
m_text_mesh.draw();
}
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override
{
switch (ka)
{
case kAttribute::Text:
m_text = attr->Value();
break;
case kAttribute::FontFace:
m_font = attr->Value();
break;
case kAttribute::FontSize:
m_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;
}
}
virtual void update() override
{
mvp = proj * pos;
}
};
class WidgetImage : public Widget
{
public:
static Plane m_plane;
static Sampler m_sampler;
bool m_use_atlas;
glm::vec4 m_region;
glm::vec2 m_off;
glm::vec2 m_sz;
std::string m_path;
uint16_t m_id;
static void init()
{
m_plane.create<1>(1, 1);
m_sampler.create();
}
virtual std::unique_ptr<Widget> clone() override
{
auto ret = std::make_unique<WidgetImage>();
*ret = *this;
return std::move(ret);
}
virtual void create() override
{
if (TextureManager::load(m_path.c_str()))
{
auto tex_sz = TextureManager::get(m_id).size();
m_off = m_region.xy / tex_sz;
m_sz = (m_region.zw - m_region.xy) / tex_sz;
}
}
virtual void draw() override
{
TextureManager::get(m_id).bind();
m_sampler.bind(0);
if (m_use_atlas)
{
ShaderManager::use(kShader::Atlas);
ShaderManager::u_vec2(kShaderUniform::Tof, m_off);
ShaderManager::u_vec2(kShaderUniform::Tsz, m_sz);
}
else
{
ShaderManager::use(kShader::Texture);
}
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
m_plane.draw_fill();
m_sampler.unbind();
TextureManager::get(m_id).unbind();
}
virtual void parse_attributes(kAttribute id, const tinyxml2::XMLAttribute* attr) override
{
switch (id)
{
case kAttribute::Path:
m_path = attr->Value();
m_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;
}
}
};
class Node
{
friend class LayoutManager;
const Node* parent{ nullptr };
YGNodeRef y_node{ nullptr };
class LayoutManager* m_manager;
public:
std::vector<Node> m_children;
std::unique_ptr<Widget> 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<Node>(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 SetSize(glm::vec2 value) { SetWidth(value.x); SetHeight(value.y); }
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();
Node clone();
class iterator
{
std::stack<Node*> 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<uint16_t, Node> 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)]; }
};