aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/launch.json27
-rw-r--r--include/MessageBoard.hpp3
-rw-r--r--include/StringView.hpp15
-rw-r--r--include/Text.hpp8
-rw-r--r--src/Channel.cpp10
-rw-r--r--src/Message.cpp2
-rw-r--r--src/MessageBoard.cpp52
-rw-r--r--src/Text.cpp95
-rw-r--r--src/main.cpp6
9 files changed, 171 insertions, 47 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..d49b13b
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/sibs-build/debug/dchat",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${workspaceFolder}",
+ "environment": [],
+ "externalConsole": true,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/include/MessageBoard.hpp b/include/MessageBoard.hpp
index 06a7cd1..ca1405f 100644
--- a/include/MessageBoard.hpp
+++ b/include/MessageBoard.hpp
@@ -29,5 +29,8 @@ namespace dchat
sf::Vector2f mousePos;
sf::Vector2f selectingTextStart;
std::vector<Message*> messages;
+ double scroll;
+ double scrollSpeed;
+ sf::Clock frameTimer;
};
}
diff --git a/include/StringView.hpp b/include/StringView.hpp
index 4e9066b..9eea387 100644
--- a/include/StringView.hpp
+++ b/include/StringView.hpp
@@ -15,7 +15,7 @@ namespace dchat
}
- BasicStringView(const BasicStringView<CharType> &other) : data(other.data), size(other.size)
+ BasicStringView(const BasicStringView<CharType> &other) : data(other.data), size(other.size)
{
}
@@ -30,13 +30,14 @@ namespace dchat
}
- BasicStringView<CharType> operator = (const BasicStringView<CharType> &other)
+ BasicStringView<CharType>& operator = (const BasicStringView<CharType> &other)
{
- BasicStringView<CharType> result(other.data, other.size);
- return result;
+ data = other.data;
+ size = other.size;
+ return *this;
}
- BasicStringView( BasicStringView<CharType> &&other)
+ BasicStringView(BasicStringView<CharType> &&other)
{
data = other.data;
size = other.size;
@@ -45,10 +46,10 @@ namespace dchat
other.size = 0;
}
- bool equals(const BasicStringView<CharType> &other) const
+ bool equals(const BasicStringView<CharType> &other) const
{
if(size != other.size) return false;
- return memcmp(data, other.data, size) == 0;
+ return memcmp(data, other.data, size * sizeof(CharType)) == 0;
}
CharType operator [] (usize index) const
diff --git a/include/Text.hpp b/include/Text.hpp
index 53db93b..93ec0c1 100644
--- a/include/Text.hpp
+++ b/include/Text.hpp
@@ -17,6 +17,8 @@ namespace dchat
Text(const sf::String &str, const sf::Font &font, unsigned int characterSize, float maxWidth, bool plainText = true);
void setString(const sf::String &str);
+ void appendStringNewLine(const sf::String &str);
+
void setPosition(float x, float y);
void setPosition(const sf::Vector2f &position);
void setMaxWidth(float maxWidth);
@@ -26,9 +28,10 @@ namespace dchat
// Warning: won't update until @draw is called
float getHeight() const;
+ // Performs culling. @updateGeometry is called even if text is not visible if text is dirty, because updateGeometry might change the dimension of the text and make is visible
void draw(sf::RenderTarget &target, Cache &cache);
private:
- void stringSplitElements();
+ void stringSplitElements(sf::String &stringToSplit, usize startIndex);
void updateGeometry();
private:
struct TextElement
@@ -40,11 +43,12 @@ namespace dchat
};
TextElement() {}
- TextElement(const StringViewUtf32 &_text, Type _type) : text(_text), type(_type) {}
+ 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
};
sf::String str;
diff --git a/src/Channel.cpp b/src/Channel.cpp
index f240085..e16b25f 100644
--- a/src/Channel.cpp
+++ b/src/Channel.cpp
@@ -28,6 +28,16 @@ namespace dchat
Message *message = new Message(&localOfflineUser, u8"xddd");
messageBoard.addMessage(message);
}
+
+ {
+ Message *message = new Message(&localOfflineUser, u8"[emoji](https://discordemoji.com/assets/emoji/SlowbroDumb.png)");
+ messageBoard.addMessage(message);
+ }
+
+ {
+ Message *message = new Message(&localOfflineUser, u8"Message after big emoji");
+ messageBoard.addMessage(message);
+ }
}
Channel::~Channel()
diff --git a/src/Message.cpp b/src/Message.cpp
index 285a722..b72eabe 100644
--- a/src/Message.cpp
+++ b/src/Message.cpp
@@ -8,7 +8,7 @@ namespace dchat
{
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)
+ text(sf::String::fromUtf8(_text.begin(), _text.end()), ResourceCache::getFont("fonts/Roboto-Regular.ttf"), 20 * Settings::getScaling(), 0.0f, false)
{
}
diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp
index fd41f02..0b0e2fe 100644
--- a/src/MessageBoard.cpp
+++ b/src/MessageBoard.cpp
@@ -19,7 +19,9 @@ namespace dchat
MessageBoard::MessageBoard(const sf::Vector2u &size) :
selectingText(false),
- leftMouseButtonPressed(false)
+ leftMouseButtonPressed(false),
+ scroll(0.0),
+ scrollSpeed(0.0)
{
updateStaticContentTexture(size);
}
@@ -74,6 +76,10 @@ namespace dchat
mousePos.x = event.mouseMove.x;
mousePos.y = event.mouseMove.y;
}
+ else if(event.type == sf::Event::MouseWheelScrolled && event.mouseWheelScroll.wheel == sf::Mouse::Wheel::VerticalWheel)
+ {
+ scrollSpeed += (event.mouseWheelScroll.delta * 30.0);
+ }
if(selectingText && !leftMouseButtonPressed)
{
@@ -93,41 +99,53 @@ namespace dchat
sf::Vector2u backgroundSize(floor(windowSize.x * 0.7f), floor(windowSize.y));
sf::Vector2f backgroundPos(floor(windowSize.x * 0.5f - backgroundSize.x * 0.5f), 0.0f);
- if(backgroundSize != staticContentTexture.getSize())
- updateStaticContentTexture(backgroundSize);
+ //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(BACKGROUND_COLOR);
+ //if(dirty)
+ // staticContentTexture.clear(BACKGROUND_COLOR);
const sf::Font &usernameFont = ResourceCache::getFont("fonts/Roboto-Regular.ttf");
+ double deltaTimeMicro = (double)frameTimer.getElapsedTime().asMicroseconds();
+ frameTimer.restart();
+
+ scroll += scrollSpeed;
+ scrollSpeed /= (deltaTimeMicro * 0.0001);
+
if(dirty)
{
- sf::Vector2f position;
+ sf::Vector2f position = backgroundPos;
+ position.y += scroll;
for(Message *message : messages)
{
- 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;
+ sf::Text usernameText(message->user->getName(), usernameFont, 24 * Settings::getScaling());
+ float usernameTextHeight = usernameText.getFont()->getLineSpacing(usernameText.getCharacterSize());
+ if(position.y + usernameTextHeight > 0.0f && position.y < backgroundSize.y)
+ {
+ usernameText.setFillColor(sf::Color(15, 192, 252));
+ usernameText.setPosition(sf::Vector2f(floor(position.x), floor(position.y)));
+ window.draw(usernameText);
+ }
+ position.y += usernameTextHeight + USERNAME_PADDING_BOTTOM;
+ // No need to perform culling here, that is done in @Text draw function
message->text.setMaxWidth(backgroundSize.x);
- message->text.setPosition(position);
- message->text.draw(staticContentTexture, cache);
+ message->text.setPosition(sf::Vector2f(floor(position.x), floor(position.y)));
+ message->text.draw(window, cache);
position.y += message->text.getHeight() + MESSAGE_PADDING_BOTTOM;
}
}
- staticContentTexture.display();
+ //staticContentTexture.display();
dirty = false;
// TODO: Save this, expensive to create on fly?
- sf::Sprite textureSprite(staticContentTexture.getTexture());
- textureSprite.setPosition(backgroundPos);
- window.draw(textureSprite);
+ //sf::Sprite textureSprite(staticContentTexture.getTexture());
+ //textureSprite.setPosition(backgroundPos);
+ //window.draw(textureSprite);
if(!selectingText) return;
diff --git a/src/Text.cpp b/src/Text.cpp
index 41eb4e3..fc3effe 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -2,11 +2,14 @@
#include "../include/Cache.hpp"
#include "../include/Gif.hpp"
#include <SFML/Graphics/RectangleShape.hpp>
+#include <cmath>
namespace dchat
{
const float TAB_WIDTH = 4.0f;
const float EMOJI_PADDING = 5.0f;
+ const float EMOJI_SCALE_WITH_TEXT = 1.0f;
+ const float EMOJI_SCALE_STANDALONE = 5.0f;
Text::Text(const sf::Font &_font) :
font(_font),
@@ -39,10 +42,20 @@ namespace dchat
{
this->str = str;
dirty = true;
- stringSplitElements();
+ textElements.clear();
+ stringSplitElements(this->str, 0);
}
}
+ void Text::appendStringNewLine(const sf::String &str)
+ {
+ usize prevSize = this->str.getSize();
+ this->str += '\n';
+ this->str += str;
+ dirty = true;
+ stringSplitElements(this->str, prevSize);
+ }
+
void Text::setPosition(float x, float y)
{
position.x = x;
@@ -86,44 +99,51 @@ namespace dchat
return totalHeight;
}
- void Text::stringSplitElements()
+ void Text::stringSplitElements(sf::String &stringToSplit, usize startIndex)
{
- textElements.clear();
if(plainText)
{
- StringViewUtf32 wholeStr(&str[0], str.getSize());
+ StringViewUtf32 wholeStr(&stringToSplit[startIndex], stringToSplit.getSize() - startIndex);
textElements.push_back({ wholeStr, TextElement::Type::TEXT });
return;
}
- size_t offset = 0;
- while(offset < str.getSize())
+ size_t offset = startIndex;
+ while(offset < stringToSplit.getSize())
{
size_t stringStart = offset;
- size_t foundStartIndex = str.find("[emoji](", offset);
+ size_t foundStartIndex = stringToSplit.find("[emoji](", offset);
size_t foundEndIndex = -1;
if(foundStartIndex != -1)
{
offset += (foundStartIndex + 8);
- foundEndIndex = str.find(")", offset);
+ foundEndIndex = stringToSplit.find(")", offset);
}
if(foundEndIndex != -1)
{
- StringViewUtf32 beforeEmojiStr(&str[stringStart], foundStartIndex - stringStart);
+ StringViewUtf32 beforeEmojiStr(&stringToSplit[stringStart], foundStartIndex - stringStart);
textElements.push_back({ beforeEmojiStr, TextElement::Type::TEXT });
- StringViewUtf32 url(&str[offset], foundEndIndex - offset);
+ StringViewUtf32 url(&stringToSplit[offset], foundEndIndex - offset);
textElements.push_back({ url, TextElement::Type::EMOJI });
offset = foundEndIndex + 1;
}
else
{
- StringViewUtf32 strToEnd(&str[stringStart], str.getSize() - stringStart);
+ StringViewUtf32 strToEnd(&stringToSplit[stringStart], stringToSplit.getSize() - stringStart);
textElements.push_back({ strToEnd, TextElement::Type::TEXT });
- offset = str.getSize();
+ offset = stringToSplit.getSize();
}
}
+
+ for(std::vector<TextElement>::iterator it = textElements.begin(); it != textElements.end();)
+ {
+ if(it->text.size == 0)
+ it = textElements.erase(it);
+ else
+ ++it;
+ }
}
// Logic loosely based on https://github.com/SFML/SFML/wiki/Source:-CurvedText
@@ -132,25 +152,60 @@ namespace dchat
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)
+ for(usize textElementIndex = 0; textElementIndex < textElements.size(); ++textElementIndex)
{
+ TextElement &textElement = textElements[textElementIndex];
if(textElement.type == TextElement::Type::EMOJI)
{
+ bool ownLineLeft = false;
+ if(textElementIndex == 0)
+ ownLineLeft = true;
+ else
+ {
+ TextElement &prevElement = textElements[textElementIndex - 1];
+ if(prevElement.text[prevElement.text.size - 1] == '\n')
+ ownLineLeft = true;
+ }
+
+ bool ownLineRight = false;
+ if(textElementIndex == textElements.size() - 1)
+ ownLineRight = true;
+ else
+ {
+ TextElement &nextElement = textElements[textElementIndex + 1];
+ if(nextElement.text[0] == '\n')
+ ownLineRight = true;
+ }
+
+ if(ownLineLeft && ownLineRight)
+ textElement.ownLine = true;
+
+ float emojiSize = vspace * (textElement.ownLine ? EMOJI_SCALE_STANDALONE : EMOJI_SCALE_WITH_TEXT);
+
glyphPos.x += EMOJI_PADDING;
textElement.position.x = glyphPos.x;
- textElement.position.y = glyphPos.y + vspace * 0.5f - emojiSize * 0.5f;
+ if(textElement.ownLine)
+ {
+ textElement.position.y = glyphPos.y;
+ // TODO: Find a better way to do this, @totalHeight is wrong because we add emojiSize and then vspace
+ glyphPos.y += emojiSize;
+ }
+ else
+ {
+ 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;
}
@@ -257,11 +312,14 @@ namespace dchat
}
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
+ pos.y += floor(vspace); // Origin is at bottom left, we want it to be at top left
+
+ // TODO: Do not use maxWidth here. Max width might be set to 99999 and actual text width might be 200. Text width should be calculated instead
+ if(pos.x + maxWidth <= 0.0f || pos.x >= maxWidth || pos.y + totalHeight <= 0.0f || pos.y >= target.getSize().y) return;
+
states.transform.translate(pos);
states.texture = &font.getTexture(characterSize);
target.draw(vertices, states);
@@ -272,6 +330,9 @@ namespace dchat
{
sf::Vector2f pos = position;
pos += textElement.position;
+ pos.x = floor(pos.x);
+ pos.y = floor(pos.y);
+ float emojiSize = vspace * (textElement.ownLine ? EMOJI_SCALE_STANDALONE : EMOJI_SCALE_WITH_TEXT);
sf::Vector2f size(emojiSize, emojiSize);
// TODO: Optimize this (add unordered_map that takes StringViewUtf32 as key)
diff --git a/src/main.cpp b/src/main.cpp
index df5c558..6d718e5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -31,12 +31,12 @@ int main(int argc, char **argv)
Cache cache;
Channel channel;
- ChannelSidePanel channelSidePanel;
- channelSidePanel.addChannel(&channel);
+ //ChannelSidePanel channelSidePanel;
+ //channelSidePanel.addChannel(&channel);
+ sf::Event event;
while (window.isOpen())
{
- sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)