Text.hpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. #pragma once
  2. #include "NetUtils.hpp"
  3. #include <mglpp/system/FloatRect.hpp>
  4. #include <mglpp/system/vec.hpp>
  5. #include <mglpp/graphics/Color.hpp>
  6. #include <mglpp/graphics/VertexBuffer.hpp>
  7. #include <mglpp/graphics/Vertex.hpp>
  8. #include <mglpp/system/Clock.hpp>
  9. #include <vector>
  10. #include <string_view>
  11. #include <array>
  12. namespace mgl {
  13. class Font;
  14. class Event;
  15. class Window;
  16. }
  17. namespace QuickMedia
  18. {
  19. static constexpr size_t FONT_ARRAY_SIZE = 6;
  20. enum FormattedTextFlag : uint8_t {
  21. FORMATTED_TEXT_FLAG_NONE = 0,
  22. //FORMATTED_TEXT_FLAG_BOLD = 1 << 0,
  23. FORMATTED_TEXT_FLAG_CODE = 1 << 1,
  24. FORMATTED_TEXT_FLAG_COLOR = 1 << 2
  25. };
  26. struct TextElement
  27. {
  28. enum class Type {
  29. TEXT,
  30. FORMAT_START,
  31. FORMAT_END,
  32. IMAGE
  33. };
  34. enum class TextType {
  35. TEXT,
  36. EMOJI
  37. };
  38. TextElement() {}
  39. void create_text(std::string_view text) {
  40. this->text = text;
  41. text_type = TextType::TEXT;
  42. type = Type::TEXT;
  43. }
  44. // If size is {0, 0} then the image is drawn at its original size
  45. void create_image(std::string url, bool local, mgl::vec2i size, std::string alt) {
  46. this->url = std::move(url);
  47. this->alt = std::move(alt);
  48. this->local = local;
  49. this->size = size;
  50. type = Type::IMAGE;
  51. }
  52. // TODO: Remove some fields
  53. // TODO: union grouped
  54. std::string_view text;
  55. TextType text_type = TextType::TEXT;
  56. mgl::Color color = mgl::Color(255, 255, 255, 255);
  57. uint8_t text_flags = FORMATTED_TEXT_FLAG_NONE; // FormattedTextFlag
  58. // TODO: Remove these
  59. std::string url;
  60. std::string alt;
  61. bool local = false;
  62. mgl::vec2i pos;
  63. mgl::vec2i size;
  64. int vertex_ref_index = 0; // Note: only used temporary within updateGeometry
  65. int text_num_bytes = 0; // Note: only used temporary within updateGeometry
  66. Type type;
  67. };
  68. struct VertexRef {
  69. int vertices_index; // index to |vertices| VertexArray
  70. int index; // index within vertices[vertices_index]
  71. int line;
  72. int text_num_bytes;
  73. uint32_t codepoint;
  74. };
  75. class Text
  76. {
  77. public:
  78. Text(std::string str, bool bold_font, unsigned int characterSize, float maxWidth, bool highlight_urls = false);
  79. void setString(std::string str);
  80. const std::string& getString() const;
  81. void appendText(const std::string &str);
  82. // size = {0, 0} = keep original image size
  83. static std::string formatted_image(const std::string &url, bool local, mgl::vec2i size, const std::string &alt);
  84. // text_flags is bit-or of FormattedTextFlag
  85. static std::string formatted_text(const std::string &text, mgl::Color color, uint8_t text_flags);
  86. void insert_text_at_caret_position(const std::string &str);
  87. // Set to 0 to disable max lines
  88. void set_max_lines(int max_lines);
  89. static std::string to_printable_string(const std::string &str);
  90. void set_position(float x, float y);
  91. void set_position(const mgl::vec2f &position);
  92. mgl::vec2f get_position() const;
  93. void setMaxWidth(float maxWidth);
  94. float getMaxWidth() const;
  95. void setCharacterSize(unsigned int characterSize);
  96. unsigned int get_character_size() const;
  97. void replace(size_t start_index, size_t length, const std::string &insert_str);
  98. int getCaretIndex() const;
  99. int getNumLines() const;
  100. void set_color(mgl::Color color, bool force_color = false);
  101. void setLineSpacing(float lineSpacing);
  102. void setCharacterSpacing(float characterSpacing);
  103. void setEditable(bool editable);
  104. bool isEditable() const;
  105. // Note: only call after initial updateGeometry or draw. TODO: Automatically do this internally
  106. void moveCaretToEnd();
  107. // Note: won't update until @draw is called
  108. float getWidth() const;
  109. // Note: won't update until @draw is called
  110. float getHeight() const;
  111. void processEvent(mgl::Window &window, const mgl::Event &event);
  112. // 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.
  113. // Returns true if text was drawn on screen (if text is within window borders)
  114. bool draw(mgl::Window &target);
  115. void updateGeometry(bool update_even_if_not_dirty = false);
  116. bool single_line_edit = false;
  117. private:
  118. enum class CaretMoveDirection : uint8_t
  119. {
  120. NONE,
  121. UP,
  122. DOWN,
  123. HOME,
  124. END,
  125. LEFT,
  126. RIGHT,
  127. LEFT_WORD,
  128. RIGHT_WORD
  129. };
  130. Text();
  131. void updateCaret();
  132. int getStartOfLine(int startIndex) const;
  133. int getEndOfLine(int startIndex) const;
  134. int getStartOfWord(int startIndex) const;
  135. int getEndOfWord(int startIndex) const;
  136. int getPreviousLineClosestPosition(int startIndex) const;
  137. int getNextLineClosestPosition(int startIndex) const;
  138. static void split_text_by_type(std::vector<TextElement> &text_elements, std::string_view str, float vspace);
  139. void split_text_by_type();
  140. float font_get_real_height(mgl::Font *font);
  141. float get_text_quad_left_side(const VertexRef &vertex_ref) const;
  142. float get_text_quad_right_side(const VertexRef &vertex_ref) const;
  143. float get_text_quad_top_side(const VertexRef &vertex_ref) const;
  144. float get_text_quad_bottom_side(const VertexRef &vertex_ref) const;
  145. float get_text_quad_height(const VertexRef &vertex_ref) const;
  146. void move_vertex_lines_by_largest_items(int vertices_linear_end);
  147. // If the index is past the end, then the caret offset is the right side of the last character, rather than the left side
  148. float get_caret_offset_by_caret_index(int index) const;
  149. VertexRef& get_vertex_ref_clamp(int index);
  150. // Takes into consideration if index is the last vertex and the last vertex is a newline, then it should be on its own line
  151. int get_vertex_line(int index) const;
  152. uint32_t get_vertex_codepoint(int index) const;
  153. size_t get_string_index_from_caret_index(size_t caret_index) const;
  154. private:
  155. std::string str; // TODO: Remove this for non-editable text???
  156. bool bold_font;
  157. unsigned int characterSize;
  158. std::array<std::vector<mgl::Vertex>, FONT_ARRAY_SIZE> vertices;
  159. std::array<mgl::VertexBuffer, FONT_ARRAY_SIZE> vertex_buffers;
  160. float maxWidth;
  161. mgl::vec2f position;
  162. mgl::Color color;
  163. bool force_color = false;
  164. bool dirty;
  165. bool dirtyText;
  166. bool dirtyCaret;
  167. bool editable;
  168. bool highlight_urls;
  169. CaretMoveDirection caretMoveDirection;
  170. mgl::FloatRect boundingBox;
  171. int num_lines;
  172. float lineSpacing;
  173. float characterSpacing;
  174. std::vector<TextElement> textElements;
  175. int caretIndex;
  176. float caret_offset_x;
  177. mgl::vec2f caretPosition;
  178. int max_lines = 0;
  179. std::vector<VertexRef> vertices_linear; // TODO: Use textElements instead
  180. std::vector<Range> url_ranges;
  181. };
  182. }