Merge pull request #400 from MihailRis/syntax-hightlighting

Text Formatting
This commit is contained in:
MihailRis
2024-12-06 22:21:16 +03:00
committed by GitHub
65 changed files with 1593 additions and 550 deletions
+2 -1
View File
@@ -14,9 +14,10 @@ Documentation for in-development version 0.26.
- [Content-packs](content-packs.md) - [Content-packs](content-packs.md)
- [Engine usage recommendations](engine-use-recommendations.md) - [Engine usage recommendations](engine-use-recommendations.md)
- [Item properties](item-properties.md) - [Item properties](item-properties.md)
- [Particles](particles.md)
- [Resources (resources.json)](resources.md) - [Resources (resources.json)](resources.md)
- [Rigging](rigging.md) - [Rigging](rigging.md)
- [Scripting](scripting.md) - [Scripting](scripting.md)
- [Text styles](text-styles.md)
- [World generator engine](world-generator.md) - [World generator engine](world-generator.md)
- [XML UI building](xml-ui-layouts.md) - [XML UI building](xml-ui-layouts.md)
- [Particles](particles.md)
+22
View File
@@ -39,3 +39,25 @@ gui.get_locales_info() -> table of tables {
``` ```
Returns information about all loaded locales (res/texts/\*). 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.
+14 -11
View File
@@ -32,7 +32,7 @@ document["worlds-panel"]:clear()
Properties that apply to all elements: Properties that apply to all elements:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ------------- | ------ | ---- | ----- | ------------------------------------------- | | ------------- | ------ | ---- | ----- | ------------------------------------------- |
| id | string | yes | *no* | element id | | id | string | yes | *no* | element id |
| pos | vec2 | yes | yes | element position inside a container | | pos | vec2 | yes | yes | element position inside a container |
@@ -61,7 +61,7 @@ Common element methods:
Common methods for containers (elements: container, panel, button, pagebox): Common methods for containers (elements: container, panel, button, pagebox):
| Method | Description | | Method | Description |
| ------------------------------- | ------------------------------------------------------------------ | | ------------------------------- | -------------------------------------------------------------------------------------------- |
| clear() | clears content | | clear() | clears content |
| add(xml) | adds an element, creating it using xml code. Example: `container:add("<image src='test'/>")` | | add(xml) | adds an element, creating it using xml code. Example: `container:add("<image src='test'/>")` |
| setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds | | setInterval(interval, callback) | assigns a function to be executed repeatedly at an interval specified in milliseconds |
@@ -70,7 +70,7 @@ Common methods for containers (elements: container, panel, button, pagebox):
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ | | ----------- | ------ | ---- | ----- | ------------------------------------------------------------------------------------ |
| text | string | yes | yes | entered text or placeholder | | text | string | yes | yes | entered text or placeholder |
| placeholder | string | yes | yes | placeholder (used if nothing has been entered) | | 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") | | textWrap | bool | yes | yes | automatic text wrapping (only with multiline: "true") |
| valid | bool | yes | no | is the entered text correct | | valid | bool | yes | no | is the entered text correct |
| textColor | vec4 | yes | yes | text color | | textColor | vec4 | yes | yes | text color |
| syntax | string | yes | yes | syntax highlighting ("lua" - Lua) |
| markup | string | yes | yes | text markup language ("md" - Markdown) |
Methods: Methods:
@@ -95,7 +97,7 @@ Methods:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ---------- | ----- | ---- | ----- | --------------------- | | ---------- | ----- | ---- | ----- | --------------------- |
| value | float | yes | yes | current value | | value | float | yes | yes | current value |
| min | float | yes | yes | minimum value | | min | float | yes | yes | minimum value |
@@ -108,7 +110,7 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ | | ----- | ------ | ---- | ----- | ------------ |
| page | string | yes | yes | current page | | page | string | yes | yes | current page |
@@ -123,7 +125,7 @@ Methods:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ------- | ---- | ---- | ----- | ----------- | | ------- | ---- | ---- | ----- | ----------- |
| checked | bool | yes | yes | mark status | | checked | bool | yes | yes | mark status |
@@ -131,7 +133,7 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ | | ----- | ------ | ---- | ----- | ------------ |
| text | string | yes | yes | button text | | text | string | yes | yes | button text |
@@ -139,15 +141,16 @@ Properties:
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ----------- | | ------ | ------ | ---- | ----- | -------------------------------------- |
| text | string | yes | yes | label text | | text | string | yes | yes | label text |
| markup | string | yes | yes | text markup language ("md" - Markdown) |
## Image ## Image
Properties: Properties:
| Title | Type | Read | Write | Description | | Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ | | ----- | ------ | ---- | ----- | ------------ |
| src | string | yes | yes | texture name | | src | string | yes | yes | texture name |
@@ -155,6 +158,6 @@ Properties:
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 | | inventory | int | yes | yes | id of the inventory to which the element is bound |
+21
View File
@@ -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__` | <ins>Underlined text</ins> |
| Strikethrough | `~~Strikethrough text~~` | ~~Strikethrough text~~ |
Styles can be combined. Example:
```md
***__Message__*** using *~~combed~~ combined* styles__~~.~~__
```
Output:
***<ins>Message</ins>*** using *~~combed~~ combined* styles<ins>~~.~~</ins>
+3
View File
@@ -87,6 +87,7 @@ Inner text is a button text.
- `autoresize` - automatic change of element size (default - false). Does not affect font size. - `autoresize` - automatic change of element size (default - false). Does not affect font size.
- `multiline` - allows display of multiline text. - `multiline` - allows display of multiline text.
- `text-wrap` - allows automatic text wrapping (works only with multiline: "true"). - `text-wrap` - allows automatic text wrapping (works only with multiline: "true").
- `markup` - text markup language ("md" - Markdown).
## *image* ## *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. - `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. - `onup` - lua function called when the up arrow is pressed.
- `ondown` - lua function called when the down 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* ## *trackbar*
+1
View File
@@ -19,4 +19,5 @@
- [Свойства блоков](block-properties.md) - [Свойства блоков](block-properties.md)
- [Свойства предметов](item-properties.md) - [Свойства предметов](item-properties.md)
- [Скриптинг](scripting.md) - [Скриптинг](scripting.md)
- [Стили текста](text-styles.md)
- [Частицы](particles.md) - [Частицы](particles.md)
+22
View File
@@ -36,3 +36,25 @@ gui.get_locales_info() -> таблица таблиц где
``` ```
Возвращает информацию о всех загруженных локалях (res/texts/\*). Возвращает информацию о всех загруженных локалях (res/texts/\*).
```lua
gui.clear_markup(
-- язык разметки ("md" - Markdown)
language: str,
-- текст с разметкой
text: str
) -> str
```
Удаляет разметку из текста.
```lua
gui.escape_markup(
-- язык разметки ("md" - Markdown)
language: str,
-- текст с разметкой
text: str
) -> str
```
Экранирует разметку в тексте.
+4 -1
View File
@@ -82,6 +82,8 @@ document["worlds-panel"]:clear()
| textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") | | textWrap | bool | да | да | автоматический перенос текста (только при multiline: "true") |
| valid | bool | да | нет | является ли введенный текст корректным | | valid | bool | да | нет | является ли введенный текст корректным |
| textColor | vec4 | да | да | цвет текста | | textColor | vec4 | да | да | цвет текста |
| syntax | string | да | да | подсветка синтаксиса ("lua" - Lua) |
| markup | string | да | да | язык разметки текста ("md" - Markdown) |
Методы: Методы:
@@ -140,8 +142,9 @@ document["worlds-panel"]:clear()
Свойства: Свойства:
| Название | Тип | Чтение | Запись | Описание | | Название | Тип | Чтение | Запись | Описание |
| -------- | ------ | ------ | ------ | ----------- | | -------- | ------ | ------ | ------ | -------------------------------------- |
| text | string | да | да | текст метки | | text | string | да | да | текст метки |
| markup | string | да | да | язык разметки текста ("md" - Markdown) |
## Изображение (image) ## Изображение (image)
+21
View File
@@ -0,0 +1,21 @@
# Стили текста
Для разметки стилей текста используется собственный диалект Markdown.
Форматирование работает на UI элементах: label и textbox, если явно указано `markup="md"`.
## Стили
| Стиль | Пример | Вывод |
| ------------ | ------------------------- | ----------------------------- |
| Жирный | `**Жирный шрифт**` | **Жирный шрифт** |
| Курсив | `*Текст курсивом*` | *Текст курсивом* |
| Подчеркнутый | `__Подчеркнутый текст__` | <ins>Подчеркнутый текст</ins> |
| Зачеркнутый | `~~Зачеркнутый текст~~` | ~~Зачеркнутый текст~~ |
Стили могут объединяться. Пример:
```md
***__Сообщение__***, демонстрирующее *~~обедненные~~ объединенные* стили__~~.~~__
```
Вывод:
***<ins>Сообщение</ins>***, демонстрирующее *~~обедненные~~ объединенные* стили<ins>~~.~~</ins>
+3
View File
@@ -89,6 +89,7 @@
- `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта. - `autoresize` - автоматическое изменение размера элемента (по-умолчанию - false). Не влияет на размер шрифта.
- `multiline` - разрешает отображение многострочного текста. - `multiline` - разрешает отображение многострочного текста.
- `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true") - `text-wrap` - разрешает автоматический перенос текста (работает только при multiline: "true")
- `markup` - язык разметки текста ("md" - Markdown).
## Изображение - *image* ## Изображение - *image*
@@ -113,6 +114,8 @@
- `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен. - `validator` - lua функция, проверяющая текст на корректность. Принимает на вход строку, возвращает true если текст корректен.
- `onup` - lua функция вызываемая при нажатии стрелки вверх. - `onup` - lua функция вызываемая при нажатии стрелки вверх.
- `ondown` - lua функция вызываемая при нажатии стрелки вниз. - `ondown` - lua функция вызываемая при нажатии стрелки вниз.
- `syntax` - подстветка синтаксиса ("lua" - Lua).
- `markup` - язык разметки текста ("md" - Markdown).
## Ползунок - *trackbar* ## Ползунок - *trackbar*
+3 -2
View File
@@ -22,6 +22,7 @@
multiline='true' multiline='true'
size-func="gui.get_viewport()[1],40" size-func="gui.get_viewport()[1],40"
gravity="bottom-left" gravity="bottom-left"
markup="md"
></textbox> ></textbox>
</container> </container>
<container id="editorContainer" pos="0,60" color="#00000080" <container id="editorContainer" pos="0,60" color="#00000080"
@@ -32,10 +33,9 @@
autoresize='true' autoresize='true'
margin='0' margin='0'
padding='5' padding='5'
editable='false'
multiline='true' multiline='true'
line-numbers='true' line-numbers='true'
text-color="#FFFFFFA0" syntax='lua'
size-func="gui.get_viewport()[1]-350,40" size-func="gui.get_viewport()[1]-350,40"
gravity="top-left" gravity="top-left"
text-wrap='false' text-wrap='false'
@@ -55,6 +55,7 @@
<textbox id='prompt' <textbox id='prompt'
consumer='submit' consumer='submit'
margin='0' margin='0'
markup="md"
gravity='bottom-left' gravity='bottom-left'
size-func="gui.get_viewport()[1],40" size-func="gui.get_viewport()[1],40"
onup="on_history_up()" onup="on_history_up()"
+22
View File
@@ -214,6 +214,28 @@ std::string_view BasicParser::readUntil(char c) {
return source.substr(start, pos - start); return source.substr(start, pos - start);
} }
std::string_view BasicParser::readUntil(std::string_view s, bool nothrow) {
int start = pos;
size_t found = source.find(s, pos);
if (found == std::string::npos) {
if (nothrow) {
pos = source.size();
return source.substr(start);
}
throw error(util::quote(std::string(s))+" expected");
}
skip(found - pos);
return source.substr(start, pos - start);
}
std::string_view BasicParser::readUntilWhitespace() {
int start = pos;
while (hasNext() && !is_whitespace(source[pos])) {
pos++;
}
return source.substr(start, pos - start);
}
std::string_view BasicParser::readUntilEOL() { std::string_view BasicParser::readUntilEOL() {
int start = pos; int start = pos;
while (hasNext() && source[pos] != '\r' && source[pos] != '\n') { while (hasNext() && source[pos] != '\r' && source[pos] != '\n') {
+2
View File
@@ -105,6 +105,8 @@ protected:
parsing_error error(const std::string& message); parsing_error error(const std::string& message);
public: public:
std::string_view readUntil(char c); std::string_view readUntil(char c);
std::string_view readUntil(std::string_view s, bool nothrow);
std::string_view readUntilWhitespace();
std::string_view readUntilEOL(); std::string_view readUntilEOL();
std::string parseName(); std::string parseName();
std::string parseXmlName(); std::string parseXmlName();
+2
View File
@@ -11,6 +11,7 @@
using namespace json; using namespace json;
namespace {
class Parser : BasicParser { class Parser : BasicParser {
dv::value parseList(); dv::value parseList();
dv::value parseObject(); dv::value parseObject();
@@ -20,6 +21,7 @@ public:
dv::value parse(); dv::value parse();
}; };
}
inline void newline( inline void newline(
std::stringstream& ss, bool nice, uint indent, const std::string& indentstr std::stringstream& ss, bool nice, uint indent, const std::string& indentstr
+188
View File
@@ -0,0 +1,188 @@
#include "lua_parsing.hpp"
#include <set>
#include "commons.hpp"
using namespace lua;
using namespace devtools;
static std::set<std::string_view> 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<Token> 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<int>(pos),
static_cast<int>(linestart),
static_cast<int>(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<Token> 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<Token> lua::tokenize(std::string_view file, std::string_view source) {
return Tokenizer(file, source).tokenize();
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <vector>
#include "devtools/syntax.hpp"
namespace lua {
bool is_lua_keyword(std::string_view view);
std::vector<devtools::Token> tokenize(
std::string_view file, std::string_view source
);
}
+111 -99
View File
@@ -107,8 +107,8 @@ glm::vec4 Attribute::asColor() const {
Node::Node(std::string tag) : tag(std::move(tag)) { Node::Node(std::string tag) : tag(std::move(tag)) {
} }
void Node::add(const xmlelement& element) { void Node::add(std::unique_ptr<Node> element) {
elements.push_back(element); elements.push_back(std::move(element));
} }
void Node::set(const std::string& name, const std::string& text) { void Node::set(const std::string& name, const std::string& text) {
@@ -119,7 +119,7 @@ const std::string& Node::getTag() const {
return tag; 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); auto found = attrs.find(name);
if (found == attrs.end()) { if (found == attrs.end()) {
throw std::runtime_error( throw std::runtime_error(
@@ -129,7 +129,7 @@ const xmlattribute& Node::attr(const std::string& name) const {
return found->second; 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); auto found = attrs.find(name);
if (found == attrs.end()) { if (found == attrs.end()) {
return Attribute(name, def); return Attribute(name, def);
@@ -142,19 +142,23 @@ bool Node::has(const std::string& name) const {
return found != attrs.end(); return found != attrs.end();
} }
xmlelement Node::sub(size_t index) { Node& Node::sub(size_t index) {
return elements.at(index); return *elements.at(index);
}
const Node& Node::sub(size_t index) const {
return *elements.at(index);
} }
size_t Node::size() const { size_t Node::size() const {
return elements.size(); return elements.size();
} }
const std::vector<xmlelement>& Node::getElements() const { const std::vector<std::unique_ptr<Node>>& Node::getElements() const {
return elements; return elements;
} }
const xmlelements_map& Node::getAttributes() const { const std::unordered_map<std::string, Attribute>& Node::getAttributes() const {
return attrs; return attrs;
} }
@@ -162,12 +166,12 @@ Document::Document(std::string version, std::string encoding)
: version(std::move(version)), encoding(std::move(encoding)) { : version(std::move(version)), encoding(std::move(encoding)) {
} }
void Document::setRoot(const xmlelement& element) { void Document::setRoot(std::unique_ptr<Node> element) {
this->root = element; root = std::move(element);
} }
xmlelement Document::getRoot() const { const Node* Document::getRoot() const {
return root; return root.get();
} }
const std::string& Document::getVersion() const { const std::string& Document::getVersion() const {
@@ -178,13 +182,21 @@ const std::string& Document::getEncoding() const {
return encoding; return encoding;
} }
Parser::Parser(std::string_view filename, std::string_view source) inline bool is_xml_identifier_start(char c) {
: BasicParser(filename, source) { return is_identifier_start(c) || c == ':';
} }
xmlelement Parser::parseOpenTag() { inline bool is_xml_identifier_part(char c) {
return is_identifier_part(c) || c == '-' || c == '.' || c == ':';
}
namespace {
class Parser : BasicParser {
std::unique_ptr<Document> document;
std::unique_ptr<Node> parseOpenTag() {
std::string tag = parseXMLName(); std::string tag = parseXMLName();
auto node = std::make_shared<Node>(tag); auto node = std::make_unique<Node>(tag);
char c; char c;
while (true) { while (true) {
@@ -210,74 +222,10 @@ xmlelement Parser::parseOpenTag() {
return node; return node;
} }
void Parser::parseDeclaration() { std::unique_ptr<Node> parseElement() {
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<Document>(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 == ':';
}
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));
}
xmlelement Parser::parseElement() {
// text element // text element
if (peek() != '<') { if (peek() != '<') {
auto element = std::make_shared<Node>("#"); auto element = std::make_unique<Node>("#");
auto text = parseText(); auto text = parseText();
util::replaceAll(text, "&quot;", "\""); util::replaceAll(text, "&quot;", "\"");
util::replaceAll(text, "&apos;", "'"); util::replaceAll(text, "&apos;", "'");
@@ -311,7 +259,7 @@ xmlelement Parser::parseElement() {
while (!isNext("</")) { while (!isNext("</")) {
auto sub = parseElement(); auto sub = parseElement();
if (sub) { if (sub) {
element->add(sub); element->add(std::move(sub));
} }
skipWhitespace(); skipWhitespace();
} }
@@ -326,18 +274,82 @@ xmlelement Parser::parseElement() {
return element; return element;
} }
xmldocument Parser::parse() { 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");
}
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_unique<Document>(version, encoding);
}
void parseComment() {
expect("!--");
if (skipTo("-->")) {
skip(3);
} else {
throw error("comment close missing");
}
}
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<Document> parse() {
parseDeclaration(); parseDeclaration();
xmlelement root = nullptr; std::unique_ptr<Node> root;
while (root == nullptr) { while (root == nullptr) {
root = parseElement(); root = parseElement();
} }
document->setRoot(root); document->setRoot(std::move(root));
return document; return std::move(document);
}
};
} }
xmldocument xml::parse(std::string_view filename, std::string_view source) { std::unique_ptr<Document> xml::parse(
std::string_view filename, std::string_view source
) {
Parser parser(filename, source); Parser parser(filename, source);
return parser.parse(); return parser.parse();
} }
@@ -354,13 +366,13 @@ inline void newline(
static void stringifyElement( static void stringifyElement(
std::stringstream& ss, std::stringstream& ss,
const xmlelement& element, const Node& element,
bool nice, bool nice,
const std::string& indentStr, const std::string& indentStr,
int indent int indent
) { ) {
if (element->isText()) { if (element.isText()) {
std::string text = element->attr("#").getText(); std::string text = element.attr("#").getText();
util::replaceAll(text, "&", "&amp;"); util::replaceAll(text, "&", "&amp;");
util::replaceAll(text, "\"", "&quot;"); util::replaceAll(text, "\"", "&quot;");
util::replaceAll(text, "'", "&apos;"); util::replaceAll(text, "'", "&apos;");
@@ -369,10 +381,10 @@ static void stringifyElement(
ss << text; ss << text;
return; return;
} }
const std::string& tag = element->getTag(); const std::string& tag = element.getTag();
ss << '<' << tag; ss << '<' << tag;
auto& attrs = element->getAttributes(); auto& attrs = element.getAttributes();
if (!attrs.empty()) { if (!attrs.empty()) {
ss << ' '; ss << ' ';
int count = 0; int count = 0;
@@ -388,10 +400,10 @@ static void stringifyElement(
count++; count++;
} }
} }
auto& elements = element->getElements(); auto& elements = element.getElements();
if (elements.size() == 1 && elements[0]->isText()) { if (elements.size() == 1 && elements[0]->isText()) {
ss << ">"; ss << ">";
stringifyElement(ss, elements[0], nice, indentStr, indent + 1); stringifyElement(ss, *elements[0], nice, indentStr, indent + 1);
ss << "</" << tag << ">"; ss << "</" << tag << ">";
return; return;
} }
@@ -399,7 +411,7 @@ static void stringifyElement(
ss << '>'; ss << '>';
for (auto& sub : elements) { for (auto& sub : elements) {
newline(ss, nice, indentStr, indent + 1); 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); newline(ss, nice, indentStr, indent);
ss << "</" << tag << ">"; ss << "</" << tag << ">";
@@ -410,16 +422,16 @@ static void stringifyElement(
} }
std::string xml::stringify( 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; std::stringstream ss;
// XML declaration // XML declaration
ss << "<?xml version=\"" << document->getVersion(); ss << "<?xml version=\"" << document.getVersion();
ss << "\" encoding=\"UTF-8\" ?>"; ss << "\" encoding=\"UTF-8\" ?>";
newline(ss, nice, indentStr, 0); newline(ss, nice, indentStr, 0);
stringifyElement(ss, document->getRoot(), nice, indentStr, 0); stringifyElement(ss, *document.getRoot(), nice, indentStr, 0);
return ss.str(); return ss.str();
} }
+20 -35
View File
@@ -13,11 +13,6 @@ namespace xml {
class Attribute; class Attribute;
class Document; class Document;
using xmlattribute = Attribute;
using xmlelement = std::shared_ptr<Node>;
using xmldocument = std::shared_ptr<Document>;
using xmlelements_map = std::unordered_map<std::string, xmlattribute>;
class Attribute { class Attribute {
std::string name; std::string name;
std::string text; std::string text;
@@ -40,13 +35,15 @@ namespace xml {
/// 'text' /// 'text'
class Node { class Node {
std::string tag; std::string tag;
std::unordered_map<std::string, xmlattribute> attrs; std::unordered_map<std::string, Attribute> attrs;
std::vector<xmlelement> elements; std::vector<std::unique_ptr<Node>> elements;
public: public:
Node(std::string tag); Node(std::string tag);
Node(const Node&) = delete;
/// @brief Add sub-element /// @brief Add sub-element
void add(const xmlelement& element); void add(std::unique_ptr<Node> element);
/// @brief Set attribute value. Creates attribute if does not exists /// @brief Set attribute value. Creates attribute if does not exists
/// @param name attribute name /// @param name attribute name
@@ -68,14 +65,14 @@ namespace xml {
/// @param name attribute name /// @param name attribute name
/// @throws std::runtime_error if element has no attribute /// @throws std::runtime_error if element has no attribute
/// @return xml attribute - {name, value} /// @return xml attribute - {name, value}
const xmlattribute& attr(const std::string& name) const; const Attribute& attr(const std::string& name) const;
/// @brief Get attribute by name /// @brief Get attribute by name
/// @param name attribute name /// @param name attribute name
/// @param def default value will be returned wrapped in xmlattribute /// @param def default value will be returned wrapped in xmlattribute
/// if element has no attribute /// if element has no attribute
/// @return xmlattribute - {name, value} or {name, def} if not found*/ /// @return xml attribute - {name, value} or {name, def} if not found
xmlattribute attr(const std::string& name, const std::string& def) Attribute attr(const std::string& name, const std::string& def)
const; const;
/// @brief Check if element has attribute /// @brief Check if element has attribute
@@ -86,51 +83,37 @@ namespace xml {
/// @param index sub-element index /// @param index sub-element index
/// @throws std::out_of_range if an invalid index given /// @throws std::out_of_range if an invalid index given
/// @return sub-element /// @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 /// @brief Get number of sub-elements
size_t size() const; size_t size() const;
const std::vector<xmlelement>& getElements() const; const std::vector<std::unique_ptr<Node>>& getElements() const;
const xmlelements_map& getAttributes() const; const std::unordered_map<std::string, Attribute>& getAttributes() const;
}; };
class Document { class Document {
xmlelement root = nullptr; std::unique_ptr<Node> root = nullptr;
std::string version; std::string version;
std::string encoding; std::string encoding;
public: public:
Document(std::string version, std::string encoding); Document(std::string version, std::string encoding);
void setRoot(const xmlelement& element); void setRoot(std::unique_ptr<Node> element);
xmlelement getRoot() const; const Node* getRoot() const;
const std::string& getVersion() const; const std::string& getVersion() const;
const std::string& getEncoding() 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 /// @brief Serialize XML Document to string
/// @param document serializing document /// @param document serializing document
/// @param nice use human readable format (with indents and line-separators) /// @param nice use human readable format (with indents and line-separators)
/// @param indentStr indentation characters sequence (default - 4 spaces) /// @param indentStr indentation characters sequence (default - 4 spaces)
/// @return XML string /// @return XML string
extern std::string stringify( std::string stringify(
const xmldocument& document, const Document& document,
bool nice = true, bool nice = true,
const std::string& indentStr = " " const std::string& indentStr = " "
); );
@@ -139,7 +122,9 @@ namespace xml {
/// @param filename file name will be shown in error messages /// @param filename file name will be shown in error messages
/// @param source xml source code string /// @param source xml source code string
/// @return xml document /// @return xml document
extern xmldocument parse( std::unique_ptr<Document> parse(
std::string_view filename, std::string_view source std::string_view filename, std::string_view source
); );
using xmlelement = Node;
} }
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <string>
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)) {
}
};
}
+72
View File
@@ -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<FontStylesScheme> build_styles(
const std::vector<devtools::Token>& 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<FontStylesScheme>(std::move(styles));
}
std::unique_ptr<FontStylesScheme> devtools::syntax_highlight(
const std::string& lang, std::string_view source
) {
try {
if (lang == "lua") {
auto tokens = lua::tokenize("<string>", source);
return build_styles(tokens);
} else {
return nullptr;
}
} catch (const parsing_error& err) {
return nullptr;
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <memory>
struct FontStylesScheme;
namespace devtools {
enum SyntaxStyles {
DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR
};
std::unique_ptr<FontStylesScheme> syntax_highlight(
const std::string& lang, std::string_view source
);
}
+1 -1
View File
@@ -206,7 +206,7 @@ void Engine::renderFrame(Batch2D& batch) {
Viewport viewport(Window::width, Window::height); Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, &batch); DrawContext ctx(nullptr, viewport, &batch);
gui->draw(&ctx, assets.get()); gui->draw(ctx, *assets);
} }
void Engine::processPostRunnables() { void Engine::processPostRunnables() {
+1 -3
View File
@@ -67,9 +67,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
: scripting::create_doc_environment(penv, name); : scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(env); gui::UiXmlReader reader(env);
auto view = reader.readXML( auto view = reader.readXML(file.u8string(), *xmldoc->getRoot());
file.u8string(), xmldoc->getRoot()
);
view->setId("root"); view->setId("root");
uidocscript script {}; uidocscript script {};
auto scriptFile = fs::path(file.u8string()+".lua"); auto scriptFile = fs::path(file.u8string()+".lua");
+4 -4
View File
@@ -124,7 +124,7 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
}); });
InventoryBuilder builder; 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(); auto view = builder.build();
view->bind(accessInventory, content); view->bind(accessInventory, content);
view->setMargin(glm::vec4()); view->setMargin(glm::vec4());
@@ -137,7 +137,7 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr); SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
InventoryBuilder builder; 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(); auto view = builder.build();
view->setId("hud.hotbar"); view->setId("hud.hotbar");
view->setOrigin(glm::vec2(view->getSize().x/2, 0)); view->setOrigin(glm::vec2(view->getSize().x/2, 0));
@@ -346,9 +346,9 @@ void Hud::update(bool visible) {
element.getNode()->setVisible(visible); element.getNode()->setVisible(visible);
} }
glm::vec2 invSize = contentAccessPanel->getSize(); glm::vec2 caSize = contentAccessPanel->getSize();
contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel); 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)); contentAccess->setMinSize(glm::vec2(1, Window::height));
hotbarView->setVisible(visible && !(secondUI && !inventoryView)); hotbarView->setVisible(visible && !(secondUI && !inventoryView));
+6 -6
View File
@@ -95,16 +95,16 @@ class Hud : public util::ObjectsKeeper {
/// @brief Inventories interaction agent (grabbed item) /// @brief Inventories interaction agent (grabbed item)
std::shared_ptr<gui::SlotView> exchangeSlot; std::shared_ptr<gui::SlotView> exchangeSlot;
/// @brief Exchange slot inventory (1 slot only) /// @brief Exchange slot inventory (1 slot only)
std::shared_ptr<Inventory> exchangeSlotInv = nullptr; std::shared_ptr<Inventory> exchangeSlotInv;
/// @brief List of all controlled hud elements /// @brief List of all controlled hud elements
std::vector<HudElement> elements; std::vector<HudElement> elements;
/// @brief Player inventory view /// @brief Player inventory view
std::shared_ptr<gui::InventoryView> inventoryView = nullptr; std::shared_ptr<gui::InventoryView> inventoryView;
/// @brief Block inventory view /// @brief Block inventory view
std::shared_ptr<gui::InventoryView> blockUI = nullptr; std::shared_ptr<gui::InventoryView> blockUI;
/// @brief Secondary inventory view /// @brief Secondary inventory view
std::shared_ptr<gui::InventoryView> secondInvView = nullptr; std::shared_ptr<gui::InventoryView> secondInvView;
/// @brief Position of the block open /// @brief Position of the block open
glm::ivec3 blockPos {}; glm::ivec3 blockPos {};
/// @brief Id of the block open (used to detect block destruction or replacement) /// @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 /// @brief Provide cheat controllers to the debug panel
bool allowDebugCheats = true; bool allowDebugCheats = true;
/// @brief UI element will be dynamicly positioned near to inventory or in screen center /// @brief UI element will be dynamicly positioned near to inventory or in screen center
std::shared_ptr<gui::UINode> secondUI = nullptr; std::shared_ptr<gui::UINode> secondUI;
std::shared_ptr<gui::UINode> debugMinimap = nullptr; std::shared_ptr<gui::UINode> debugMinimap;
std::unique_ptr<ImageData> debugImgWorldGen; std::unique_ptr<ImageData> debugImgWorldGen;
+34
View File
@@ -261,6 +261,24 @@ void Batch2D::rect(
vertex(x+w, y+h, u+tx, v, r,g,b,a); 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( void Batch2D::rect(
float x, float y, float w, float h, float x, float y, float w, float h,
float r0, float g0, float b0, 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); 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() { void Batch2D::flush() {
if (index == 0) if (index == 0)
return; return;
+7
View File
@@ -45,6 +45,7 @@ public:
void setRegion(UVRegion region); 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, 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, 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); void point(float x, float y, float r, float g, float b, float a);
inline void setColor(glm::vec4 color) { inline void setColor(glm::vec4 color) {
@@ -79,6 +80,12 @@ public:
float r, float g, float b, float a 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( void rect(
float x, float y, float w, float h, float x, float y, float w, float h,
float r0, float g0, float b0, float r0, float g0, float b0,
+75 -12
View File
@@ -1,5 +1,6 @@
#include "Font.hpp" #include "Font.hpp"
#include <limits>
#include <utility> #include <utility>
#include "Texture.hpp" #include "Texture.hpp"
#include "Batch2D.hpp" #include "Batch2D.hpp"
@@ -52,18 +53,22 @@ static inline void draw_glyph(
uint c, uint c,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, const glm::vec3& up,
float glyphInterval float glyphInterval,
const FontStyle& style
) { ) {
for (int i = 0; i <= style.bold; i++) {
batch.sprite( batch.sprite(
pos.x + offset.x * right.x, pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x,
pos.y + offset.y * right.y, pos.y + offset.y * right.y,
right.x / glyphInterval, right.x / glyphInterval,
up.y, up.y,
-0.15f * style.italic,
16, 16,
c, c,
batch.getColor() batch.getColor() * style.color
); );
} }
}
static inline void draw_glyph( static inline void draw_glyph(
Batch3D& batch, Batch3D& batch,
@@ -72,18 +77,21 @@ static inline void draw_glyph(
uint c, uint c,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, const glm::vec3& up,
float glyphInterval float glyphInterval,
const FontStyle& style
) { ) {
for (int i = 0; i <= style.bold; i++) {
batch.sprite( batch.sprite(
pos + right * offset.x + up * offset.y, pos + right * (offset.x + i) + up * offset.y,
up, right / glyphInterval, up, right / glyphInterval,
0.5f, 0.5f,
0.5f, 0.5f,
16, 16,
c, c,
batch.getColor() batch.getColor() * style.color
); );
} }
}
template <class Batch> template <class Batch>
static inline void draw_text( static inline void draw_text(
@@ -93,14 +101,32 @@ static inline void draw_text(
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up, 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 page = 0;
uint next = MAX_CODEPAGES; uint next = MAX_CODEPAGES;
int x = 0; int x = 0;
int y = 0; int y = 0;
bool hasLines = false;
do { 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)) { if (!font.isPrintableChar(c)) {
x++; x++;
continue; continue;
@@ -109,7 +135,7 @@ static inline void draw_text(
if (charpage == page){ if (charpage == page){
batch.texture(font.getPage(charpage)); batch.texture(font.getPage(charpage));
draw_glyph( 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){ else if (charpage > page && charpage < next){
@@ -121,6 +147,31 @@ static inline void draw_text(
next = MAX_CODEPAGES; next = MAX_CODEPAGES;
x = 0; x = 0;
} while (page < MAX_CODEPAGES); } 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 { const Texture* Font::getPage(int charpage) const {
@@ -135,20 +186,30 @@ const Texture* Font::getPage(int charpage) const {
} }
void Font::draw( 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 { ) const {
draw_text( draw_text(
*this, batch, text, *this, batch, text,
glm::vec3(x, y, 0), glm::vec3(x, y, 0),
glm::vec3(glyphInterval*scale, 0, 0), glm::vec3(glyphInterval*scale, 0, 0),
glm::vec3(0, lineHeight*scale, 0), glm::vec3(0, lineHeight*scale, 0),
glyphInterval/static_cast<float>(lineHeight) glyphInterval/static_cast<float>(lineHeight),
styles,
styleMapOffset
); );
} }
void Font::draw( void Font::draw(
Batch3D& batch, Batch3D& batch,
std::wstring_view text, std::wstring_view text,
const FontStylesScheme* styles,
size_t styleMapOffset,
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up const glm::vec3& up
@@ -157,6 +218,8 @@ void Font::draw(
*this, batch, text, pos, *this, batch, text, pos,
right * static_cast<float>(glyphInterval), right * static_cast<float>(glyphInterval),
up * static_cast<float>(lineHeight), up * static_cast<float>(lineHeight),
glyphInterval/static_cast<float>(lineHeight) glyphInterval/static_cast<float>(lineHeight),
styles,
styleMapOffset
); );
} }
+38 -5
View File
@@ -11,10 +11,33 @@ class Batch2D;
class Batch3D; class Batch3D;
class Camera; class Camera;
enum class FontStyle { struct FontStyle {
none, bool bold = false;
shadow, bool italic = false;
outline 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<FontStyle> palette;
std::vector<ubyte> map;
}; };
class Font { class Font {
@@ -46,11 +69,21 @@ public:
/// @param codepoint character unicode codepoint /// @param codepoint character unicode codepoint
bool isPrintableChar(uint codepoint) const; 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( void draw(
Batch3D& batch, Batch3D& batch,
std::wstring_view text, std::wstring_view text,
const FontStylesScheme* styles,
size_t styleMapOffset,
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right={1, 0, 0}, const glm::vec3& right={1, 0, 0},
const glm::vec3& up={0, 1, 0} const glm::vec3& up={0, 1, 0}
+2
View File
@@ -103,6 +103,8 @@ void TextsRenderer::renderNote(
font.draw( font.draw(
batch, batch,
text, text,
nullptr,
0,
pos - xvec * (width * 0.5f) * preset.scale, pos - xvec * (width * 0.5f) * preset.scale,
xvec * preset.scale, xvec * preset.scale,
yvec * preset.scale yvec * preset.scale
+4 -4
View File
@@ -197,18 +197,18 @@ void GUI::act(float delta, const Viewport& vp) {
} }
} }
void GUI::draw(const DrawContext* pctx, Assets* assets) { void GUI::draw(const DrawContext& pctx, const Assets& assets) {
auto& viewport = pctx->getViewport(); auto& viewport = pctx.getViewport();
glm::vec2 wsize = viewport.size(); glm::vec2 wsize = viewport.size();
menu->setPos((wsize - menu->getSize()) / 2.0f); menu->setPos((wsize - menu->getSize()) / 2.0f);
uicamera->setFov(wsize.y); uicamera->setFov(wsize.y);
auto uishader = assets->get<Shader>("ui"); auto uishader = assets.get<Shader>("ui");
uishader->use(); uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView()); uishader->uniformMatrix("u_projview", uicamera->getProjView());
pctx->getBatch2D()->begin(); pctx.getBatch2D()->begin();
container->draw(pctx, assets); container->draw(pctx, assets);
} }
+1 -1
View File
@@ -94,7 +94,7 @@ namespace gui {
/// @brief Draw all visible elements on main container /// @brief Draw all visible elements on main container
/// @param pctx parent graphics context /// @param pctx parent graphics context
/// @param assets active assets storage /// @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 /// @brief Add element to the main container
/// @param node UI element /// @param node UI element
+3 -3
View File
@@ -52,7 +52,7 @@ Button::Button(
void Button::setText(std::wstring text) { void Button::setText(std::wstring text) {
if (label) { 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(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(calcColor()); batch->setColor(calcColor());
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);
+4 -2
View File
@@ -7,7 +7,7 @@ namespace gui {
class Button : public Panel { class Button : public Panel {
protected: protected:
std::shared_ptr<Label> label = nullptr; std::shared_ptr<Label> label;
public: public:
Button(const std::shared_ptr<UINode>& content, Button(const std::shared_ptr<UINode>& content,
glm::vec4 padding=glm::vec4(2.0f)); glm::vec4 padding=glm::vec4(2.0f));
@@ -17,7 +17,9 @@ namespace gui {
const onaction& action, const onaction& action,
glm::vec2 size=glm::vec2(-1)); glm::vec2 size=glm::vec2(-1));
virtual void drawBackground(const DrawContext* pctx, Assets* assets) override; virtual void drawBackground(
const DrawContext& pctx, const Assets& assets
) override;
virtual Align getTextAlign() const; virtual Align getTextAlign() const;
virtual void setTextAlign(Align align); virtual void setTextAlign(Align align);
+2 -2
View File
@@ -13,12 +13,12 @@ CheckBox::CheckBox(bool checked) : UINode(glm::vec2(32.0f)), checked(checked) {
setHoverColor({0.05f, 0.1f, 0.2f, 0.75f}); setHoverColor({0.05f, 0.1f, 0.2f, 0.75f});
} }
void CheckBox::draw(const DrawContext* pctx, Assets*) { void CheckBox::draw(const DrawContext& pctx, const Assets&) {
if (supplier) { if (supplier) {
checked = supplier(); checked = supplier();
} }
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(checked ? checkColor : calcColor()); batch->setColor(checked ? checkColor : calcColor());
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);
+1 -1
View File
@@ -15,7 +15,7 @@ namespace gui {
public: public:
CheckBox(bool checked=false); CheckBox(bool checked=false);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void mouseRelease(GUI*, int x, int y) override; virtual void mouseRelease(GUI*, int x, int y) override;
+56 -7
View File
@@ -17,11 +17,19 @@ Container::~Container() {
Container::clear(); Container::clear();
} }
std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> Container::getAt(
const glm::vec2& pos, const std::shared_ptr<UINode>& self
) {
if (!isInteractive() || !isEnabled()) { if (!isInteractive() || !isEnabled()) {
return nullptr; return nullptr;
} }
if (!isInside(pos)) return nullptr; if (!isInside(pos)) {
return nullptr;
}
int diff = (actualLength-size.y);
if (scrollable && diff > 0 && pos.x > calcPos().x + getSize().x - scrollBarWidth) {
return UINode::getAt(pos, self);
}
for (int i = nodes.size()-1; i >= 0; i--) { for (int i = nodes.size()-1; i >= 0; i--) {
auto& node = nodes[i]; auto& node = nodes[i];
@@ -35,6 +43,35 @@ std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode>
return UINode::getAt(pos, self); return UINode::getAt(pos, self);
} }
void Container::mouseMove(GUI* gui, int x, int y) {
UINode::mouseMove(gui, x, y);
if (!scrollable) {
return;
}
auto pos = calcPos();
x -= pos.x;
y -= pos.y;
if (prevScrollY == -1) {
if (x >= size.x - scrollBarWidth) {
prevScrollY = y;
}
return;
}
int diff = (actualLength-size.y);
if (diff > 0) {
scroll -= (y - prevScrollY) / static_cast<float>(size.y) * actualLength;
scroll = -glm::min(
glm::max(static_cast<float>(-scroll), 0.0f), actualLength - size.y
);
}
prevScrollY = y;
}
void Container::mouseRelease(GUI* gui, int x, int y) {
UINode::mouseRelease(gui, x, y);
prevScrollY = -1;
}
void Container::act(float delta) { void Container::act(float delta) {
for (const auto& node : nodes) { for (const auto& node : nodes) {
if (node->isVisible()) { if (node->isVisible()) {
@@ -80,32 +117,44 @@ void Container::setScrollable(bool flag) {
scrollable = flag; scrollable = flag;
} }
void Container::draw(const DrawContext* pctx, Assets* assets) { void Container::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
glm::vec2 size = getSize(); glm::vec2 size = getSize();
drawBackground(pctx, assets); drawBackground(pctx, assets);
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
if (!nodes.empty()) { if (!nodes.empty()) {
batch->flush(); batch->flush();
DrawContext ctx = pctx->sub(); DrawContext ctx = pctx.sub();
ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y))); ctx.setScissors(glm::vec4(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)));
for (const auto& node : nodes) { for (const auto& node : nodes) {
if (node->isVisible()) if (node->isVisible())
node->draw(pctx, assets); node->draw(pctx, assets);
} }
int diff = (actualLength-size.y);
if (scrollable && diff > 0) {
int h = glm::max(size.y / actualLength * size.y, scrollBarWidth / 2.0f);
batch->untexture();
batch->setColor(glm::vec4(1, 1, 1, 0.3f));
batch->rect(
pos.x + size.x - scrollBarWidth,
pos.y - scroll / static_cast<float>(diff) * (size.y - h),
scrollBarWidth, h
);
}
batch->flush(); batch->flush();
} }
} }
void Container::drawBackground(const DrawContext* pctx, Assets*) { void Container::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec4 color = calcColor(); glm::vec4 color = calcColor();
if (color.a <= 0.001f) if (color.a <= 0.001f)
return; return;
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(color); batch->setColor(color);
batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y)); batch->rect(pos.x, pos.y, glm::ceil(size.x), glm::ceil(size.y));
+12 -3
View File
@@ -7,21 +7,27 @@
namespace gui { namespace gui {
class Container : public UINode { class Container : public UINode {
int prevScrollY = -1;
protected: protected:
std::vector<std::shared_ptr<UINode>> nodes; std::vector<std::shared_ptr<UINode>> nodes;
std::vector<IntervalEvent> intervalEvents; std::vector<IntervalEvent> intervalEvents;
int scroll = 0; int scroll = 0;
int scrollStep = 40; int scrollStep = 40;
int scrollBarWidth = 10;
int actualLength = 0; int actualLength = 0;
bool scrollable = true; bool scrollable = true;
bool isScrolling() {
return prevScrollY != -1;
}
public: public:
Container(glm::vec2 size); Container(glm::vec2 size);
virtual ~Container(); virtual ~Container();
virtual void act(float delta) override; virtual void act(float delta) override;
virtual void drawBackground(const DrawContext* pctx, Assets* assets); virtual void drawBackground(const DrawContext& pctx, const Assets& assets);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override; virtual std::shared_ptr<UINode> getAt(const glm::vec2& pos, const std::shared_ptr<UINode>& self) override;
virtual void add(const std::shared_ptr<UINode>& node); virtual void add(const std::shared_ptr<UINode>& node);
virtual void add(const std::shared_ptr<UINode>& node, glm::vec2 pos); virtual void add(const std::shared_ptr<UINode>& node, glm::vec2 pos);
virtual void clear(); virtual void clear();
@@ -36,6 +42,9 @@ namespace gui {
virtual void setScrollStep(int step); virtual void setScrollStep(int step);
virtual void refresh() override; virtual void refresh() override;
virtual void mouseMove(GUI*, int x, int y) override;
virtual void mouseRelease(GUI*, int x, int y) override;
const std::vector<std::shared_ptr<UINode>>& getNodes() const; const std::vector<std::shared_ptr<UINode>>& getNodes() const;
}; };
} }
+4 -4
View File
@@ -15,21 +15,21 @@ Image::Image(std::string texture, glm::vec2 size) : UINode(size), texture(std::m
setInteractive(false); setInteractive(false);
} }
void Image::draw(const DrawContext* pctx, Assets* assets) { void Image::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
Texture* texture = nullptr; Texture* texture = nullptr;
auto separator = this->texture.find(':'); auto separator = this->texture.find(':');
if (separator == std::string::npos) { if (separator == std::string::npos) {
texture = assets->get<Texture>(this->texture); texture = assets.get<Texture>(this->texture);
batch->texture(texture); batch->texture(texture);
if (texture && autoresize) { if (texture && autoresize) {
setSize(glm::vec2(texture->getWidth(), texture->getHeight())); setSize(glm::vec2(texture->getWidth(), texture->getHeight()));
} }
} else { } else {
auto atlasName = this->texture.substr(0, separator); auto atlasName = this->texture.substr(0, separator);
if (auto atlas = assets->get<Atlas>(atlasName)) { if (auto atlas = assets.get<Atlas>(atlasName)) {
if (auto region = atlas->getIf(this->texture.substr(separator+1))) { if (auto region = atlas->getIf(this->texture.substr(separator+1))) {
texture = atlas->getTexture(); texture = atlas->getTexture();
batch->texture(atlas->getTexture()); batch->texture(atlas->getTexture());
+1 -1
View File
@@ -10,7 +10,7 @@ namespace gui {
public: public:
Image(std::string texture, glm::vec2 size=glm::vec2(32,32)); Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void setAutoResize(bool flag); virtual void setAutoResize(bool flag);
virtual bool isAutoResize() const; virtual bool isAutoResize() const;
+4 -4
View File
@@ -9,15 +9,15 @@ using namespace gui;
InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding) InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding)
: Panel(glm::vec2(100,32), padding, 0), : Panel(glm::vec2(100,32), padding, 0),
binding(binding) { binding(binding),
label = std::make_shared<Label>(L""); label(std::make_shared<Label>(L"")) {
add(label); add(label);
setScrollable(false); setScrollable(false);
} }
void InputBindBox::drawBackground(const DrawContext* pctx, Assets*) { void InputBindBox::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(isFocused() ? focusedColor : calcColor()); batch->setColor(isFocused() ? focusedColor : calcColor());
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);
+4 -1
View File
@@ -13,7 +13,10 @@ namespace gui {
Binding& binding; Binding& binding;
public: public:
InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f)); InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f));
virtual void drawBackground(const DrawContext* pctx, Assets* assets) override;
virtual void drawBackground(
const DrawContext& pctx, const Assets& assets
) override;
virtual void clicked(GUI*, mousecode button) override; virtual void clicked(GUI*, mousecode button) override;
virtual void keyPressed(keycode key) override; virtual void keyPressed(keycode key) override;
+12 -12
View File
@@ -54,7 +54,7 @@ InventoryBuilder::InventoryBuilder() {
void InventoryBuilder::addGrid( void InventoryBuilder::addGrid(
int cols, int count, int cols, int count,
glm::vec2 pos, glm::vec2 pos,
int padding, glm::vec4 padding,
bool addpanel, bool addpanel,
const SlotLayout& slotLayout const SlotLayout& slotLayout
) { ) {
@@ -63,8 +63,8 @@ void InventoryBuilder::addGrid(
int rows = ceildiv(count, cols); int rows = ceildiv(count, cols);
uint width = cols * (slotSize + interval) - interval + padding*2; uint width = cols * (slotSize + interval) - interval + padding.x + padding.z;
uint height = rows * (slotSize + interval) - interval + padding*2; uint height = rows * (slotSize + interval) - interval + padding.y + padding.w;
glm::vec2 vsize = view->getSize(); glm::vec2 vsize = view->getSize();
if (pos.x + width > vsize.x) { if (pos.x + width > vsize.x) {
@@ -87,8 +87,8 @@ void InventoryBuilder::addGrid(
break; break;
} }
glm::vec2 position ( glm::vec2 position (
col * (slotSize + interval) + padding, col * (slotSize + interval) + padding.x,
row * (slotSize + interval) + padding row * (slotSize + interval) + padding.y
); );
auto builtSlot = slotLayout; auto builtSlot = slotLayout;
builtSlot.index = row * cols + col; builtSlot.index = row * cols + col;
@@ -115,7 +115,7 @@ SlotView::SlotView(
setTooltipDelay(0.0f); setTooltipDelay(0.0f);
} }
void SlotView::draw(const DrawContext* pctx, Assets* assets) { void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
if (bound == nullptr) { if (bound == nullptr) {
return; return;
} }
@@ -144,7 +144,7 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
color = glm::vec4(1, 1, 1, 0.2f); color = glm::vec4(1, 1, 1, 0.2f);
} }
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->setColor(color); batch->setColor(color);
if (color.a > 0.0) { if (color.a > 0.0) {
batch->texture(nullptr); batch->texture(nullptr);
@@ -157,7 +157,7 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
batch->setColor(glm::vec4(1.0f)); batch->setColor(glm::vec4(1.0f));
auto previews = assets->get<Atlas>("block-previews"); auto previews = assets.get<Atlas>("block-previews");
auto indices = content->getIndices(); auto indices = content->getIndices();
auto& item = indices->items.require(stack.getItemId()); auto& item = indices->items.require(stack.getItemId());
@@ -176,7 +176,7 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
} }
case ItemIconType::SPRITE: { case ItemIconType::SPRITE: {
auto textureRegion = auto textureRegion =
util::get_texture_region(*assets, item.icon, "blocks:notfound"); util::get_texture_region(assets, item.icon, "blocks:notfound");
batch->texture(textureRegion.texture); batch->texture(textureRegion.texture);
batch->rect( batch->rect(
@@ -187,16 +187,16 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) {
} }
if (stack.getCount() > 1) { if (stack.getCount() > 1) {
auto font = assets->get<Font>("normal"); auto font = assets.get<Font>("normal");
std::wstring text = std::to_wstring(stack.getCount()); std::wstring text = std::to_wstring(stack.getCount());
int x = pos.x+slotSize-text.length()*8; int x = pos.x+slotSize-text.length()*8;
int y = pos.y+slotSize-16; int y = pos.y+slotSize-16;
batch->setColor({0, 0, 0, 1.0f}); batch->setColor({0, 0, 0, 1.0f});
font->draw(*batch, text, x+1, y+1); font->draw(*batch, text, x+1, y+1, nullptr, 0);
batch->setColor(glm::vec4(1.0f)); batch->setColor(glm::vec4(1.0f));
font->draw(*batch, text, x, y); font->draw(*batch, text, x, y, nullptr, 0);
} }
} }
+2 -2
View File
@@ -64,7 +64,7 @@ namespace gui {
public: public:
SlotView(SlotLayout layout); SlotView(SlotLayout layout);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
void setHighlighted(bool flag); void setHighlighted(bool flag);
bool isHighlighted() const; bool isHighlighted() const;
@@ -136,7 +136,7 @@ namespace gui {
void addGrid( void addGrid(
int cols, int count, int cols, int count,
glm::vec2 pos, glm::vec2 pos,
int padding, glm::vec4 padding,
bool addpanel, bool addpanel,
const SlotLayout& slotLayout const SlotLayout& slotLayout
); );
+28 -7
View File
@@ -6,6 +6,7 @@
#include "graphics/core/Font.hpp" #include "graphics/core/Font.hpp"
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "../markdown.hpp"
using namespace gui; using namespace gui;
@@ -66,6 +67,8 @@ Label::Label(const std::wstring& text, std::string fontName)
cache.update(this->text, multiline, textWrap); cache.update(this->text, multiline, textWrap);
} }
Label::~Label() = default;
glm::vec2 Label::calcSize() { glm::vec2 Label::calcSize() {
auto font = cache.font; auto font = cache.font;
uint lineHeight = font->getLineHeight(); uint lineHeight = font->getLineHeight();
@@ -78,11 +81,16 @@ glm::vec2 Label::calcSize() {
); );
} }
void Label::setText(const std::wstring& text) { void Label::setText(std::wstring text) {
if (markup == "md") {
auto [processedText, styles] = markdown::process(text, true);
text = std::move(processedText);
setStyles(std::move(styles));
}
if (text == this->text && !cache.resetFlag) { if (text == this->text && !cache.resetFlag) {
return; return;
} }
this->text = text; this->text = std::move(text);
cache.update(this->text, multiline, textWrap); cache.update(this->text, multiline, textWrap);
if (cache.font && autoresize) { if (cache.font && autoresize) {
@@ -156,9 +164,9 @@ uint Label::getLinesNumber() const {
return cache.lines.size(); return cache.lines.size();
} }
void Label::draw(const DrawContext* pctx, Assets* assets) { void Label::draw(const DrawContext& pctx, const Assets& assets) {
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
auto font = assets->get<Font>(fontName); auto font = assets.get<Font>(fontName);
cache.prepare(font, static_cast<size_t>(glm::abs(getSize().x))); cache.prepare(font, static_cast<size_t>(glm::abs(getSize().x)));
if (supplier) { if (supplier) {
@@ -201,10 +209,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) {
if (i < cache.lines.size()-1) { if (i < cache.lines.size()-1) {
view = std::wstring_view(text.c_str()+offset, cache.lines.at(i+1).offset-offset); view = std::wstring_view(text.c_str()+offset, cache.lines.at(i+1).offset-offset);
} }
font->draw(*batch, view, pos.x, pos.y + i * totalLineHeight); font->draw(*batch, view, pos.x, pos.y + i * totalLineHeight, styles.get(), offset);
} }
} else { } else {
font->draw(*batch, text, pos.x, pos.y); font->draw(*batch, text, pos.x, pos.y, styles.get(), 0);
} }
} }
@@ -239,3 +247,16 @@ void Label::setTextWrapping(bool flag) {
bool Label::isTextWrapping() const { bool Label::isTextWrapping() const {
return textWrap; return textWrap;
} }
void Label::setMarkup(std::string_view lang) {
markup = lang;
setText(text);
}
const std::string& Label::getMarkup() const {
return markup;
}
void Label::setStyles(std::unique_ptr<FontStylesScheme> styles) {
this->styles = std::move(styles);
}
+15 -2
View File
@@ -3,6 +3,7 @@
#include "UINode.hpp" #include "UINode.hpp"
class Font; class Font;
struct FontStylesScheme;
namespace gui { namespace gui {
struct LineScheme { struct LineScheme {
@@ -51,11 +52,18 @@ namespace gui {
/// @brief Auto resize label to fit text /// @brief Auto resize label to fit text
bool autoresize = false; bool autoresize = false;
/// @brief Text markup language
std::string markup;
std::unique_ptr<FontStylesScheme> styles;
public: public:
Label(const std::string& text, std::string fontName="normal"); Label(const std::string& text, std::string fontName="normal");
Label(const std::wstring& text, std::string fontName="normal"); Label(const std::wstring& text, std::string fontName="normal");
virtual void setText(const std::wstring& text); virtual ~Label();
virtual void setText(std::wstring text);
const std::wstring& getText() const; const std::wstring& getText() const;
virtual void setFontName(std::string name); virtual void setFontName(std::string name);
@@ -95,7 +103,7 @@ namespace gui {
virtual uint getLinesNumber() const; virtual uint getLinesNumber() const;
virtual bool isFakeLine(size_t line) const; virtual bool isFakeLine(size_t line) const;
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void textSupplier(wstringsupplier supplier); virtual void textSupplier(wstringsupplier supplier);
@@ -107,5 +115,10 @@ namespace gui {
virtual void setTextWrapping(bool flag); virtual void setTextWrapping(bool flag);
virtual bool isTextWrapping() const; virtual bool isTextWrapping() const;
virtual void setMarkup(std::string_view lang);
virtual const std::string& getMarkup() const;
virtual void setStyles(std::unique_ptr<FontStylesScheme> styles);
}; };
} }
+1 -1
View File
@@ -7,7 +7,7 @@
namespace gui { namespace gui {
struct Page { struct Page {
std::string name; std::string name;
std::shared_ptr<UINode> panel = nullptr; std::shared_ptr<UINode> panel;
}; };
using page_loader_func = std::function<std::shared_ptr<UINode>(const std::string& name)>; using page_loader_func = std::function<std::shared_ptr<UINode>(const std::string& name)>;
+11 -4
View File
@@ -14,9 +14,9 @@ void Plotter::act(float delta) {
points[index % dmwidth] = std::min(value, dmheight); points[index % dmwidth] = std::min(value, dmheight);
} }
void Plotter::draw(const DrawContext* pctx, Assets* assets) { void Plotter::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->lineWidth(1); batch->lineWidth(1);
for (int i = index+1; i < index+dmwidth; i++) { for (int i = index+1; i < index+dmwidth; i++) {
@@ -37,7 +37,7 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) {
} }
int current_point = static_cast<int>(points[index % dmwidth]); int current_point = static_cast<int>(points[index % dmwidth]);
auto font = assets->get<Font>("normal"); auto font = assets.get<Font>("normal");
for (int y = 0; y < dmheight; y += labelsInterval) { for (int y = 0; y < dmheight; y += labelsInterval) {
std::wstring string; std::wstring string;
if (current_point/16 == y/labelsInterval) { if (current_point/16 == y/labelsInterval) {
@@ -47,6 +47,13 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) {
batch->setColor({1,1,1,0.2f}); batch->setColor({1,1,1,0.2f});
string = util::to_wstring(y / multiplier, 3); string = util::to_wstring(y / multiplier, 3);
} }
font->draw(*batch, string, pos.x+dmwidth+2, pos.y+dmheight-y-labelsInterval); font->draw(
*batch,
string,
pos.x + dmwidth + 2,
pos.y + dmheight - y - labelsInterval,
nullptr,
0
);
} }
} }
+1 -1
View File
@@ -29,6 +29,6 @@ namespace gui {
} }
void act(float delta) override; void act(float delta) override;
void draw(const DrawContext* pctx, Assets* assets) override; void draw(const DrawContext& pctx, const Assets& assets) override;
}; };
} }
+102 -21
View File
@@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include "Label.hpp" #include "Label.hpp"
#include "devtools/syntax_highlighting.hpp"
#include "graphics/core/DrawContext.hpp" #include "graphics/core/DrawContext.hpp"
#include "graphics/core/Batch2D.hpp" #include "graphics/core/Batch2D.hpp"
#include "graphics/core/Font.hpp" #include "graphics/core/Font.hpp"
@@ -12,6 +13,7 @@
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "window/Events.hpp" #include "window/Events.hpp"
#include "window/Window.hpp" #include "window/Window.hpp"
#include "../markdown.hpp"
using namespace gui; using namespace gui;
@@ -46,10 +48,10 @@ TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
scrollStep = 0; scrollStep = 0;
} }
void TextBox::draw(const DrawContext* pctx, Assets* assets) { void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
Container::draw(pctx, assets); Container::draw(pctx, assets);
font = assets->get<Font>(label->getFontName()); font = assets.get<Font>(label->getFontName());
if (!isFocused()) { if (!isFocused()) {
return; return;
@@ -57,39 +59,67 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
glm::vec2 size = getSize(); glm::vec2 size = getSize();
auto subctx = pctx->sub(); auto subctx = pctx.sub();
subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y)); subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
const int lineHeight = font->getLineHeight() * label->getLineInterval(); const int lineHeight = font->getLineHeight() * label->getLineInterval();
glm::vec2 lcoord = label->calcPos(); glm::vec2 lcoord = label->calcPos();
lcoord.y -= 2; lcoord.y -= 2;
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(glm::vec4(1.0f));
if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) { if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) {
uint line = label->getLineByTextIndex(caret); uint line = label->getLineByTextIndex(caret);
uint lcaret = caret - label->getTextLineOffset(line); uint lcaret = caret - label->getTextLineOffset(line);
batch->setColor(glm::vec4(1.0f));
int width = font->calcWidth(input, lcaret); int width = font->calcWidth(input, lcaret);
batch->rect(lcoord.x + width, lcoord.y+label->getLineYOffset(line), 2, lineHeight); batch->rect(
lcoord.x + width,
lcoord.y + label->getLineYOffset(line),
2,
lineHeight
);
} }
if (selectionStart != selectionEnd) { if (selectionStart != selectionEnd) {
auto selectionCtx = subctx.sub(batch);
selectionCtx.setBlendMode(BlendMode::addition);
uint startLine = label->getLineByTextIndex(selectionStart); uint startLine = label->getLineByTextIndex(selectionStart);
uint endLine = label->getLineByTextIndex(selectionEnd); uint endLine = label->getLineByTextIndex(selectionEnd);
batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f)); batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f));
int start = font->calcWidth(input, selectionStart-label->getTextLineOffset(startLine)); int start = font->calcWidth(
int end = font->calcWidth(input, selectionEnd-label->getTextLineOffset(endLine)); input, selectionStart - label->getTextLineOffset(startLine)
);
int end = font->calcWidth(
input, selectionEnd - label->getTextLineOffset(endLine)
);
int lineY = label->getLineYOffset(startLine); int lineY = label->getLineYOffset(startLine);
if (startLine == endLine) { if (startLine == endLine) {
batch->rect(lcoord.x + start, lcoord.y+lineY, end-start, lineHeight); batch->rect(
lcoord.x + start, lcoord.y + lineY, end - start, lineHeight
);
} else { } else {
batch->rect(lcoord.x + start, lcoord.y+lineY, label->getSize().x-start-padding.z-padding.x-2, lineHeight); batch->rect(
lcoord.x + start,
lcoord.y + lineY,
label->getSize().x - start - padding.z - padding.x - 2,
lineHeight
);
for (uint i = startLine + 1; i < endLine; i++) { for (uint i = startLine + 1; i < endLine; i++) {
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(i), label->getSize().x-padding.z-padding.x-2, lineHeight); batch->rect(
lcoord.x,
lcoord.y + label->getLineYOffset(i),
label->getSize().x - padding.z - padding.x - 2,
lineHeight
);
} }
batch->rect(lcoord.x, lcoord.y+label->getLineYOffset(endLine), end, lineHeight); batch->rect(
lcoord.x,
lcoord.y + label->getLineYOffset(endLine),
end,
lineHeight
);
} }
} }
@@ -132,13 +162,13 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) {
} }
} }
void TextBox::drawBackground(const DrawContext* pctx, Assets*) { void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
auto subctx = pctx->sub(); auto subctx = pctx.sub();
subctx.setScissors(glm::vec4(pos.x, pos.y-0.5, size.x, size.y+1)); subctx.setScissors(glm::vec4(pos.x, pos.y-0.5, size.x, size.y+1));
if (valid) { if (valid) {
@@ -162,7 +192,18 @@ void TextBox::drawBackground(const DrawContext* pctx, Assets*) {
void TextBox::refreshLabel() { void TextBox::refreshLabel() {
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f)); label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
label->setText(input.empty() && !hint.empty() ? hint : getText());
const auto& displayText = input.empty() && !hint.empty() ? hint : getText();
if (markup == "md") {
auto [processedText, styles] = markdown::process(displayText, !focused);
label->setText(std::move(processedText));
label->setStyles(std::move(styles));
} else {
label->setText(displayText);
if (syntax.empty()) {
label->setStyles(nullptr);
}
}
if (showLineNumbers) { if (showLineNumbers) {
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) { if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
@@ -202,7 +243,8 @@ void TextBox::refreshLabel() {
if (multiline && font) { if (multiline && font) {
setScrollable(true); setScrollable(true);
uint height = label->getLinesNumber() * font->getLineHeight() * label->getLineInterval(); uint height = label->getLinesNumber() * font->getLineHeight() *
label->getLineInterval();
label->setSize(glm::vec2(label->getSize().x, height)); label->setSize(glm::vec2(label->getSize().x, height));
actualLength = height; actualLength = height;
} else { } else {
@@ -388,7 +430,7 @@ int TextBox::calcIndexAt(int x, int y) const {
return std::min(offset+label->getTextLineOffset(line), input.length()); return std::min(offset+label->getTextLineOffset(line), input.length());
} }
inline std::wstring get_alphabet(wchar_t c) { static inline std::wstring get_alphabet(wchar_t c) {
std::wstring alphabet {c}; std::wstring alphabet {c};
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') { if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') {
return L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -433,7 +475,11 @@ void TextBox::click(GUI*, int x, int y) {
selectionOrigin = index; selectionOrigin = index;
} }
void TextBox::mouseMove(GUI*, int x, int y) { void TextBox::mouseMove(GUI* gui, int x, int y) {
Container::mouseMove(gui, x, y);
if (isScrolling()) {
return;
}
ptrdiff_t index = calcIndexAt(x, y); ptrdiff_t index = calcIndexAt(x, y);
setCaret(index); setCaret(index);
extendSelection(index); extendSelection(index);
@@ -529,10 +575,21 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
} }
} }
void TextBox::refreshSyntax() {
if (!syntax.empty()) {
if (auto styles = devtools::syntax_highlight(
syntax, util::wstr2str_utf8(input)
)) {
label->setStyles(std::move(styles));
}
}
}
void TextBox::onInput() { void TextBox::onInput() {
if (subconsumer) { if (subconsumer) {
subconsumer(input); subconsumer(input);
} }
refreshSyntax();
} }
void TextBox::performEditingKeyboardEvents(keycode key) { void TextBox::performEditingKeyboardEvents(keycode key) {
@@ -632,7 +689,9 @@ size_t TextBox::getLinePos(uint line) const {
return label->getTextLineOffset(line); return label->getTextLineOffset(line);
} }
std::shared_ptr<UINode> TextBox::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> TextBox::getAt(
const glm::vec2& pos, const std::shared_ptr<UINode>& self
) {
return UINode::getAt(pos, self); return UINode::getAt(pos, self);
} }
@@ -710,6 +769,7 @@ const std::wstring& TextBox::getText() const {
void TextBox::setText(const std::wstring& value) { void TextBox::setText(const std::wstring& value) {
this->input = value; this->input = value;
input.erase(std::remove(input.begin(), input.end(), '\r'), input.end()); input.erase(std::remove(input.begin(), input.end(), '\r'), input.end());
refreshSyntax();
} }
const std::wstring& TextBox::getPlaceholder() const { const std::wstring& TextBox::getPlaceholder() const {
@@ -789,3 +849,24 @@ void TextBox::setShowLineNumbers(bool flag) {
bool TextBox::isShowLineNumbers() const { bool TextBox::isShowLineNumbers() const {
return showLineNumbers; return showLineNumbers;
} }
void TextBox::setSyntax(std::string_view lang) {
syntax = lang;
if (syntax.empty()) {
label->setStyles(nullptr);
} else {
refreshSyntax();
}
}
const std::string& TextBox::getSyntax() const {
return syntax;
}
void TextBox::setMarkup(std::string_view lang) {
markup = lang;
}
const std::string& TextBox::getMarkup() const {
return markup;
}
+15 -3
View File
@@ -56,6 +56,8 @@ namespace gui {
bool editable = true; bool editable = true;
bool autoresize = false; bool autoresize = false;
bool showLineNumbers = false; bool showLineNumbers = false;
std::string markup;
std::string syntax;
void stepLeft(bool shiftPressed, bool breakSelection); void stepLeft(bool shiftPressed, bool breakSelection);
void stepRight(bool shiftPressed, bool breakSelection); void stepRight(bool shiftPressed, bool breakSelection);
@@ -84,6 +86,8 @@ namespace gui {
void refreshLabel(); void refreshLabel();
void onInput(); void onInput();
void refreshSyntax();
public: public:
TextBox( TextBox(
std::wstring placeholder, std::wstring placeholder,
@@ -212,12 +216,20 @@ namespace gui {
virtual void click(GUI*, int, int) override; virtual void click(GUI*, int, int) override;
virtual void mouseMove(GUI*, int x, int y) override; virtual void mouseMove(GUI*, int x, int y) override;
virtual bool isFocuskeeper() const override {return true;} virtual bool isFocuskeeper() const override {return true;}
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void drawBackground(const DrawContext* pctx, Assets* assets) override; virtual void drawBackground(const DrawContext& pctx, const Assets& assets) override;
virtual void typed(unsigned int codepoint) override; virtual void typed(unsigned int codepoint) override;
virtual void keyPressed(keycode key) override; virtual void keyPressed(keycode key) override;
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override; virtual std::shared_ptr<UINode> getAt(
const glm::vec2& pos, const std::shared_ptr<UINode>& self
) override;
virtual void setOnUpPressed(const runnable &callback); virtual void setOnUpPressed(const runnable &callback);
virtual void setOnDownPressed(const runnable &callback); virtual void setOnDownPressed(const runnable &callback);
virtual void setSyntax(std::string_view lang);
virtual const std::string& getSyntax() const;
virtual void setMarkup(std::string_view lang);
virtual const std::string& getMarkup() const;
}; };
} }
+2 -2
View File
@@ -25,12 +25,12 @@ TrackBar::TrackBar(
setHoverColor(glm::vec4(0.01f, 0.02f, 0.03f, 0.5f)); setHoverColor(glm::vec4(0.01f, 0.02f, 0.03f, 0.5f));
} }
void TrackBar::draw(const DrawContext* pctx, Assets*) { void TrackBar::draw(const DrawContext& pctx, const Assets&) {
if (supplier) { if (supplier) {
value = supplier(); value = supplier();
} }
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx->getBatch2D(); auto batch = pctx.getBatch2D();
batch->texture(nullptr); batch->texture(nullptr);
batch->setColor(hover ? hoverColor : color); batch->setColor(hover ? hoverColor : color);
batch->rect(pos.x, pos.y, size.x, size.y); batch->rect(pos.x, pos.y, size.x, size.y);
+1 -1
View File
@@ -21,7 +21,7 @@ namespace gui {
double value, double value,
double step=1.0, double step=1.0,
int trackWidth=12); int trackWidth=12);
virtual void draw(const DrawContext* pctx, Assets* assets) override; virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void setSupplier(doublesupplier); virtual void setSupplier(doublesupplier);
virtual void setConsumer(doubleconsumer); virtual void setConsumer(doubleconsumer);
+2 -2
View File
@@ -111,11 +111,11 @@ bool UINode::isInside(glm::vec2 point) {
point.x < pos.x + size.x && point.y < pos.y + size.y); point.x < pos.x + size.x && point.y < pos.y + size.y);
} }
std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) { std::shared_ptr<UINode> UINode::getAt(const glm::vec2& point, const std::shared_ptr<UINode>& self) {
if (!isInteractive() || !enabled) { if (!isInteractive() || !enabled) {
return nullptr; return nullptr;
} }
return isInside(point) ? std::move(self) : nullptr; return isInside(point) ? self : nullptr;
} }
bool UINode::isInteractive() const { bool UINode::isInteractive() const {
+2 -2
View File
@@ -120,7 +120,7 @@ namespace gui {
/// @brief Called every frame for all visible elements /// @brief Called every frame for all visible elements
/// @param delta delta timУ /// @param delta delta timУ
virtual void act(float delta) {}; virtual void act(float delta) {};
virtual void draw(const DrawContext* pctx, Assets* assets) = 0; virtual void draw(const DrawContext& pctx, const Assets& assets) = 0;
virtual void setVisible(bool flag); virtual void setVisible(bool flag);
bool isVisible() const; bool isVisible() const;
@@ -190,7 +190,7 @@ namespace gui {
/// @param pos cursor screen position /// @param pos cursor screen position
/// @param self shared pointer to element /// @param self shared pointer to element
/// @return self, sub-element or nullptr if element is not interractive /// @return self, sub-element or nullptr if element is not interractive
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self); virtual std::shared_ptr<UINode> getAt(const glm::vec2& pos, const std::shared_ptr<UINode>& self);
/// @brief Check if element is opaque for cursor /// @brief Check if element is opaque for cursor
virtual bool isInteractive() const; virtual bool isInteractive() const;
+192 -165
View File
@@ -56,8 +56,8 @@ static runnable create_runnable(
const xml::xmlelement& element, const xml::xmlelement& element,
const std::string& name const std::string& name
) { ) {
if (element->has(name)) { if (element.has(name)) {
std::string text = element->attr(name).getText(); std::string text = element.attr(name).getText();
if (!text.empty()) { if (!text.empty()) {
return scripting::create_runnable( return scripting::create_runnable(
reader.getEnvironment(), text, reader.getFilename() reader.getEnvironment(), text, reader.getFilename()
@@ -83,78 +83,78 @@ static onaction create_action(
static void _readUINode( static void _readUINode(
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
) { ) {
if (element->has("id")) { if (element.has("id")) {
node.setId(element->attr("id").getText()); node.setId(element.attr("id").getText());
} }
if (element->has("pos")) { if (element.has("pos")) {
node.setPos(element->attr("pos").asVec2()); node.setPos(element.attr("pos").asVec2());
} }
if (element->has("size")) { if (element.has("size")) {
node.setSize(element->attr("size").asVec2()); node.setSize(element.attr("size").asVec2());
} }
if (element->has("color")) { if (element.has("color")) {
glm::vec4 color = element->attr("color").asColor(); glm::vec4 color = element.attr("color").asColor();
glm::vec4 hoverColor = color; glm::vec4 hoverColor = color;
glm::vec4 pressedColor = color; glm::vec4 pressedColor = color;
if (element->has("hover-color")) { if (element.has("hover-color")) {
hoverColor = node.getHoverColor(); hoverColor = node.getHoverColor();
} }
if (element->has("pressed-color")) { if (element.has("pressed-color")) {
pressedColor = node.getPressedColor(); pressedColor = node.getPressedColor();
} }
node.setColor(color); node.setColor(color);
node.setHoverColor(hoverColor); node.setHoverColor(hoverColor);
node.setPressedColor(pressedColor); node.setPressedColor(pressedColor);
} }
if (element->has("margin")) { if (element.has("margin")) {
node.setMargin(element->attr("margin").asVec4()); node.setMargin(element.attr("margin").asVec4());
} }
if (element->has("z-index")) { if (element.has("z-index")) {
node.setZIndex(element->attr("z-index").asInt()); node.setZIndex(element.attr("z-index").asInt());
} }
if (element->has("interactive")) { if (element.has("interactive")) {
node.setInteractive(element->attr("interactive").asBool()); node.setInteractive(element.attr("interactive").asBool());
} }
if (element->has("visible")) { if (element.has("visible")) {
node.setVisible(element->attr("visible").asBool()); node.setVisible(element.attr("visible").asBool());
} }
if (element->has("enabled")) { if (element.has("enabled")) {
node.setEnabled(element->attr("enabled").asBool()); node.setEnabled(element.attr("enabled").asBool());
} }
if (element->has("position-func")) { if (element.has("position-func")) {
node.setPositionFunc(scripting::create_vec2_supplier( node.setPositionFunc(scripting::create_vec2_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("position-func").getText(), element.attr("position-func").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("size-func")) { if (element.has("size-func")) {
node.setSizeFunc(scripting::create_vec2_supplier( node.setSizeFunc(scripting::create_vec2_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("size-func").getText(), element.attr("size-func").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("hover-color")) { if (element.has("hover-color")) {
node.setHoverColor(element->attr("hover-color").asColor()); node.setHoverColor(element.attr("hover-color").asColor());
} }
if (element->has("pressed-color")) { if (element.has("pressed-color")) {
node.setPressedColor(element->attr("pressed-color").asColor()); node.setPressedColor(element.attr("pressed-color").asColor());
} }
std::string alignName = element->attr("align", "").getText(); std::string alignName = element.attr("align", "").getText();
node.setAlign(align_from_string(alignName, node.getAlign())); node.setAlign(align_from_string(alignName, node.getAlign()));
if (element->has("gravity")) { if (element.has("gravity")) {
node.setGravity(gravity_from_string( node.setGravity(gravity_from_string(
element->attr("gravity").getText() element.attr("gravity").getText()
)); ));
} }
if (element->has("tooltip")) { if (element.has("tooltip")) {
node.setTooltip(util::str2wstr_utf8(element->attr("tooltip").getText())); node.setTooltip(util::str2wstr_utf8(element.attr("tooltip").getText()));
} }
if (element->has("tooltip-delay")) { if (element.has("tooltip-delay")) {
node.setTooltipDelay(element->attr("tooltip-delay").asFloat()); node.setTooltipDelay(element.attr("tooltip-delay").asFloat());
} }
if (auto onclick = create_action(reader, element, "onclick")) { if (auto onclick = create_action(reader, element, "onclick")) {
@@ -169,16 +169,16 @@ static void _readUINode(
static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) { static void _readContainer(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
_readUINode(reader, element, container); _readUINode(reader, element, container);
if (element->has("scrollable")) { if (element.has("scrollable")) {
container.setScrollable(element->attr("scrollable").asBool()); container.setScrollable(element.attr("scrollable").asBool());
} }
if (element->has("scroll-step")) { if (element.has("scroll-step")) {
container.setScrollStep(element->attr("scroll-step").asInt()); container.setScrollStep(element.attr("scroll-step").asInt());
} }
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
auto subnode = reader.readUINode(sub); auto subnode = reader.readUINode(*sub);
if (subnode) { if (subnode) {
container.add(subnode); container.add(subnode);
} }
@@ -189,15 +189,17 @@ void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element
_readContainer(reader, element, container); _readContainer(reader, element, container);
} }
void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element, UINode& node) { void UiXmlReader::readUINode(
const UiXmlReader& reader, const xml::xmlelement& element, UINode& node
) {
_readUINode(reader, element, node); _readUINode(reader, element, node);
} }
static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) { static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Panel& panel, bool subnodes=true) {
_readUINode(reader, element, panel); _readUINode(reader, element, panel);
if (element->has("padding")) { if (element.has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4(); glm::vec4 padding = element.attr("padding").asVec4();
panel.setPadding(padding); panel.setPadding(padding);
glm::vec2 size = panel.getSize(); glm::vec2 size = panel.getSize();
panel.setSize(glm::vec2( panel.setSize(glm::vec2(
@@ -205,23 +207,23 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
size.y + padding.y + padding.w size.y + padding.y + padding.w
)); ));
} }
if (element->has("size")) { if (element.has("size")) {
panel.setResizing(false); panel.setResizing(false);
} }
if (element->has("max-length")) { if (element.has("max-length")) {
panel.setMaxLength(element->attr("max-length").asInt()); panel.setMaxLength(element.attr("max-length").asInt());
} }
if (element->has("orientation")) { if (element.has("orientation")) {
auto &oname = element->attr("orientation").getText(); auto &oname = element.attr("orientation").getText();
if (oname == "horizontal") { if (oname == "horizontal") {
panel.setOrientation(Orientation::horizontal); panel.setOrientation(Orientation::horizontal);
} }
} }
if (subnodes) { if (subnodes) {
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->isText()) if (sub->isText())
continue; continue;
auto subnode = reader.readUINode(sub); auto subnode = reader.readUINode(*sub);
if (subnode) { if (subnode) {
panel.add(subnode); panel.add(subnode);
} }
@@ -231,8 +233,8 @@ static void _readPanel(UiXmlReader& reader, const xml::xmlelement& element, Pane
static std::wstring readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) { static std::wstring readAndProcessInnerText(const xml::xmlelement& element, const std::string& context) {
std::wstring text = L""; std::wstring text = L"";
if (element->size() == 1) { if (element.size() == 1) {
std::string source = element->sub(0)->attr("#").getText(); std::string source = element.sub(0).attr("#").getText();
util::trim(source); util::trim(source);
text = util::str2wstr_utf8(source); text = util::str2wstr_utf8(source);
if (text[0] == '@') { if (text[0] == '@') {
@@ -246,33 +248,38 @@ static std::wstring readAndProcessInnerText(const xml::xmlelement& element, cons
return text; return text;
} }
static std::shared_ptr<UINode> readLabel(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readLabel(
const UiXmlReader& reader, const xml::xmlelement& element
) {
std::wstring text = readAndProcessInnerText(element, reader.getContext()); std::wstring text = readAndProcessInnerText(element, reader.getContext());
auto label = std::make_shared<Label>(text); auto label = std::make_shared<Label>(text);
_readUINode(reader, element, *label); _readUINode(reader, element, *label);
if (element->has("valign")) { if (element.has("valign")) {
label->setVerticalAlign( label->setVerticalAlign(
align_from_string(element->attr("valign").getText(), label->getVerticalAlign()) align_from_string(element.attr("valign").getText(), label->getVerticalAlign())
); );
} }
if (element->has("supplier")) { if (element.has("supplier")) {
label->textSupplier(scripting::create_wstring_supplier( label->textSupplier(scripting::create_wstring_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("autoresize")) { if (element.has("autoresize")) {
label->setAutoResize(element->attr("autoresize").asBool()); label->setAutoResize(element.attr("autoresize").asBool());
} }
if (element->has("multiline")) { if (element.has("multiline")) {
label->setMultiline(element->attr("multiline").asBool()); label->setMultiline(element.attr("multiline").asBool());
if (!element->has("valign")) { if (!element.has("valign")) {
label->setVerticalAlign(Align::top); label->setVerticalAlign(Align::top);
} }
} }
if (element->has("text-wrap")) { if (element.has("text-wrap")) {
label->setTextWrapping(element->attr("text-wrap").asBool()); label->setTextWrapping(element.attr("text-wrap").asBool());
}
if (element.has("markup")) {
label->setMarkup(element.attr("markup").getText());
} }
return label; return label;
} }
@@ -284,19 +291,19 @@ static std::shared_ptr<UINode> readContainer(UiXmlReader& reader, const xml::xml
} }
static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readPanel(UiXmlReader& reader, const xml::xmlelement& element) {
float interval = element->attr("interval", "2").asFloat(); float interval = element.attr("interval", "2").asFloat();
auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval); auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval);
_readPanel(reader, element, *panel); _readPanel(reader, element, *panel);
return panel; return panel;
} }
static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlelement& element) {
glm::vec4 padding = element->attr("padding", "10").asVec4(); glm::vec4 padding = element.attr("padding", "10").asVec4();
std::shared_ptr<Button> button; std::shared_ptr<Button> button;
auto& elements = element->getElements(); auto& elements = element.getElements();
if (!elements.empty() && elements[0]->getTag() != "#") { if (!elements.empty() && elements[0]->getTag() != "#") {
auto inner = reader.readUINode(element->getElements().at(0)); auto inner = reader.readUINode(*elements.at(0));
if (inner != nullptr) { if (inner != nullptr) {
button = std::make_shared<Button>(inner, padding); button = std::make_shared<Button>(inner, padding);
} else { } else {
@@ -308,30 +315,32 @@ static std::shared_ptr<UINode> readButton(UiXmlReader& reader, const xml::xmlele
button = std::make_shared<Button>(text, padding, nullptr); button = std::make_shared<Button>(text, padding, nullptr);
_readPanel(reader, element, *button, true); _readPanel(reader, element, *button, true);
} }
if (element->has("text-align")) { if (element.has("text-align")) {
button->setTextAlign(align_from_string(element->attr("text-align").getText(), button->getTextAlign())); button->setTextAlign(align_from_string(
element.attr("text-align").getText(), button->getTextAlign()
));
} }
return button; return button;
} }
static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto text = readAndProcessInnerText(element, reader.getContext()); auto text = readAndProcessInnerText(element, reader.getContext());
bool checked = element->attr("checked", "false").asBool(); bool checked = element.attr("checked", "false").asBool();
auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked); auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked);
_readPanel(reader, element, *checkbox); _readPanel(reader, element, *checkbox);
if (element->has("consumer")) { if (element.has("consumer")) {
checkbox->setConsumer(scripting::create_bool_consumer( checkbox->setConsumer(scripting::create_bool_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("consumer").getText(), element.attr("consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
checkbox->setSupplier(scripting::create_bool_supplier( checkbox->setSupplier(scripting::create_bool_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
@@ -339,15 +348,15 @@ static std::shared_ptr<UINode> readCheckBox(UiXmlReader& reader, const xml::xmle
} }
static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto placeholder = util::str2wstr_utf8(element->attr("placeholder", "").getText()); auto placeholder = util::str2wstr_utf8(element.attr("placeholder", "").getText());
auto hint = util::str2wstr_utf8(element->attr("hint", "").getText()); auto hint = util::str2wstr_utf8(element.attr("hint", "").getText());
auto text = readAndProcessInnerText(element, reader.getContext()); auto text = readAndProcessInnerText(element, reader.getContext());
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f)); auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
textbox->setHint(hint); textbox->setHint(hint);
_readContainer(reader, element, *textbox); _readContainer(reader, element, *textbox);
if (element->has("padding")) { if (element.has("padding")) {
glm::vec4 padding = element->attr("padding").asVec4(); glm::vec4 padding = element.attr("padding").asVec4();
textbox->setPadding(padding); textbox->setPadding(padding);
glm::vec2 size = textbox->getSize(); glm::vec2 size = textbox->getSize();
textbox->setSize(glm::vec2( textbox->setSize(glm::vec2(
@@ -357,55 +366,61 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
} }
textbox->setText(text); textbox->setText(text);
if (element->has("multiline")) { if (element.has("syntax")) {
textbox->setMultiline(element->attr("multiline").asBool()); textbox->setSyntax(element.attr("syntax").getText());
} }
if (element->has("text-wrap")) { if (element.has("multiline")) {
textbox->setTextWrapping(element->attr("text-wrap").asBool()); textbox->setMultiline(element.attr("multiline").asBool());
} }
if (element->has("editable")) { if (element.has("text-wrap")) {
textbox->setEditable(element->attr("editable").asBool()); textbox->setTextWrapping(element.attr("text-wrap").asBool());
} }
if (element->has("autoresize")) { if (element.has("editable")) {
textbox->setAutoResize(element->attr("autoresize").asBool()); textbox->setEditable(element.attr("editable").asBool());
} }
if (element->has("line-numbers")) { if (element.has("autoresize")) {
textbox->setShowLineNumbers(element->attr("line-numbers").asBool()); textbox->setAutoResize(element.attr("autoresize").asBool());
} }
if (element->has("consumer")) { if (element.has("line-numbers")) {
textbox->setShowLineNumbers(element.attr("line-numbers").asBool());
}
if (element.has("markup")) {
textbox->setMarkup(element.attr("markup").getText());
}
if (element.has("consumer")) {
textbox->setTextConsumer(scripting::create_wstring_consumer( textbox->setTextConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("consumer").getText(), element.attr("consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("sub-consumer")) { if (element.has("sub-consumer")) {
textbox->setTextSubConsumer(scripting::create_wstring_consumer( textbox->setTextSubConsumer(scripting::create_wstring_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("sub-consumer").getText(), element.attr("sub-consumer").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
textbox->setTextSupplier(scripting::create_wstring_supplier( textbox->setTextSupplier(scripting::create_wstring_supplier(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("supplier").getText(), element.attr("supplier").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
if (element->has("focused-color")) { if (element.has("focused-color")) {
textbox->setFocusedColor(element->attr("focused-color").asColor()); textbox->setFocusedColor(element.attr("focused-color").asColor());
} }
if (element->has("error-color")) { if (element.has("error-color")) {
textbox->setErrorColor(element->attr("error-color").asColor()); textbox->setErrorColor(element.attr("error-color").asColor());
} }
if (element->has("text-color")) { if (element.has("text-color")) {
textbox->setTextColor(element->attr("text-color").asColor()); textbox->setTextColor(element.attr("text-color").asColor());
} }
if (element->has("validator")) { if (element.has("validator")) {
textbox->setTextValidator(scripting::create_wstring_validator( textbox->setTextValidator(scripting::create_wstring_validator(
reader.getEnvironment(), reader.getEnvironment(),
element->attr("validator").getText(), element.attr("validator").getText(),
reader.getFilename() reader.getFilename()
)); ));
} }
@@ -418,61 +433,70 @@ static std::shared_ptr<UINode> readTextBox(UiXmlReader& reader, const xml::xmlel
return textbox; return textbox;
} }
static std::shared_ptr<UINode> readImage(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readImage(
std::string src = element->attr("src", "").getText(); const UiXmlReader& reader, const xml::xmlelement& element
) {
std::string src = element.attr("src", "").getText();
auto image = std::make_shared<Image>(src); auto image = std::make_shared<Image>(src);
_readUINode(reader, element, *image); _readUINode(reader, element, *image);
return image; return image;
} }
static std::shared_ptr<UINode> readTrackBar(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readTrackBar(
const UiXmlReader& reader, const xml::xmlelement& element
) {
const auto& env = reader.getEnvironment(); const auto& env = reader.getEnvironment();
const auto& file = reader.getFilename(); const auto& file = reader.getFilename();
float minv = element->attr("min", "0.0").asFloat(); float minv = element.attr("min", "0.0").asFloat();
float maxv = element->attr("max", "1.0").asFloat(); float maxv = element.attr("max", "1.0").asFloat();
float def = element->attr("value", "0.0").asFloat(); float def = element.attr("value", "0.0").asFloat();
float step = element->attr("step", "1.0").asFloat(); float step = element.attr("step", "1.0").asFloat();
int trackWidth = element->attr("track-width", "12").asInt(); int trackWidth = element.attr("track-width", "12").asInt();
auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth); auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth);
_readUINode(reader, element, *bar); _readUINode(reader, element, *bar);
if (element->has("consumer")) { if (element.has("consumer")) {
bar->setConsumer(scripting::create_number_consumer( bar->setConsumer(scripting::create_number_consumer(
env, element->attr("consumer").getText(), file)); env, element.attr("consumer").getText(), file));
} }
if (element->has("sub-consumer")) { if (element.has("sub-consumer")) {
bar->setSubConsumer(scripting::create_number_consumer( bar->setSubConsumer(scripting::create_number_consumer(
env, element->attr("sub-consumer").getText(), file)); env, element.attr("sub-consumer").getText(), file));
} }
if (element->has("supplier")) { if (element.has("supplier")) {
bar->setSupplier(scripting::create_number_supplier( bar->setSupplier(scripting::create_number_supplier(
env, element->attr("supplier").getText(), file)); env, element.attr("supplier").getText(), file));
} }
if (element->has("track-color")) { if (element.has("track-color")) {
bar->setTrackColor(element->attr("track-color").asColor()); bar->setTrackColor(element.attr("track-color").asColor());
} }
if (element->has("change-on-release")) { if (element.has("change-on-release")) {
bar->setChangeOnRelease(element->attr("change-on-release").asBool()); bar->setChangeOnRelease(element.attr("change-on-release").asBool());
} }
return bar; return bar;
} }
static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) { static std::shared_ptr<UINode> readInputBindBox(UiXmlReader& reader, const xml::xmlelement& element) {
auto bindname = element->attr("binding").getText(); auto bindname = element.attr("binding").getText();
auto found = Events::bindings.find(bindname); auto found = Events::bindings.find(bindname);
if (found == Events::bindings.end()) { if (found == Events::bindings.end()) {
throw std::runtime_error("binding does not exists "+util::quote(bindname)); throw std::runtime_error("binding does not exists "+util::quote(bindname));
} }
glm::vec4 padding = element->attr("padding", "6").asVec4(); glm::vec4 padding = element.attr("padding", "6").asVec4();
auto bindbox = std::make_shared<InputBindBox>(found->second, padding); auto bindbox = std::make_shared<InputBindBox>(found->second, padding);
_readPanel(reader, element, *bindbox); _readPanel(reader, element, *bindbox);
return bindbox; return bindbox;
} }
static slotcallback readSlotFunc(InventoryView* view, UiXmlReader& reader, xml::xmlelement& element, const std::string& attr) { static slotcallback readSlotFunc(
InventoryView* view,
const UiXmlReader& reader,
const xml::xmlelement& element,
const std::string& attr
) {
auto consumer = scripting::create_int_array_consumer( auto consumer = scripting::create_int_array_consumer(
reader.getEnvironment(), reader.getEnvironment(),
element->attr(attr).getText() element.attr(attr).getText()
); );
return [=](uint slot, ItemStack&) { return [=](uint slot, ItemStack&) {
int args[] {int(view->getInventory()->getId()), int(slot)}; int args[] {int(view->getInventory()->getId()), int(slot)};
@@ -480,22 +504,24 @@ static slotcallback readSlotFunc(InventoryView* view, UiXmlReader& reader, xml::
}; };
} }
static void readSlot(InventoryView* view, UiXmlReader& reader, xml::xmlelement element) { static void readSlot(
int index = element->attr("index", "0").asInt(); InventoryView* view, UiXmlReader& reader, const xml::xmlelement& element
bool itemSource = element->attr("item-source", "false").asBool(); ) {
bool taking = element->attr("taking", "true").asBool(); int index = element.attr("index", "0").asInt();
bool placing = element->attr("placing", "true").asBool(); bool itemSource = element.attr("item-source", "false").asBool();
bool taking = element.attr("taking", "true").asBool();
bool placing = element.attr("placing", "true").asBool();
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr); SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
if (element->has("pos")) { if (element.has("pos")) {
layout.position = element->attr("pos").asVec2(); layout.position = element.attr("pos").asVec2();
} }
if (element->has("updatefunc")) { if (element.has("updatefunc")) {
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc"); layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
} }
if (element->has("sharefunc")) { if (element.has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc"); layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
} }
if (element->has("onrightclick")) { if (element.has("onrightclick")) {
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick"); layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
} }
layout.taking = taking; layout.taking = taking;
@@ -505,19 +531,21 @@ static void readSlot(InventoryView* view, UiXmlReader& reader, xml::xmlelement e
view->add(slot); view->add(slot);
} }
static void readSlotsGrid(InventoryView* view, UiXmlReader& reader, xml::xmlelement element) { static void readSlotsGrid(
int startIndex = element->attr("start-index", "0").asInt(); InventoryView* view, const UiXmlReader& reader, const xml::xmlelement& element
int rows = element->attr("rows", "0").asInt(); ) {
int cols = element->attr("cols", "0").asInt(); int startIndex = element.attr("start-index", "0").asInt();
int count = element->attr("count", "0").asInt(); int rows = element.attr("rows", "0").asInt();
int cols = element.attr("cols", "0").asInt();
int count = element.attr("count", "0").asInt();
const int slotSize = InventoryView::SLOT_SIZE; const int slotSize = InventoryView::SLOT_SIZE;
bool taking = element->attr("taking", "true").asBool(); bool taking = element.attr("taking", "true").asBool();
bool placing = element->attr("placing", "true").asBool(); bool placing = element.attr("placing", "true").asBool();
int interval = element->attr("interval", "-1").asInt(); int interval = element.attr("interval", "-1").asInt();
if (interval < 0) { if (interval < 0) {
interval = InventoryView::SLOT_INTERVAL; interval = InventoryView::SLOT_INTERVAL;
} }
int padding = element->attr("padding", "-1").asInt(); int padding = element.attr("padding", "-1").asInt();
if (padding < 0) { if (padding < 0) {
padding = interval; padding = interval;
} }
@@ -528,18 +556,18 @@ static void readSlotsGrid(InventoryView* view, UiXmlReader& reader, xml::xmlelem
} else if (count == 0) { } else if (count == 0) {
count = rows * cols; count = rows * cols;
} }
bool itemSource = element->attr("item-source", "false").asBool(); bool itemSource = element.attr("item-source", "false").asBool();
SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr); SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
if (element->has("pos")) { if (element.has("pos")) {
layout.position = element->attr("pos").asVec2(); layout.position = element.attr("pos").asVec2();
} }
if (element->has("updatefunc")) { if (element.has("updatefunc")) {
layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc"); layout.updateFunc = readSlotFunc(view, reader, element, "updatefunc");
} }
if (element->has("sharefunc")) { if (element.has("sharefunc")) {
layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc"); layout.shareFunc = readSlotFunc(view, reader, element, "sharefunc");
} }
if (element->has("onrightclick")) { if (element.has("onrightclick")) {
layout.rightClick = readSlotFunc(view, reader, element, "onrightclick"); layout.rightClick = readSlotFunc(view, reader, element, "onrightclick");
} }
layout.padding = padding; layout.padding = padding;
@@ -571,11 +599,11 @@ static std::shared_ptr<UINode> readInventory(UiXmlReader& reader, const xml::xml
reader.addIgnore("slots-grid"); reader.addIgnore("slots-grid");
reader.readUINode(reader, element, *view); reader.readUINode(reader, element, *view);
for (auto& sub : element->getElements()) { for (auto& sub : element.getElements()) {
if (sub->getTag() == "slot") { if (sub->getTag() == "slot") {
readSlot(view.get(), reader, sub); readSlot(view.get(), reader, *sub);
} else if (sub->getTag() == "slots-grid") { } else if (sub->getTag() == "slots-grid") {
readSlotsGrid(view.get(), reader, sub); readSlotsGrid(view.get(), reader, *sub);
} }
} }
return view; return view;
@@ -618,18 +646,18 @@ void UiXmlReader::addIgnore(const std::string& tag) {
} }
std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) { std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) {
if (element->has("if")) { if (element.has("if")) {
const auto& cond = element->attr("if").getText(); const auto& cond = element.attr("if").getText();
if (cond.empty() || cond == "false" || cond == "nil") if (cond.empty() || cond == "false" || cond == "nil")
return nullptr; return nullptr;
} }
if (element->has("ifnot")) { if (element.has("ifnot")) {
const auto& cond = element->attr("ifnot").getText(); const auto& cond = element.attr("ifnot").getText();
if (!(cond.empty() || cond == "false" || cond == "nil")) if (!(cond.empty() || cond == "false" || cond == "nil"))
return nullptr; return nullptr;
} }
const std::string& tag = element->getTag(); const std::string& tag = element.getTag();
auto found = readers.find(tag); auto found = readers.find(tag);
if (found == readers.end()) { if (found == readers.end()) {
if (ignored.find(tag) != ignored.end()) { if (ignored.find(tag) != ignored.end()) {
@@ -638,9 +666,9 @@ std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element)
throw std::runtime_error("unsupported element '"+tag+"'"); throw std::runtime_error("unsupported element '"+tag+"'");
} }
bool hascontext = element->has("context"); bool hascontext = element.has("context");
if (hascontext) { if (hascontext) {
contextStack.push(element->attr("context").getText()); contextStack.push(element.attr("context").getText());
} }
auto node = found->second(*this, element); auto node = found->second(*this, element);
if (hascontext) { if (hascontext) {
@@ -655,8 +683,7 @@ std::shared_ptr<UINode> UiXmlReader::readXML(
) { ) {
this->filename = filename; this->filename = filename;
auto document = xml::parse(filename, source); auto document = xml::parse(filename, source);
auto root = document->getRoot(); return readUINode(*document->getRoot());
return readUINode(root);
} }
std::shared_ptr<UINode> UiXmlReader::readXML( std::shared_ptr<UINode> UiXmlReader::readXML(
+3 -2
View File
@@ -11,7 +11,8 @@
namespace gui { namespace gui {
class UiXmlReader; class UiXmlReader;
using uinode_reader = std::function<std::shared_ptr<UINode>(UiXmlReader&, xml::xmlelement)>; using uinode_reader = std::function<
std::shared_ptr<UINode>(UiXmlReader&, const xml::xmlelement&)>;
class UiXmlReader { class UiXmlReader {
std::unordered_map<std::string, uinode_reader> readers; std::unordered_map<std::string, uinode_reader> readers;
@@ -29,7 +30,7 @@ namespace gui {
std::shared_ptr<UINode> readUINode(const xml::xmlelement& element); std::shared_ptr<UINode> readUINode(const xml::xmlelement& element);
void readUINode( void readUINode(
UiXmlReader& reader, const UiXmlReader& reader,
const xml::xmlelement& element, const xml::xmlelement& element,
UINode& node UINode& node
); );
+111
View File
@@ -0,0 +1,111 @@
#include "markdown.hpp"
#include "graphics/core/Font.hpp"
using namespace markdown;
template <typename CharT>
static inline void emit(
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
) {
ss << c;
styles.map.emplace_back(styles.palette.size()-1);
}
template <typename CharT>
static inline void emit_md(
CharT c, FontStylesScheme& styles, std::basic_stringstream<CharT>& ss
) {
ss << c;
styles.map.emplace_back(0);
}
template <typename CharT>
static inline void restyle(
CharT c,
FontStyle& style,
FontStylesScheme& styles,
std::basic_stringstream<CharT>& ss,
int& pos,
bool eraseMarkdown
) {
styles.palette.push_back(style);
if (!eraseMarkdown) {
emit_md(c, styles, ss);
}
pos++;
}
template <typename CharT>
Result<CharT> process_markdown(
std::basic_string_view<CharT> source, bool eraseMarkdown
) {
std::basic_stringstream<CharT> ss;
FontStylesScheme styles {
// markdown default
{{false, false, false, false, glm::vec4(1,1,1,0.5f)}, {}},
{}
};
FontStyle style;
int pos = 0;
while (pos < source.size()) {
CharT first = source[pos];
if (first == '\\') {
if (pos + 1 < source.size()) {
CharT second = source[++pos];
switch (second) {
case '*':
case '_':
case '~':
if (!eraseMarkdown) {
emit_md(first, styles, ss);
}
emit(second, styles, ss);
pos++;
continue;
}
pos--;
}
} else if (first == '*') {
if (pos + 1 < source.size() && source[pos+1] == '*') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.bold = !style.bold;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
}
style.italic = !style.italic;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
} else if (first == '_' && pos + 1 < source.size() && source[pos+1] == '_') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.underline = !style.underline;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
} else if (first == '~' && pos + 1 < source.size() && source[pos+1] == '~') {
pos++;
if (!eraseMarkdown)
emit_md(first, styles, ss);
style.strikethrough = !style.strikethrough;
restyle(first, style, styles, ss, pos, eraseMarkdown);
continue;
}
if (first == '\n') {
styles.palette.push_back(styles.palette.at(1));
}
emit(first, styles, ss);
pos++;
}
return {ss.str(), std::make_unique<FontStylesScheme>(std::move(styles))};
}
Result<char> markdown::process(std::string_view source, bool eraseMarkdown) {
return process_markdown(source, eraseMarkdown);
}
Result<wchar_t> markdown::process(std::wstring_view source, bool eraseMarkdown) {
return process_markdown(source, eraseMarkdown);
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <string>
#include <memory>
#include <sstream>
struct FontStylesScheme;
// VoxelCore Markdown dialect
namespace markdown {
template <typename CharT>
struct Result {
/// @brief Text with erased markdown
std::basic_string<CharT> text;
/// @brief Text styles scheme
std::unique_ptr<FontStylesScheme> styles;
};
Result<char> process(std::string_view source, bool eraseMarkdown);
Result<wchar_t> process(std::wstring_view source, bool eraseMarkdown);
template <typename CharT>
inline std::basic_string<CharT> escape(std::string_view source) {
std::basic_stringstream<CharT> ss;
int pos = 0;
while (pos < source.size()) {
CharT first = source[pos];
if (first == '\\' && pos + 1 < source.size()) {
CharT second = source[++pos];
ss << first << second;
pos++;
continue;
} else if (first == '*' || first == '~' || first == '_') {
ss << '\\';
}
ss << first;
pos++;
}
return ss.str();
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
Inventory::Inventory(int64_t id, size_t size) : id(id), slots(size) { Inventory::Inventory(int64_t id, size_t size) : id(id), slots(size) {
} }
Inventory::Inventory(const Inventory& orig) { Inventory::Inventory(const Inventory& orig) : id(0) {
this->slots = orig.slots; this->slots = orig.slots;
} }
@@ -31,9 +31,16 @@ static int l_add_command(lua::State* L) {
static int l_execute(lua::State* L) { static int l_execute(lua::State* L) {
auto prompt = lua::require_string(L, 1); auto prompt = lua::require_string(L, 1);
try {
auto result = engine->getCommandsInterpreter()->execute(prompt); auto result = engine->getCommandsInterpreter()->execute(prompt);
lua::pushvalue(L, result); lua::pushvalue(L, result);
return 1; return 1;
} catch (const parsing_error& err) {
if (std::string(err.what()).find("unknown command ") == 0) {
throw;
}
throw std::runtime_error(err.errorLog());
}
} }
static int l_set(lua::State* L) { static int l_set(lua::State* L) {
+57 -1
View File
@@ -12,6 +12,8 @@
#include "graphics/ui/elements/TrackBar.hpp" #include "graphics/ui/elements/TrackBar.hpp"
#include "graphics/ui/elements/UINode.hpp" #include "graphics/ui/elements/UINode.hpp"
#include "graphics/ui/gui_util.hpp" #include "graphics/ui/gui_util.hpp"
#include "graphics/ui/markdown.hpp"
#include "graphics/core/Font.hpp"
#include "items/Inventories.hpp" #include "items/Inventories.hpp"
#include "util/stringutil.hpp" #include "util/stringutil.hpp"
#include "world/Level.hpp" #include "world/Level.hpp"
@@ -299,6 +301,22 @@ static int p_get_line_numbers(UINode* node, lua::State* L) {
return 0; return 0;
} }
static int p_get_syntax(UINode* node, lua::State* L) {
if (auto box = dynamic_cast<TextBox*>(node)) {
return lua::pushstring(L, box->getSyntax());
}
return 0;
}
static int p_get_markup(UINode* node, lua::State* L) {
if (auto box = dynamic_cast<TextBox*>(node)) {
return lua::pushstring(L, box->getMarkup());
} else if (auto label = dynamic_cast<Label*>(node)) {
return lua::pushstring(L, label->getMarkup());
}
return 0;
}
static int p_get_src(UINode* node, lua::State* L) { static int p_get_src(UINode* node, lua::State* L) {
if (auto image = dynamic_cast<Image*>(node)) { if (auto image = dynamic_cast<Image*>(node)) {
return lua::pushstring(L, image->getTexture()); return lua::pushstring(L, image->getTexture());
@@ -420,6 +438,8 @@ static int l_gui_getattr(lua::State* L) {
{"lineNumbers", p_get_line_numbers}, {"lineNumbers", p_get_line_numbers},
{"lineAt", p_get_line_at}, {"lineAt", p_get_line_at},
{"linePos", p_get_line_pos}, {"linePos", p_get_line_pos},
{"syntax", p_get_syntax},
{"markup", p_get_markup},
{"src", p_get_src}, {"src", p_get_src},
{"value", p_get_value}, {"value", p_get_value},
{"min", p_get_min}, {"min", p_get_min},
@@ -512,6 +532,18 @@ static void p_set_line_numbers(UINode* node, lua::State* L, int idx) {
box->setShowLineNumbers(lua::toboolean(L, idx)); box->setShowLineNumbers(lua::toboolean(L, idx));
} }
} }
static void p_set_syntax(UINode* node, lua::State* L, int idx) {
if (auto box = dynamic_cast<TextBox*>(node)) {
box->setSyntax(lua::require_string(L, idx));
}
}
static void p_set_markup(UINode* node, lua::State* L, int idx) {
if (auto box = dynamic_cast<TextBox*>(node)) {
box->setMarkup(lua::require_string(L, idx));
} else if (auto label = dynamic_cast<Label*>(node)) {
label->setMarkup(lua::require_string(L, idx));
}
}
static void p_set_src(UINode* node, lua::State* L, int idx) { static void p_set_src(UINode* node, lua::State* L, int idx) {
if (auto image = dynamic_cast<Image*>(node)) { if (auto image = dynamic_cast<Image*>(node)) {
image->setTexture(lua::require_string(L, idx)); image->setTexture(lua::require_string(L, idx));
@@ -612,6 +644,8 @@ static int l_gui_setattr(lua::State* L) {
{"text", p_set_text}, {"text", p_set_text},
{"editable", p_set_editable}, {"editable", p_set_editable},
{"lineNumbers", p_set_line_numbers}, {"lineNumbers", p_set_line_numbers},
{"syntax", p_set_syntax},
{"markup", p_set_markup},
{"src", p_set_src}, {"src", p_set_src},
{"caret", p_set_caret}, {"caret", p_set_caret},
{"value", p_set_value}, {"value", p_set_value},
@@ -694,6 +728,25 @@ static int l_gui_getviewport(lua::State* L) {
return lua::pushvec2(L, engine->getGUI()->getContainer()->getSize()); return lua::pushvec2(L, engine->getGUI()->getContainer()->getSize());
} }
static int l_gui_clear_markup(lua::State* L) {
auto lang = lua::require_string(L, 1);
std::string text = lua::require_string(L, 2);
if (std::strcmp(lang, "md") == 0) {
auto [processed, _] = markdown::process(text, true);
text = std::move(processed);
}
return lua::pushstring(L, text);
}
static int l_gui_escape_markup(lua::State* L) {
auto lang = lua::require_string(L, 1);
std::string text = lua::require_string(L, 2);
if (std::strcmp(lang, "md") == 0) {
text = std::move(markdown::escape<char>(text));
}
return lua::pushstring(L, text);
}
const luaL_Reg guilib[] = { const luaL_Reg guilib[] = {
{"get_viewport", lua::wrap<l_gui_getviewport>}, {"get_viewport", lua::wrap<l_gui_getviewport>},
{"getattr", lua::wrap<l_gui_getattr>}, {"getattr", lua::wrap<l_gui_getattr>},
@@ -701,5 +754,8 @@ const luaL_Reg guilib[] = {
{"get_env", lua::wrap<l_gui_get_env>}, {"get_env", lua::wrap<l_gui_get_env>},
{"str", lua::wrap<l_gui_str>}, {"str", lua::wrap<l_gui_str>},
{"get_locales_info", lua::wrap<l_gui_get_locales_info>}, {"get_locales_info", lua::wrap<l_gui_get_locales_info>},
{"clear_markup", lua::wrap<l_gui_clear_markup>},
{"escape_markup", lua::wrap<l_gui_escape_markup>},
{"__reindex", lua::wrap<l_gui_reindex>}, {"__reindex", lua::wrap<l_gui_reindex>},
{NULL, NULL}}; {NULL, NULL}
};
+1 -1
View File
@@ -53,7 +53,7 @@ struct ParticlesPreset : public Serializable {
/// @brief Size of random sub-uv region /// @brief Size of random sub-uv region
float randomSubUV = 1.0f; float randomSubUV = 1.0f;
/// @brief Animation frames /// @brief Animation frames
std::vector<std::string> frames {}; std::vector<std::string> frames;
dv::value serialize() const override; dv::value serialize() const override;
void deserialize(const dv::value& src) override; void deserialize(const dv::value& src) override;
+20
View File
@@ -0,0 +1,20 @@
#include <gtest/gtest.h>
#include "coders/commons.hpp"
#include "coders/lua_parsing.hpp"
#include "files/files.hpp"
#include "util/stringutil.hpp"
TEST(lua_parsing, Tokenizer) {
auto filename = "../../res/scripts/stdlib.lua";
auto source = files::read_string(std::filesystem::u8path(filename));
try {
auto tokens = lua::tokenize(filename, source);
for (const auto& token : tokens) {
std::cout << (int)token.tag << " " << util::quote(token.text) << std::endl;
}
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw err;
}
}