chat.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // Luanti
  2. // SPDX-License-Identifier: LGPL-2.1-or-later
  3. // Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. #pragma once
  5. #include <set>
  6. #include <string>
  7. #include <vector>
  8. #include <list>
  9. #include <optional>
  10. #include "irrlichttypes.h"
  11. #include "util/enriched_string.h"
  12. // Chat console related classes
  13. struct ChatLine
  14. {
  15. // age in seconds
  16. f32 age = 0.0f;
  17. // name of sending player, or empty if sent by server
  18. EnrichedString name;
  19. // message text
  20. EnrichedString text;
  21. ChatLine(const std::wstring &a_name, const std::wstring &a_text):
  22. name(a_name),
  23. text(a_text)
  24. {
  25. }
  26. ChatLine(const EnrichedString &a_name, const EnrichedString &a_text):
  27. name(a_name),
  28. text(a_text)
  29. {
  30. }
  31. };
  32. struct ChatFormattedFragment
  33. {
  34. // text string
  35. EnrichedString text;
  36. // starting column
  37. u32 column;
  38. // web link is empty for most frags
  39. std::string weblink;
  40. // formatting
  41. //u8 bold:1;
  42. };
  43. struct ChatFormattedLine
  44. {
  45. // Array of text fragments
  46. std::vector<ChatFormattedFragment> fragments;
  47. // true if first line of one formatted ChatLine
  48. bool first;
  49. };
  50. class ChatBuffer
  51. {
  52. public:
  53. ChatBuffer(u32 scrollback);
  54. ~ChatBuffer() = default;
  55. // Append chat line
  56. // Removes oldest chat line if scrollback size is reached
  57. void addLine(const std::wstring &name, const std::wstring &text);
  58. // Remove all chat lines
  59. void clear();
  60. // Get number of lines currently in buffer.
  61. u32 getLineCount() const;
  62. // Get reference to i-th chat line.
  63. const ChatLine& getLine(u32 index) const;
  64. // Increase each chat line's age by dtime.
  65. void step(f32 dtime);
  66. // Delete oldest N chat lines.
  67. void deleteOldest(u32 count);
  68. // Delete lines older than maxAge.
  69. void deleteByAge(f32 maxAge);
  70. // Get number of rows, 0 if reformat has not been called yet.
  71. u32 getRows() const;
  72. // Update console size and reformat all formatted lines.
  73. void reformat(u32 cols, u32 rows);
  74. // Get formatted line for a given row (0 is top of screen).
  75. // Only valid after reformat has been called at least once
  76. const ChatFormattedLine& getFormattedLine(u32 row) const;
  77. // Scrolling in formatted buffer (relative)
  78. // positive rows == scroll up, negative rows == scroll down
  79. void scroll(s32 rows);
  80. // Scrolling in formatted buffer (absolute)
  81. void scrollAbsolute(s32 scroll);
  82. // Scroll to bottom of buffer (newest)
  83. void scrollBottom();
  84. // Functions for keeping track of whether the lines were modified by any
  85. // preceding operations
  86. // If they were not changed, getLineCount() and getLine() output the same as
  87. // before
  88. bool getLinesModified() const { return m_lines_modified; }
  89. void resetLinesModified() { m_lines_modified = false; }
  90. // Format a chat line for the given number of columns.
  91. // Appends the formatted lines to the destination array and
  92. // returns the number of formatted lines.
  93. u32 formatChatLine(const ChatLine& line, u32 cols,
  94. std::vector<ChatFormattedLine>& destination) const;
  95. void resize(u32 scrollback);
  96. protected:
  97. s32 getTopScrollPos() const;
  98. s32 getBottomScrollPos() const;
  99. private:
  100. // Scrollback size
  101. u32 m_scrollback;
  102. // Array of unformatted chat lines
  103. std::vector<ChatLine> m_unformatted;
  104. // Number of character columns in console
  105. u32 m_cols = 0;
  106. // Number of character rows in console
  107. u32 m_rows = 0;
  108. // Scroll position (console's top line index into m_formatted)
  109. s32 m_scroll = 0;
  110. // Array of formatted lines
  111. std::vector<ChatFormattedLine> m_formatted;
  112. // Empty formatted line, for error returns
  113. ChatFormattedLine m_empty_formatted_line;
  114. // Enable clickable chat weblinks
  115. bool m_cache_clickable_chat_weblinks;
  116. // Color of clickable chat weblinks
  117. irr::video::SColor m_cache_chat_weblink_color;
  118. // Whether the lines were modified since last markLinesUnchanged()
  119. // Is always set to true when m_unformatted is modified, because that's what
  120. // determines the output of getLineCount() and getLine()
  121. bool m_lines_modified = true;
  122. };
  123. class ChatPrompt
  124. {
  125. public:
  126. ChatPrompt(const std::wstring &prompt, u32 history_limit);
  127. ~ChatPrompt() = default;
  128. // Input character or string
  129. void input(wchar_t ch);
  130. void input(const std::wstring &str);
  131. // Add a string to the history
  132. void addToHistory(const std::wstring &line);
  133. // Get current line
  134. std::wstring getLine() const { return getLineRef(); }
  135. // Get section of line that is currently selected
  136. std::wstring getSelection() const { return getLineRef().substr(m_cursor, m_cursor_len); }
  137. // Clear the current line
  138. void clear();
  139. // Replace the current line with the given text
  140. std::wstring replace(const std::wstring &line);
  141. // Select previous command from history
  142. void historyPrev();
  143. // Select next command from history
  144. void historyNext();
  145. // Nick completion
  146. void nickCompletion(const std::set<std::string> &names, bool backwards);
  147. // Update console size and reformat the visible portion of the prompt
  148. void reformat(u32 cols);
  149. // Get visible portion of the prompt.
  150. std::wstring getVisiblePortion() const;
  151. // Get cursor position (relative to visible portion). -1 if invalid
  152. s32 getVisibleCursorPosition() const;
  153. // Get length of cursor selection
  154. s32 getCursorLength() const { return m_cursor_len; }
  155. // Cursor operations
  156. enum CursorOp {
  157. CURSOROP_MOVE,
  158. CURSOROP_SELECT,
  159. CURSOROP_DELETE
  160. };
  161. // Cursor operation direction
  162. enum CursorOpDir {
  163. CURSOROP_DIR_LEFT,
  164. CURSOROP_DIR_RIGHT
  165. };
  166. // Cursor operation scope
  167. enum CursorOpScope {
  168. CURSOROP_SCOPE_CHARACTER,
  169. CURSOROP_SCOPE_WORD,
  170. CURSOROP_SCOPE_LINE,
  171. CURSOROP_SCOPE_SELECTION
  172. };
  173. // Cursor operation
  174. // op specifies whether it's a move or delete operation
  175. // dir specifies whether the operation goes left or right
  176. // scope specifies how far the operation will reach (char/word/line)
  177. // Examples:
  178. // cursorOperation(CURSOROP_MOVE, CURSOROP_DIR_RIGHT, CURSOROP_SCOPE_LINE)
  179. // moves the cursor to the end of the line.
  180. // cursorOperation(CURSOROP_DELETE, CURSOROP_DIR_LEFT, CURSOROP_SCOPE_WORD)
  181. // deletes the word to the left of the cursor.
  182. void cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope scope);
  183. protected:
  184. const std::wstring &getLineRef() const;
  185. std::wstring &makeLineRef();
  186. // set m_view to ensure that 0 <= m_view <= m_cursor < m_view + m_cols
  187. // if line can be fully shown, set m_view to zero
  188. // else, also ensure m_view <= m_line.size() + 1 - m_cols
  189. void clampView();
  190. private:
  191. struct HistoryEntry {
  192. std::wstring line;
  193. // If line is edited, saved holds the unedited version.
  194. std::optional<std::wstring> saved;
  195. HistoryEntry(const std::wstring &line): line(line) {}
  196. bool operator==(const HistoryEntry &other);
  197. bool operator!=(const HistoryEntry &other) { return !(*this == other); }
  198. };
  199. // Prompt prefix
  200. std::wstring m_prompt = L"";
  201. // Non-historical edited line
  202. std::wstring m_line = L"";
  203. // History buffer
  204. std::vector<HistoryEntry> m_history;
  205. // History index (0 <= m_history_index <= m_history.size())
  206. u32 m_history_index = 0;
  207. // Maximum number of history entries
  208. u32 m_history_limit;
  209. // Number of columns excluding columns reserved for the prompt
  210. s32 m_cols = 0;
  211. // Start of visible portion (index into m_line)
  212. s32 m_view = 0;
  213. // Cursor (index into m_line)
  214. s32 m_cursor = 0;
  215. // Cursor length (length of selected portion of line)
  216. s32 m_cursor_len = 0;
  217. // Last nick completion start (index into m_line)
  218. s32 m_nick_completion_start = 0;
  219. // Last nick completion start (index into m_line)
  220. s32 m_nick_completion_end = 0;
  221. };
  222. class ChatBackend
  223. {
  224. public:
  225. ChatBackend();
  226. ~ChatBackend() = default;
  227. // Add chat message
  228. void addMessage(const std::wstring &name, std::wstring text);
  229. // Parse and add unparsed chat message
  230. void addUnparsedMessage(std::wstring line);
  231. // Get the console buffer
  232. ChatBuffer& getConsoleBuffer();
  233. // Get the recent messages buffer
  234. ChatBuffer& getRecentBuffer();
  235. // Concatenate all recent messages
  236. EnrichedString getRecentChat() const;
  237. // Get the console prompt
  238. ChatPrompt& getPrompt();
  239. // Reformat all buffers
  240. void reformat(u32 cols, u32 rows);
  241. // Clear all recent messages
  242. void clearRecentChat();
  243. // Age recent messages
  244. void step(float dtime);
  245. // Scrolling
  246. void scroll(s32 rows);
  247. void scrollPageDown();
  248. void scrollPageUp();
  249. // Resize recent buffer based on settings
  250. void applySettings();
  251. private:
  252. ChatBuffer m_console_buffer;
  253. ChatBuffer m_recent_buffer;
  254. ChatPrompt m_prompt;
  255. };