diff --git a/doc/en/main-page.md b/doc/en/main-page.md index 67db8c10..0daa4729 100644 --- a/doc/en/main-page.md +++ b/doc/en/main-page.md @@ -14,9 +14,10 @@ Documentation for in-development version 0.26. - [Content-packs](content-packs.md) - [Engine usage recommendations](engine-use-recommendations.md) - [Item properties](item-properties.md) +- [Particles](particles.md) - [Resources (resources.json)](resources.md) - [Rigging](rigging.md) - [Scripting](scripting.md) +- [Text styles](text-styles.md) - [World generator engine](world-generator.md) - [XML UI building](xml-ui-layouts.md) -- [Particles](particles.md) diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md index 5ec2490f..3132bce3 100644 --- a/doc/en/scripting/builtins/libgui.md +++ b/doc/en/scripting/builtins/libgui.md @@ -39,3 +39,25 @@ gui.get_locales_info() -> table of tables { ``` Returns information about all loaded locales (res/texts/\*). + +```lua +gui.clear_markup( + -- markup language ("md" - Markdown) + language: str, + -- text with markup + text: str +) -> str +``` + +Removes markup from text. + +```lua +gui.escape_markup( + -- markup language ("md" - Markdown) + language: str, + -- text with markup + text: str +) -> str +``` + +Escapes markup in text. diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 4f132462..c990a197 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -32,7 +32,7 @@ document["worlds-panel"]:clear() Properties that apply to all elements: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ------------- | ------ | ---- | ----- | ------------------------------------------- | | id | string | yes | *no* | element id | | pos | vec2 | yes | yes | element position inside a container | @@ -60,17 +60,17 @@ Common element methods: Common methods for containers (elements: container, panel, button, pagebox): -| Method | Description | -| ------------------------------- | ------------------------------------------------------------------ | -| clear() | clears content | -| add(xml) | adds an element, creating it using xml code. Example: `container:add("")` | -| setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds | +| Method | Description | +| ------------------------------- | -------------------------------------------------------------------------------------------- | +| clear() | clears content | +| add(xml) | adds an element, creating it using xml code. Example: `container:add("")` | +| setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds | ## Textbox Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ | | text | string | yes | yes | entered text or placeholder | | placeholder | string | yes | yes | placeholder (used if nothing has been entered) | @@ -82,6 +82,8 @@ Properties: | textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") | | valid | bool | yes | no | is the entered text correct | | textColor | vec4 | yes | yes | text color | +| syntax | string | yes | yes | syntax highlighting ("lua" - Lua) | +| markup | string | yes | yes | text markup language ("md" - Markdown) | Methods: @@ -95,7 +97,7 @@ Methods: Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ---------- | ----- | ---- | ----- | --------------------- | | value | float | yes | yes | current value | | min | float | yes | yes | minimum value | @@ -108,7 +110,7 @@ Properties: Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ----- | ------ | ---- | ----- | ------------ | | page | string | yes | yes | current page | @@ -123,7 +125,7 @@ Methods: Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ------- | ---- | ---- | ----- | ----------- | | checked | bool | yes | yes | mark status | @@ -131,7 +133,7 @@ Properties: Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ----- | ------ | ---- | ----- | ------------ | | text | string | yes | yes | button text | @@ -139,15 +141,16 @@ Properties: Properties: -| Title | Type | Read | Write | Description | -| ----- | ------ | ---- | ----- | ----------- | -| text | string | yes | yes | label text | +| Name | Type | Read | Write | Description | +| ------ | ------ | ---- | ----- | -------------------------------------- | +| text | string | yes | yes | label text | +| markup | string | yes | yes | text markup language ("md" - Markdown) | ## Image Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | ----- | ------ | ---- | ----- | ------------ | | src | string | yes | yes | texture name | @@ -155,6 +158,6 @@ Properties: Properties: -| Title | Type | Read | Write | Description | +| Name | Type | Read | Write | Description | | --------- | ---- | ---- | ----- | ------------------------------------------------- | | inventory | int | yes | yes | id of the inventory to which the element is bound | diff --git a/doc/en/text-styles.md b/doc/en/text-styles.md new file mode 100644 index 00000000..e85437e1 --- /dev/null +++ b/doc/en/text-styles.md @@ -0,0 +1,21 @@ +# Text styles + +A proprietary Markdown dialect is used to mark up text styles. +Formatting works on UI elements: label and textbox, if `markup="md"` is explicitly specified. + +## Styles + +| Style | Example | Output | +| ------------- | ------------------------ | -------------------------- | +| Bold | `**Bold font**` | **Bold font** | +| Italic | `*Text in italics*` | *Text in italics* | +| Underline | `__Underlined text__` | Underlined text | +| Strikethrough | `~~Strikethrough text~~` | ~~Strikethrough text~~ | + +Styles can be combined. Example: +```md +***__Message__*** using *~~combed~~ combined* styles__~~.~~__ +``` +Output: + +***Message*** using *~~combed~~ combined* styles~~.~~ diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index d77a93ed..e7008682 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -87,6 +87,7 @@ Inner text is a button text. - `autoresize` - automatic change of element size (default - false). Does not affect font size. - `multiline` - allows display of multiline text. - `text-wrap` - allows automatic text wrapping (works only with multiline: "true"). +- `markup` - text markup language ("md" - Markdown). ## *image* @@ -112,6 +113,8 @@ Inner text - initially entered text - `validator` - lua function that checks text for correctness. Takes a string as input, returns true if the text is correct. - `onup` - lua function called when the up arrow is pressed. - `ondown` - lua function called when the down arrow is pressed. +- `syntax` - syntax highlighting ("lua" - Lua). +- `markup` - text markup language ("md" - Markdown). ## *trackbar* diff --git a/doc/ru/main-page.md b/doc/ru/main-page.md index 17dbe6ba..10dff292 100644 --- a/doc/ru/main-page.md +++ b/doc/ru/main-page.md @@ -19,4 +19,5 @@ - [Свойства блоков](block-properties.md) - [Свойства предметов](item-properties.md) - [Скриптинг](scripting.md) +- [Стили текста](text-styles.md) - [Частицы](particles.md) diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md index eb81cd5f..11dfde03 100644 --- a/doc/ru/scripting/builtins/libgui.md +++ b/doc/ru/scripting/builtins/libgui.md @@ -36,3 +36,25 @@ gui.get_locales_info() -> таблица таблиц где ``` Возвращает информацию о всех загруженных локалях (res/texts/\*). + +```lua +gui.clear_markup( + -- язык разметки ("md" - Markdown) + language: str, + -- текст с разметкой + text: str +) -> str +``` + +Удаляет разметку из текста. + +```lua +gui.escape_markup( + -- язык разметки ("md" - Markdown) + language: str, + -- текст с разметкой + text: str +) -> str +``` + +Экранирует разметку в тексте. diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 4d244210..07d93013 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -82,6 +82,8 @@ document["worlds-panel"]:clear() | textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") | | valid | bool | да | нет | является ли введенный текст корректным | | textColor | vec4 | да | да | цвет текста | +| syntax | string | да | да | подсветка синтаксиса ("lua" - Lua) | +| markup | string | да | да | язык разметки текста ("md" - Markdown) | Методы: @@ -139,9 +141,10 @@ document["worlds-panel"]:clear() Свойства: -| Название | Тип | Чтение | Запись | Описание | -| -------- | ------ | ------ | ------ | ----------- | -| text | string | да | да | текст метки | +| Название | Тип | Чтение | Запись | Описание | +| -------- | ------ | ------ | ------ | -------------------------------------- | +| text | string | да | да | текст метки | +| markup | string | да | да | язык разметки текста ("md" - Markdown) | ## Изображение (image) diff --git a/doc/ru/text-styles.md b/doc/ru/text-styles.md new file mode 100644 index 00000000..0a09abe3 --- /dev/null +++ b/doc/ru/text-styles.md @@ -0,0 +1,21 @@ +# Стили текста + +Для разметки стилей текста используется собственный диалект Markdown. +Форматирование работает на UI элементах: label и textbox, если явно указано `markup="md"`. + +## Стили + +| Стиль | Пример | Вывод | +| ------------ | ------------------------- | ----------------------------- | +| Жирный | `**Жирный шрифт**` | **Жирный шрифт** | +| Курсив | `*Текст курсивом*` | *Текст курсивом* | +| Подчеркнутый | `__Подчеркнутый текст__` | Подчеркнутый текст | +| Зачеркнутый | `~~Зачеркнутый текст~~` | ~~Зачеркнутый текст~~ | + +Стили могут объединяться. Пример: +```md +***__Сообщение__***, демонстрирующее *~~обедненные~~ объединенные* стили__~~.~~__ +``` +Вывод: + +***Сообщение***, демонстрирующее *~~обедненные~~ объединенные* стили~~.~~ diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index 5c71690f..1ad93c4f 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -89,6 +89,7 @@ - `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. - `multiline` - разрешает отображение многострочного текста. - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") +- `markup` - язык разметки текста ("md" - Markdown). ## Изображение - *image* @@ -113,6 +114,8 @@ - `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен. - `onup` - lua функция вызываемая при нажатии стрелки вверх. - `ondown` - lua функция вызываемая при нажатии стрелки вниз. +- `syntax` - подстветка синтаксиса ("lua" - Lua). +- `markup` - язык разметки текста ("md" - Markdown). ## Ползунок - *trackbar* diff --git a/res/layouts/console.xml b/res/layouts/console.xml index c367545c..43e72dd8 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -22,6 +22,7 @@ multiline='true' size-func="gui.get_viewport()[1],40" gravity="bottom-left" + markup="md" > + +#include "commons.hpp" + +using namespace lua; +using namespace devtools; + +static std::set keywords { + "and", "break", "do", "else", "elseif", "end", "false", "for", "function", + "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", + "until", "while" +}; + +bool lua::is_lua_keyword(std::string_view view) { + return keywords.find(view) != keywords.end(); +} + +inline bool is_lua_identifier_start(int c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'; +} + +inline bool is_lua_identifier_part(int c) { + return is_lua_identifier_start(c) || is_digit(c); +} + +inline bool is_lua_operator_start(int c) { + return c == '=' || c == '~' || c == '+' || c == '-' || c == '/' || c == '*' + || c == '%' || c == '^' || c == '#' || c == '<' || c == '>' || c == ':' + || c == '.'; +} + +class Tokenizer : BasicParser { + std::vector tokens; +public: + Tokenizer(std::string_view file, std::string_view source) + : BasicParser(file, source) { + } + + std::string parseLuaName() { + char c = peek(); + if (!is_identifier_start(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_identifier_part(source[pos])) { + pos++; + } + return std::string(source.substr(start, pos - start)); + } + + inline Location currentLocation() const { + return Location { + static_cast(pos), + static_cast(linestart), + static_cast(line)}; + } + + void emitToken( + TokenTag tag, std::string name, Location start, bool standalone=false + ) { + tokens.emplace_back( + tag, + std::move(name), + std::move(start), + currentLocation() + ); + if (standalone) skip(1); + } + + /// @brief Get next operator token without checking operator for existing + std::string parseOperator() { + int start = pos; + char first = peek(); + switch (first) { + case '#': case '+': case '/': case '*': case '^': + case '%': + skip(1); + return std::string({first}); + case '-': + skip(1); + if (peekNoJump() == '-') { + skip(1); + return "--"; + } + return std::string({first}); + } + skip(1); + char second = peekNoJump(); + if ((first == '=' && second == '=') || (first == '~' && second == '=') || + (first == '<' && second == '=') || (first == '>' && second == '=')) { + skip(1); + return std::string(source.substr(start, pos - start)); + } + if (first == '.' && second == '.') { + skip(1); + if (peekNoJump() == '.') { + skip(1); + } + } + return std::string(source.substr(start, pos - start)); + } + + std::vector tokenize() { + skipWhitespace(); + while (hasNext()) { + skipWhitespace(); + if (!hasNext()) { + continue; + } + char c = peek(); + auto start = currentLocation(); + if (is_lua_identifier_start(c)) { + auto name = parseLuaName(); + emitToken( + is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME, + std::move(name), + start + ); + continue; + } else if (is_digit(c)) { + dv::value value; + auto tag = TokenTag::UNEXPECTED; + try { + value = parseNumber(1); + tag = value.isInteger() ? TokenTag::INTEGER + : TokenTag::NUMBER; + } catch (const parsing_error& err) {} + + auto literal = source.substr(start.pos, pos - start.pos); + emitToken(tag, std::string(literal), start); + continue; + } + switch (c) { + case '(': case '[': case '{': + if (isNext("[==[")) { + auto string = readUntil("]==]", true); + skip(4); + emitToken(TokenTag::COMMENT, std::string(string)+"]==]", start); + continue; + } else if (isNext("[[")) { + skip(2); + auto string = readUntil("]]", true); + skip(2); + emitToken(TokenTag::STRING, std::string(string), start); + continue; + } + emitToken(TokenTag::OPEN_BRACKET, std::string({c}), start, true); + continue; + case ')': case ']': case '}': + emitToken(TokenTag::CLOSE_BRACKET, std::string({c}), start, true); + continue; + case ',': + emitToken(TokenTag::COMMA, std::string({c}), start, true); + continue; + case ';': + emitToken(TokenTag::SEMICOLON, std::string({c}), start, true); + continue; + case '\'': case '"': { + skip(1); + auto string = parseString(c, false); + emitToken(TokenTag::STRING, std::move(string), start); + continue; + } + default: break; + } + if (is_lua_operator_start(c)) { + auto text = parseOperator(); + if (text == "--") { + auto string = readUntilEOL(); + emitToken(TokenTag::COMMENT, std::string(string), start); + skipLine(); + continue; + } + emitToken(TokenTag::OPERATOR, std::move(text), start); + continue; + } + auto text = readUntilWhitespace(); + emitToken(TokenTag::UNEXPECTED, std::string(text), start); + } + return std::move(tokens); + } +}; + +std::vector lua::tokenize(std::string_view file, std::string_view source) { + return Tokenizer(file, source).tokenize(); +} diff --git a/src/coders/lua_parsing.hpp b/src/coders/lua_parsing.hpp new file mode 100644 index 00000000..0054e4d0 --- /dev/null +++ b/src/coders/lua_parsing.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include "devtools/syntax.hpp" + +namespace lua { + bool is_lua_keyword(std::string_view view); + + std::vector tokenize( + std::string_view file, std::string_view source + ); +} diff --git a/src/coders/xml.cpp b/src/coders/xml.cpp index faf84f15..b2cb8809 100644 --- a/src/coders/xml.cpp +++ b/src/coders/xml.cpp @@ -107,8 +107,8 @@ glm::vec4 Attribute::asColor() const { Node::Node(std::string tag) : tag(std::move(tag)) { } -void Node::add(const xmlelement& element) { - elements.push_back(element); +void Node::add(std::unique_ptr element) { + elements.push_back(std::move(element)); } void Node::set(const std::string& name, const std::string& text) { @@ -119,7 +119,7 @@ const std::string& Node::getTag() const { return tag; } -const xmlattribute& Node::attr(const std::string& name) const { +const Attribute& Node::attr(const std::string& name) const { auto found = attrs.find(name); if (found == attrs.end()) { throw std::runtime_error( @@ -129,7 +129,7 @@ const xmlattribute& Node::attr(const std::string& name) const { return found->second; } -xmlattribute Node::attr(const std::string& name, const std::string& def) const { +Attribute Node::attr(const std::string& name, const std::string& def) const { auto found = attrs.find(name); if (found == attrs.end()) { return Attribute(name, def); @@ -142,19 +142,23 @@ bool Node::has(const std::string& name) const { return found != attrs.end(); } -xmlelement Node::sub(size_t index) { - return elements.at(index); +Node& Node::sub(size_t index) { + return *elements.at(index); +} + +const Node& Node::sub(size_t index) const { + return *elements.at(index); } size_t Node::size() const { return elements.size(); } -const std::vector& Node::getElements() const { +const std::vector>& Node::getElements() const { return elements; } -const xmlelements_map& Node::getAttributes() const { +const std::unordered_map& Node::getAttributes() const { return attrs; } @@ -162,12 +166,12 @@ Document::Document(std::string version, std::string encoding) : version(std::move(version)), encoding(std::move(encoding)) { } -void Document::setRoot(const xmlelement& element) { - this->root = element; +void Document::setRoot(std::unique_ptr element) { + root = std::move(element); } -xmlelement Document::getRoot() const { - return root; +const Node* Document::getRoot() const { + return root.get(); } const std::string& Document::getVersion() const { @@ -178,82 +182,6 @@ const std::string& Document::getEncoding() const { return encoding; } -Parser::Parser(std::string_view filename, std::string_view source) - : BasicParser(filename, source) { -} - -xmlelement Parser::parseOpenTag() { - std::string tag = parseXMLName(); - auto node = std::make_shared(tag); - - char c; - while (true) { - skipWhitespace(); - c = peek(); - if (c == '/' || c == '>' || c == '?') break; - std::string attrname = parseXMLName(); - std::string attrtext = ""; - skipWhitespace(); - if (peek() == '=') { - nextChar(); - skipWhitespace(); - - char quote = peek(); - if (quote != '\'' && quote != '"') { - throw error("string literal expected"); - } - skip(1); - attrtext = parseString(quote); - } - node->set(attrname, attrtext); - } - return node; -} - -void Parser::parseDeclaration() { - std::string version = "1.0"; - std::string encoding = "UTF-8"; - expect('<'); - if (peek() == '?') { - nextChar(); - xmlelement node = parseOpenTag(); - expect("?>"); - if (node->getTag() != "xml") { - throw error("invalid declaration"); - } - version = node->attr("version", version).getText(); - encoding = node->attr("encoding", encoding).getText(); - if (encoding != "utf-8" && encoding != "UTF-8") { - throw error("UTF-8 encoding is only supported"); - } - } else { - goBack(); - } - document = std::make_shared(version, encoding); -} - -void Parser::parseComment() { - expect("!--"); - if (skipTo("-->")) { - skip(3); - } else { - throw error("comment close missing"); - } -} - -std::string Parser::parseText() { - size_t start = pos; - while (hasNext()) { - char c = peek(); - if (c == '<') { - break; - } - nextChar(); - } - return Parser("[string]", std::string(source.substr(start, pos - start))) - .parseString('\0', false); -} - inline bool is_xml_identifier_start(char c) { return is_identifier_start(c) || c == ':'; } @@ -262,82 +190,166 @@ inline bool is_xml_identifier_part(char c) { return is_identifier_part(c) || c == '-' || c == '.' || c == ':'; } -std::string Parser::parseXMLName() { - char c = peek(); - if (!is_xml_identifier_start(c)) { - throw error("identifier expected"); - } - int start = pos; - while (hasNext() && is_xml_identifier_part(source[pos])) { - pos++; - } - return std::string(source.substr(start, pos - start)); -} +namespace { +class Parser : BasicParser { + std::unique_ptr document; -xmlelement Parser::parseElement() { - // text element - if (peek() != '<') { - auto element = std::make_shared("#"); - auto text = parseText(); - util::replaceAll(text, """, "\""); - util::replaceAll(text, "'", "'"); - util::replaceAll(text, "<", "<"); - util::replaceAll(text, ">", ">"); - util::replaceAll(text, "&", "&"); - element->set("#", text); + std::unique_ptr parseOpenTag() { + std::string tag = parseXMLName(); + auto node = std::make_unique(tag); + + char c; + while (true) { + skipWhitespace(); + c = peek(); + if (c == '/' || c == '>' || c == '?') break; + std::string attrname = parseXMLName(); + std::string attrtext = ""; + skipWhitespace(); + if (peek() == '=') { + nextChar(); + skipWhitespace(); + + char quote = peek(); + if (quote != '\'' && quote != '"') { + throw error("string literal expected"); + } + skip(1); + attrtext = parseString(quote); + } + node->set(attrname, attrtext); + } + return node; + } + + std::unique_ptr parseElement() { + // text element + if (peek() != '<') { + auto element = std::make_unique("#"); + auto text = parseText(); + util::replaceAll(text, """, "\""); + util::replaceAll(text, "'", "'"); + util::replaceAll(text, "<", "<"); + util::replaceAll(text, ">", ">"); + util::replaceAll(text, "&", "&"); + element->set("#", text); + return element; + } + nextChar(); + + // + if (peek() == '!') { + if (isNext("!DOCTYPE ")) { + throw error("XML DTD is not supported yet"); + } + parseComment(); + return nullptr; + } + + auto element = parseOpenTag(); + char c = nextChar(); + + // + if (c == '/') { + expect('>'); + } + // ... + else if (c == '>') { + skipWhitespace(); + while (!isNext("add(std::move(sub)); + } + skipWhitespace(); + } + skip(2); + expect(element->getTag()); + expect('>'); + } + // + else { + throw error("invalid syntax"); + } return element; } - nextChar(); - // - if (peek() == '!') { - if (isNext("!DOCTYPE ")) { - throw error("XML DTD is not supported yet"); - } - parseComment(); - return nullptr; - } - - auto element = parseOpenTag(); - char c = nextChar(); - - // - if (c == '/') { - expect('>'); - } - // ... - else if (c == '>') { - skipWhitespace(); - while (!isNext("add(sub); + void parseDeclaration() { + std::string version = "1.0"; + std::string encoding = "UTF-8"; + expect('<'); + if (peek() == '?') { + nextChar(); + auto node = parseOpenTag(); + expect("?>"); + if (node->getTag() != "xml") { + throw error("invalid declaration"); } - skipWhitespace(); + version = node->attr("version", version).getText(); + encoding = node->attr("encoding", encoding).getText(); + if (encoding != "utf-8" && encoding != "UTF-8") { + throw error("UTF-8 encoding is only supported"); + } + } else { + goBack(); } - skip(2); - expect(element->getTag()); - expect('>'); + document = std::make_unique(version, encoding); } - // - else { - throw error("invalid syntax"); + + void parseComment() { + expect("!--"); + if (skipTo("-->")) { + skip(3); + } else { + throw error("comment close missing"); + } } - return element; + + std::string parseText() { + size_t start = pos; + while (hasNext()) { + char c = peek(); + if (c == '<') { + break; + } + nextChar(); + } + return Parser("[string]", std::string(source.substr(start, pos - start))) + .parseString('\0', false); + } + + std::string parseXMLName() { + char c = peek(); + if (!is_xml_identifier_start(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_xml_identifier_part(source[pos])) { + pos++; + } + return std::string(source.substr(start, pos - start)); + } +public: + Parser(std::string_view filename, std::string_view source) + : BasicParser(filename, source) { + } + + std::unique_ptr parse() { + parseDeclaration(); + + std::unique_ptr root; + while (root == nullptr) { + root = parseElement(); + } + document->setRoot(std::move(root)); + return std::move(document); + } +}; } -xmldocument Parser::parse() { - parseDeclaration(); - - xmlelement root = nullptr; - while (root == nullptr) { - root = parseElement(); - } - document->setRoot(root); - return document; -} - -xmldocument xml::parse(std::string_view filename, std::string_view source) { +std::unique_ptr xml::parse( + std::string_view filename, std::string_view source +) { Parser parser(filename, source); return parser.parse(); } @@ -354,13 +366,13 @@ inline void newline( static void stringifyElement( std::stringstream& ss, - const xmlelement& element, + const Node& element, bool nice, const std::string& indentStr, int indent ) { - if (element->isText()) { - std::string text = element->attr("#").getText(); + if (element.isText()) { + std::string text = element.attr("#").getText(); util::replaceAll(text, "&", "&"); util::replaceAll(text, "\"", """); util::replaceAll(text, "'", "'"); @@ -369,10 +381,10 @@ static void stringifyElement( ss << text; return; } - const std::string& tag = element->getTag(); + const std::string& tag = element.getTag(); ss << '<' << tag; - auto& attrs = element->getAttributes(); + auto& attrs = element.getAttributes(); if (!attrs.empty()) { ss << ' '; int count = 0; @@ -388,10 +400,10 @@ static void stringifyElement( count++; } } - auto& elements = element->getElements(); + auto& elements = element.getElements(); if (elements.size() == 1 && elements[0]->isText()) { ss << ">"; - stringifyElement(ss, elements[0], nice, indentStr, indent + 1); + stringifyElement(ss, *elements[0], nice, indentStr, indent + 1); ss << ""; return; } @@ -399,7 +411,7 @@ static void stringifyElement( ss << '>'; for (auto& sub : elements) { newline(ss, nice, indentStr, indent + 1); - stringifyElement(ss, sub, nice, indentStr, indent + 1); + stringifyElement(ss, *sub, nice, indentStr, indent + 1); } newline(ss, nice, indentStr, indent); ss << ""; @@ -410,16 +422,16 @@ static void stringifyElement( } std::string xml::stringify( - const xmldocument& document, bool nice, const std::string& indentStr + const Document& document, bool nice, const std::string& indentStr ) { std::stringstream ss; // XML declaration - ss << "getVersion(); + ss << ""; newline(ss, nice, indentStr, 0); - stringifyElement(ss, document->getRoot(), nice, indentStr, 0); + stringifyElement(ss, *document.getRoot(), nice, indentStr, 0); return ss.str(); } diff --git a/src/coders/xml.hpp b/src/coders/xml.hpp index 54a2b589..5831c29a 100644 --- a/src/coders/xml.hpp +++ b/src/coders/xml.hpp @@ -13,11 +13,6 @@ namespace xml { class Attribute; class Document; - using xmlattribute = Attribute; - using xmlelement = std::shared_ptr; - using xmldocument = std::shared_ptr; - using xmlelements_map = std::unordered_map; - class Attribute { std::string name; std::string text; @@ -40,13 +35,15 @@ namespace xml { /// 'text' class Node { std::string tag; - std::unordered_map attrs; - std::vector elements; + std::unordered_map attrs; + std::vector> elements; public: Node(std::string tag); + Node(const Node&) = delete; + /// @brief Add sub-element - void add(const xmlelement& element); + void add(std::unique_ptr element); /// @brief Set attribute value. Creates attribute if does not exists /// @param name attribute name @@ -67,15 +64,15 @@ namespace xml { /// @brief Get attribute by name /// @param name attribute name /// @throws std::runtime_error if element has no attribute - /// @return xmlattribute - {name, value} - const xmlattribute& attr(const std::string& name) const; + /// @return xml attribute - {name, value} + const Attribute& attr(const std::string& name) const; /// @brief Get attribute by name /// @param name attribute name /// @param def default value will be returned wrapped in xmlattribute /// if element has no attribute - /// @return xmlattribute - {name, value} or {name, def} if not found*/ - xmlattribute attr(const std::string& name, const std::string& def) + /// @return xml attribute - {name, value} or {name, def} if not found + Attribute attr(const std::string& name, const std::string& def) const; /// @brief Check if element has attribute @@ -86,51 +83,37 @@ namespace xml { /// @param index sub-element index /// @throws std::out_of_range if an invalid index given /// @return sub-element - xmlelement sub(size_t index); + Node& sub(size_t index); + const Node& sub(size_t index) const; /// @brief Get number of sub-elements size_t size() const; - const std::vector& getElements() const; - const xmlelements_map& getAttributes() const; + const std::vector>& getElements() const; + const std::unordered_map& getAttributes() const; }; class Document { - xmlelement root = nullptr; + std::unique_ptr root = nullptr; std::string version; std::string encoding; public: Document(std::string version, std::string encoding); - void setRoot(const xmlelement& element); - xmlelement getRoot() const; + void setRoot(std::unique_ptr element); + const Node* getRoot() const; const std::string& getVersion() const; const std::string& getEncoding() const; }; - class Parser : BasicParser { - xmldocument document; - - xmlelement parseOpenTag(); - xmlelement parseElement(); - void parseDeclaration(); - void parseComment(); - std::string parseText(); - std::string parseXMLName(); - public: - Parser(std::string_view filename, std::string_view source); - - xmldocument parse(); - }; - /// @brief Serialize XML Document to string /// @param document serializing document /// @param nice use human readable format (with indents and line-separators) /// @param indentStr indentation characters sequence (default - 4 spaces) /// @return XML string - extern std::string stringify( - const xmldocument& document, + std::string stringify( + const Document& document, bool nice = true, const std::string& indentStr = " " ); @@ -139,7 +122,9 @@ namespace xml { /// @param filename file name will be shown in error messages /// @param source xml source code string /// @return xml document - extern xmldocument parse( + std::unique_ptr parse( std::string_view filename, std::string_view source ); + + using xmlelement = Node; } diff --git a/src/devtools/syntax.hpp b/src/devtools/syntax.hpp new file mode 100644 index 00000000..6c7d4b15 --- /dev/null +++ b/src/devtools/syntax.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace devtools { + struct Location { + int pos; + int lineStart; + int line; + }; + + enum class TokenTag { + KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING, + OPERATOR, COMMA, SEMICOLON, UNEXPECTED, COMMENT + }; + + struct Token { + TokenTag tag; + std::string text; + Location start; + Location end; + + Token(TokenTag tag, std::string text, Location start, Location end) + : tag(tag), + text(std::move(text)), + start(std::move(start)), + end(std::move(end)) { + } + }; +} diff --git a/src/devtools/syntax_highlighting.cpp b/src/devtools/syntax_highlighting.cpp new file mode 100644 index 00000000..f68780df --- /dev/null +++ b/src/devtools/syntax_highlighting.cpp @@ -0,0 +1,72 @@ +#include "syntax_highlighting.hpp" + +#include "coders/commons.hpp" +#include "coders/lua_parsing.hpp" +#include "graphics/core/Font.hpp" + +using namespace devtools; + +static std::unique_ptr build_styles( + const std::vector& tokens +) { + using devtools::TokenTag; + FontStylesScheme styles { + { + {false, false, false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default + {true, false, false, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword + {false, false, false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string + {false, false, false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment + {true, false, false, false, glm::vec4(1.0f, 0.2f, 0.1f, 1)}, // unexpected + }, + {} + }; + size_t offset = 0; + for (int i = 0; i < tokens.size(); i++) { + const auto& token = tokens.at(i); + if (token.tag != TokenTag::KEYWORD && + token.tag != TokenTag::STRING && + token.tag != TokenTag::INTEGER && + token.tag != TokenTag::NUMBER && + token.tag != TokenTag::COMMENT && + token.tag != TokenTag::UNEXPECTED) { + continue; + } + if (token.start.pos > offset) { + int n = token.start.pos - offset; + styles.map.insert(styles.map.end(), token.start.pos - offset, 0); + } + offset = token.end.pos; + int styleIndex; + switch (token.tag) { + case TokenTag::KEYWORD: styleIndex = SyntaxStyles::KEYWORD; break; + case TokenTag::STRING: + case TokenTag::INTEGER: + case TokenTag::NUMBER: styleIndex = SyntaxStyles::LITERAL; break; + case TokenTag::COMMENT: styleIndex = SyntaxStyles::COMMENT; break; + case TokenTag::UNEXPECTED: styleIndex = SyntaxStyles::ERROR; break; + default: + styleIndex = 0; + break; + } + styles.map.insert( + styles.map.end(), token.end.pos - token.start.pos, styleIndex + ); + } + styles.map.push_back(0); + return std::make_unique(std::move(styles)); +} + +std::unique_ptr devtools::syntax_highlight( + const std::string& lang, std::string_view source +) { + try { + if (lang == "lua") { + auto tokens = lua::tokenize("", source); + return build_styles(tokens); + } else { + return nullptr; + } + } catch (const parsing_error& err) { + return nullptr; + } +} diff --git a/src/devtools/syntax_highlighting.hpp b/src/devtools/syntax_highlighting.hpp new file mode 100644 index 00000000..18db6389 --- /dev/null +++ b/src/devtools/syntax_highlighting.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct FontStylesScheme; + +namespace devtools { + enum SyntaxStyles { + DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR + }; + + std::unique_ptr syntax_highlight( + const std::string& lang, std::string_view source + ); +} diff --git a/src/engine.cpp b/src/engine.cpp index 31db4537..129dc68d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -206,7 +206,7 @@ void Engine::renderFrame(Batch2D& batch) { Viewport viewport(Window::width, Window::height); DrawContext ctx(nullptr, viewport, &batch); - gui->draw(&ctx, assets.get()); + gui->draw(ctx, *assets); } void Engine::processPostRunnables() { diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp index fff98f3f..2108b46e 100644 --- a/src/frontend/UiDocument.cpp +++ b/src/frontend/UiDocument.cpp @@ -67,9 +67,7 @@ std::unique_ptr UiDocument::read( : scripting::create_doc_environment(penv, name); gui::UiXmlReader reader(env); - auto view = reader.readXML( - file.u8string(), xmldoc->getRoot() - ); + auto view = reader.readXML(file.u8string(), *xmldoc->getRoot()); view->setId("root"); uidocscript script {}; auto scriptFile = fs::path(file.u8string()+".lua"); diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 30659969..21111e18 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -124,7 +124,7 @@ std::shared_ptr Hud::createContentAccess() { }); InventoryBuilder builder; - builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout); + builder.addGrid(8, itemsCount-1, glm::vec2(), glm::vec4(8, 8, 12, 8), true, slotLayout); auto view = builder.build(); view->bind(accessInventory, content); view->setMargin(glm::vec4()); @@ -137,7 +137,7 @@ std::shared_ptr Hud::createHotbar() { SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr); InventoryBuilder builder; - builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout); + builder.addGrid(10, 10, glm::vec2(), glm::vec4(4), true, slotLayout); auto view = builder.build(); view->setId("hud.hotbar"); view->setOrigin(glm::vec2(view->getSize().x/2, 0)); @@ -346,9 +346,9 @@ void Hud::update(bool visible) { element.getNode()->setVisible(visible); } - glm::vec2 invSize = contentAccessPanel->getSize(); + glm::vec2 caSize = contentAccessPanel->getSize(); contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel); - contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height)); + contentAccessPanel->setSize(glm::vec2(caSize.x, Window::height)); contentAccess->setMinSize(glm::vec2(1, Window::height)); hotbarView->setVisible(visible && !(secondUI && !inventoryView)); diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index 580ab54d..5594664a 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -95,16 +95,16 @@ class Hud : public util::ObjectsKeeper { /// @brief Inventories interaction agent (grabbed item) std::shared_ptr exchangeSlot; /// @brief Exchange slot inventory (1 slot only) - std::shared_ptr exchangeSlotInv = nullptr; + std::shared_ptr exchangeSlotInv; /// @brief List of all controlled hud elements std::vector elements; /// @brief Player inventory view - std::shared_ptr inventoryView = nullptr; + std::shared_ptr inventoryView; /// @brief Block inventory view - std::shared_ptr blockUI = nullptr; + std::shared_ptr blockUI; /// @brief Secondary inventory view - std::shared_ptr secondInvView = nullptr; + std::shared_ptr secondInvView; /// @brief Position of the block open glm::ivec3 blockPos {}; /// @brief Id of the block open (used to detect block destruction or replacement) @@ -114,9 +114,9 @@ class Hud : public util::ObjectsKeeper { /// @brief Provide cheat controllers to the debug panel bool allowDebugCheats = true; /// @brief UI element will be dynamicly positioned near to inventory or in screen center - std::shared_ptr secondUI = nullptr; + std::shared_ptr secondUI; - std::shared_ptr debugMinimap = nullptr; + std::shared_ptr debugMinimap; std::unique_ptr debugImgWorldGen; diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index c4c67db2..b7ccf5ff 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -261,6 +261,24 @@ void Batch2D::rect( vertex(x+w, y+h, u+tx, v, r,g,b,a); } +void Batch2D::parallelogram( + float x, float y, float w, float h, float skew, + float u, float v, float tx, float ty, + float r, float g, float b, float a +){ + if (index + 6*B2D_VERTEX_SIZE >= capacity) { + flush(); + } + setPrimitive(DrawPrimitive::triangle); + vertex(x-skew*w, y, u, v+ty, r,g,b,a); + vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a); + vertex(x+skew*w, y+h, u, v, r,g,b,a); + + vertex(x-skew*w, y, u, v+ty, r,g,b,a); + vertex(x+w-skew*w, y, u+tx, v+ty, r,g,b,a); + vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a); +} + void Batch2D::rect( float x, float y, float w, float h, float r0, float g0, float b0, @@ -336,6 +354,22 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a); } +void Batch2D::sprite( + float x, + float y, + float w, + float h, + float skew, + int atlasRes, + int index, + glm::vec4 tint +) { + float scale = 1.0f / (float)atlasRes; + float u = (index % atlasRes) * scale; + float v = 1.0f - ((index / atlasRes) * scale) - scale; + parallelogram(x, y, w, h, skew, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a); +} + void Batch2D::flush() { if (index == 0) return; diff --git a/src/graphics/core/Batch2D.hpp b/src/graphics/core/Batch2D.hpp index 2877f5bd..6bfdb14d 100644 --- a/src/graphics/core/Batch2D.hpp +++ b/src/graphics/core/Batch2D.hpp @@ -45,6 +45,7 @@ public: void setRegion(UVRegion region); void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint); void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint); + void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint); void point(float x, float y, float r, float g, float b, float a); inline void setColor(glm::vec4 color) { @@ -79,6 +80,12 @@ public: float r, float g, float b, float a ); + void parallelogram( + float x, float y, float w, float h, float skew, + float u, float v, float tx, float ty, + float r, float g, float b, float a + ); + void rect( float x, float y, float w, float h, float r0, float g0, float b0, diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index 9c40809f..2116fdd3 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -1,5 +1,6 @@ #include "Font.hpp" +#include #include #include "Texture.hpp" #include "Batch2D.hpp" @@ -52,17 +53,21 @@ static inline void draw_glyph( uint c, const glm::vec3& right, const glm::vec3& up, - float glyphInterval + float glyphInterval, + const FontStyle& style ) { - batch.sprite( - pos.x + offset.x * right.x, - pos.y + offset.y * right.y, - right.x / glyphInterval, - up.y, - 16, - c, - batch.getColor() - ); + for (int i = 0; i <= style.bold; i++) { + batch.sprite( + pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x, + pos.y + offset.y * right.y, + right.x / glyphInterval, + up.y, + -0.15f * style.italic, + 16, + c, + batch.getColor() * style.color + ); + } } static inline void draw_glyph( @@ -72,17 +77,20 @@ static inline void draw_glyph( uint c, const glm::vec3& right, const glm::vec3& up, - float glyphInterval + float glyphInterval, + const FontStyle& style ) { - batch.sprite( - pos + right * offset.x + up * offset.y, - up, right / glyphInterval, - 0.5f, - 0.5f, - 16, - c, - batch.getColor() - ); + for (int i = 0; i <= style.bold; i++) { + batch.sprite( + pos + right * (offset.x + i) + up * offset.y, + up, right / glyphInterval, + 0.5f, + 0.5f, + 16, + c, + batch.getColor() * style.color + ); + } } template @@ -93,14 +101,32 @@ static inline void draw_text( const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up, - float glyphInterval + float interval, + const FontStylesScheme* styles, + size_t styleMapOffset ) { + static FontStylesScheme defStyles {{{}}, {0}}; + + if (styles == nullptr) { + styles = &defStyles; + } + uint page = 0; uint next = MAX_CODEPAGES; int x = 0; int y = 0; + bool hasLines = false; + do { - for (uint c : text){ + for (size_t i = 0; i < text.length(); i++) { + uint c = text[i]; + size_t styleIndex = styles->map.at( + std::min(styles->map.size() - 1, i + styleMapOffset) + ); + const FontStyle& style = styles->palette.at(styleIndex); + hasLines |= style.strikethrough; + hasLines |= style.underline; + if (!font.isPrintableChar(c)) { x++; continue; @@ -109,7 +135,7 @@ static inline void draw_text( if (charpage == page){ batch.texture(font.getPage(charpage)); draw_glyph( - batch, pos, glm::vec2(x, y), c, right, up, glyphInterval + batch, pos, glm::vec2(x, y), c, right, up, interval, style ); } else if (charpage > page && charpage < next){ @@ -121,6 +147,31 @@ static inline void draw_text( next = MAX_CODEPAGES; x = 0; } while (page < MAX_CODEPAGES); + + if (!hasLines) { + return; + } + batch.texture(font.getPage(0)); + for (size_t i = 0; i < text.length(); i++) { + uint c = text[i]; + size_t styleIndex = styles->map.at( + std::min(styles->map.size() - 1, i + styleMapOffset) + ); + const FontStyle& style = styles->palette.at(styleIndex); + FontStyle lineStyle = style; + lineStyle.bold = true; + if (style.strikethrough) { + draw_glyph( + batch, pos, glm::vec2(x, y), '-', right, up, interval, lineStyle + ); + } + if (style.underline) { + draw_glyph( + batch, pos, glm::vec2(x, y), '_', right, up, interval, lineStyle + ); + } + x++; + } } const Texture* Font::getPage(int charpage) const { @@ -135,20 +186,30 @@ const Texture* Font::getPage(int charpage) const { } void Font::draw( - Batch2D& batch, std::wstring_view text, int x, int y, float scale + Batch2D& batch, + std::wstring_view text, + int x, + int y, + const FontStylesScheme* styles, + size_t styleMapOffset, + float scale ) const { draw_text( *this, batch, text, glm::vec3(x, y, 0), glm::vec3(glyphInterval*scale, 0, 0), glm::vec3(0, lineHeight*scale, 0), - glyphInterval/static_cast(lineHeight) + glyphInterval/static_cast(lineHeight), + styles, + styleMapOffset ); } void Font::draw( Batch3D& batch, std::wstring_view text, + const FontStylesScheme* styles, + size_t styleMapOffset, const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up @@ -157,6 +218,8 @@ void Font::draw( *this, batch, text, pos, right * static_cast(glyphInterval), up * static_cast(lineHeight), - glyphInterval/static_cast(lineHeight) + glyphInterval/static_cast(lineHeight), + styles, + styleMapOffset ); } diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index deb07534..07fe0532 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -11,10 +11,33 @@ class Batch2D; class Batch3D; class Camera; -enum class FontStyle { - none, - shadow, - outline +struct FontStyle { + bool bold = false; + bool italic = false; + bool strikethrough = false; + bool underline = false; + glm::vec4 color {1, 1, 1, 1}; + + FontStyle() = default; + + FontStyle( + bool bold, + bool italic, + bool strikethrough, + bool underline, + glm::vec4 color + ) + : bold(bold), + italic(italic), + strikethrough(strikethrough), + underline(underline), + color(std::move(color)) { + } +}; + +struct FontStylesScheme { + std::vector palette; + std::vector map; }; class Font { @@ -45,12 +68,22 @@ public: /// @brief Check if character is visible (non-whitespace) /// @param codepoint character unicode codepoint bool isPrintableChar(uint codepoint) const; - - void draw(Batch2D& batch, std::wstring_view text, int x, int y, float scale=1) const; + + void draw( + Batch2D& batch, + std::wstring_view text, + int x, + int y, + const FontStylesScheme* styles, + size_t styleMapOffset, + float scale = 1 + ) const; void draw( Batch3D& batch, std::wstring_view text, + const FontStylesScheme* styles, + size_t styleMapOffset, const glm::vec3& pos, const glm::vec3& right={1, 0, 0}, const glm::vec3& up={0, 1, 0} diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index fa24b61b..32681ce5 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -103,6 +103,8 @@ void TextsRenderer::renderNote( font.draw( batch, text, + nullptr, + 0, pos - xvec * (width * 0.5f) * preset.scale, xvec * preset.scale, yvec * preset.scale diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 40b74a4c..38bc2257 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -197,18 +197,18 @@ void GUI::act(float delta, const Viewport& vp) { } } -void GUI::draw(const DrawContext* pctx, Assets* assets) { - auto& viewport = pctx->getViewport(); +void GUI::draw(const DrawContext& pctx, const Assets& assets) { + auto& viewport = pctx.getViewport(); glm::vec2 wsize = viewport.size(); menu->setPos((wsize - menu->getSize()) / 2.0f); uicamera->setFov(wsize.y); - auto uishader = assets->get("ui"); + auto uishader = assets.get("ui"); uishader->use(); uishader->uniformMatrix("u_projview", uicamera->getProjView()); - pctx->getBatch2D()->begin(); + pctx.getBatch2D()->begin(); container->draw(pctx, assets); } diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index b2b20bd0..c0dec649 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -94,7 +94,7 @@ namespace gui { /// @brief Draw all visible elements on main container /// @param pctx parent graphics context /// @param assets active assets storage - void draw(const DrawContext* pctx, Assets* assets); + void draw(const DrawContext& pctx, const Assets& assets); /// @brief Add element to the main container /// @param node UI element diff --git a/src/graphics/ui/elements/Button.cpp b/src/graphics/ui/elements/Button.cpp index b6a11232..dfdf864d 100644 --- a/src/graphics/ui/elements/Button.cpp +++ b/src/graphics/ui/elements/Button.cpp @@ -52,7 +52,7 @@ Button::Button( void Button::setText(std::wstring text) { if (label) { - label->setText(text); + label->setText(std::move(text)); } } @@ -77,9 +77,9 @@ void Button::refresh() { } } -void Button::drawBackground(const DrawContext* pctx, Assets*) { +void Button::drawBackground(const DrawContext& pctx, const Assets&) { glm::vec2 pos = calcPos(); - auto batch = pctx->getBatch2D(); + auto batch = pctx.getBatch2D(); batch->texture(nullptr); batch->setColor(calcColor()); batch->rect(pos.x, pos.y, size.x, size.y); diff --git a/src/graphics/ui/elements/Button.hpp b/src/graphics/ui/elements/Button.hpp index 61bfea03..39b23426 100644 --- a/src/graphics/ui/elements/Button.hpp +++ b/src/graphics/ui/elements/Button.hpp @@ -7,7 +7,7 @@ namespace gui { class Button : public Panel { protected: - std::shared_ptr