Parsing css and css groups

This commit is contained in:
Antoine Pilote
2021-06-02 09:41:39 -04:00
parent 39aaa4cd2c
commit e3052e2e1f
16 changed files with 455 additions and 19 deletions

View File

@@ -1,7 +1,11 @@
.button {
width: 10px;
height: 100px;
color: rgba(1, 1, 2, 1);
}
width: 25%;
height: 100%;
}
.left {
width: 100px;
height: 150px;
margin: 50px 100px;
}

View File

@@ -0,0 +1,31 @@
<Canvas
script="Interface/Testing.lua"
height="100%"
width="100%"
padding="10px 10px 10px 10px"
color="0 0 128 255"
groups="button"
>
<!-- PINK -->
<Rect
height="80%"
width="80%"
padding="10px 10px 10px 10px"
color="255 0 255 255"
>
<!-- BROWN -->
<Rect
groups="left"
margin="0px 1%"
color="111 77 22 255"
>
</Rect>
<!-- BLUE -->
<Rect
width="49%"
height="50%"
color="15 66 128 255"
></Rect>
</Rect>
</Canvas>

5
Nuake/src/UI/Canvas.cpp Normal file
View File

@@ -0,0 +1,5 @@
#include "Canvas.h"
Canvas::Canvas()
{
}

View File

@@ -0,0 +1,3 @@
#include "InterfaceParser.h"
Ref<Canvas> InterfaceParser::Root = CreateRef<Canvas>();

View File

@@ -32,6 +32,8 @@ public:
return Root;
}
static void Iterate(const pugi::xml_node& xml_node, Ref<Node> node)
{
for (auto& e : xml_node.children())
@@ -58,6 +60,13 @@ public:
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")
@@ -72,7 +81,6 @@ public:
node->Position = GetVec4Unit(a.value());
if (name == "color")
node->BackgroundColor = GetColor(a.value());
}
return node;
@@ -200,6 +208,13 @@ public:
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")

View File

@@ -1,8 +1,62 @@
#include "Node.h"
void Node::ApplyStyle(Ref<StyleGroup> stylegroup)
{
for (auto& s : stylegroup->GetProps())
{
switch (s.first)
{
case PropType::HEIGHT:
{
Height.Unit = (Layout::Unit)(s.second.type);
float value = s.second.value.Number;
Height.Value = value;
}
break;
case PropType::MIN_HEIGHT:
MinHeight.Unit = (Layout::Unit)s.second.type;
MinHeight.Value = s.second.value.Number;
break;
case PropType::MAX_HEIGHT:
MaxHeight.Unit = (Layout::Unit)s.second.type;
MaxHeight.Value = s.second.value.Number;
break;
case PropType::WIDTH:
Width.Unit = (Layout::Unit)s.second.type;
Width.Value = s.second.value.Number;
break;
case PropType::MIN_WIDTH:
MinWidth.Unit = (Layout::Unit)s.second.type;
MinWidth.Value = s.second.value.Number;
break;
case PropType::MAX_WIDTH:
MaxWidth.Unit = (Layout::Unit)s.second.type;
MaxWidth.Value = s.second.value.Number;
break;
case PropType::MARGIN_RIGHT:
Margin.Right.Unit = (Layout::Unit)s.second.type;
Margin.Right.Value = s.second.value.Number;
break;
case PropType::MARGIN_LEFT:
Margin.Left.Unit = (Layout::Unit)s.second.type;
Margin.Left.Value = s.second.value.Number;
break;
case PropType::MARGIN_TOP:
Margin.Top.Unit = (Layout::Unit)s.second.type;
Margin.Top.Value = s.second.value.Number;
break;
case PropType::MARGIN_BOTTOM:
Margin.Bottom.Unit = (Layout::Unit)s.second.type;
Margin.Bottom.Value = s.second.value.Number;
break;
}
}
}
Node::Node()
{
Childrens = std::vector<Ref<Node>>();
Groups = std::vector<std::string>();
PositionType = Layout::PositionType::RELATIVE;
Position = Layout::LayoutVec4();

View File

@@ -4,6 +4,8 @@
#include "yoga/Yoga.h"
#include "../Rendering/Renderer2D.h"
#include "../Core/Logger.h"
#include "Style.h"
namespace Layout
{
enum class LayoutDirection
@@ -70,6 +72,10 @@ public:
YGNodeRef YogaNode;
std::vector<Ref<Node>> Childrens = std::vector<Ref<Node>>();
void ApplyStyle(Ref<StyleGroup> stylegroup);
Ref<Style> style;
std::vector<std::string> Groups;
Layout::PositionType PositionType;
Layout::LayoutVec4 Position;
@@ -97,6 +103,20 @@ public:
Node();
void AddGroup(const std::string& group)
{
Groups.push_back(group);
}
bool IsInGroup(const std::string& group)
{
for (auto& g : Groups)
if (g == group) return true;
return false;
}
std::vector<std::string> GetGroups() const { return Groups; }
void SetMargin()
{
switch (Margin.Left.Unit)

84
Nuake/src/UI/Style.h Normal file
View File

@@ -0,0 +1,84 @@
#pragma once
#include "../Core/Maths.h"
#include <map>
enum class PropType
{
HEIGHT, MAX_HEIGHT, MIN_HEIGHT, WIDTH, MAX_WIDTH, MIN_WIDTH,
LAYOUT_DIRECTION, JUSTIFY_CONTENT,
FLEX_BASIS, FLEX_GROW, FLEX_SHRINK,FLEX_WRAP, FLEX_DIRECTION,
ASPECT_RATIO,
ALIGN_ITEMS, SELF_ALIGN, ALIGN_CONTENT,
POSITION,
LEFT, RIGHT, TOP, BOTTOM,
MARGIN, MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM,
PADDING, PADDING_LEFT, PADDING_RIGHT, PADDING_TOP, PADDING_BOTTOM,
BORDER, BORDER_LEFT, BORDER_RIGHT, BORDER_TOP, BORDER_BOTTOM,
BORDER_RADIUS, BORDER_RADIUS_LEFT, BORDER_RADIUS_RIGHT, BORDER_RADIUS_TOP, BORDER_RADIUS_BOTTOM,
BACKGROUND_COLOR, COLOR,
};
enum class PropValueType
{
PIXEL, PERCENT, AUTO, COLOR, ENUM
};
union Value
{
float Number;
Color Color;
int Enum;
};
struct PropValue
{
PropValueType type;
Value value;
};
class StyleGroup
{
public:
std::map<PropType, PropValue> Props;
StyleGroup()
{
this->Props = std::map<PropType, PropValue>();
}
void SetProp(PropType type, PropValue prop)
{
Props[type] = prop;
}
PropValue& GetProp(PropType type)
{
if (Props.find(type) != Props.end())
return Props[type];
return PropValue();
}
bool HasProp(PropType type)
{
return Props.find(type) != Props.end();
}
std::map<PropType, PropValue> GetProps()
{
return Props;
}
StyleGroup operator+(StyleGroup other)
{
for (auto p : other.Props)
{
this->Props[p.first] = p.second;
}
return *this;
}
};
class Style
{
};

View File

@@ -0,0 +1,53 @@
#include "StyleSheetParser.h"
#include <regex>
#include <src/Vendors/pugixml/pugixml.hpp>
PropValue StyleSheetParser::ParsePropType(const std::string& str, PropType type)
{
switch (type)
{
case PropType::HEIGHT: case PropType::MIN_HEIGHT: case PropType::MAX_HEIGHT:
case PropType::WIDTH: case PropType::MIN_WIDTH: case PropType::MAX_WIDTH:
case PropType::LEFT: case PropType::RIGHT: case PropType::TOP: case PropType::BOTTOM:
case PropType::MARGIN_LEFT: case PropType::MARGIN_RIGHT: case PropType::MARGIN_TOP: case PropType::MARGIN_BOTTOM:
case PropType::PADDING_LEFT: case PropType::PADDING_RIGHT: case PropType::PADDING_TOP: case PropType::PADDING_BOTTOM:
case PropType::BORDER_LEFT: case PropType::BORDER_RIGHT: case PropType::BORDER_TOP: case PropType::BORDER_BOTTOM:
{
std::regex only_number("[0-9]+");
std::regex not_number("[^0-9]+");
// is percentage
std::smatch match_value;
if (std::regex_search(str.begin(), str.end(), match_value, only_number))
{
std::smatch match_type;
if (std::regex_search(str.begin(), str.end(), match_type, not_number))
{
float value = std::stof(match_value[0]);
if (match_type[0] == "%")
{
PropValue returnValue = PropValue{
PropValueType::PERCENT,
{0}
};
returnValue.value.Number = value;
return returnValue;
}
else if (match_type[0] == "px")
{
PropValue returnValue = PropValue{
PropValueType::PIXEL,
{0}
};
returnValue.value.Number = value;
return returnValue;
}
}
}
}
break;
}
return PropValue();
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
#include "Style.h"
class StyleSheetParser
{
public:
static PropValue ParsePropType(const std::string& str, PropType type);
};

View File

@@ -4,9 +4,12 @@ namespace UI
{
Ref<StyleSheet> StyleSheet::New(const std::string& path)
{
return CreateRef<StyleSheet>(path);
return CreateRef<StyleSheet>(path);
}
}

View File

@@ -1,20 +1,48 @@
#pragma once
#include <string>
#include "katana-parser/katana.h"
#include "../Core/FileSystem.h"
#include "../Core/Logger.h"
#include "../Core/Core.h"
#include "Style.h"
#include "katana-parser/katana.h"
#include <string>
#include <map>
#include <src/UI/StyleSheetParser.h>
#include <regex>
#include <regex>
#include <src/UI/InterfaceParser.h>
#include <src/UI/InterfaceParser.h>
namespace UI
{
class StyleSheet
{
private:
KatanaOutput* Data;
std::map<std::string, Ref<StyleGroup>> Styles;
public:
std::string Path;
static Ref<StyleSheet> New(const std::string& path);
void AddStyleGroup(std::string selector, Ref<StyleGroup> group)
{
Styles[selector] = group;
}
bool HasStyleGroup(const std::string& group)
{
return Styles.find(group) != Styles.end();
}
Ref<StyleGroup> GetStyleGroup(const std::string group)
{
if (!HasStyleGroup(group))
return nullptr;
return Styles[group];
}
StyleSheet(const std::string& path)
{
@@ -22,29 +50,96 @@ namespace UI
ParseSheet();
}
static Ref<StyleSheet> New(const std::string& path);
~StyleSheet()
{
katana_destroy_output(Data);
}
bool ParseRule(KatanaRule* rule)
{
if (rule->type == KatanaRuleStyle)
{
Ref<StyleGroup> styleGroup = CreateRef<StyleGroup>();
// Selectors
KatanaStyleRule* stylerule = (KatanaStyleRule*)rule;
for (int j = 0; j < stylerule->selectors->length; j++)
{
KatanaSelector* selector = (KatanaSelector*)stylerule->selectors->data[j];
std::string name = selector->data->value;
AddStyleGroup(name, styleGroup);
}
// Declarations
for (int k = 0; k < stylerule->declarations->length; k++)
{
KatanaDeclaration* declaration = (KatanaDeclaration*)(stylerule->declarations->data[k]);
std::string value = declaration->raw;
std::string name = declaration->property;
PropType type;
if (name == "height") type = PropType::HEIGHT;
if (name == "width")type = PropType::WIDTH;
if (name == "margin")
{
std::regex regex("[0-9]+[^ ]+");
std::smatch match_value;
Layout::LayoutVec4 result;
std::vector<std::string> splits = InterfaceParser::split(value, ' ');
int idx = 0;
for (auto& s : splits)
{
if (idx == 0)
{
type = PropType::MARGIN_LEFT;
styleGroup->SetProp(type, StyleSheetParser::ParsePropType(s, type));
}
else if (idx == 1)
{
type = PropType::MARGIN_RIGHT;
styleGroup->SetProp(type, StyleSheetParser::ParsePropType(s, type));
}
else if (idx == 2)
{
type = PropType::MARGIN_TOP;
styleGroup->SetProp(type, StyleSheetParser::ParsePropType(s, type));
}
else if (idx == 3)
{
type = PropType::MARGIN_BOTTOM;
styleGroup->SetProp(type, StyleSheetParser::ParsePropType(s, type));
}
idx++;
}
return true;
}
styleGroup->SetProp(type, StyleSheetParser::ParsePropType(value, type));
Logger::Log(declaration->property);
}
Logger::Log(rule->name);
}
return true;
}
bool ParseSheet()
{
std::string content = FileSystem::ReadFile(this->Path);
Data = katana_parse(content.c_str(), content.length(), KatanaParserModeStylesheet);
for (int i = 0; i < Data->stylesheet->rules.length; i++)
{
KatanaRule* rule = (KatanaRule*)Data->stylesheet->rules.data[i];
ParseRule(rule);
}
if (Data->errors.length > 0)
{
KatanaArray errors = Data->errors;
return false;
}
//Data->stylesheet->rules
katana_dump_output(Data);
return true;
}
};
}

View File

@@ -10,8 +10,10 @@ namespace UI
{
m_Name = name;
Root = InterfaceParser::Parse("resources/Interface/Testing.interface");
m_Stylesheet = StyleSheet::New("/Interface\\Testing.css");
Root = InterfaceParser::Parse("resources/Interface/Testing.interface");
if (!Root)
{
Logger::Log("Failed to generate interface structure");
@@ -31,6 +33,7 @@ namespace UI
void UserInterface::Reload()
{
m_Stylesheet = StyleSheet::New("/Interface\\Testing.css");
Root = InterfaceParser::Parse("resources/Interface/Testing.interface");
if (!Root)
{
@@ -57,6 +60,10 @@ namespace UI
{
yoga_root = YGNodeNewWithConfig(yoga_config);
Root->YogaNode = yoga_root;
for (auto& g : Root->GetGroups())
if (m_Stylesheet->HasStyleGroup(g))
Root->ApplyStyle(m_Stylesheet->GetStyleGroup(g));
Root->SetYogaLayout();
CreateYogaLayoutRecursive(Root, yoga_root);
}
@@ -71,6 +78,11 @@ namespace UI
{
YGNodeRef newYogaNode = YGNodeNew();
n->YogaNode = newYogaNode;
for (auto& g : n->GetGroups())
if (m_Stylesheet->HasStyleGroup(g))
n->ApplyStyle(m_Stylesheet->GetStyleGroup(g));
n->SetYogaLayout();
YGNodeInsertChild(yoga_node, newYogaNode, index);
CreateYogaLayoutRecursive(n, newYogaNode);

View File

@@ -6,6 +6,7 @@
#include "Node.h"
#include <src/UI/Canvas.h>
#include "../Core/Maths.h"
#include "Stylesheet.h"
namespace UI
{
class UserInterface
@@ -14,6 +15,7 @@ namespace UI
Ref<FrameBuffer> m_Framebuffer; // Texture of the interface.
std::string m_Name;
Ref<Canvas> Root;
Ref<StyleSheet> m_Stylesheet;
YGConfigRef yoga_config;
YGNodeRef yoga_root;
public:

28
docs/Index.md Normal file
View File

@@ -0,0 +1,28 @@
# Nuake engine
# UI system
The UI system uses yoga layout and custom styling properties. The styling properties will mimick the standard css properties but will be rendererd using a shader. Since the layout system is based on yoga, the actual writting of the layout will look a lot like html, the differences will lie in the actual components.
# The UI pipeline maybe
1. load xml file containing pseudo html with components
  Potential nodes
- Canvas basically a container for UI.
- Button with basic state and event
- Texture which display a engine texture(can use framebuffer!!)
- Slider(ouch)
2. Create an in engien structure of the layout
3. Create a yoga layotu from node and calculate when changes occurs
4. Draw each node in the structure using styling loaded somewhere idk.
# Styling
TODO MAN

17
docs/Interface.md Normal file
View File

@@ -0,0 +1,17 @@
# Interface design
Interface component
Layout
Styling
Behaviour
## Layout
The layout is represented using XML
  ## Components
  The components all derives from Rect
## Styling
The styling could be done a file that looks like css
## Behaviour
The behaviour needs to be done using the scripting API