From 391f7fd6d832cb40f74fb37f9e0af7ff33db202f Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 20 Apr 2018 01:29:54 +0200 Subject: Add message board, in the middle of text selection --- src/Message.cpp | 30 +++++++ src/MessageBoard.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/MessagePart.cpp | 37 ++++++++ src/ResourceCache.cpp | 29 +++++++ src/Settings.cpp | 19 +++++ src/User.cpp | 15 ++++ src/main.cpp | 56 ++++++++++++ 7 files changed, 415 insertions(+) create mode 100644 src/Message.cpp create mode 100644 src/MessageBoard.cpp create mode 100644 src/MessagePart.cpp create mode 100644 src/ResourceCache.cpp create mode 100644 src/Settings.cpp create mode 100644 src/User.cpp create mode 100644 src/main.cpp (limited to 'src') 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& 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 +#include +#include +#include +#include + +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(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(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 +#include + +using namespace std; + +namespace dchat +{ + unordered_map 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 +#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; +} -- cgit v1.2.3