chat.h 8.8 KB

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