#pragma once #include "NetUtils.hpp" #include #include #include #include #include #include #include #include #include #include "types.hpp" #include namespace mgl { class Font; class Event; class Window; } namespace QuickMedia { struct TextElement { enum class Type { TEXT, IMAGE }; enum class TextType { LATIN, CJK, SYMBOL, EMOJI }; TextElement() {} void create_text(std::string_view text) { this->text = text; text_type = TextType::LATIN; type = Type::TEXT; } // If size is {0, 0} then the image is drawn at its original size void create_image(std::string url, bool local, mgl::vec2i size) { this->url = std::move(url); this->local = local; this->size = size; type = Type::IMAGE; } // TODO: union grouped std::string_view text; TextType text_type = TextType::LATIN; // TODO: Remove these std::string url; bool local = false; mgl::vec2i pos; mgl::vec2i size; int vertex_ref_index = 0; // Note: only used temporary within updateGeometry int text_num_bytes = 0; // Note: only used temporary within updateGeometry Type type; }; struct VertexRef { int vertices_index; // index to |vertices| VertexArray int index; // index within vertices[vertices_index] int line; int text_num_bytes; uint32_t codepoint; }; class Text { public: Text(std::string str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls = false); Text(const Text &other); Text& operator=(const Text&); void setString(std::string str); const std::string& getString() const; void appendText(const std::string &str); // size = {0, 0} = keep original image size void append_image(const std::string &url, bool local, mgl::vec2i size); static std::string formatted_image(const std::string &url, bool local, mgl::vec2i size); static std::string formatted_text(const std::string &text, mgl::Color color, bool bold); void insert_text_at_caret_position(const std::string &str); void set_position(float x, float y); void set_position(const mgl::vec2f &position); mgl::vec2f get_position() const; void setMaxWidth(float maxWidth); float getMaxWidth() const; void setCharacterSize(unsigned int characterSize); unsigned int get_character_size() const; void replace(size_t start_index, size_t length, const std::string &insert_str); int getCaretIndex() const; int getNumLines() const; void set_color(mgl::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(mgl::Window &window, const mgl::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(mgl::Window &target); void updateGeometry(bool update_even_if_not_dirty = false); bool single_line_edit = false; private: enum class CaretMoveDirection : u8 { NONE, UP, DOWN, HOME, END, LEFT, RIGHT, LEFT_WORD, RIGHT_WORD }; Text(); 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 split_text_by_type(std::vector &text_elements, const std::string &str); float font_get_real_height(mgl::Font *font); 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; uint32_t get_vertex_codepoint(int index) const; size_t get_string_index_from_caret_index(size_t caret_index) const; private: std::string str; // TODO: Remove this for non-editable text??? also replace with std::string? then we get more efficient editing of text bool bold_font; unsigned int characterSize; std::array, 5> vertices; std::array vertex_buffers; float maxWidth; mgl::vec2f position; mgl::Color color; bool dirty; bool dirtyText; bool dirtyCaret; bool editable; bool highlight_urls; CaretMoveDirection caretMoveDirection; mgl::FloatRect boundingBox; int num_lines; float lineSpacing; float characterSpacing; std::vector textElements; int caretIndex; float caret_offset_x; mgl::vec2f caretPosition; mgl::vec2u renderTarget_size; std::vector vertices_linear; // TODO: Use textElements instead std::vector url_ranges; }; }