aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Channel.cpp16
-rw-r--r--src/Chatbar.cpp2
-rw-r--r--src/Gif.cpp4
-rw-r--r--src/Message.cpp62
-rw-r--r--src/MessageBoard.cpp170
-rw-r--r--src/MessagePart.cpp62
-rw-r--r--src/Text.cpp308
-rw-r--r--src/main.cpp11
8 files changed, 351 insertions, 284 deletions
diff --git a/src/Channel.cpp b/src/Channel.cpp
index 5e81f37..f240085 100644
--- a/src/Channel.cpp
+++ b/src/Channel.cpp
@@ -10,22 +10,22 @@ namespace dchat
localOfflineUser("You")
{
{
- Message *message = new Message(&localOfflineUser);
- message->addText(u8"hello, worldåäö1!", false);
- message->addEmoji("https://discordemoji.com/assets/emoji/playtime.png");
+ Message *message = new Message(&localOfflineUser, u8"hello, worldåäö1![emoji](https://discordemoji.com/assets/emoji/playtime.png)");
messageBoard.addMessage(message);
}
{
- Message *message = new Message(&localOfflineUser);
- message->addText(u8"hello, world2!", false);
- message->addEmoji("https://discordemoji.com/assets/emoji/Feels3DMan.gif");
+ Message *message = new Message(&localOfflineUser, u8"hello, world2![emoji](https://discordemoji.com/assets/emoji/Feels3DMan.gif)");
messageBoard.addMessage(message);
}
{
- Message *message = new Message(&localOfflineUser);
- message->addText(u8"hello, world3!");
+ Message *message = new Message(&localOfflineUser, 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(&localOfflineUser, u8"xddd");
messageBoard.addMessage(message);
}
}
diff --git a/src/Chatbar.cpp b/src/Chatbar.cpp
index bdfc75d..d775db4 100644
--- a/src/Chatbar.cpp
+++ b/src/Chatbar.cpp
@@ -114,7 +114,7 @@ namespace dchat
string msg;
msg.resize(chatbarMsgUtf8.size());
memcpy(&msg[0], chatbarMsgUtf8.data(), chatbarMsgUtf8.size());
- channel->getMessageBoard().addMessage(Message::buildFromString(channel->getLocalUser(), msg));
+ channel->getMessageBoard().addMessage(new Message(channel->getLocalUser(), msg));
clear();
}
}
diff --git a/src/Gif.cpp b/src/Gif.cpp
index 3f7216e..b1f4fd3 100644
--- a/src/Gif.cpp
+++ b/src/Gif.cpp
@@ -127,7 +127,7 @@ namespace dchat
sprite.setScale(size.x / (float)textureSize.x, size.y / (float)textureSize.y);
}
- void Gif::draw(sf::RenderWindow &window)
+ void Gif::draw(sf::RenderTarget &target)
{
double frameDeltaCs = (double)frameTimer.getElapsedTime().asMilliseconds() * 0.1; // Centisecond
frameTimer.restart();
@@ -159,7 +159,7 @@ namespace dchat
texture.update(image);
sprite.setTexture(texture, true);
}
- window.draw(sprite);
+ target.draw(sprite);
}
bool Gif::isDataGif(const StringView &data)
diff --git a/src/Message.cpp b/src/Message.cpp
index 19630b3..285a722 100644
--- a/src/Message.cpp
+++ b/src/Message.cpp
@@ -1,67 +1,15 @@
#include "../include/Message.hpp"
-#include "../include/StringView.hpp"
+#include "../include/ResourceCache.hpp"
+#include "../include/Settings.hpp"
using namespace std;
namespace dchat
{
- Message::Message(User *_user) :
- user(_user)
+ Message::Message(User *_user, const std::string &_text) :
+ user(_user),
+ text(sf::String::fromUtf8(_text.begin(), _text.end()), ResourceCache::getFont("fonts/Roboto-Regular.ttf"), 17 * Settings::getScaling(), 0.0f, false)
{
}
-
- Message::~Message()
- {
- for(MessagePart *messagePart : messageParts)
- {
- delete messagePart;
- }
- }
-
- void Message::addText(const string &text, bool newLine)
- {
- messageParts.push_back(new MessagePartText(text, newLine));
- }
-
- void Message::addEmoji(const string &url, bool newLine)
- {
- messageParts.push_back(new MessagePartEmoji(url, newLine));
- }
-
- vector<MessagePart*>& Message::getParts()
- {
- return messageParts;
- }
-
- StringView getNextNewLine(const StringView &str)
- {
- for(usize i = 0; i < str.size; ++i)
- {
- if(str[i] == '\n')
- return StringView(str.data, i);
- }
- return StringView();
- }
-
- Message* Message::buildFromString(User *user, const std::string &str)
- {
- Message *message = new Message(user);
- usize strOffset = 0;
- while(strOffset < str.size())
- {
- usize foundIndex = str.find('\n', strOffset);
- usize lineEnd = foundIndex;
- if(foundIndex == string::npos)
- lineEnd = str.size();
-
- message->addText(str.substr(strOffset, lineEnd - strOffset), foundIndex != string::npos);
-
- if(foundIndex == string::npos)
- break;
- else
- strOffset = lineEnd + 1;
- }
- return message;
- }
}
diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp
index d39e8be..fd41f02 100644
--- a/src/MessageBoard.cpp
+++ b/src/MessageBoard.cpp
@@ -6,6 +6,7 @@
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Window/Mouse.hpp>
#include <SFML/Graphics/Rect.hpp>
+#include <SFML/Graphics/Text.hpp>
#include <cmath>
using namespace std;
@@ -13,7 +14,7 @@ using namespace std;
namespace dchat
{
const sf::Color BACKGROUND_COLOR(40, 40, 40);
- const float USERNAME_PADDING_BOTTOM = 5.0f;
+ const float USERNAME_PADDING_BOTTOM = 0.0f;
const float MESSAGE_PADDING_BOTTOM = 20.0f;
MessageBoard::MessageBoard(const sf::Vector2u &size) :
@@ -95,95 +96,29 @@ namespace dchat
if(backgroundSize != staticContentTexture.getSize())
updateStaticContentTexture(backgroundSize);
+ // TODO: Remove this when dchat::Text can render to static and dynamic render target
+ dirty = true;
if(dirty)
- staticContentTexture.clear(sf::Color::Transparent);
+ staticContentTexture.clear(BACKGROUND_COLOR);
const sf::Font &usernameFont = ResourceCache::getFont("fonts/Roboto-Regular.ttf");
- sf::Vector2f position;
- for(Message *message : messages)
+ if(dirty)
{
- sf::Text usernameText(message->user->getName(), usernameFont, MessagePartText::getFontSizeScaled() * 1.3f);
- usernameText.setFillColor(sf::Color(15, 192, 252));
- usernameText.setPosition(position);
- if(dirty)
- staticContentTexture.draw(usernameText);
- position.y += usernameText.getCharacterSize() + USERNAME_PADDING_BOTTOM;
-
- int index = 0;
- int numParts = message->getParts().size();
- for(MessagePart *messagePart : message->getParts())
+ sf::Vector2f position;
+ for(Message *message : messages)
{
- switch(messagePart->type)
- {
- case MessagePart::Type::TEXT:
- {
- MessagePartText *messagePartText = static_cast<MessagePartText*>(messagePart);
- messagePartText->text.setFillColor(sf::Color(240, 240, 240));
- messagePartText->text.setCharacterSize(MessagePartText::getFontSizeScaled());
- messagePartText->text.setPosition(floor(position.x), floor(position.y + MessagePart::getSizeScaled() * 0.5f - MessagePartText::getFontSizeScaled() * 0.5f));
- if(dirty)
- staticContentTexture.draw(messagePartText->text);
- position.x += messagePartText->text.getLocalBounds().width;
- break;
- }
- case MessagePart::Type::EMOJI:
- {
- MessagePartEmoji *messagePartEmoji = static_cast<MessagePartEmoji*>(messagePart);
- position.x += 5.0f;
- auto imageByUrlResult = cache.getImageByUrl(messagePartEmoji->url, 1024 * 512);
- bool imageDrawn = false;
- if(imageByUrlResult.isGif && imageByUrlResult.gif)
- {
- sf::Vector2f pos(backgroundPos.x + floor(position.x), backgroundPos.y + floor(position.y + MessagePart::getSizeScaled() * 0.5f - MessagePartEmoji::getHeightScaled() * 0.5f));
- imageByUrlResult.gif->setPosition(pos);
- imageByUrlResult.gif->setSize(sf::Vector2f(MessagePartEmoji::getHeightScaled(), MessagePartEmoji::getHeightScaled()));
- imageByUrlResult.gif->draw(window);
- imageDrawn = true;
- }
- else
- {
- // Emoji is dirty when it's created, but render target can become dirty after emoji has been added, so we need to set emoji as dirty then
- if(dirty)
- messagePartEmoji->dirty = true;
- if(imageByUrlResult.texture)
- {
- // TODO: Verify this doesn't cause lag
- messagePartEmoji->sprite.setTexture(*imageByUrlResult.texture, true);
- sf::Vector2f spriteSize(MessagePartEmoji::getHeightScaled(), MessagePartEmoji::getHeightScaled());
- messagePartEmoji->sprite.setScale(spriteSize.x / (float)imageByUrlResult.texture->getSize().x, spriteSize.y / (float)imageByUrlResult.texture->getSize().y);
- messagePartEmoji->sprite.setPosition(floor(position.x), floor(position.y + MessagePart::getSizeScaled() * 0.5f - MessagePartEmoji::getHeightScaled() * 0.5f));
- if(messagePartEmoji->dirty)
- {
- messagePartEmoji->dirty = false;
- staticContentTexture.draw(messagePartEmoji->sprite);
- }
- imageDrawn = true;
- }
- }
-
- if(!imageDrawn)
- {
- // TODO: Replace this with a loading gif
- sf::RectangleShape emojiDownloadRect(sf::Vector2f(MessagePartEmoji::getHeightScaled(), MessagePartEmoji::getHeightScaled()));
- emojiDownloadRect.setPosition(backgroundPos.x + floor(position.x), backgroundPos.y + floor(position.y + MessagePart::getSizeScaled() * 0.5f - MessagePartEmoji::getHeightScaled() * 0.5f));
- emojiDownloadRect.setFillColor(sf::Color::White);
- window.draw(emojiDownloadRect);
- }
- position.x += MessagePartEmoji::getHeightScaled() + 5.0f;
- break;
- }
- }
+ sf::Text usernameText(message->user->getName(), usernameFont, 20 * Settings::getScaling());
+ usernameText.setFillColor(sf::Color(15, 192, 252));
+ usernameText.setPosition(position);
+ staticContentTexture.draw(usernameText);
+ position.y += usernameText.getFont()->getLineSpacing(usernameText.getCharacterSize()) + USERNAME_PADDING_BOTTOM;
- if(index < numParts - 1 && messagePart->newLine)
- {
- position.x = 0.0f;
- position.y += MessagePart::getSizeScaled();
- }
- ++index;
+ message->text.setMaxWidth(backgroundSize.x);
+ message->text.setPosition(position);
+ message->text.draw(staticContentTexture, cache);
+ position.y += message->text.getHeight() + MESSAGE_PADDING_BOTTOM;
}
- position.x = 0.0f;
- position.y += MessagePart::getSizeScaled() + MESSAGE_PADDING_BOTTOM;
}
staticContentTexture.display();
@@ -199,76 +134,5 @@ namespace dchat
sf::Vector2f selectionRectStart(min((float)mousePos.x, selectingTextStart.x), min((float)mousePos.y, selectingTextStart.y));
sf::Vector2f selectionRectEnd(max((float)mousePos.x, selectingTextStart.x), max((float)mousePos.y, selectingTextStart.y));
sf::FloatRect selectionRect(selectionRectStart, selectionRectEnd - selectionRectStart);
-#if 0
- // TODO: Remove this, put logic in render loop above
- for(Message *message : messages)
- {
- float messagePartStartX = -999.0f;
- float messagePartEndX = 0.0f;
- float messagePartX = 0.0f;
- float messagePartStartY = 0.0f;
-
- for(MessagePart *messagePart : message->getParts())
- {
- sf::Vector2f position = messagePart->getPosition();
- sf::Vector2f size = messagePart->getSize();
- sf::FloatRect messagePartRect(position, size);
- if(!selectionRect.intersects(messagePartRect)) continue;
-
- switch(messagePart->type)
- {
- case MessagePart::Type::TEXT:
- {
- MessagePartText *messagePartText = static_cast<MessagePartText*>(messagePart);
- messagePartStartY = position.y;
- sf::Uint32 prevCodePoint = -1;
- for(int i = 0; i < messagePartText->text.getString().getSize(); ++i)
- {
- sf::Uint32 codePoint = messagePartText->text.getString()[i];
- const sf::Glyph &glyph = messagePartText->text.getFont()->getGlyph(codePoint, messagePartText->text.getCharacterSize(), false);
- float glyphWidth = glyph.advance;
- if(prevCodePoint != -1)
- glyphWidth += messagePartText->text.getFont()->getKerning(prevCodePoint, codePoint, messagePartText->text.getCharacterSize());
-
- if(selectionRect.left < messagePartX + glyph.advance * 0.5f)
- {
- if(messagePartStartX < 0.0f)
- {
- messagePartStartX = messagePartX;
- if(mousePos.y > messagePartStartY + MessagePart::getSizeScaled())
- {
- messagePartEndX = position.x + size.x;
- goto nextMessagePart;
- }
- }
- }
-
- if(selectionRect.left + selectionRect.width > messagePartX + glyph.advance * 0.5f)
- {
- messagePartEndX = messagePartX + glyphWidth;
- }
- else
- break;
-
- messagePartX += glyphWidth;
- prevCodePoint = codePoint;
- }
- break;
- }
- }
-
- nextMessagePart:
- ;
- }
-
- if(messagePartStartX >= 0.0f)
- {
- sf::RectangleShape selectionShape(sf::Vector2f(floor(messagePartEndX - messagePartStartX), floor(MessagePart::getSizeScaled())));
- selectionShape.setPosition(messagePartStartX, messagePartStartY);
- selectionShape.setFillColor(sf::Color(100, 100, 255, 100));
- window.draw(selectionShape);
- }
- }
-#endif
}
}
diff --git a/src/MessagePart.cpp b/src/MessagePart.cpp
deleted file mode 100644
index 215c239..0000000
--- a/src/MessagePart.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "../include/MessagePart.hpp"
-#include "../include/ResourceCache.hpp"
-#include "../include/Settings.hpp"
-
-using namespace std;
-
-namespace dchat
-{
- const float MESSAGE_PART_SIZE = 27;
-
- float MessagePart::getSizeScaled()
- {
- return MESSAGE_PART_SIZE * Settings::getScaling();
- }
-
- MessagePartText::MessagePartText(const string &_text, bool _newLine) :
- MessagePart(Type::TEXT, _newLine),
- text("", ResourceCache::getFont("fonts/Roboto-Regular.ttf"), MessagePartText::getFontSizeScaled())
- {
- text.setString(sf::String::fromUtf8(_text.begin(), _text.end()));
- }
-
- float MessagePartText::getFontSizeScaled()
- {
- return MessagePart::getSizeScaled() * 0.8f;
- }
-
- sf::Vector2f MessagePartText::getPosition() const
- {
- return text.getPosition();
- }
-
- sf::Vector2f MessagePartText::getSize() const
- {
- return sf::Vector2f(text.getLocalBounds().width, getFontSizeScaled());
- }
-
- MessagePartEmoji::MessagePartEmoji(const string &_url, bool _newLine) :
- MessagePart(Type::EMOJI, _newLine),
- url(_url),
- dirty(true)
- {
-
- }
-
- float MessagePartEmoji::getHeightScaled()
- {
- return MessagePart::getSizeScaled() * 1.0f;
- }
-
- sf::Vector2f MessagePartEmoji::getPosition() const
- {
- return sprite.getPosition();
- }
-
- sf::Vector2f MessagePartEmoji::getSize() const
- {
- auto spriteScale = sprite.getScale();
- auto textureSize = sprite.getTexture()->getSize();
- return { (float)textureSize.x * spriteScale.x, (float)textureSize.y * spriteScale.y };
- }
-}
diff --git a/src/Text.cpp b/src/Text.cpp
new file mode 100644
index 0000000..41eb4e3
--- /dev/null
+++ b/src/Text.cpp
@@ -0,0 +1,308 @@
+#include "../include/Text.hpp"
+#include "../include/Cache.hpp"
+#include "../include/Gif.hpp"
+#include <SFML/Graphics/RectangleShape.hpp>
+
+namespace dchat
+{
+ const float TAB_WIDTH = 4.0f;
+ const float EMOJI_PADDING = 5.0f;
+
+ Text::Text(const sf::Font &_font) :
+ font(_font),
+ characterSize(0),
+ maxWidth(0.0f),
+ color(sf::Color::White),
+ dirty(false),
+ plainText(false),
+ totalHeight(0.0f)
+ {
+
+ }
+
+ Text::Text(const sf::String &_str, const sf::Font &_font, unsigned int _characterSize, float _maxWidth, bool _plainText) :
+ font(_font),
+ characterSize(_characterSize),
+ vertices(sf::PrimitiveType::Quads),
+ maxWidth(_maxWidth),
+ color(sf::Color::White),
+ dirty(true),
+ plainText(_plainText),
+ totalHeight(0.0f)
+ {
+ setString(_str);
+ }
+
+ void Text::setString(const sf::String &str)
+ {
+ if(str != this->str)
+ {
+ this->str = str;
+ dirty = true;
+ stringSplitElements();
+ }
+ }
+
+ void Text::setPosition(float x, float y)
+ {
+ position.x = x;
+ position.y = y;
+ }
+
+ void Text::setPosition(const sf::Vector2f &position)
+ {
+ this->position = position;
+ }
+
+ void Text::setMaxWidth(float maxWidth)
+ {
+ if(maxWidth != this->maxWidth)
+ {
+ this->maxWidth = maxWidth;
+ dirty = true;
+ }
+ }
+
+ void Text::setCharacterSize(unsigned int characterSize)
+ {
+ if(characterSize != this->characterSize)
+ {
+ this->characterSize = characterSize;
+ dirty = true;
+ }
+ }
+
+ void Text::setFillColor(sf::Color color)
+ {
+ if(color != this->color)
+ {
+ this->color = color;
+ dirty = true;
+ }
+ }
+
+ float Text::getHeight() const
+ {
+ return totalHeight;
+ }
+
+ void Text::stringSplitElements()
+ {
+ textElements.clear();
+ if(plainText)
+ {
+ StringViewUtf32 wholeStr(&str[0], str.getSize());
+ textElements.push_back({ wholeStr, TextElement::Type::TEXT });
+ return;
+ }
+
+ size_t offset = 0;
+ while(offset < str.getSize())
+ {
+ size_t stringStart = offset;
+ size_t foundStartIndex = str.find("[emoji](", offset);
+ size_t foundEndIndex = -1;
+ if(foundStartIndex != -1)
+ {
+ offset += (foundStartIndex + 8);
+ foundEndIndex = str.find(")", offset);
+ }
+
+ if(foundEndIndex != -1)
+ {
+ StringViewUtf32 beforeEmojiStr(&str[stringStart], foundStartIndex - stringStart);
+ textElements.push_back({ beforeEmojiStr, TextElement::Type::TEXT });
+
+ StringViewUtf32 url(&str[offset], foundEndIndex - offset);
+ textElements.push_back({ url, TextElement::Type::EMOJI });
+ offset = foundEndIndex + 1;
+ }
+ else
+ {
+ StringViewUtf32 strToEnd(&str[stringStart], str.getSize() - stringStart);
+ textElements.push_back({ strToEnd, TextElement::Type::TEXT });
+ offset = str.getSize();
+ }
+ }
+ }
+
+ // Logic loosely based on https://github.com/SFML/SFML/wiki/Source:-CurvedText
+ void Text::updateGeometry()
+ {
+ vertices.clear();
+ float hspace = font.getGlyph(' ', characterSize, false).advance;
+ float vspace = font.getLineSpacing(characterSize);
+ float emojiSize = vspace * 1.0;
+
+ sf::Vector2f glyphPos;
+ sf::Uint32 prevCodePoint = 0;
+ size_t lastSpacingWordWrapIndex = -1;
+ float lastSpacingAccumulatedOffset = 0.0f;
+ for(TextElement &textElement : textElements)
+ {
+ if(textElement.type == TextElement::Type::EMOJI)
+ {
+ glyphPos.x += EMOJI_PADDING;
+ textElement.position.x = glyphPos.x;
+ textElement.position.y = glyphPos.y + vspace * 0.5f - emojiSize * 0.5f;
+ glyphPos.x += emojiSize + EMOJI_PADDING;
+ if(glyphPos.x > maxWidth)
+ {
+ glyphPos.x = 0.0f;
+ glyphPos.y += vspace;
+ }
+ continue;
+ }
+
+ usize vertexOffset = vertices.getVertexCount();
+ vertices.resize(vertices.getVertexCount() + (4 * textElement.text.size));
+ textElement.position = glyphPos;
+ for(size_t i = 0; i < textElement.text.size; ++i)
+ {
+ sf::Uint32 codePoint = textElement.text[i];
+ float kerning = font.getKerning(prevCodePoint, codePoint, characterSize);
+ prevCodePoint = codePoint;
+ glyphPos.x += kerning;
+
+ switch(codePoint)
+ {
+ case ' ':
+ {
+ glyphPos.x += hspace;
+ if(glyphPos.x > maxWidth * 0.5f)
+ {
+ lastSpacingWordWrapIndex = i;
+ lastSpacingAccumulatedOffset = glyphPos.x;
+ }
+ continue;
+ }
+ case '\t':
+ {
+ glyphPos.x += (hspace * TAB_WIDTH);
+ if(glyphPos.x > maxWidth * 0.5f)
+ {
+ lastSpacingWordWrapIndex = i;
+ lastSpacingAccumulatedOffset = glyphPos.x;
+ }
+ continue;
+ }
+ case '\n':
+ {
+ glyphPos.x = 0.0f;
+ glyphPos.y += vspace;
+ continue;
+ }
+ case '\v':
+ {
+ glyphPos.y += (vspace * TAB_WIDTH);
+ continue;
+ }
+ }
+
+ const sf::Glyph &glyph = font.getGlyph(codePoint, characterSize, false);
+ if(glyphPos.x + glyph.advance > maxWidth)
+ {
+ // If there was a space in the text and text width is too long, then we need to word wrap at space index instead,
+ // which means we need to change the position of all vertices after the space to the current vertex
+ //printf("last spacing word wrap index: %zu\n", lastSpacingWordWrapIndex);
+ if(lastSpacingWordWrapIndex != -1)
+ {
+ for(size_t j = lastSpacingWordWrapIndex; j < i; ++j)
+ {
+ for(size_t k = 0; k < 4; ++k)
+ {
+ sf::Vector2f &vertexPos = vertices[vertexOffset + j * 4 + k].position;
+ vertexPos.x -= lastSpacingAccumulatedOffset;
+ vertexPos.y += vspace;
+ }
+ }
+
+ glyphPos.x -= lastSpacingAccumulatedOffset;
+ lastSpacingWordWrapIndex = -1;
+ lastSpacingAccumulatedOffset = 0.0f;
+ }
+ else
+ glyphPos.x = 0.0f;
+
+ glyphPos.y += vspace;
+ }
+
+ sf::Vector2f vertexTopLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top);
+ sf::Vector2f vertexTopRight(glyphPos.x + glyph.bounds.left + glyph.bounds.width, glyphPos.y + glyph.bounds.top);
+ sf::Vector2f vertexBottomLeft(glyphPos.x + glyph.bounds.left, glyphPos.y + glyph.bounds.top + glyph.bounds.height);
+ sf::Vector2f vertexBottomRight(glyphPos.x + glyph.bounds.left + glyph.bounds.width, glyphPos.y + glyph.bounds.top + glyph.bounds.height);
+
+ sf::Vector2f textureTopLeft(glyph.textureRect.left, glyph.textureRect.top);
+ sf::Vector2f textureTopRight(glyph.textureRect.left + glyph.textureRect.width, glyph.textureRect.top);
+ 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 };
+
+ glyphPos.x += glyph.advance;
+ }
+ }
+ totalHeight = glyphPos.y + vspace;
+ }
+
+ void Text::draw(sf::RenderTarget &target, Cache &cache)
+ {
+ if(dirty)
+ {
+ updateGeometry();
+ dirty = false;
+ }
+
+ float vspace = font.getLineSpacing(characterSize);
+ float emojiSize = vspace * 1.0;
+
+ sf::RenderStates states;
+ sf::Vector2f pos = position;
+ pos.y += vspace; // Origin is at bottom left, we want it to be at top left
+ states.transform.translate(pos);
+ states.texture = &font.getTexture(characterSize);
+ target.draw(vertices, states);
+
+ for(TextElement &textElement : textElements)
+ {
+ if(textElement.type == TextElement::Type::EMOJI)
+ {
+ sf::Vector2f pos = position;
+ pos += textElement.position;
+ sf::Vector2f size(emojiSize, emojiSize);
+
+ // TODO: Optimize this (add unordered_map that takes StringViewUtf32 as key)
+ auto u8Str = sf::String::fromUtf32(textElement.text.data, textElement.text.data + textElement.text.size).toUtf8();
+ const std::string &utf8Str = *(std::basic_string<char>*)&u8Str;
+ const ImageByUrlResult imageByUrlResult = cache.getImageByUrl(utf8Str);
+ if(imageByUrlResult.type == ImageByUrlResult::Type::CACHED)
+ {
+ if(imageByUrlResult.isGif)
+ {
+ imageByUrlResult.gif->setPosition(pos);
+ imageByUrlResult.gif->setSize(size);
+ imageByUrlResult.gif->draw(target);
+ }
+ else
+ {
+ // TODO: Store this sprite somewhere, might not be efficient to create a new sprite object every frame
+ sf::Sprite sprite(*imageByUrlResult.texture);
+ sprite.setPosition(pos);
+ sprite.setScale(size.x / (float)imageByUrlResult.texture->getSize().x, size.y / (float)imageByUrlResult.texture->getSize().y);
+ target.draw(sprite);
+ }
+ }
+ else
+ {
+ sf::RectangleShape rect(size);
+ rect.setFillColor(sf::Color::White);
+ rect.setPosition(pos);
+ target.draw(rect);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main.cpp b/src/main.cpp
index 3ca986f..df5c558 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,17 +1,26 @@
#include "../include/Channel.hpp"
#include "../include/ChannelSidePanel.hpp"
#include "../include/Cache.hpp"
+#include "../include/ResourceCache.hpp"
#include <string>
#include <SFML/Graphics.hpp>
#include <cstring>
#include <X11/Xlib.h>
+#include <boost/filesystem/path.hpp>
using namespace std;
using namespace dchat;
using namespace TinyProcessLib;
-int main()
+int main(int argc, char **argv)
{
+ /*
+ boost::filesystem::path programPath(argv[0]);
+ auto parentPath = programPath.parent_path();
+ printf("parent path: %s\n", parentPath.string().c_str());
+ boost::filesystem::current_path(parentPath); // Ensures loading of resources works no matter which path we run this executable from
+ */
+
XInitThreads();
sf::RenderWindow window(sf::VideoMode(1920, 1080), "dchat");
window.setVerticalSyncEnabled(false);