1938 lines
60 KiB
C++
1938 lines
60 KiB
C++
#pragma once
|
|
#include "shape.h"
|
|
#include "util.h"
|
|
#include "shader.h"
|
|
#include "font.h"
|
|
#include "asset.h"
|
|
#include "rtt.h"
|
|
#include "bezier.h"
|
|
#include "canvas.h"
|
|
#include <tinyxml2.h>
|
|
#include <yoga/Yoga.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"),
|
|
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"),
|
|
};
|
|
|
|
enum class kEventResult : uint8_t
|
|
{
|
|
Consumed,
|
|
Available,
|
|
};
|
|
|
|
enum class kEventCategory : uint8_t
|
|
{
|
|
MouseEvent,
|
|
KeyEvent,
|
|
};
|
|
enum class kEventType : uint8_t
|
|
{
|
|
MouseDownL,
|
|
MouseDownR,
|
|
MouseMove,
|
|
MouseUpL,
|
|
MouseUpR,
|
|
MouseEnter,
|
|
MouseLeave,
|
|
KeyDown,
|
|
KeyUp,
|
|
KeyChar,
|
|
};
|
|
|
|
class Event
|
|
{
|
|
public:
|
|
kEventCategory m_cat;
|
|
kEventType m_type;
|
|
};
|
|
|
|
class MouseEvent : public Event
|
|
{
|
|
public:
|
|
MouseEvent() { m_cat = kEventCategory::MouseEvent; }
|
|
glm::vec2 m_pos;
|
|
};
|
|
|
|
class KeyEvent : public Event
|
|
{
|
|
public:
|
|
KeyEvent() { m_cat = kEventCategory::KeyEvent; }
|
|
int m_key;
|
|
};
|
|
|
|
class LayoutManager
|
|
{
|
|
std::map<uint16_t, std::unique_ptr<class Node>> m_layouts;
|
|
std::string m_path;
|
|
struct stat m_file_info { 0 };
|
|
public:
|
|
std::function<void()> on_loaded;
|
|
bool load(const char* path);
|
|
bool reload();
|
|
class Node* operator[](uint16_t id)
|
|
{
|
|
auto i = m_layouts.find(id);
|
|
return i == m_layouts.end() ? nullptr : i->second.get();
|
|
}
|
|
//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<std::shared_ptr<Node>> 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;
|
|
std::string m_name;
|
|
bool m_display = true;
|
|
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_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 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 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;
|
|
}
|
|
|
|
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<void(Node*)> 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<class T = Node> T* find(const char* ids)
|
|
{
|
|
uint16_t id = const_hash(ids);
|
|
if (id == m_nodeID)
|
|
return static_cast<T*>(this);
|
|
for (auto& c : m_children)
|
|
if (auto found = c->find(ids))
|
|
return static_cast<T*>(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<Node> n);
|
|
void add_child(std::shared_ptr<Node> 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 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.get());
|
|
// 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 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<NodeBorder*>(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<NodeText*>(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 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<NodeImage*>(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()))
|
|
{
|
|
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 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 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<void(Node* target)> 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<NodeButton*>(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<NodeButton*>(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);
|
|
}
|
|
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:
|
|
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_key >= 32 && ke->m_key < 32+96)
|
|
{
|
|
m_string += (char)ke->m_key;
|
|
m_text->set_text(m_string.c_str());
|
|
}
|
|
break;
|
|
default:
|
|
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<NodeButton>("btn-ok");
|
|
btnOk->on_click = [&](Node*) { destroy(); };
|
|
}
|
|
};
|
|
|
|
class NodePopupMenu : public Node
|
|
{
|
|
public:
|
|
std::function<void(Node* target, int index)> 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
|
|
{
|
|
auto pos = ((MouseEvent*)e)->m_pos;
|
|
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:
|
|
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<void(Node* target)> 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<NodeButtonCustom*>(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;
|
|
}
|
|
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:
|
|
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<NodeButton>("btn-ok");
|
|
btnOk->on_click = [&](Node*) { destroy(); };
|
|
}
|
|
virtual kEventResult handle_event(Event* e) override
|
|
{
|
|
return kEventResult::Consumed;
|
|
}
|
|
};
|
|
|
|
class NodeIcon : public NodeImage
|
|
{
|
|
static std::map<std::string, glm::vec4> 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();
|
|
}
|
|
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<NodeIcon*>(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<ui::Plane> m_faces;
|
|
std::unique_ptr<Sampler> 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<float>(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<ui::Plane>();
|
|
m_faces->create<1>(10, 10);
|
|
m_sampler = std::make_unique<Sampler>();
|
|
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<void(Node* target, float value)> 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<NodeSliderH*>(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)
|
|
{
|
|
}
|
|
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:
|
|
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<void(Node* target, glm::vec4 color)> 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<NodeSliderHue*>(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);
|
|
};
|
|
}
|
|
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:
|
|
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<NodeCheckBox*>(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;
|
|
break;
|
|
default:
|
|
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<void(NodeLayer* target)> on_selected;
|
|
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;
|
|
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<NodeLayer*>(dest);
|
|
n->m_label = n->find<NodeText>("label");
|
|
n->m_visibility = n->find<NodeCheckBox>("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<NodeText>("label");
|
|
m_visibility = find<NodeCheckBox>("cb");
|
|
}
|
|
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());
|
|
}
|
|
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:
|
|
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<void(Node* target, int old_idx, int new_idx)> on_layer_change;
|
|
std::function<void(Node* target, int index)> on_layer_delete;
|
|
std::function<void(Node* target)> on_layer_add;
|
|
NodeLayer* m_current_layer = nullptr;
|
|
std::vector<NodeLayer*> 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<NodeBorder>("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<NodeButtonCustom>("btn-add");
|
|
btn_remove = find<NodeButtonCustom>("btn-remove");
|
|
btn_up = find<NodeButtonCustom>("btn-up");
|
|
btn_down = find<NodeButtonCustom>("btn-down");
|
|
LOG("attach events");
|
|
btn_add->on_click = [this](Node*) {
|
|
add_layer();
|
|
};
|
|
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*) {
|
|
m_layers_container->move_child_offset(m_current_layer, -1);
|
|
};
|
|
btn_down->on_click = [this](Node*) {
|
|
m_layers_container->move_child_offset(m_current_layer, +1);
|
|
};
|
|
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);
|
|
m_layers.push_back(l);
|
|
if (on_layer_add)
|
|
on_layer_add(this);
|
|
}
|
|
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<int>(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_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));
|
|
}
|
|
};
|
|
|
|
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<NodeButtonBrush*> m_brushes;
|
|
NodeButtonBrush* m_current = nullptr;
|
|
Node* m_container;
|
|
public:
|
|
std::function<void(Node* target, int id)> 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<NodeBorder>("layers-container");
|
|
static auto icons = FindAllBrushes("data/Icons/");
|
|
if ((m_container = find<NodeBorder>("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<std::string> FindAllBrushes(std::string folder)
|
|
{
|
|
std::vector<std::string> 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<void(Node* target, glm::vec2 value)> 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);
|
|
}
|
|
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:
|
|
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<void(Node* target, glm::vec4 color)> on_color_changed;
|
|
virtual Node* clone_instantiate() const override { return new NodePanelColor(); }
|
|
virtual void clone_finalize(Node* dest) const override
|
|
{
|
|
NodePanelColor* n = static_cast<NodePanelColor*>(dest);
|
|
n->init_controls();
|
|
}
|
|
virtual void init() override
|
|
{
|
|
const auto& m_template = (Node*)init_template("tpl-panel-color");
|
|
init_controls();
|
|
}
|
|
void init_controls()
|
|
{
|
|
m_quad = find<NodeColorQuad>("quad");
|
|
m_hue = find<NodeSliderHue>("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);
|
|
};
|
|
}
|
|
};
|
|
|
|
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<glm::vec2> 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");
|
|
}
|
|
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);
|
|
|
|
double w = (double)m_rtt.getWidth();
|
|
double h = (double)m_rtt.getHeight();
|
|
|
|
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<float>(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, m_brush.m_tip_color);
|
|
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<glm::vec2> 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_angle;
|
|
NodeSliderH* m_jitter_scale;
|
|
NodeSliderH* m_jitter_angle;
|
|
NodeSliderH* m_jitter_spread;
|
|
NodeSliderH* m_jitter_flow;
|
|
std::function<void(Node* target)> on_stroke_change;
|
|
virtual Node* clone_instantiate() const override { return new NodePanelStroke(); }
|
|
virtual void clone_finalize(Node* dest) const override
|
|
{
|
|
NodePanelColor* n = static_cast<NodePanelColor*>(dest);
|
|
n->init_controls();
|
|
}
|
|
virtual void init() override
|
|
{
|
|
const auto& m_template = (Node*)init_template("tpl-panel-stroke");
|
|
init_controls();
|
|
}
|
|
void init_controls()
|
|
{
|
|
m_canvas = find<NodeStrokePreview>("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_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<NodeSliderH>(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 NodeCanvas : public Node
|
|
{
|
|
bool m_dragging = false;
|
|
public:
|
|
std::unique_ptr<ui::Canvas> m_canvas;
|
|
ui::Brush m_brush;
|
|
virtual Node* clone_instantiate() const override { return new NodeCanvas(); }
|
|
virtual void init() override
|
|
{
|
|
m_mouse_ignore = false;
|
|
m_canvas = std::make_unique<ui::Canvas>();
|
|
m_canvas->create(512, 512);
|
|
m_canvas->clear();
|
|
}
|
|
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<float>(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(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);
|
|
|
|
glm::vec2 sz = { m_canvas->m_width, m_canvas->m_height };
|
|
auto mvp = glm::ortho(0.f, box.z, box.w, 0.f, -1.f, 1.f) *
|
|
//glm::translate(glm::vec3((m_size - sz) * 0.5f, 0)) * // center
|
|
glm::translate(glm::vec3(0)) * // corner
|
|
glm::scale(glm::vec3(sz * zoom, 1)) *
|
|
glm::translate(glm::vec3(.5f, .5f, 0.f)); // pivot
|
|
|
|
m_canvas->m_fb.bindTexture();
|
|
m_canvas->m_sampler.bind(0);
|
|
ui::ShaderManager::use(kShader::Texture);
|
|
ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
|
|
ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp);
|
|
NodeBorder::m_plane.draw_fill();
|
|
m_canvas->m_sampler.unbind();
|
|
m_canvas->m_fb.unbindTexture();
|
|
|
|
|
|
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<MouseEvent*>(e);
|
|
auto loc = me->m_pos - m_pos;
|
|
auto cur = glm::vec2(loc.x, m_canvas->m_height - loc.y - 1);
|
|
switch (e->m_type)
|
|
{
|
|
case kEventType::MouseDownL:
|
|
{
|
|
m_canvas->stroke_start(cur, 1.f, m_brush);
|
|
m_dragging = true;
|
|
mouse_capture();
|
|
break;
|
|
}
|
|
case kEventType::MouseUpL:
|
|
m_canvas->stroke_end();
|
|
m_dragging = false;
|
|
mouse_release();
|
|
break;
|
|
case kEventType::MouseMove:
|
|
if (m_dragging)
|
|
m_canvas->stroke_update(cur, 1.f);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return kEventResult::Consumed;
|
|
}
|
|
};
|