Added Wren scripting with module system 📚

Added Input pressed, released system
Added interface button support with wren onclick callback
This commit is contained in:
Antoine Pilote
2021-06-09 22:15:53 -04:00
parent aa2ebd04c8
commit 19db72aa22
1185 changed files with 320199 additions and 486 deletions

View File

@@ -2,28 +2,276 @@
Ref<Canvas> InterfaceParser::Root = CreateRef<Canvas>();
void InterfaceParser::Iterate(const pugi::xml_node& xml_node, Ref<Node> node)
void InterfaceParser::Iterate(const pugi::xml_node& xml_node, Ref<Node> node, int depth)
{
int currentDepth = depth + 1;
for (auto& e : xml_node.children())
{
std::string type = e.name();
Ref<Node> newNode;
if (type == "Canvas")
{
Ref<Canvas> canvas = CreateCanvas(e);
node->Childrens.push_back(canvas);
Iterate(e, canvas);
}
newNode = CreateCanvas(e);
else if (type == "Rect")
{
Ref<Node> newNode = CreateNode(e);
node->Childrens.push_back(newNode);
Iterate(e, newNode);
}
newNode = CreateNode(e);
else if (type == "p")
newNode = CreateTextNode(e);
else
continue;
newNode->Depth = currentDepth;
node->Childrens.push_back(newNode);
Iterate(e, newNode, currentDepth);
}
}
Ref<Canvas> InterfaceParser::Parse(const std::string path)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(path.c_str());
std::string name = doc.first_child().name();
if (name != "Canvas")
{
Logger::Log("InterfaceParser error: First child should be a canvas - " + path);
return nullptr;
}
Root = CreateCanvas(doc.first_child());
Root->Depth = 0;
Iterate(doc.first_child(), Root, 0);
return Root;
}
Ref<TextNode> InterfaceParser::CreateTextNode(const pugi::xml_node& xml_node)
{
Ref<TextNode> node = CreateRef<TextNode>();
node->content = xml_node.text().as_string();
node->Type = NodeType::Text;
TextStyle style{
FontManager::GetFont("resources/Fonts/RobotoMono-Regular.ttf"),
2.0,
Color(255, 255, 255, 255)
};
node->SetTextStyle(style);
for (auto& a : xml_node.attributes())
{
std::string name = a.name();
if (name == "groups")
{
Ref<TextNode> textnode = CreateTextNode(e);
node->Childrens.push_back(textnode);
Iterate(e, textnode);
for (auto& g : split(a.value(), ' '))
{
node->AddGroup(g);
}
}
if (name == "height")
node->Height = GetUnit(a.value());
if (name == "width")
node->Width = GetUnit(a.value());
if (name == "margin")
node->Margin = GetVec4Unit(a.value());
if (name == "padding")
node->Padding = GetVec4Unit(a.value());
if (name == "border")
node->Border = GetVec4Unit(a.value());
if (name == "position")
node->Position = GetVec4Unit(a.value());
if (name == "color")
node->BackgroundColor = GetColor(a.value());
}
return node;
}
Ref<Node> InterfaceParser::CreateNode(const pugi::xml_node& xml_node)
{
Ref<Node> node = CreateRef<Node>();
node->Root = Root;
for (auto& a : xml_node.attributes())
{
std::string name = a.name();
if (name == "groups")
{
for (auto& g : split(a.value(), ' '))
{
node->AddGroup(g);
}
}
if (name == "height")
node->Height = GetUnit(a.value());
if (name == "width")
node->Width = GetUnit(a.value());
if (name == "margin")
node->Margin = GetVec4Unit(a.value());
if (name == "padding")
node->Padding = GetVec4Unit(a.value());
if (name == "border")
node->Border = GetVec4Unit(a.value());
if (name == "position")
node->Position = GetVec4Unit(a.value());
if (name == "color")
node->BackgroundColor = GetColor(a.value());
if (name == "onclick")
{
std::string signature = a.value();
Root->Script->RegisterMethod(a.value());
node->OnClickSignature = a.value();
}
}
return node;
}
Layout::LayoutUnit InterfaceParser::GetUnit(const std::string& value)
{
std::regex only_number("[0-9]+");
std::regex not_number("[^0-9]+");
// is percentage
std::smatch match_value;
if (std::regex_search(value.begin(), value.end(), match_value, only_number))
{
std::smatch match_type;
if (std::regex_search(value.begin(), value.end(), match_type, not_number))
{
float value = std::stof(match_value[0]);
if (match_type[0] == "%")
{
return Layout::LayoutUnit{
value,
Layout::Unit::PERCENT
};
}
else if (match_type[0] == "px")
{
return Layout::LayoutUnit{
value,
Layout::Unit::PIXEL
};
}
}
}
}
std::vector<std::string> InterfaceParser::split(std::string const& str, const char delim)
{
std::vector<std::string> result;
size_t start;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
{
end = str.find(delim, start);
result.push_back(str.substr(start, end - start));
}
return result;
}
Layout::LayoutVec4 InterfaceParser::GetVec4Unit(const std::string& value)
{
std::regex regex("[0-9]+[^ ]+");
std::smatch match_value;
Layout::LayoutVec4 result;
std::vector<std::string> splits = split(value, ' ');
int idx = 0;
for (auto& s : splits)
{
if (std::regex_search(value.begin(), value.end(), match_value, regex))
{
if (idx == 0)
{
result.Left = GetUnit(s);
}
else if (idx == 1)
{
result.Right = GetUnit(s);
}
else if (idx == 2)
{
result.Top = GetUnit(s);
}
else
{
result.Bottom = GetUnit(s);
}
idx++;
}
}
return result;
}
Color InterfaceParser::GetColor(const std::string& value)
{
std::regex regex("[0-9]+[^ ]+");
std::smatch match_value;
Color result;
std::vector<std::string> splits = split(value, ' ');
int idx = 0;
for (auto& s : splits)
{
if (std::regex_search(value.begin(), value.end(), match_value, regex))
{
if (idx == 0)
{
result.r = std::stof(s);
}
else if (idx == 1)
{
result.g = std::stof(s);
}
else if (idx == 2)
{
result.b = std::stof(s);
}
else
{
result.a = std::stof(s);
}
idx++;
}
}
return result;
}
Ref<Canvas> InterfaceParser::CreateCanvas(const pugi::xml_node& xml_node)
{
Ref<Canvas> node = CreateRef<Canvas>();
node->Root = Root;
for (auto& a : xml_node.attributes())
{
std::string name = a.name();
if (name == "groups")
{
for (auto& g : split(a.value(), ' '))
{
node->AddGroup(g);
}
}
if (name == "height")
node->Height = GetUnit(a.value());
if (name == "width")
node->Width = GetUnit(a.value());
if (name == "margin")
node->Margin = GetVec4Unit(a.value());
if (name == "padding")
node->Padding = GetVec4Unit(a.value());
if (name == "border")
node->Border = GetVec4Unit(a.value());
if (name == "position")
node->Position = GetVec4Unit(a.value());
if (name == "color")
node->BackgroundColor = GetColor(a.value());
if (name == "script")
{
auto s = split(a.value(), ' ');
std::string path = s[0];
std::string module = s[1];
node->Script = ScriptingEngine::RegisterScript(path, module);
}
}
return node;
}

View File

@@ -8,229 +8,23 @@
#include <src/Core/Logger.h>
#include "Nodes/TextNode.h"
#include "Font/FontManager.h"
#include "../Scripting/ScriptingEngine.h"
class InterfaceParser
{
static Ref<Canvas> Root;
private:
static Ref<Canvas> Root;
public:
static Ref<Canvas> Parse(const std::string path)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(path.c_str());
static Ref<Canvas> Parse(const std::string path);
std::string name = doc.first_child().name();
if (name != "Canvas")
{
Logger::Log("InterfaceParser error: First child should be a canvas - " + path);
return nullptr;
}
Root = CreateCanvas(doc.first_child());
static void Iterate(const pugi::xml_node& xml_node, Ref<Node> node, int depth);
Iterate(doc.first_child(), Root);
return Root;
}
static Ref<Node> CreateNode(const pugi::xml_node& xml_node);
static Ref<Canvas> CreateCanvas(const pugi::xml_node& xml_node);
static Ref<TextNode> CreateTextNode(const pugi::xml_node& xml_node);
static std::vector<std::string> split(std::string const& str, const char delim);
static void Iterate(const pugi::xml_node& xml_node, Ref<Node> node);
static Ref<TextNode> CreateTextNode(const pugi::xml_node& xml_node)
{
Ref<TextNode> node = CreateRef<TextNode>();
node->content = xml_node.text().as_string();
TextStyle style{
FontManager::GetFont("resources/Fonts/OpenSans-Regular.ttf"),
2.0,
Color(255, 255, 255, 255)
};
node->SetTextStyle(style);
return node;
}
static Ref<Node> CreateNode(const pugi::xml_node& xml_node)
{
Ref<Node> node = CreateRef<Node>();
for (auto& a : xml_node.attributes())
{
std::string name = a.name();
if (name == "groups")
{
for (auto& g : split(a.value(), ' '))
{
node->AddGroup(g);
}
}
if (name == "height")
node->Height = GetUnit(a.value());
if (name == "width")
node->Width = GetUnit(a.value());
if (name == "margin")
node->Margin = GetVec4Unit(a.value());
if (name == "padding")
node->Padding = GetVec4Unit(a.value());
if (name == "border")
node->Border = GetVec4Unit(a.value());
if (name == "position")
node->Position = GetVec4Unit(a.value());
if (name == "color")
node->BackgroundColor = GetColor(a.value());
}
return node;
}
static Layout::LayoutUnit GetUnit(const std::string& value)
{
std::regex only_number("[0-9]+");
std::regex not_number("[^0-9]+");
// is percentage
std::smatch match_value;
if (std::regex_search(value.begin(), value.end(), match_value, only_number))
{
std::smatch match_type;
if (std::regex_search(value.begin(), value.end(), match_type, not_number))
{
float value = std::stof(match_value[0]);
if (match_type[0] == "%")
{
return Layout::LayoutUnit{
value,
Layout::Unit::PERCENT
};
}
else if (match_type[0] == "px")
{
return Layout::LayoutUnit{
value,
Layout::Unit::PIXEL
};
}
}
}
}
static std::vector<std::string> split(std::string const& str, const char delim)
{
std::vector<std::string> result;
size_t start;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
{
end = str.find(delim, start);
result.push_back(str.substr(start, end - start));
}
return result;
}
static Layout::LayoutVec4 GetVec4Unit(const std::string& value)
{
std::regex regex("[0-9]+[^ ]+");
std::smatch match_value;
Layout::LayoutVec4 result;
std::vector<std::string> splits = split(value, ' ');
int idx = 0;
for (auto& s : splits)
{
if (std::regex_search(value.begin(), value.end(), match_value, regex))
{
if (idx == 0)
{
result.Left = GetUnit(s);
}
else if (idx == 1)
{
result.Right = GetUnit(s);
}
else if (idx == 2)
{
result.Top = GetUnit(s);
}
else
{
result.Bottom = GetUnit(s);
}
idx++;
}
}
return result;
}
static Color GetColor(const std::string& value)
{
std::regex regex("[0-9]+[^ ]+");
std::smatch match_value;
Color result;
std::vector<std::string> splits = split(value, ' ');
int idx = 0;
for (auto& s : splits)
{
if (std::regex_search(value.begin(), value.end(), match_value, regex))
{
if (idx == 0)
{
result.r = std::stof(s);
}
else if (idx == 1)
{
result.g = std::stof(s);
}
else if (idx == 2)
{
result.b = std::stof(s);
}
else
{
result.a = std::stof(s);
}
idx++;
}
}
return result;
}
static Ref<Canvas> CreateCanvas(const pugi::xml_node& xml_node)
{
Ref<Canvas> node = CreateRef<Canvas>();
for (auto& a : xml_node.attributes())
{
std::string name = a.name();
if (name == "groups")
{
for (auto& g : split(a.value(), ' '))
{
node->AddGroup(g);
}
}
if (name == "height")
node->Height = GetUnit(a.value());
if (name == "width")
node->Width = GetUnit(a.value());
if (name == "margin")
node->Margin = GetVec4Unit(a.value());
if (name == "padding")
node->Padding = GetVec4Unit(a.value());
if (name == "border")
node->Border = GetVec4Unit(a.value());
if (name == "position")
node->Position = GetVec4Unit(a.value());
if (name == "color")
node->BackgroundColor = GetColor(a.value());
}
return node;
}
static Layout::LayoutUnit GetUnit(const std::string& value);
static Layout::LayoutVec4 GetVec4Unit(const std::string& value);
static Color GetColor(const std::string& value);
};

View File

@@ -1,5 +1,8 @@
#pragma once
#include "Node.h"
#include "../Core/Core.h"
#include "../Scripting/WrenScript.h"
#include <map>
// Base container for UI.
class Canvas : public Node
{
@@ -7,7 +10,6 @@ private:
// Script here
public:
Ref<WrenScript> Script;
Canvas();
};

View File

@@ -1,5 +1,24 @@
#include "Node.h"
Node::Node()
{
Childrens = std::vector<Ref<Node>>();
Groups = std::vector<std::string>();
PositionType = Layout::PositionType::RELATIVE;
Position = Layout::LayoutVec4();
Height = MaxHeight = MinHeight = Layout::LayoutUnit();
Margin = Padding = Border = Layout::LayoutVec4();
Direction = Layout::LayoutDirection::LTR;
FlexDirection = Layout::FlexDirection::ROW;
FlexWrap = Layout::FlexWrap::WRAP;
FlexGrow = 0.f;
FlexShrink = 1.f;
FlexBasis = 10.f;
AspectRatio = 1.f;
AlignItems = Layout::AlignItems::AUTO;
AlignSelf = Layout::AlignItems::AUTO;
}
void Node::ApplyStyle(Ref<StyleGroup> stylegroup)
{
for (auto& s : stylegroup->GetProps())
@@ -90,21 +109,33 @@ void Node::ApplyStyle(Ref<StyleGroup> stylegroup)
}
}
Node::Node()
bool Node::IsPositionInside(Vector2 position)
{
Childrens = std::vector<Ref<Node>>();
Groups = std::vector<std::string>();
PositionType = Layout::PositionType::RELATIVE;
Position = Layout::LayoutVec4();
Height = MaxHeight = MinHeight = Layout::LayoutUnit();
Margin = Padding = Border = Layout::LayoutVec4();
Direction = Layout::LayoutDirection::LTR;
FlexDirection = Layout::FlexDirection::ROW;
FlexWrap = Layout::FlexWrap::WRAP;
FlexGrow = 0.f;
FlexShrink = 1.f;
FlexBasis = 10.f;
AspectRatio = 1.f;
AlignItems = Layout::AlignItems::AUTO;
AlignSelf = Layout::AlignItems::AUTO;
float width = YGNodeLayoutGetWidth(YogaNode);
float height = YGNodeLayoutGetHeight(YogaNode);
float padding = YGNodeLayoutGetPadding(YogaNode, YGEdgeLeft);
float left = YGNodeLayoutGetLeft(YogaNode); //+ offset.x;
float top = YGNodeLayoutGetTop(YogaNode);// +offset.y;
float parentLeft = 0.0f;
float parentTop = 0.0f;
auto parent = YGNodeGetParent(YogaNode);
if (parent)
{
parentLeft = YGNodeLayoutGetLeft(YGNodeGetParent(YogaNode));
parentTop = YGNodeLayoutGetTop(YGNodeGetParent(YogaNode));
float parentPaddingTop = YGNodeLayoutGetPadding(parent, YGEdgeTop);
float parentPaddingLeft = YGNodeLayoutGetPadding(parent, YGEdgeTop);
// Overflow hidden.
float parentwidth = YGNodeLayoutGetWidth(parent);
if (parentwidth - YGNodeLayoutGetMargin(YogaNode, YGEdgeLeft) < width)
width = parentwidth - parentPaddingLeft;
float parentHeight = YGNodeLayoutGetHeight(parent);
if (parentHeight < height)
height = parentHeight - parentPaddingTop;
}
left += parentLeft;
top += parentTop;
return position.x > left && position.x < left + width && position.y > top && position.y < top + height;
}

View File

@@ -65,47 +65,56 @@ namespace Layout
}
enum class NodeType
{
Normal,
Text
};
// Base UI node.
class Node
{
public:
Ref<Node*> Parent;
YGNodeRef YogaNode;
int Depth;
Ref<Node> Root;
Ref<Node> Parent;
std::vector<std::string> Groups;
std::vector<Ref<Node>> Childrens = std::vector<Ref<Node>>();
void ApplyStyle(Ref<StyleGroup> stylegroup);
std::string OnClickSignature = "";
NodeType Type = NodeType::Normal;
YGNodeRef YogaNode;
Ref<Style> style;
std::vector<std::string> Groups;
Layout::PositionType PositionType;
Layout::LayoutVec4 Position;
Layout::LayoutUnit Width, MaxWidth, MinWidth;
Layout::LayoutUnit Height, MaxHeight, MinHeight;
Layout::LayoutVec4 Margin;
Layout::LayoutVec4 Padding;
Layout::LayoutVec4 Border;
Color BackgroundColor;
Layout::LayoutDirection Direction;
Layout::FlexDirection FlexDirection = Layout::FlexDirection::ROW;
Layout::FlexWrap FlexWrap;
float FlexGrow;
float FlexShrink;
float FlexBasis;
float AspectRatio;
Layout::JustifyContent JustifyContent;
Layout::AlignItems AlignItems;
Layout::AlignItems AlignSelf;
Layout::AlignContent AlignContent;
Node();
void ApplyStyle(Ref<StyleGroup> stylegroup);
bool IsPositionInside(Vector2 position);
void AddGroup(const std::string& group)
{
Groups.push_back(group);

View File

@@ -21,7 +21,12 @@ public:
this->style = textStyle;
}
void Draw()
void CalculateSize()
{
}
void Draw(float z)
{
Renderer2D::TextShader->Bind();
@@ -36,29 +41,25 @@ public:
auto parent = YGNodeGetParent(this->YogaNode);
if (parent)
{
parentLeft = YGNodeLayoutGetLeft(YGNodeGetParent(this->YogaNode));
parentTop = YGNodeLayoutGetTop(YGNodeGetParent(this->YogaNode));
float parentPaddingTop = YGNodeLayoutGetPadding(parent, YGEdgeTop);
float parentPaddingLeft = YGNodeLayoutGetPadding(parent, YGEdgeTop);
// Overflow hidden.
float parentwidth = YGNodeLayoutGetWidth(parent);
if (parentwidth - YGNodeLayoutGetMargin(this->YogaNode, YGEdgeLeft) < width)
width = parentwidth - parentPaddingLeft;
float parentHeight = YGNodeLayoutGetHeight(parent);
if (parentHeight < height)
height = parentHeight - YGNodeLayoutGetMargin(this->YogaNode, YGEdgeTop) - parentPaddingTop;
//parentLeft = YGNodeLayoutGetLeft(YGNodeGetParent(this->YogaNode));
//parentTop = YGNodeLayoutGetTop(YGNodeGetParent(this->YogaNode));
//float parentPaddingTop = YGNodeLayoutGetPadding(parent, YGEdgeTop);
//float parentPaddingLeft = YGNodeLayoutGetPadding(parent, YGEdgeTop);
//// Overflow hidden.
//float parentwidth = YGNodeLayoutGetWidth(parent);
//if (parentwidth - YGNodeLayoutGetMargin(this->YogaNode, YGEdgeLeft) < width)
// width = parentwidth - parentPaddingLeft;
//float parentHeight = YGNodeLayoutGetHeight(parent);
//if (parentHeight < height)
// height = parentHeight - YGNodeLayoutGetMargin(this->YogaNode, YGEdgeTop) - parentPaddingTop;
}
transform = glm::translate(transform, Vector3(left + parentLeft, top + parentTop, 0.f));
Vector3 scale = Vector3(width, height, 0);
transform = glm::scale(transform, scale);
//Logger::Log("Left: " + std::to_string(left) + " Top:" + std::to_string(top));
Renderer2D::UIShader->SetUniformMat4f("model", transform);
Renderer2D::DrawString(content, style, transform);
//Renderer2D::DrawString(content, style.font, Vector2(Position.Left, Position.Top), 2.0);

View File

@@ -5,6 +5,8 @@
#include "yoga/YGConfig.h"
#include "InterfaceParser.h"
#include <src/UI/Font/FontLoader.h>
#include "../Core/Input.h"
namespace UI
{
UserInterface::UserInterface(const std::string& name)
@@ -118,7 +120,10 @@ namespace UI
if (!node)
return;
node->Draw(z);
if (node->Type == NodeType::Text)
std::static_pointer_cast<TextNode>(node)->Draw(z);
else
node->Draw(z);
if (node->Childrens.size() <= 0)
return;
@@ -131,6 +136,28 @@ namespace UI
void UserInterface::Update(Timestep ts)
{
// Check Input
if (Input::IsMouseButtonPressed(0))
{
ConsumeMouseClick(Input::GetMousePosition());
}
}
void UserInterface::RecursiveMouseClick(Ref<Node> node, Vector2 pos)
{
for (auto& c : node->Childrens)
{
RecursiveMouseClick(c, pos);
if (c->IsPositionInside(pos) && c->OnClickSignature != "")
this->Root->Script->CallMethod(c->OnClickSignature);
}
}
void UserInterface::ConsumeMouseClick(Vector2 pos)
{
RecursiveMouseClick(Root, pos);
}
}

View File

@@ -37,5 +37,9 @@ namespace UI
void Draw(Vector2 size);
void DrawRecursive(Ref<Node> node, float z);
void Update(Timestep ts);
void RecursiveMouseClick(Ref<Node> node, Vector2 pos);
void ConsumeMouseClick(Vector2 pos);
};
}