From 640d8df5277af4ac4b545cc6d4cf2830509e61b9 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 4 May 2018 20:50:49 +0200 Subject: Add proper parsing of Text, add url --- include/StringView.hpp | 18 ++++++ include/Text.hpp | 38 ++++++------ project.conf | 1 + src/Channel.cpp | 26 +------- src/MessageBoard.cpp | 23 -------- src/Text.cpp | 157 +++++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 173 insertions(+), 90 deletions(-) diff --git a/include/StringView.hpp b/include/StringView.hpp index 45d0f5b..c4e7ce3 100644 --- a/include/StringView.hpp +++ b/include/StringView.hpp @@ -68,6 +68,24 @@ namespace dchat return data[index]; } + // Returns -1 if substr not found. + // TODO: Make this more efficient + usize find(const BasicStringView &substr, usize offset = 0) const + { + if(substr.size == 0) + return -1; + + if(offset + substr.size > size) + return -1; + + for(usize i = offset; i < size - (substr.size - 1); ++i) + { + if(memcmp(data + i, substr.data, substr.size) == 0) + return i; + } + return -1; + } + const CharType *data; usize size; }; diff --git a/include/Text.hpp b/include/Text.hpp index 016e852..daea7ca 100644 --- a/include/Text.hpp +++ b/include/Text.hpp @@ -10,6 +10,24 @@ namespace dchat { + struct TextElement + { + enum class Type + { + TEXT, + EMOJI, + URL + }; + + TextElement() {} + TextElement(const StringViewUtf32 &_text, Type _type) : text(_text), type(_type), ownLine(false) {} + + StringViewUtf32 text; + sf::Vector2f position; + Type type; + bool ownLine; // Currently only used for emoji, to make emoji bigger when it's the only thing on a line + }; + class Text { public: @@ -34,24 +52,7 @@ namespace dchat private: void stringSplitElements(sf::String &stringToSplit, usize startIndex); void updateGeometry(); - private: - struct TextElement - { - enum class Type - { - TEXT, - EMOJI - }; - - TextElement() {} - TextElement(const StringViewUtf32 &_text, Type _type) : text(_text), type(_type), ownLine(false) {} - - StringViewUtf32 text; - sf::Vector2f position; - Type type; - bool ownLine; // Currently only used for emoji, to make emoji bigger when it's the only thing on a line - }; - + private: sf::String str; const sf::Font *font; unsigned int characterSize; @@ -59,6 +60,7 @@ namespace dchat float maxWidth; sf::Vector2f position; sf::Color color; + sf::Color urlColor; bool dirty; bool plainText; float totalHeight; diff --git a/project.conf b/project.conf index eaf5266..f488112 100644 --- a/project.conf +++ b/project.conf @@ -14,3 +14,4 @@ mpv = "1.25.0" gl = "17.3" x11 = "1.6.5" libnsgif = "0.2.0" +libpreview = "0.1.0" diff --git a/src/Channel.cpp b/src/Channel.cpp index ae66e3c..e25057a 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -20,37 +20,17 @@ namespace dchat { addUserLocally(localUser); { - Message *message = new Message(&systemUser, u8"hello, worldåäö1![emoji](https://discordemoji.com/assets/emoji/playtime.png)"); + Message *message = new Message(&systemUser, u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif) deaf [emoji](https://discordemoji.com/assets/emoji/COGGERS.gif)"); messageBoard.addMessage(message); } { - Message *message = new Message(&systemUser, u8"hello, world2![emoji](https://discordemoji.com/assets/emoji/Feels3DMan.gif)"); + Message *message = new Message(&systemUser, u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif)[emoji](https://discordemoji.com/assets/emoji/COGGERS.gif)"); messageBoard.addMessage(message); } { - Message *message = new Message(&systemUser, u8"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."); - messageBoard.addMessage(message); - } - - { - Message *message = new Message(&systemUser, u8"Lorem ipsumdolorsitamet,consecteturadipiscingelit,seddoeiusmodtemporincididuntutaboreetdoloremagnaaliqua.Utenimadminimveniam"); - messageBoard.addMessage(message); - } - - { - Message *message = new Message(&systemUser, u8"xddd"); - messageBoard.addMessage(message); - } - - { - Message *message = new Message(&systemUser, u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif)[emoji](https://cdn.discordapp.com/emojis/398569871761473538.png?v=1)"); - messageBoard.addMessage(message); - } - - { - Message *message = new Message(&systemUser, u8"Message after big emoji"); + Message *message = new Message(&systemUser, u8"pepedab https://discordemoji.com/assets/emoji/PepeDab.gif coggers https://discordemoji.com/assets/emoji/COGGERS.gif"); messageBoard.addMessage(message); } diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp index 71b622f..250f234 100644 --- a/src/MessageBoard.cpp +++ b/src/MessageBoard.cpp @@ -33,29 +33,6 @@ namespace dchat const float USERNAME_TIMESTAMP_SIDE_PADDING = 10.0f; const double SCROLL_MAX_SPEED = 20.0; - const LineColor LINE_COLOR - { - .sideColor = ColorScheme::getBackgroundColor() + sf::Color(10, 10, 10), - .centerColor = ColorScheme::getBackgroundColor() + sf::Color(10, 10, 10) - }; - - static void drawGradientLine(const sf::Vector2f &position, const sf::Vector2f &size, const LineColor &color, sf::RenderWindow &window) - { - sf::Vertex rectangle[] = - { - sf::Vertex(position, color.sideColor), - sf::Vertex(sf::Vector2f(position.x + size.x * 0.5f, position.y), color.centerColor), - sf::Vertex(sf::Vector2f(position.x + size.x * 0.5f, position.y + size.y), color.centerColor), - sf::Vertex(sf::Vector2f(position.x, position.y + size.y), color.sideColor), - - sf::Vertex(sf::Vector2f(position.x + size.x * 0.5f, position.y), color.centerColor), - sf::Vertex(sf::Vector2f(position.x + size.x, position.y), color.sideColor), - sf::Vertex(sf::Vector2f(position.x + size.x, position.y + size.y), color.sideColor), - sf::Vertex(sf::Vector2f(position.x + size.x * 0.5f, position.y + size.y), color.centerColor) - }; - window.draw(rectangle, 8, sf::Quads); - } - MessageBoard::MessageBoard(const sf::Vector2u &size) : selectingText(false), leftMouseButtonPressed(false), diff --git a/src/Text.cpp b/src/Text.cpp index 02ea6fa..c433ddc 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -11,11 +11,14 @@ namespace dchat const float EMOJI_SCALE_WITH_TEXT = 1.7f; const float EMOJI_SCALE_STANDALONE = 5.0f; + const sf::Color URL_COLOR(15, 192, 252); + Text::Text(const sf::Font *_font) : font(_font), characterSize(0), maxWidth(0.0f), color(sf::Color::White), + urlColor(URL_COLOR), dirty(false), plainText(false), totalHeight(0.0f) @@ -29,6 +32,7 @@ namespace dchat vertices(sf::PrimitiveType::Quads), maxWidth(_maxWidth), color(sf::Color::White), + urlColor(URL_COLOR), dirty(true), plainText(_plainText), totalHeight(0.0f), @@ -109,43 +113,142 @@ namespace dchat return totalHeight; } + size_t stringSplitUrl(const StringViewUtf32 textElementStr, const sf::String &urlStr, size_t offset, std::vector &newTextElements) + { + size_t stringStart = offset; + size_t urlStart = textElementStr.find(StringViewUtf32(urlStr.getData(), urlStr.getSize()), offset); + if(urlStart != -1) + { + offset = urlStart + urlStr.getSize(); + while(offset < textElementStr.size) + { + if(isspace(textElementStr[offset])) + break; + ++offset; + } + + StringViewUtf32 beforeUrlStr(textElementStr.data + stringStart, urlStart - stringStart); + newTextElements.push_back({ beforeUrlStr, TextElement::Type::TEXT }); + + StringViewUtf32 url(textElementStr.data + urlStart, offset - urlStart); + newTextElements.push_back({ url, TextElement::Type::URL }); + return offset; + } + return -1; + } + void Text::stringSplitElements(sf::String &stringToSplit, usize startIndex) { + StringViewUtf32 wholeStr(&stringToSplit[startIndex], stringToSplit.getSize() - startIndex); + textElements.push_back({ wholeStr, TextElement::Type::TEXT }); if(plainText) - { - StringViewUtf32 wholeStr(&stringToSplit[startIndex], stringToSplit.getSize() - startIndex); - textElements.push_back({ wholeStr, TextElement::Type::TEXT }); return; + + const char *httpStrRaw = "http://"; + const sf::String httpStr = sf::String::fromUtf8(httpStrRaw, httpStrRaw + 7); + + const char *httpsStrRaw = "https://"; + const sf::String httpsStr = sf::String::fromUtf8(httpsStrRaw, httpsStrRaw + 8); + + const char *emojiStrRaw = "[emoji]("; + const sf::String emojiStr = sf::String::fromUtf8(emojiStrRaw, emojiStrRaw + 8); + + const char *parentheseStrRaw = ")"; + const sf::String parentheseStr = sf::String::fromUtf8(parentheseStrRaw, parentheseStrRaw + 1); + static_assert(sizeof(*parentheseStr.getData()) == sizeof(u32), "sf::String size has changed..."); + + std::vector newTextElements; + for(size_t i = 0; i < textElements.size(); ++i) + { + TextElement textElement = textElements[i]; + if(textElement.type != TextElement::Type::TEXT) + { + newTextElements.push_back(textElement); + continue; + } + + size_t offset = 0; + while(offset < textElement.text.size) + { + size_t stringStart = offset; + size_t foundStartIndex = textElement.text.find(StringViewUtf32(emojiStr.getData(), emojiStr.getSize()), offset); + size_t foundEndIndex = -1; + if(foundStartIndex != -1) + { + offset = foundStartIndex + 8; + foundEndIndex = textElement.text.find(StringViewUtf32(parentheseStr.getData(), parentheseStr.getSize()), offset); + } + + if(foundEndIndex != -1) + { + StringViewUtf32 beforeEmojiStr(textElement.text.data + stringStart, foundStartIndex - stringStart); + newTextElements.push_back({ beforeEmojiStr, TextElement::Type::TEXT }); + + StringViewUtf32 url(textElement.text.data + offset, foundEndIndex - offset); + newTextElements.push_back({ url, TextElement::Type::EMOJI }); + offset = foundEndIndex + 1; + } + else + { + StringViewUtf32 strToEnd(textElement.text.data + stringStart, textElement.text.size - stringStart); + newTextElements.push_back({ strToEnd, TextElement::Type::TEXT }); + offset = textElement.text.size; + } + } } + textElements = newTextElements; - size_t offset = startIndex; - while(offset < stringToSplit.getSize()) + newTextElements.clear(); + for(size_t i = 0; i < textElements.size(); ++i) { - size_t stringStart = offset; - size_t foundStartIndex = stringToSplit.find("[emoji](", offset); - size_t foundEndIndex = -1; - if(foundStartIndex != -1) + TextElement textElement = textElements[i]; + if(textElement.type != TextElement::Type::TEXT) { - offset += (foundStartIndex + 8); - foundEndIndex = stringToSplit.find(")", offset); + newTextElements.push_back(textElement); + continue; } - if(foundEndIndex != -1) + size_t offset = 0; + while(offset < textElement.text.size) { - StringViewUtf32 beforeEmojiStr(&stringToSplit[stringStart], foundStartIndex - stringStart); - textElements.push_back({ beforeEmojiStr, TextElement::Type::TEXT }); - - StringViewUtf32 url(&stringToSplit[offset], foundEndIndex - offset); - textElements.push_back({ url, TextElement::Type::EMOJI }); - offset = foundEndIndex + 1; + size_t urlEnd = stringSplitUrl(textElement.text, httpStr, offset, newTextElements); + if(urlEnd == -1) + { + StringViewUtf32 strToEnd(textElement.text.data + offset, textElement.text.size - offset); + newTextElements.push_back({ strToEnd, TextElement::Type::TEXT }); + offset = textElement.text.size; + } + else + offset = urlEnd; } - else + } + textElements = newTextElements; + + newTextElements.clear(); + for(size_t i = 0; i < textElements.size(); ++i) + { + TextElement textElement = textElements[i]; + if(textElement.type != TextElement::Type::TEXT) + { + newTextElements.push_back(textElement); + continue; + } + + size_t offset = 0; + while(offset < textElement.text.size) { - StringViewUtf32 strToEnd(&stringToSplit[stringStart], stringToSplit.getSize() - stringStart); - textElements.push_back({ strToEnd, TextElement::Type::TEXT }); - offset = stringToSplit.getSize(); + size_t urlEnd = stringSplitUrl(textElement.text, httpsStr, offset, newTextElements); + if(urlEnd == -1) + { + StringViewUtf32 strToEnd(textElement.text.data + offset, textElement.text.size - offset); + newTextElements.push_back({ strToEnd, TextElement::Type::TEXT }); + offset = textElement.text.size; + } + else + offset = urlEnd; } } + textElements = newTextElements; for(std::vector::iterator it = textElements.begin(); it != textElements.end();) { @@ -302,10 +405,12 @@ namespace dchat sf::Vector2f textureBottomLeft(glyph.textureRect.left, glyph.textureRect.top + glyph.textureRect.height); sf::Vector2f textureBottomRight(glyph.textureRect.left + glyph.textureRect.width, glyph.textureRect.top + glyph.textureRect.height); - vertices[vertexOffset + i * 4 + 0] = { vertexTopLeft, color, textureTopLeft }; - vertices[vertexOffset + i * 4 + 1] = { vertexTopRight, color, textureTopRight }; - vertices[vertexOffset + i * 4 + 2] = { vertexBottomRight, color, textureBottomRight }; - vertices[vertexOffset + i * 4 + 3] = { vertexBottomLeft, color, textureBottomLeft }; + sf::Color fontColor = (textElement.type == TextElement::Type::TEXT ? color : urlColor); + + vertices[vertexOffset + i * 4 + 0] = { vertexTopLeft, fontColor, textureTopLeft }; + vertices[vertexOffset + i * 4 + 1] = { vertexTopRight, fontColor, textureTopRight }; + vertices[vertexOffset + i * 4 + 2] = { vertexBottomRight, fontColor, textureBottomRight }; + vertices[vertexOffset + i * 4 + 3] = { vertexBottomLeft, fontColor, textureBottomLeft }; glyphPos.x += glyph.advance; } -- cgit v1.2.3