From 28f49ac948371f99d8786f6c48ba75941323010d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Dec 2024 16:26:55 +0300 Subject: [PATCH 01/22] add Lua code tokenizer --- src/coders/commons.cpp | 18 ++++ src/coders/commons.hpp | 2 + src/coders/lua_parsing.cpp | 181 ++++++++++++++++++++++++++++++++++++ src/coders/lua_parsing.hpp | 35 +++++++ test/coders/lua_parsing.cpp | 20 ++++ 5 files changed, 256 insertions(+) create mode 100644 src/coders/lua_parsing.cpp create mode 100644 src/coders/lua_parsing.hpp create mode 100644 test/coders/lua_parsing.cpp diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index de4fb755..74fb87f5 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -214,6 +214,24 @@ std::string_view BasicParser::readUntil(char c) { return source.substr(start, pos - start); } +std::string_view BasicParser::readUntil(std::string_view s) { + int start = pos; + size_t found = source.find(s, pos); + if (found == std::string::npos) { + 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() { int start = pos; while (hasNext() && source[pos] != '\r' && source[pos] != '\n') { diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp index abd4c01a..c01601cb 100644 --- a/src/coders/commons.hpp +++ b/src/coders/commons.hpp @@ -105,6 +105,8 @@ protected: parsing_error error(const std::string& message); public: std::string_view readUntil(char c); + std::string_view readUntil(std::string_view s); + std::string_view readUntilWhitespace(); std::string_view readUntilEOL(); std::string parseName(); std::string parseXmlName(); diff --git a/src/coders/lua_parsing.cpp b/src/coders/lua_parsing.cpp new file mode 100644 index 00000000..f3ca21ef --- /dev/null +++ b/src/coders/lua_parsing.cpp @@ -0,0 +1,181 @@ +#include "lua_parsing.hpp" + +#include + +#include "commons.hpp" + +using namespace lua; + +static std::set keywords { + "and", "break", "do", "else", "elseif", "end", "false", "for", "function", + "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", + "until", "while" +}; + +bool lua::is_lua_keyword(std::string_view view) { + return keywords.find(view) != keywords.end(); +} + +inline bool is_lua_identifier_start(int c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'; +} + +inline bool is_lua_identifier_part(int c) { + return is_lua_identifier_start(c) || is_digit(c); +} + +inline bool is_lua_operator_start(int c) { + return c == '=' || c == '~' || c == '+' || c == '-' || c == '/' || c == '*' + || c == '%' || c == '^' || c == '#' || c == '<' || c == '>' || c == ':' + || c == '.'; +} + +class Tokenizer : BasicParser { + std::vector tokens; +public: + Tokenizer(std::string_view file, std::string_view source) + : BasicParser(file, source) { + } + + std::string parseLuaName() { + char c = peek(); + if (!is_identifier_start(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_identifier_part(source[pos])) { + pos++; + } + return std::string(source.substr(start, pos - start)); + } + + inline Location currentLocation() const { + return Location { + static_cast(pos), + static_cast(linestart), + static_cast(line)}; + } + + void emitToken( + TokenTag tag, std::string name, Location start, bool standalone=false + ) { + tokens.emplace_back( + tag, + std::move(name), + std::move(start), + currentLocation() + ); + if (standalone) skip(1); + } + + /// @brief Get next operator token without checking operator for existing + std::string parseOperator() { + int start = pos; + char first = peek(); + switch (first) { + case '#': case '+': case '/': case '*': case '^': + case '%': + skip(1); + return std::string({first}); + case '-': + skip(1); + if (peekNoJump() == '-') { + skip(1); + return "--"; + } + return std::string({first}); + } + skip(1); + char second = peekNoJump(); + if ((first == '=' && second == '=') || (first == '~' && second == '=') || + (first == '<' && second == '=') || (first == '>' && second == '=')) { + skip(1); + return std::string(source.substr(start, pos - start)); + } + if (first == '.' && second == '.') { + skip(1); + if (peekNoJump() == '.') { + skip(1); + } + } + return std::string(source.substr(start, pos - start)); + } + + std::vector tokenize() { + skipWhitespace(); + while (hasNext()) { + skipWhitespace(); + if (!hasNext()) { + continue; + } + char c = peek(); + auto start = currentLocation(); + if (is_lua_identifier_start(c)) { + auto name = parseLuaName(); + emitToken( + is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME, + std::move(name), + start + ); + continue; + } else if (is_digit(c)) { + auto value = parseNumber(1); + auto literal = source.substr(start.pos, pos - start.pos); + emitToken( + value.isInteger() ? TokenTag::INTEGER : TokenTag::NUMBER, + std::string(literal), + start + ); + continue; + } + switch (c) { + case '(': case '[': case '{': + if (isNext("[==[")) { + readUntil("]==]"); + skip(4); + continue; + } else if (isNext("[[")) { + skip(2); + auto string = readUntil("]]"); + 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); + emitToken(TokenTag::STRING, std::move(string), start); + continue; + } + default: break; + } + if (is_lua_operator_start(c)) { + auto text = parseOperator(); + if (text == "--") { + skipLine(); + continue; + } + emitToken(TokenTag::OPERATOR, std::move(text), start); + continue; + } + auto text = readUntilWhitespace(); + emitToken(TokenTag::UNEXPECTED, std::string(text), start); + } + return std::move(tokens); + } +}; + +std::vector lua::tokenize(std::string_view file, std::string_view source) { + return Tokenizer(file, source).tokenize(); +} diff --git a/src/coders/lua_parsing.hpp b/src/coders/lua_parsing.hpp new file mode 100644 index 00000000..68cc385a --- /dev/null +++ b/src/coders/lua_parsing.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace lua { + struct Location { + int pos; + int lineStart; + int line; + }; + + enum class TokenTag { + KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING, + OPERATOR, COMMA, SEMICOLON, UNEXPECTED + }; + + 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)) { + } + }; + + bool is_lua_keyword(std::string_view view); + + std::vector tokenize(std::string_view file, std::string_view source); +} diff --git a/test/coders/lua_parsing.cpp b/test/coders/lua_parsing.cpp new file mode 100644 index 00000000..9788d1d7 --- /dev/null +++ b/test/coders/lua_parsing.cpp @@ -0,0 +1,20 @@ +#include + +#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; + } +} From b15725913e4370d878b3bfde6b4c086e40c3697e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Dec 2024 17:08:04 +0300 Subject: [PATCH 02/22] add FontStyle 'bold' and 'italic' properties to 2D text --- src/graphics/core/Batch2D.cpp | 34 +++++++++++++++++++++++ src/graphics/core/Batch2D.hpp | 7 +++++ src/graphics/core/Font.cpp | 52 +++++++++++++++++++++-------------- src/graphics/core/Font.hpp | 8 +++--- 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp index c4c67db2..b7ccf5ff 100644 --- a/src/graphics/core/Batch2D.cpp +++ b/src/graphics/core/Batch2D.cpp @@ -261,6 +261,24 @@ void Batch2D::rect( vertex(x+w, y+h, u+tx, v, r,g,b,a); } +void Batch2D::parallelogram( + float x, float y, float w, float h, float skew, + float u, float v, float tx, float ty, + float r, float g, float b, float a +){ + if (index + 6*B2D_VERTEX_SIZE >= capacity) { + flush(); + } + setPrimitive(DrawPrimitive::triangle); + vertex(x-skew*w, y, u, v+ty, r,g,b,a); + vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a); + vertex(x+skew*w, y+h, u, v, r,g,b,a); + + vertex(x-skew*w, y, u, v+ty, r,g,b,a); + vertex(x+w-skew*w, y, u+tx, v+ty, r,g,b,a); + vertex(x+(1+skew)*w, y+h, u+tx, v, r,g,b,a); +} + void Batch2D::rect( float x, float y, float w, float h, float r0, float g0, float b0, @@ -336,6 +354,22 @@ void Batch2D::sprite(float x, float y, float w, float h, int atlasRes, int index rect(x, y, w, h, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a); } +void Batch2D::sprite( + float x, + float y, + float w, + float h, + float skew, + int atlasRes, + int index, + glm::vec4 tint +) { + float scale = 1.0f / (float)atlasRes; + float u = (index % atlasRes) * scale; + float v = 1.0f - ((index / atlasRes) * scale) - scale; + parallelogram(x, y, w, h, skew, u, v, scale, scale, tint.r, tint.g, tint.b, tint.a); +} + void Batch2D::flush() { if (index == 0) return; diff --git a/src/graphics/core/Batch2D.hpp b/src/graphics/core/Batch2D.hpp index 2877f5bd..6bfdb14d 100644 --- a/src/graphics/core/Batch2D.hpp +++ b/src/graphics/core/Batch2D.hpp @@ -45,6 +45,7 @@ public: void setRegion(UVRegion region); void sprite(float x, float y, float w, float h, const UVRegion& region, glm::vec4 tint); void sprite(float x, float y, float w, float h, int atlasRes, int index, glm::vec4 tint); + void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint); void point(float x, float y, float r, float g, float b, float a); inline void setColor(glm::vec4 color) { @@ -79,6 +80,12 @@ public: float r, float g, float b, float a ); + void parallelogram( + float x, float y, float w, float h, float skew, + float u, float v, float tx, float ty, + float r, float g, float b, float a + ); + void rect( float x, float y, float w, float h, float r0, float g0, float b0, diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index 9c40809f..c148ae72 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -52,17 +52,21 @@ static inline void draw_glyph( uint c, const glm::vec3& right, const glm::vec3& up, - float glyphInterval + float glyphInterval, + const FontStyle& style ) { - batch.sprite( - pos.x + offset.x * right.x, - pos.y + offset.y * right.y, - right.x / glyphInterval, - up.y, - 16, - c, - batch.getColor() - ); + for (int i = 0; i <= style.bold; i++) { + batch.sprite( + pos.x + (offset.x + i / (right.x/glyphInterval/2.0f)) * right.x, + pos.y + offset.y * right.y, + right.x / glyphInterval, + up.y, + -0.2f * style.italic, + 16, + c, + batch.getColor() + ); + } } static inline void draw_glyph( @@ -72,17 +76,20 @@ static inline void draw_glyph( uint c, const glm::vec3& right, const glm::vec3& up, - float glyphInterval + float glyphInterval, + const FontStyle& style ) { - batch.sprite( - pos + right * offset.x + up * offset.y, - up, right / glyphInterval, - 0.5f, - 0.5f, - 16, - c, - batch.getColor() - ); + for (int i = 0; i <= style.bold; i++) { + batch.sprite( + pos + right * (offset.x + i) + up * offset.y, + up, right / glyphInterval, + 0.5f, + 0.5f, + 16, + c, + batch.getColor() + ); + } } template @@ -99,6 +106,9 @@ static inline void draw_text( uint next = MAX_CODEPAGES; int x = 0; int y = 0; + + FontStyle style {}; + do { for (uint c : text){ if (!font.isPrintableChar(c)) { @@ -109,7 +119,7 @@ static inline void draw_text( if (charpage == page){ batch.texture(font.getPage(charpage)); draw_glyph( - batch, pos, glm::vec2(x, y), c, right, up, glyphInterval + batch, pos, glm::vec2(x, y), c, right, up, glyphInterval, style ); } else if (charpage > page && charpage < next){ diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index deb07534..121a19a6 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -11,10 +11,10 @@ class Batch2D; class Batch3D; class Camera; -enum class FontStyle { - none, - shadow, - outline +struct FontStyle { + bool bold = false; + bool italic = false; + glm::vec4 color {1, 1, 1, 1}; }; class Font { From ce1e9f76cffc8e8076ceeb6423523a3af1af0079 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Dec 2024 18:04:19 +0300 Subject: [PATCH 03/22] add FontStylesScheme --- src/graphics/core/Font.cpp | 52 +++++++++++++++++----- src/graphics/core/Font.hpp | 17 ++++++- src/graphics/render/TextsRenderer.cpp | 2 + src/graphics/ui/elements/InventoryView.cpp | 4 +- src/graphics/ui/elements/Label.cpp | 10 ++++- src/graphics/ui/elements/Label.hpp | 7 +++ src/graphics/ui/elements/Plotter.cpp | 8 +++- 7 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index c148ae72..189bec14 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -1,5 +1,6 @@ #include "Font.hpp" +#include #include #include "Texture.hpp" #include "Batch2D.hpp" @@ -64,7 +65,7 @@ static inline void draw_glyph( -0.2f * style.italic, 16, c, - batch.getColor() + batch.getColor() * style.color ); } } @@ -87,7 +88,7 @@ static inline void draw_glyph( 0.5f, 16, c, - batch.getColor() + batch.getColor() * style.color ); } } @@ -100,17 +101,33 @@ static inline void draw_text( const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up, - float glyphInterval + float glyphInterval, + const FontStylesScheme* styles ) { + static FontStylesScheme defStyles { + {{std::numeric_limits::max()}}, + }; + if (styles == nullptr) { + styles = &defStyles; + } + uint page = 0; uint next = MAX_CODEPAGES; int x = 0; int y = 0; - FontStyle style {}; - do { - for (uint c : text){ + size_t entryIndex = 0; + int styleCharsCounter = -1; + const FontStyle* style = &styles->palette.at(entryIndex); + + for (uint c : text) { + styleCharsCounter++; + if (styleCharsCounter > style->n && + entryIndex + 1 < styles->palette.size()) { + style = &styles->palette.at(++entryIndex); + styleCharsCounter = -1; + } if (!font.isPrintableChar(c)) { x++; continue; @@ -119,7 +136,14 @@ static inline void draw_text( if (charpage == page){ batch.texture(font.getPage(charpage)); draw_glyph( - batch, pos, glm::vec2(x, y), c, right, up, glyphInterval, style + batch, + pos, + glm::vec2(x, y), + c, + right, + up, + glyphInterval, + *style ); } else if (charpage > page && charpage < next){ @@ -145,20 +169,27 @@ const Texture* Font::getPage(int charpage) const { } void Font::draw( - Batch2D& batch, std::wstring_view text, int x, int y, float scale + Batch2D& batch, + std::wstring_view text, + int x, + int y, + const FontStylesScheme* styles, + float scale ) const { draw_text( *this, batch, text, glm::vec3(x, y, 0), glm::vec3(glyphInterval*scale, 0, 0), glm::vec3(0, lineHeight*scale, 0), - glyphInterval/static_cast(lineHeight) + glyphInterval/static_cast(lineHeight), + styles ); } void Font::draw( Batch3D& batch, std::wstring_view text, + const FontStylesScheme* styles, const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up @@ -167,6 +198,7 @@ void Font::draw( *this, batch, text, pos, right * static_cast(glyphInterval), up * static_cast(lineHeight), - glyphInterval/static_cast(lineHeight) + glyphInterval/static_cast(lineHeight), + styles ); } diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index 121a19a6..0543065a 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -12,11 +12,16 @@ class Batch3D; class Camera; struct FontStyle { + size_t n = -1; bool bold = false; bool italic = false; glm::vec4 color {1, 1, 1, 1}; }; +struct FontStylesScheme { + std::vector palette; +}; + class Font { int lineHeight; int yoffset; @@ -45,12 +50,20 @@ public: /// @brief Check if character is visible (non-whitespace) /// @param codepoint character unicode codepoint bool isPrintableChar(uint codepoint) const; - - void draw(Batch2D& batch, std::wstring_view text, int x, int y, float scale=1) const; + + void draw( + Batch2D& batch, + std::wstring_view text, + int x, + int y, + const FontStylesScheme* styles, + float scale = 1 + ) const; void draw( Batch3D& batch, std::wstring_view text, + const FontStylesScheme* styles, const glm::vec3& pos, const glm::vec3& right={1, 0, 0}, const glm::vec3& up={0, 1, 0} diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index fa24b61b..2a7d09db 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -98,11 +98,13 @@ void TextsRenderer::renderNote( pos + xvec * (width * 0.5f * preset.scale))) { return; } + static FontStylesScheme styles {}; auto color = preset.color; batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity)); font.draw( batch, text, + &styles, pos - xvec * (width * 0.5f) * preset.scale, xvec * preset.scale, yvec * preset.scale diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index 78751cea..04e2c28b 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -194,9 +194,9 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) { int y = pos.y+slotSize-16; batch->setColor({0, 0, 0, 1.0f}); - font->draw(*batch, text, x+1, y+1); + font->draw(*batch, text, x+1, y+1, nullptr); batch->setColor(glm::vec4(1.0f)); - font->draw(*batch, text, x, y); + font->draw(*batch, text, x, y, nullptr); } } diff --git a/src/graphics/ui/elements/Label.cpp b/src/graphics/ui/elements/Label.cpp index c356f13b..c1a4dd0d 100644 --- a/src/graphics/ui/elements/Label.cpp +++ b/src/graphics/ui/elements/Label.cpp @@ -66,6 +66,8 @@ Label::Label(const std::wstring& text, std::string fontName) cache.update(this->text, multiline, textWrap); } +Label::~Label() = default; + glm::vec2 Label::calcSize() { auto font = cache.font; uint lineHeight = font->getLineHeight(); @@ -201,10 +203,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) { if (i < cache.lines.size()-1) { 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()); } } else { - font->draw(*batch, text, pos.x, pos.y); + font->draw(*batch, text, pos.x, pos.y, styles.get()); } } @@ -239,3 +241,7 @@ void Label::setTextWrapping(bool flag) { bool Label::isTextWrapping() const { return textWrap; } + +void Label::setStyles(std::unique_ptr styles) { + this->styles = std::move(styles); +} diff --git a/src/graphics/ui/elements/Label.hpp b/src/graphics/ui/elements/Label.hpp index 4aca8a7a..f2101147 100644 --- a/src/graphics/ui/elements/Label.hpp +++ b/src/graphics/ui/elements/Label.hpp @@ -3,6 +3,7 @@ #include "UINode.hpp" class Font; +struct FontStylesScheme; namespace gui { struct LineScheme { @@ -51,10 +52,14 @@ namespace gui { /// @brief Auto resize label to fit text bool autoresize = false; + + std::unique_ptr styles; public: Label(const std::string& text, std::string fontName="normal"); Label(const std::wstring& text, std::string fontName="normal"); + virtual ~Label(); + virtual void setText(const std::wstring& text); const std::wstring& getText() const; @@ -107,5 +112,7 @@ namespace gui { virtual void setTextWrapping(bool flag); virtual bool isTextWrapping() const; + + virtual void setStyles(std::unique_ptr styles); }; } diff --git a/src/graphics/ui/elements/Plotter.cpp b/src/graphics/ui/elements/Plotter.cpp index 293d84b5..d5d906e6 100644 --- a/src/graphics/ui/elements/Plotter.cpp +++ b/src/graphics/ui/elements/Plotter.cpp @@ -47,6 +47,12 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) { batch->setColor({1,1,1,0.2f}); 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 + ); } } From ed3865964b78f2b38cc5f7c1050f7c75df74f318 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Dec 2024 22:13:17 +0300 Subject: [PATCH 04/22] add syntax highlighting (WIP) --- res/layouts/console.xml | 3 +- src/coders/commons.cpp | 6 +- src/coders/commons.hpp | 2 +- src/coders/lua_parsing.cpp | 24 +++++--- src/coders/lua_parsing.hpp | 2 +- src/devtools/syntax_highlighting.cpp | 72 ++++++++++++++++++++++ src/devtools/syntax_highlighting.hpp | 12 ++++ src/graphics/core/Font.cpp | 37 ++++++----- src/graphics/core/Font.hpp | 4 +- src/graphics/render/TextsRenderer.cpp | 4 +- src/graphics/ui/elements/InventoryView.cpp | 4 +- src/graphics/ui/elements/Label.cpp | 4 +- src/graphics/ui/elements/Plotter.cpp | 3 +- src/graphics/ui/elements/TextBox.cpp | 25 +++++++- src/graphics/ui/elements/TextBox.hpp | 6 ++ src/graphics/ui/gui_xml.cpp | 3 + 16 files changed, 168 insertions(+), 43 deletions(-) create mode 100644 src/devtools/syntax_highlighting.cpp create mode 100644 src/devtools/syntax_highlighting.hpp diff --git a/res/layouts/console.xml b/res/layouts/console.xml index c367545c..3f7331ee 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -32,10 +32,9 @@ autoresize='true' margin='0' padding='5' - editable='false' multiline='true' line-numbers='true' - text-color="#FFFFFFA0" + syntax='lua' size-func="gui.get_viewport()[1]-350,40" gravity="top-left" text-wrap='false' diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 74fb87f5..cd9a2d4c 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -214,10 +214,14 @@ std::string_view BasicParser::readUntil(char c) { return source.substr(start, pos - start); } -std::string_view BasicParser::readUntil(std::string_view s) { +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); diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp index c01601cb..9251d57d 100644 --- a/src/coders/commons.hpp +++ b/src/coders/commons.hpp @@ -105,7 +105,7 @@ protected: parsing_error error(const std::string& message); public: std::string_view readUntil(char c); - std::string_view readUntil(std::string_view s); + std::string_view readUntil(std::string_view s, bool nothrow); std::string_view readUntilWhitespace(); std::string_view readUntilEOL(); std::string parseName(); diff --git a/src/coders/lua_parsing.cpp b/src/coders/lua_parsing.cpp index f3ca21ef..e4b3c089 100644 --- a/src/coders/lua_parsing.cpp +++ b/src/coders/lua_parsing.cpp @@ -119,24 +119,28 @@ public: ); continue; } else if (is_digit(c)) { - auto value = parseNumber(1); + 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( - value.isInteger() ? TokenTag::INTEGER : TokenTag::NUMBER, - std::string(literal), - start - ); + emitToken(tag, std::string(literal), start); continue; } switch (c) { case '(': case '[': case '{': if (isNext("[==[")) { - readUntil("]==]"); + auto string = readUntil("]==]", true); skip(4); + emitToken(TokenTag::COMMENT, std::string(string)+"]==]", start); continue; } else if (isNext("[[")) { skip(2); - auto string = readUntil("]]"); + auto string = readUntil("]]", true); skip(2); emitToken(TokenTag::STRING, std::string(string), start); continue; @@ -154,7 +158,7 @@ public: continue; case '\'': case '"': { skip(1); - auto string = parseString(c); + auto string = parseString(c, false); emitToken(TokenTag::STRING, std::move(string), start); continue; } @@ -163,6 +167,8 @@ public: if (is_lua_operator_start(c)) { auto text = parseOperator(); if (text == "--") { + auto string = readUntilEOL(); + emitToken(TokenTag::COMMENT, std::string(string), start); skipLine(); continue; } diff --git a/src/coders/lua_parsing.hpp b/src/coders/lua_parsing.hpp index 68cc385a..1578b7fa 100644 --- a/src/coders/lua_parsing.hpp +++ b/src/coders/lua_parsing.hpp @@ -12,7 +12,7 @@ namespace lua { enum class TokenTag { KEYWORD, NAME, INTEGER, NUMBER, OPEN_BRACKET, CLOSE_BRACKET, STRING, - OPERATOR, COMMA, SEMICOLON, UNEXPECTED + OPERATOR, COMMA, SEMICOLON, UNEXPECTED, COMMENT }; struct Token { diff --git a/src/devtools/syntax_highlighting.cpp b/src/devtools/syntax_highlighting.cpp new file mode 100644 index 00000000..8442229e --- /dev/null +++ b/src/devtools/syntax_highlighting.cpp @@ -0,0 +1,72 @@ +#include "syntax_highlighting.hpp" + +#include "coders/commons.hpp" +#include "coders/lua_parsing.hpp" +#include "graphics/core/Font.hpp" + +using namespace devtools; + +static std::unique_ptr build_styles( + const std::vector& tokens +) { + FontStylesScheme styles { + { + {false, false, glm::vec4(0.8f, 0.8f, 0.8f, 1)}, // default + {true, false, glm::vec4(0.9, 0.6f, 0.4f, 1)}, // keyword + {false, false, glm::vec4(0.4, 0.8f, 0.5f, 1)}, // string + {false, false, glm::vec4(0.3, 0.3f, 0.3f, 1)}, // comment + {false, false, glm::vec4(0.4, 0.45f, 0.5f, 1)}, // self + {true, 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 != lua::TokenTag::KEYWORD && + token.tag != lua::TokenTag::STRING && + token.tag != lua::TokenTag::INTEGER && + token.tag != lua::TokenTag::NUMBER && + token.tag != lua::TokenTag::COMMENT && + token.tag != lua::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 lua::TokenTag::KEYWORD: styleIndex = 1; break; + case lua::TokenTag::STRING: + case lua::TokenTag::INTEGER: + case lua::TokenTag::NUMBER: styleIndex = 2; break; + case lua::TokenTag::COMMENT: styleIndex = 3; break; + case lua::TokenTag::UNEXPECTED: styleIndex = 5; break; + default: + styleIndex = 0; + break; + } + styles.map.insert( + styles.map.end(), token.end.pos - token.start.pos, styleIndex + ); + } + styles.map.push_back(0); + return std::make_unique(std::move(styles)); +} + +std::unique_ptr devtools::syntax_highlight( + const std::string& lang, std::string_view source +) { + try { + if (lang == "lua") { + auto tokens = lua::tokenize("", source); + return build_styles(tokens); + } else { + return nullptr; + } + } catch (const parsing_error& err) { + return nullptr; + } +} diff --git a/src/devtools/syntax_highlighting.hpp b/src/devtools/syntax_highlighting.hpp new file mode 100644 index 00000000..c2f33e45 --- /dev/null +++ b/src/devtools/syntax_highlighting.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +struct FontStylesScheme; + +namespace devtools { + std::unique_ptr syntax_highlight( + const std::string& lang, std::string_view source + ); +} diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index 189bec14..d3a6caf6 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -62,7 +62,7 @@ static inline void draw_glyph( pos.y + offset.y * right.y, right.x / glyphInterval, up.y, - -0.2f * style.italic, + -0.15f * style.italic, 16, c, batch.getColor() * style.color @@ -102,11 +102,11 @@ static inline void draw_text( const glm::vec3& right, const glm::vec3& up, float glyphInterval, - const FontStylesScheme* styles + const FontStylesScheme* styles, + size_t styleMapOffset ) { - static FontStylesScheme defStyles { - {{std::numeric_limits::max()}}, - }; + static FontStylesScheme defStyles {{{}}, {0}}; + if (styles == nullptr) { styles = &defStyles; } @@ -117,17 +117,12 @@ static inline void draw_text( int y = 0; do { - size_t entryIndex = 0; - int styleCharsCounter = -1; - const FontStyle* style = &styles->palette.at(entryIndex); - - for (uint c : text) { - styleCharsCounter++; - if (styleCharsCounter > style->n && - entryIndex + 1 < styles->palette.size()) { - style = &styles->palette.at(++entryIndex); - styleCharsCounter = -1; - } + 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); if (!font.isPrintableChar(c)) { x++; continue; @@ -143,7 +138,7 @@ static inline void draw_text( right, up, glyphInterval, - *style + style ); } else if (charpage > page && charpage < next){ @@ -174,6 +169,7 @@ void Font::draw( int x, int y, const FontStylesScheme* styles, + size_t styleMapOffset, float scale ) const { draw_text( @@ -182,7 +178,8 @@ void Font::draw( glm::vec3(glyphInterval*scale, 0, 0), glm::vec3(0, lineHeight*scale, 0), glyphInterval/static_cast(lineHeight), - styles + styles, + styleMapOffset ); } @@ -190,6 +187,7 @@ void Font::draw( Batch3D& batch, std::wstring_view text, const FontStylesScheme* styles, + size_t styleMapOffset, const glm::vec3& pos, const glm::vec3& right, const glm::vec3& up @@ -199,6 +197,7 @@ void Font::draw( right * static_cast(glyphInterval), up * static_cast(lineHeight), glyphInterval/static_cast(lineHeight), - styles + styles, + styleMapOffset ); } diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index 0543065a..6721be98 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -12,7 +12,6 @@ class Batch3D; class Camera; struct FontStyle { - size_t n = -1; bool bold = false; bool italic = false; glm::vec4 color {1, 1, 1, 1}; @@ -20,6 +19,7 @@ struct FontStyle { struct FontStylesScheme { std::vector palette; + std::vector map; }; class Font { @@ -57,6 +57,7 @@ public: int x, int y, const FontStylesScheme* styles, + size_t styleMapOffset, float scale = 1 ) const; @@ -64,6 +65,7 @@ public: Batch3D& batch, std::wstring_view text, const FontStylesScheme* styles, + size_t styleMapOffset, const glm::vec3& pos, const glm::vec3& right={1, 0, 0}, const glm::vec3& up={0, 1, 0} diff --git a/src/graphics/render/TextsRenderer.cpp b/src/graphics/render/TextsRenderer.cpp index 2a7d09db..32681ce5 100644 --- a/src/graphics/render/TextsRenderer.cpp +++ b/src/graphics/render/TextsRenderer.cpp @@ -98,13 +98,13 @@ void TextsRenderer::renderNote( pos + xvec * (width * 0.5f * preset.scale))) { return; } - static FontStylesScheme styles {}; auto color = preset.color; batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity)); font.draw( batch, text, - &styles, + nullptr, + 0, pos - xvec * (width * 0.5f) * preset.scale, xvec * preset.scale, yvec * preset.scale diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index 04e2c28b..2c763ca4 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -194,9 +194,9 @@ void SlotView::draw(const DrawContext* pctx, Assets* assets) { int y = pos.y+slotSize-16; batch->setColor({0, 0, 0, 1.0f}); - font->draw(*batch, text, x+1, y+1, nullptr); + font->draw(*batch, text, x+1, y+1, nullptr, 0); batch->setColor(glm::vec4(1.0f)); - font->draw(*batch, text, x, y, nullptr); + font->draw(*batch, text, x, y, nullptr, 0); } } diff --git a/src/graphics/ui/elements/Label.cpp b/src/graphics/ui/elements/Label.cpp index c1a4dd0d..87346db5 100644 --- a/src/graphics/ui/elements/Label.cpp +++ b/src/graphics/ui/elements/Label.cpp @@ -203,10 +203,10 @@ void Label::draw(const DrawContext* pctx, Assets* assets) { if (i < cache.lines.size()-1) { 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, styles.get()); + font->draw(*batch, view, pos.x, pos.y + i * totalLineHeight, styles.get(), offset); } } else { - font->draw(*batch, text, pos.x, pos.y, styles.get()); + font->draw(*batch, text, pos.x, pos.y, styles.get(), 0); } } diff --git a/src/graphics/ui/elements/Plotter.cpp b/src/graphics/ui/elements/Plotter.cpp index d5d906e6..122734a3 100644 --- a/src/graphics/ui/elements/Plotter.cpp +++ b/src/graphics/ui/elements/Plotter.cpp @@ -52,7 +52,8 @@ void Plotter::draw(const DrawContext* pctx, Assets* assets) { string, pos.x + dmwidth + 2, pos.y + dmheight - y - labelsInterval, - nullptr + nullptr, + 0 ); } } diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 6a54d682..3403e813 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -5,6 +5,7 @@ #include #include "Label.hpp" +#include "devtools/syntax_highlighting.hpp" #include "graphics/core/DrawContext.hpp" #include "graphics/core/Batch2D.hpp" #include "graphics/core/Font.hpp" @@ -65,11 +66,10 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) { lcoord.y -= 2; auto batch = pctx->getBatch2D(); batch->texture(nullptr); + batch->setColor(glm::vec4(1.0f)); if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) { uint line = label->getLineByTextIndex(caret); uint lcaret = caret - label->getTextLineOffset(line); - batch->setColor(glm::vec4(1.0f)); - int width = font->calcWidth(input, lcaret); batch->rect(lcoord.x + width, lcoord.y+label->getLineYOffset(line), 2, lineHeight); } @@ -529,10 +529,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() { if (subconsumer) { subconsumer(input); } + refreshSyntax(); } void TextBox::performEditingKeyboardEvents(keycode key) { @@ -710,6 +721,7 @@ const std::wstring& TextBox::getText() const { void TextBox::setText(const std::wstring& value) { this->input = value; input.erase(std::remove(input.begin(), input.end(), '\r'), input.end()); + refreshSyntax(); } const std::wstring& TextBox::getPlaceholder() const { @@ -789,3 +801,12 @@ void TextBox::setShowLineNumbers(bool flag) { bool TextBox::isShowLineNumbers() const { return showLineNumbers; } + +void TextBox::setSyntax(const std::string& lang) { + syntax = lang; + if (syntax.empty()) { + label->setStyles(nullptr); + } else { + refreshSyntax(); + } +} diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index d2c4fd8c..c23cf740 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -57,6 +57,8 @@ namespace gui { bool autoresize = false; bool showLineNumbers = false; + std::string syntax; + void stepLeft(bool shiftPressed, bool breakSelection); void stepRight(bool shiftPressed, bool breakSelection); void stepDefaultDown(bool shiftPressed, bool breakSelection); @@ -84,6 +86,8 @@ namespace gui { void refreshLabel(); void onInput(); + + void refreshSyntax(); public: TextBox( std::wstring placeholder, @@ -219,5 +223,7 @@ namespace gui { virtual std::shared_ptr getAt(glm::vec2 pos, std::shared_ptr self) override; virtual void setOnUpPressed(const runnable &callback); virtual void setOnDownPressed(const runnable &callback); + + virtual void setSyntax(const std::string& lang); }; } diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index c25b65b2..0c721524 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -357,6 +357,9 @@ static std::shared_ptr readTextBox(UiXmlReader& reader, const xml::xmlel } textbox->setText(text); + if (element->has("syntax")) { + textbox->setSyntax(element->attr("syntax").getText()); + } if (element->has("multiline")) { textbox->setMultiline(element->attr("multiline").asBool()); } From dbd27d4423a8b8c9dcf2c6e3c64b9786bc7656da Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Dec 2024 23:03:36 +0300 Subject: [PATCH 05/22] add scrollbar (WIP) --- src/graphics/ui/elements/Container.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index fdd0bc02..f5a6a533 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -95,6 +95,19 @@ void Container::draw(const DrawContext* pctx, Assets* assets) { if (node->isVisible()) node->draw(pctx, assets); } + + int diff = (actualLength-size.y); + if (scrollable && diff > 0) { + int w = 10; + int h = glm::max(size.y / actualLength * size.y, w / 2.0f); + batch->untexture(); + batch->setColor(glm::vec4(1, 1, 1, 0.5f)); + batch->rect( + pos.x + size.x - w, + pos.y - scroll / static_cast(diff) * (size.y - h), + w, h + ); + } batch->flush(); } } From 2fa78b7b2178799cdcff644e4ae414086e526ff7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 4 Dec 2024 23:32:22 +0300 Subject: [PATCH 06/22] fix random fatal error in socket:recv --- src/logic/scripting/lua/libs/libnetwork.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 61d44de7..dc752710 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -101,7 +101,8 @@ static int l_recv(lua::State* L) { if (connection == nullptr) { return 0; } - util::Buffer buffer(glm::min(length, connection->available())); + length = glm::min(length, connection->available()); + util::Buffer buffer(length); int size = connection->recv(buffer.data(), length); if (size == -1) { From bad2b44c968af419fb78ffa0e17f84f111030cfa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Dec 2024 14:42:33 +0300 Subject: [PATCH 07/22] fix incorrect virtual inventory id in scripting --- src/items/Inventories.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/items/Inventories.cpp b/src/items/Inventories.cpp index d3af607d..4d2baa36 100644 --- a/src/items/Inventories.cpp +++ b/src/items/Inventories.cpp @@ -20,7 +20,9 @@ std::shared_ptr Inventories::create(size_t size) { std::shared_ptr Inventories::createVirtual(size_t size) { int64_t id; do { - id = -std::max(1LL, std::llabs(random.rand64())); + // lua does not support long integers because Number is floating-point + // type. Changing int_consumer to use 64 bit integer does not change anything + id = -std::max(1LL, std::llabs(random.rand64() % 1000'000'000)); } while (map.find(id) != map.end()); auto inv = std::make_shared(id, size); From b65ba61f9a978e72ef015e938de35b2da2b4d374 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Dec 2024 14:43:24 +0300 Subject: [PATCH 08/22] make scrollbar interactive --- src/frontend/hud.cpp | 8 ++-- src/graphics/ui/elements/Container.cpp | 43 +++++++++++++++++++--- src/graphics/ui/elements/Container.hpp | 9 +++++ src/graphics/ui/elements/InventoryView.cpp | 10 ++--- src/graphics/ui/elements/InventoryView.hpp | 2 +- src/graphics/ui/elements/TextBox.cpp | 9 ++++- 6 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 30659969..21111e18 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -124,7 +124,7 @@ std::shared_ptr Hud::createContentAccess() { }); InventoryBuilder builder; - builder.addGrid(8, itemsCount-1, glm::vec2(), 8, true, slotLayout); + builder.addGrid(8, itemsCount-1, glm::vec2(), glm::vec4(8, 8, 12, 8), true, slotLayout); auto view = builder.build(); view->bind(accessInventory, content); view->setMargin(glm::vec4()); @@ -137,7 +137,7 @@ std::shared_ptr Hud::createHotbar() { SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr); InventoryBuilder builder; - builder.addGrid(10, 10, glm::vec2(), 4, true, slotLayout); + builder.addGrid(10, 10, glm::vec2(), glm::vec4(4), true, slotLayout); auto view = builder.build(); view->setId("hud.hotbar"); view->setOrigin(glm::vec2(view->getSize().x/2, 0)); @@ -346,9 +346,9 @@ void Hud::update(bool visible) { element.getNode()->setVisible(visible); } - glm::vec2 invSize = contentAccessPanel->getSize(); + glm::vec2 caSize = contentAccessPanel->getSize(); contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel); - contentAccessPanel->setSize(glm::vec2(invSize.x, Window::height)); + contentAccessPanel->setSize(glm::vec2(caSize.x, Window::height)); contentAccess->setMinSize(glm::vec2(1, Window::height)); hotbarView->setVisible(visible && !(secondUI && !inventoryView)); diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index f5a6a533..86914077 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -23,6 +23,11 @@ std::shared_ptr Container::getAt(glm::vec2 pos, std::shared_ptr } 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--) { auto& node = nodes[i]; if (!node->isVisible()) @@ -35,6 +40,35 @@ std::shared_ptr Container::getAt(glm::vec2 pos, std::shared_ptr 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(size.y) * actualLength; + scroll = -glm::min( + glm::max(static_cast(-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) { for (const auto& node : nodes) { if (node->isVisible()) { @@ -98,14 +132,13 @@ void Container::draw(const DrawContext* pctx, Assets* assets) { int diff = (actualLength-size.y); if (scrollable && diff > 0) { - int w = 10; - int h = glm::max(size.y / actualLength * size.y, w / 2.0f); + int h = glm::max(size.y / actualLength * size.y, scrollBarWidth / 2.0f); batch->untexture(); - batch->setColor(glm::vec4(1, 1, 1, 0.5f)); + batch->setColor(glm::vec4(1, 1, 1, 0.3f)); batch->rect( - pos.x + size.x - w, + pos.x + size.x - scrollBarWidth, pos.y - scroll / static_cast(diff) * (size.y - h), - w, h + scrollBarWidth, h ); } batch->flush(); diff --git a/src/graphics/ui/elements/Container.hpp b/src/graphics/ui/elements/Container.hpp index bfe40ab5..420a7e55 100644 --- a/src/graphics/ui/elements/Container.hpp +++ b/src/graphics/ui/elements/Container.hpp @@ -7,13 +7,19 @@ namespace gui { class Container : public UINode { + int prevScrollY = -1; protected: std::vector> nodes; std::vector intervalEvents; int scroll = 0; int scrollStep = 40; + int scrollBarWidth = 10; int actualLength = 0; bool scrollable = true; + + bool isScrolling() { + return prevScrollY != -1; + } public: Container(glm::vec2 size); virtual ~Container(); @@ -36,6 +42,9 @@ namespace gui { virtual void setScrollStep(int step); 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>& getNodes() const; }; } diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index 2c763ca4..217de57f 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -54,7 +54,7 @@ InventoryBuilder::InventoryBuilder() { void InventoryBuilder::addGrid( int cols, int count, glm::vec2 pos, - int padding, + glm::vec4 padding, bool addpanel, const SlotLayout& slotLayout ) { @@ -63,8 +63,8 @@ void InventoryBuilder::addGrid( int rows = ceildiv(count, cols); - uint width = cols * (slotSize + interval) - interval + padding*2; - uint height = rows * (slotSize + interval) - interval + padding*2; + uint width = cols * (slotSize + interval) - interval + padding.x + padding.z; + uint height = rows * (slotSize + interval) - interval + padding.y + padding.w; glm::vec2 vsize = view->getSize(); if (pos.x + width > vsize.x) { @@ -87,8 +87,8 @@ void InventoryBuilder::addGrid( break; } glm::vec2 position ( - col * (slotSize + interval) + padding, - row * (slotSize + interval) + padding + col * (slotSize + interval) + padding.x, + row * (slotSize + interval) + padding.y ); auto builtSlot = slotLayout; builtSlot.index = row * cols + col; diff --git a/src/graphics/ui/elements/InventoryView.hpp b/src/graphics/ui/elements/InventoryView.hpp index e37e6681..920636cb 100644 --- a/src/graphics/ui/elements/InventoryView.hpp +++ b/src/graphics/ui/elements/InventoryView.hpp @@ -136,7 +136,7 @@ namespace gui { void addGrid( int cols, int count, glm::vec2 pos, - int padding, + glm::vec4 padding, bool addpanel, const SlotLayout& slotLayout ); diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 3403e813..4bd72fff 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -74,6 +74,9 @@ void TextBox::draw(const DrawContext* pctx, Assets* assets) { batch->rect(lcoord.x + width, lcoord.y+label->getLineYOffset(line), 2, lineHeight); } if (selectionStart != selectionEnd) { + auto selectionCtx = subctx.sub(batch); + selectionCtx.setBlendMode(BlendMode::addition); + uint startLine = label->getLineByTextIndex(selectionStart); uint endLine = label->getLineByTextIndex(selectionEnd); @@ -433,7 +436,11 @@ void TextBox::click(GUI*, int x, int y) { 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); setCaret(index); extendSelection(index); From 04b6b6b5469dfc7338873441ab30e398bd225e4e Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 5 Dec 2024 19:18:53 +0300 Subject: [PATCH 09/22] minor refactor --- src/engine.cpp | 2 +- src/frontend/hud.hpp | 12 ++-- src/graphics/ui/GUI.cpp | 8 +-- src/graphics/ui/GUI.hpp | 2 +- src/graphics/ui/elements/Button.cpp | 6 +- src/graphics/ui/elements/Button.hpp | 6 +- src/graphics/ui/elements/CheckBox.cpp | 4 +- src/graphics/ui/elements/CheckBox.hpp | 2 +- src/graphics/ui/elements/Container.cpp | 21 ++++--- src/graphics/ui/elements/Container.hpp | 10 ++-- src/graphics/ui/elements/Image.cpp | 8 +-- src/graphics/ui/elements/Image.hpp | 2 +- src/graphics/ui/elements/InputBindBox.cpp | 8 +-- src/graphics/ui/elements/InputBindBox.hpp | 5 +- src/graphics/ui/elements/InventoryView.cpp | 10 ++-- src/graphics/ui/elements/InventoryView.hpp | 2 +- src/graphics/ui/elements/Label.cpp | 6 +- src/graphics/ui/elements/Label.hpp | 2 +- src/graphics/ui/elements/Menu.hpp | 4 +- src/graphics/ui/elements/Panel.hpp | 2 +- src/graphics/ui/elements/Plotter.cpp | 6 +- src/graphics/ui/elements/Plotter.hpp | 2 +- src/graphics/ui/elements/TextBox.cpp | 67 ++++++++++++++++------ src/graphics/ui/elements/TextBox.hpp | 8 ++- src/graphics/ui/elements/TrackBar.cpp | 4 +- src/graphics/ui/elements/TrackBar.hpp | 2 +- src/graphics/ui/elements/UINode.cpp | 4 +- src/graphics/ui/elements/UINode.hpp | 4 +- src/presets/ParticlesPreset.hpp | 2 +- 29 files changed, 130 insertions(+), 91 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 31db4537..129dc68d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -206,7 +206,7 @@ void Engine::renderFrame(Batch2D& batch) { Viewport viewport(Window::width, Window::height); DrawContext ctx(nullptr, viewport, &batch); - gui->draw(&ctx, assets.get()); + gui->draw(ctx, *assets); } void Engine::processPostRunnables() { diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index 580ab54d..5594664a 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -95,16 +95,16 @@ class Hud : public util::ObjectsKeeper { /// @brief Inventories interaction agent (grabbed item) std::shared_ptr exchangeSlot; /// @brief Exchange slot inventory (1 slot only) - std::shared_ptr exchangeSlotInv = nullptr; + std::shared_ptr exchangeSlotInv; /// @brief List of all controlled hud elements std::vector elements; /// @brief Player inventory view - std::shared_ptr inventoryView = nullptr; + std::shared_ptr inventoryView; /// @brief Block inventory view - std::shared_ptr blockUI = nullptr; + std::shared_ptr blockUI; /// @brief Secondary inventory view - std::shared_ptr secondInvView = nullptr; + std::shared_ptr secondInvView; /// @brief Position of the block open glm::ivec3 blockPos {}; /// @brief Id of the block open (used to detect block destruction or replacement) @@ -114,9 +114,9 @@ class Hud : public util::ObjectsKeeper { /// @brief Provide cheat controllers to the debug panel bool allowDebugCheats = true; /// @brief UI element will be dynamicly positioned near to inventory or in screen center - std::shared_ptr secondUI = nullptr; + std::shared_ptr secondUI; - std::shared_ptr debugMinimap = nullptr; + std::shared_ptr debugMinimap; std::unique_ptr debugImgWorldGen; diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 40b74a4c..38bc2257 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -197,18 +197,18 @@ void GUI::act(float delta, const Viewport& vp) { } } -void GUI::draw(const DrawContext* pctx, Assets* assets) { - auto& viewport = pctx->getViewport(); +void GUI::draw(const DrawContext& pctx, const Assets& assets) { + auto& viewport = pctx.getViewport(); glm::vec2 wsize = viewport.size(); menu->setPos((wsize - menu->getSize()) / 2.0f); uicamera->setFov(wsize.y); - auto uishader = assets->get("ui"); + auto uishader = assets.get("ui"); uishader->use(); uishader->uniformMatrix("u_projview", uicamera->getProjView()); - pctx->getBatch2D()->begin(); + pctx.getBatch2D()->begin(); container->draw(pctx, assets); } diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index b2b20bd0..c0dec649 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -94,7 +94,7 @@ namespace gui { /// @brief Draw all visible elements on main container /// @param pctx parent graphics context /// @param assets active assets storage - void draw(const DrawContext* pctx, Assets* assets); + void draw(const DrawContext& pctx, const Assets& assets); /// @brief Add element to the main container /// @param node UI element diff --git a/src/graphics/ui/elements/Button.cpp b/src/graphics/ui/elements/Button.cpp index b6a11232..dfdf864d 100644 --- a/src/graphics/ui/elements/Button.cpp +++ b/src/graphics/ui/elements/Button.cpp @@ -52,7 +52,7 @@ Button::Button( void Button::setText(std::wstring text) { if (label) { - label->setText(text); + label->setText(std::move(text)); } } @@ -77,9 +77,9 @@ void Button::refresh() { } } -void Button::drawBackground(const DrawContext* pctx, Assets*) { +void Button::drawBackground(const DrawContext& pctx, const Assets&) { glm::vec2 pos = calcPos(); - auto batch = pctx->getBatch2D(); + auto batch = pctx.getBatch2D(); batch->texture(nullptr); batch->setColor(calcColor()); batch->rect(pos.x, pos.y, size.x, size.y); diff --git a/src/graphics/ui/elements/Button.hpp b/src/graphics/ui/elements/Button.hpp index 61bfea03..39b23426 100644 --- a/src/graphics/ui/elements/Button.hpp +++ b/src/graphics/ui/elements/Button.hpp @@ -7,7 +7,7 @@ namespace gui { class Button : public Panel { protected: - std::shared_ptr