#pragma once #include #include #include #include #include "types.hpp" #include namespace sf { class Font; class Event; class RenderTarget; } namespace QuickMedia { struct StringViewUtf32 { const u32 *data; size_t size; StringViewUtf32() : data(nullptr), size(0) {} StringViewUtf32(const u32 *data, usize size) : data(data), size(size) {} size_t find(const StringViewUtf32 &other, size_t offset = 0) const; u32 operator [] (usize index) const { assert(index < size); return data[index]; } }; struct TextElement { enum class Type { TEXT }; enum class TextType { LATIN, CJK, EMOJI }; TextElement() {} TextElement(const StringViewUtf32 &_text, Type _type) : text(_text), type(_type), text_type(TextType::LATIN) {} 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 TextType text_type; }; struct VertexRef { int vertices_index; // index to |vertices| VertexArray int index; // index within vertices[vertices_index] int line; sf::Uint32 codepoint; }; class Text { public: Text(bool bold_font); Text(sf::String str, bool bold_font, unsigned int characterSize, float maxWidth); void setString(sf::String str); const sf::String& getString() const; void appendText(sf::String str); void setPosition(float x, float y); void setPosition(const sf::Vector2f &position); sf::Vector2f getPosition() const; void setMaxWidth(float maxWidth); float getMaxWidth() const; void setCharacterSize(unsigned int characterSize); unsigned int getCharacterSize() const; void setFillColor(sf::Color color); void setLineSpacing(float lineSpacing); void setCharacterSpacing(float characterSpacing); void setEditable(bool editable); bool isEditable() const; // Note: only call after initial updateGeometry or draw. TODO: Automatically do this internally void moveCaretToEnd(); // Note: won't update until @draw is called float getWidth() const; // Note: won't update until @draw is called float getHeight() const; void processEvent(const sf::Event &event); // 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. // Returns true if text was drawn on screen (if text is within window borders) bool draw(sf::RenderTarget &target); void updateGeometry(bool update_even_if_not_dirty = false); private: enum class CaretMoveDirection : u8 { NONE, UP, DOWN, HOME, END, LEFT, RIGHT, LEFT_WORD, RIGHT_WORD }; void updateCaret(); int getStartOfLine(int startIndex) const; int getEndOfLine(int startIndex) const; int getStartOfWord(int startIndex) const; int getEndOfWord(int startIndex) const; int getPreviousLineClosestPosition(int startIndex) const; int getNextLineClosestPosition(int startIndex) const; void splitTextByFont(); float get_text_quad_left_side(const VertexRef &vertex_ref) const; float get_text_quad_right_side(const VertexRef &vertex_ref) const; // If the index is past the end, then the caret offset is the right side of the last character, rather than the left side float get_caret_offset_by_caret_index(int index) const; VertexRef& get_vertex_ref_clamp(int index); // Takes into consideration if index is the last vertex and the last vertex is a newline, then it should be on its own line int get_vertex_line(int index) const; sf::Uint32 get_vertex_codepoint(int index) const; private: sf::String str; // TODO: Remove this for non-editable text??? also replace with std::string? then we get more efficient editing of text const bool bold_font; unsigned int characterSize; sf::VertexArray vertices[3]; float maxWidth; sf::Vector2f position; sf::Color color; sf::Color urlColor; bool dirty; bool dirtyText; bool dirtyCaret; bool editable; CaretMoveDirection caretMoveDirection; sf::FloatRect boundingBox; int num_lines; float lineSpacing; float characterSpacing; std::vector textElements; int caretIndex; float caret_offset_x; sf::Vector2f caretPosition; sf::Vector2u renderTargetSize; std::vector vertices_linear; // TODO: Use textElements instead }; }