Merge branch 'MihailRis:main' into main

This commit is contained in:
RomanDonw 2026-07-23 16:35:52 +10:00 • committed by GitHub
commit 77e1e29eb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 237 additions and 3 deletions

View file

@ -52,6 +52,60 @@ yaml.parse(code: str) -> table
Parses a YAML string into a table.
## XML Library
The library contains functions for serializing and deserializing XML:
Further, XML as a data type denotes a Lua table with the following structure:
- Element tag is accessible by the key `#`
- XML ​​attributes are accessible as key = value pairs
- Inner elements are accessible by index
The table is used both as a dictionary and an array.
Example:
```xml
<panel size='400' color='0' interval='1' context='menu'>
<button onclick='menu.page="worlds"'>@Worlds</button>
</panel>
```
Equivalent Lua table:
```lua
{
['#'] = "panel",
size = "400",
color = "0",
interval = "1",
context = "menu",
{
['#'] = "button",
onclick = 'menu.page="worlds"',
{
"@Worlds"
}
}
}
```
Consequently, the button element is accessible as `panel_element[1]`.
```lua
-- Serializes a tree in XML format
-- multiline - use multiline formatting
xml.tostring(data: XML, multiline: bool = true) -> str
-- Parses XML
xml.parse(code: str) -> XML
-- Parses the VCD (VoxelCore Declaration) format used by the VCM format.
-- root_tag - a root element with the specified tag will be implicitly created,
-- containing all root elements
xml.parse_vcd(code: str, root_tag: str = "root") -> XML
```
## *bjson* library
The library contains functions for working with the binary data exchange format [vcbjson](../../specs/binary_json_spec.md).

View file

@ -52,6 +52,60 @@ yaml.parse(code: str) -> table
Парсит YAML строку в таблицу.
## Библиотека xml
Библиотека содержит функции для сериализации и десериализации XML:
Далее XML как тип данных обозначает Lua-таблицу со следующей структурой:
- Тег элемента доступен по ключу `#`
- XML-аттрибуты доступны как пары ключ = значение
- Вложенные элементы доступны по индексу
Таблица используется как словарь, так и массив.
Пример:
```xml
<panel size='400' color='0' interval='1' context='menu'>
<button onclick='menu.page="worlds"'>@Worlds</button>
</panel>
```
Эквивалентная Lua-таблица:
```lua
{
['#'] = "panel",
size = "400",
color = "0",
interval = "1",
context = "menu",
{
['#'] = "button",
onclick = 'menu.page="worlds"',
{
"@Worlds"
}
}
}
```
Соответственно, элемент button доступен как `элемент_panel[1]`.
```lua
-- Сериализует дерево в формате XML
-- multiline - использовать ли многострочное форматирование
xml.tostring(data: XML, multiline: bool = true) -> str
-- Парсит XML
xml.parse(code: str) -> XML
-- Парсит формат VCD (VoxelCore Declaration), используемый форматом VCM.
-- root_tag - будет неявно создан корневой элемент с указанным тегом,
-- содержащий все корневые элементы
xml.parse_vcd(code: str, root_tag: str | nil = nil) -> XML
```
## Библиотека bjson
Библиотека содержит функции для работы с двоичным форматом обмена данными [vcbjson](../../specs/binary_json_spec.md).

View file

@ -528,3 +528,11 @@ std::string xml::stringify(
return ss.str();
}
std::string xml::stringify(
const Node& element, bool nice, const std::string& indentStr
) {
std::stringstream ss;
stringifyElement(ss, element, nice, indentStr, 0);
return ss.str();
}

View file

@ -119,6 +119,12 @@ namespace xml {
const std::string& indentStr = " "
);
std::string stringify(
const Node& element,
bool nice = true,
const std::string& indentStr = " "
);
/// @brief Read XML Document from string
/// @param filename file name will be shown in error messages
/// @param source xml source code string

View file

@ -9,8 +9,8 @@ class Font;
struct FontMetrics {
std::optional<std::weak_ptr<Font>> font;
int lineHeight;
int yoffset;
int lineHeight = 0;
int yoffset = 0;
int _glyphInterval = 8;
~FontMetrics();

View file

@ -207,6 +207,9 @@ uint Label::getLinesNumber() const {
void Label::draw(const DrawContext& pctx, const Assets& assets) {
auto batch = pctx.getBatch2D();
auto font = assets.getShared<Font>(fontName);
if (font == nullptr) {
return;
}
cache.prepare(
font,
font->getMetrics(),

View file

@ -353,7 +353,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) {
auto font = assets.getShared<Font>(label->getFontName());
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
if (font != nullptr) {
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
}
glm::vec2 pos = calcPos();

View file

@ -53,6 +53,7 @@ extern const luaL_Reg vec3lib[]; // vecn.cpp
extern const luaL_Reg vec4lib[]; // vecn.cpp
extern const luaL_Reg weatherlib[]; // gfx.weather
extern const luaL_Reg worldlib[];
extern const luaL_Reg xmllib[];
extern const luaL_Reg yamllib[];
// Components

View file

@ -0,0 +1,105 @@
#include "coders/xml.hpp"
#include "api_lua.hpp"
static const char* TAG_ATTR = "#";
static const char* DEFAULT_ROOT_TAG = "root";
static int push_xml(lua::State* L, const xml::xmlelement& elem) {
if (elem.isText()) {
return lua::pushlstring(L, elem.getInnerText());
}
const auto& attrs = elem.getAttributes();
const auto& elems = elem.getElements();
const auto& tag = elem.getTag();
lua::createtable(L, elem.size(), attrs.size() + 1);
lua::pushlstring(L, tag);
lua::setfield(L, TAG_ATTR);
for (int i = 0; i < elems.size(); i++) {
push_xml(L, *elems[i]);
lua::rawseti(L, i + 1);
}
for (const auto& [key, value] : attrs) {
lua::pushlstring(L, value.getText());
lua::setfield(L, key);
}
return 1;
}
static std::unique_ptr<xml::xmlelement> toxml(lua::State* L) {
lua::getfield(L, TAG_ATTR);
auto tag = lua::require_lstring(L, -1);
lua::pop(L);
auto elem = std::make_unique<xml::xmlelement>(std::string(tag));
int length = lua::objlen(L, -1);
for (int i = 0; i < length; i++) {
lua::rawgeti(L, i + 1);
elem->add(toxml(L));
lua::pop(L);
}
lua::pushnil(L);
while (lua::next(L, -2)) {
lua::pushvalue(L, -2);
do {
if (!lua::isstring(L, -1)) {
break;
}
auto key = lua::require_lstring(L, -1);
if (key == "@") {
break;
}
auto value = lua::require_lstring(L, -2);
elem->set(std::string(key), std::string(value));
} while (false);
lua::pop(L, 2);
}
return elem;
}
static int l_tostring(lua::State* L) {
bool nice = lua::isboolean(L, 2) ? lua::toboolean(L, 2) : true;
lua::pushvalue(L, 1);
auto xml = toxml(L);
lua::pop(L);
return lua::pushstring(L, xml::stringify(*xml, nice));
}
static int l_parse(lua::State* L) {
auto string = lua::require_string(L, 1);
auto document = xml::parse("[string]", string);
return push_xml(L, *document->getRoot());
}
static int l_parse_vcd(lua::State* L) {
auto string = lua::require_string(L, 1);
auto rootTag = lua::tostring(L, 2);
auto document = xml::parse_vcm(
"[string]", string, rootTag ? rootTag : ""
);
const auto& root = *document->getRoot();
if (rootTag != nullptr) {
return push_xml(L, root);
}
int rootElements = root.size();
if (rootElements != 1) {
throw std::runtime_error(
"one root element expected, got " + std::to_string(rootElements)
);
}
return push_xml(L, *root.getElements()[0]);
}
const luaL_Reg xmllib[] = {
{"tostring", lua::wrap<l_tostring>},
{"parse", lua::wrap<l_parse>},
{"parse_vcd", lua::wrap<l_parse_vcd>},
{nullptr, nullptr}
};

View file

@ -66,6 +66,7 @@ static void create_libs(State* L, StateType stateType) {
openlib(L, "vec2", vec2lib);
openlib(L, "vec3", vec3lib);
openlib(L, "vec4", vec4lib);
openlib(L, "xml", xmllib);
openlib(L, "yaml", yamllib);
openlib(L, "__vc_app", applib);