aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2018-04-20 01:29:54 +0200
committerdec05eba <dec05eba@protonmail.com>2018-04-20 01:31:23 +0200
commit391f7fd6d832cb40f74fb37f9e0af7ff33db202f (patch)
tree1d3e2b54dfade403da579a23029ae98f1a5c8f5b /src
parentc670ad2839d886107189a5a0d0854a02aa0ace53 (diff)
Add message board, in the middle of text selection
Diffstat (limited to 'src')
-rw-r--r--src/Message.cpp30
-rw-r--r--src/MessageBoard.cpp229
-rw-r--r--src/MessagePart.cpp37
-rw-r--r--src/ResourceCache.cpp29
-rw-r--r--src/Settings.cpp19
-rw-r--r--src/User.cpp15
-rw-r--r--src/main.cpp56
7 files changed, 415 insertions, 0 deletions
diff --git a/src/Message.cpp b/src/Message.cpp
new file mode 100644
index 0000000..44174ec
--- /dev/null
+++ b/src/Message.cpp
@@ -0,0 +1,30 @@
+#include "../include/Message.hpp"
+
+using namespace std;
+
+namespace dchat
+{
+ Message::Message(User *_user) :
+ user(_user)
+ {
+
+ }
+
+ Message::~Message()
+ {
+ for(MessagePart *messagePart : messageParts)
+ {
+ delete messagePart;
+ }
+ }
+
+ void Message::addText(const string &text)
+ {
+ messageParts.push_back(new MessagePartText(text));
+ }
+
+ vector<MessagePart*>& Message::getParts()
+ {
+ return messageParts;
+ }
+}
diff --git a/src/MessageBoard.cpp b/src/MessageBoard.cpp
new file mode 100644
index 0000000..2b9115a
--- /dev/null
+++ b/src/MessageBoard.cpp
@@ -0,0 +1,229 @@
+#include "../include/MessageBoard.hpp"
+#include "../include/Settings.hpp"
+#include "../include/ResourceCache.hpp"
+#include <SFML/Graphics/RectangleShape.hpp>
+#include <SFML/Graphics/Sprite.hpp>
+#include <SFML/Window/Mouse.hpp>
+#include <SFML/Graphics/Rect.hpp>
+#include <cmath>
+
+using namespace std;
+
+namespace dchat
+{
+ const sf::Color BACKGROUND_COLOR(40, 40, 40);
+ const float USERNAME_PADDING_BOTTOM = 5.0f;
+ const float MESSAGE_PADDING_BOTTOM = 20.0f;
+
+ MessageBoard::MessageBoard(const sf::Vector2u &size) :
+ selectingText(false),
+ leftMouseButtonPressed(false)
+ {
+ updateStaticContentTexture(size);
+ }
+
+ MessageBoard::~MessageBoard()
+ {
+ for(Message *message : messages)
+ {
+ delete message;
+ }
+ }
+
+ void MessageBoard::updateStaticContentTexture(const sf::Vector2u &newSize)
+ {
+ useStaticContentTexture = staticContentTexture.create(newSize.x, newSize.y);
+ dirty = true;
+ }
+
+ void MessageBoard::addMessage(Message *message)
+ {
+ messages.push_back(message);
+ dirty = true;
+ }
+
+ void MessageBoard::processEvent(const sf::Event &event)
+ {
+ if(event.type == sf::Event::MouseButtonPressed)
+ {
+ if(event.mouseButton.button == sf::Mouse::Button::Left)
+ {
+ leftMouseButtonPressed = true;
+ mousePos.x = event.mouseButton.x;
+ mousePos.y = event.mouseButton.y;
+ }
+ }
+ else if(event.type == sf::Event::MouseButtonReleased)
+ {
+ if(event.mouseButton.button == sf::Mouse::Button::Left)
+ {
+ leftMouseButtonPressed = false;
+ mousePos.x = event.mouseButton.x;
+ mousePos.y = event.mouseButton.y;
+ }
+ }
+ else if(event.type == sf::Event::LostFocus)
+ {
+ leftMouseButtonPressed = false;
+ }
+ else if(event.type == sf::Event::MouseMoved)
+ {
+ mousePos.x = event.mouseMove.x;
+ mousePos.y = event.mouseMove.y;
+ }
+
+ if(selectingText && !leftMouseButtonPressed)
+ {
+ selectingText = false;
+ }
+ else if(!selectingText && leftMouseButtonPressed)
+ {
+ selectingText = true;
+ selectingTextStart.x = mousePos.x;
+ selectingTextStart.y = mousePos.y;
+ }
+ }
+
+ void MessageBoard::draw(sf::RenderWindow &window)
+ {
+ sf::RenderTarget *renderTarget = nullptr;
+ if(useStaticContentTexture)
+ {
+ renderTarget = &staticContentTexture;
+ if(window.getSize() != staticContentTexture.getSize())
+ updateStaticContentTexture(window.getSize());
+ }
+ else
+ {
+ renderTarget = &window;
+ dirty = true;
+ }
+
+ if(dirty)
+ {
+ dirty = false;
+ auto renderTargetSize = renderTarget->getSize();
+
+ if(useStaticContentTexture)
+ staticContentTexture.clear(BACKGROUND_COLOR);
+ else
+ {
+ sf::RectangleShape background(sf::Vector2f(renderTargetSize.x, renderTargetSize.y));
+ background.setFillColor(BACKGROUND_COLOR);
+ renderTarget->draw(background);
+ }
+
+ const sf::Font &usernameFont = ResourceCache::getFont("fonts/Roboto-Regular.ttf");
+
+ sf::Vector2f position;
+ for(Message *message : messages)
+ {
+ sf::Text usernameText(message->user->getName(), usernameFont, MessagePartText::getFontSizeScaled() * 1.3f);
+ usernameText.setFillColor(sf::Color(15, 192, 252));
+ usernameText.setPosition(position);
+ renderTarget->draw(usernameText);
+ position.y += usernameText.getCharacterSize() + USERNAME_PADDING_BOTTOM;
+
+ for(MessagePart *messagePart : message->getParts())
+ {
+ 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));
+ renderTarget->draw(messagePartText->text);
+ position.x += messagePartText->text.getLocalBounds().width;
+ break;
+ }
+ }
+ }
+ position.x = 0.0f;
+ position.y += MessagePart::getSizeScaled() + MESSAGE_PADDING_BOTTOM;
+ }
+
+ if(useStaticContentTexture)
+ staticContentTexture.display();
+ }
+
+ if(useStaticContentTexture)
+ window.draw(sf::Sprite(staticContentTexture.getTexture()));
+
+ if(!selectingText) return;
+
+ 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);
+
+ 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);
+ }
+ }
+ }
+}
diff --git a/src/MessagePart.cpp b/src/MessagePart.cpp
new file mode 100644
index 0000000..47c96bd
--- /dev/null
+++ b/src/MessagePart.cpp
@@ -0,0 +1,37 @@
+#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) :
+ MessagePart(Type::TEXT),
+ text(_text, ResourceCache::getFont("fonts/Roboto-Regular.ttf"), MessagePartText::getFontSizeScaled())
+ {
+
+ }
+
+ 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());
+ }
+}
diff --git a/src/ResourceCache.cpp b/src/ResourceCache.cpp
new file mode 100644
index 0000000..474360c
--- /dev/null
+++ b/src/ResourceCache.cpp
@@ -0,0 +1,29 @@
+#include "../include/ResourceCache.hpp"
+#include <unordered_map>
+#include <stdexcept>
+
+using namespace std;
+
+namespace dchat
+{
+ unordered_map<string, sf::Font*> fonts;
+
+ const sf::Font& ResourceCache::getFont(const string &filepath)
+ {
+ auto it = fonts.find(filepath);
+ if(it != fonts.end())
+ return *it->second;
+
+ sf::Font *font = new sf::Font();
+ if(!font->loadFromFile(filepath))
+ {
+ delete font;
+ string errMsg = "Failed to load font: ";
+ errMsg += filepath;
+ throw runtime_error(errMsg);
+ }
+
+ fonts[filepath] = font;
+ return *font;
+ }
+}
diff --git a/src/Settings.cpp b/src/Settings.cpp
new file mode 100644
index 0000000..93f230c
--- /dev/null
+++ b/src/Settings.cpp
@@ -0,0 +1,19 @@
+#include "../include/Settings.hpp"
+
+namespace dchat
+{
+ float Settings::getScaling()
+ {
+ // TODO: Load scaling from settings file
+ /*
+ CLUTTER_SCALE
+ GDK_SCALE
+ GDK_DPI_SCALE
+ QT_AUTO_SCREEN_SCALE_FACTOR
+ QT_SCALE_FACTOR
+ QT_SCREEN_SCALE_FACTORS
+ QT_DEVICE_PIXEL_RATIO
+ */
+ return 1.0f;
+ }
+}
diff --git a/src/User.cpp b/src/User.cpp
new file mode 100644
index 0000000..6b07b5b
--- /dev/null
+++ b/src/User.cpp
@@ -0,0 +1,15 @@
+#include "../include/User.hpp"
+
+namespace dchat
+{
+ OfflineUser::OfflineUser(const std::string &_name) :
+ name(_name)
+ {
+
+ }
+
+ const std::string& OfflineUser::getName() const
+ {
+ return name;
+ }
+}
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..85f1075
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,56 @@
+#include <SFML/Graphics.hpp>
+#include "../include/MessageBoard.hpp"
+#include "../include/User.hpp"
+
+using namespace dchat;
+
+int main()
+{
+ sf::RenderWindow window(sf::VideoMode(1920, 1080), "dchat");
+ window.setVerticalSyncEnabled(false);
+ window.setFramerateLimit(60);
+
+ OfflineUser *localOfflineUser = new OfflineUser("You");
+
+ MessageBoard messageBoard(window.getSize());
+
+ {
+ Message *message = new Message(localOfflineUser);
+ message->addText(u8"hello, world!");
+ messageBoard.addMessage(message);
+ }
+
+ {
+ Message *message = new Message(localOfflineUser);
+ message->addText(u8"hello, world!");
+ messageBoard.addMessage(message);
+ }
+
+ {
+ Message *message = new Message(localOfflineUser);
+ message->addText(u8"hello, world!");
+ messageBoard.addMessage(message);
+ }
+
+ while (window.isOpen())
+ {
+ sf::Event event;
+ while (window.pollEvent(event))
+ {
+ if (event.type == sf::Event::Closed)
+ window.close();
+ else if(event.type == sf::Event::Resized)
+ {
+ sf::View view(sf::FloatRect(0.0f, 0.0f, event.size.width, event.size.height));
+ window.setView(view);
+ }
+ messageBoard.processEvent(event);
+ }
+
+ window.clear();
+ messageBoard.draw(window);
+ window.display();
+ }
+
+ return 0;
+}