From 7f2597ebe0f755fa2fa95224ef2b6449dec2fbea Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sat, 18 Jul 2026 17:34:38 +0800 Subject: [PATCH] feat(chat): implement in-game chat box with UTF-8 support - Add ChatMessage struct and ChatBox widget for displaying and sending messages - Introduce split_utf8() in text_tools for splitting UTF-8 strings by character count - Add TimeTools utility (get_time_ticks) for message timestamps - Update TextField with start_typing/stop_typing methods and set_typing() - Integrate text input handling in WorldScene and WorldUIManager for chat - Add utf8cpp library for robust UTF-8 validation and processing - Add ChatBox rendering and input logic (src/ui/chat_box.cpp) - Register ChatBox source in CMakeLists.txt - Fix Renderer::render_lable to skip rendering if VAO is null - Update credits to include utf8cpp library --- include/Cubed/gameplay/chat_message.hpp | 11 + include/Cubed/scene/world_scene.hpp | 5 +- include/Cubed/tools/text_tools.hpp | 28 ++ include/Cubed/tools/time_tools.hpp | 10 + include/Cubed/ui/chat_box.hpp | 53 ++ include/Cubed/ui/text_field.hpp | 4 + include/Cubed/ui/widget.hpp | 4 +- include/Cubed/ui/world_ui_manager.hpp | 5 + include/utf8cpp/.clang-format | 2 + include/utf8cpp/utf8.h | 46 ++ include/utf8cpp/utf8/checked.h | 359 ++++++++++++++ include/utf8cpp/utf8/core.h | 610 ++++++++++++++++++++++++ include/utf8cpp/utf8/cpp11.h | 70 +++ include/utf8cpp/utf8/cpp17.h | 96 ++++ include/utf8cpp/utf8/cpp20.h | 124 +++++ include/utf8cpp/utf8/unchecked.h | 286 +++++++++++ src/CMakeLists.txt | 1 + src/app.cpp | 6 +- src/render/renderer.cpp | 3 + src/scene/world_scene.cpp | 47 +- src/ui/chat_box.cpp | 114 +++++ src/ui/credits_ui.cpp | 2 +- src/ui/text_field.cpp | 41 +- src/ui/world_ui_manager.cpp | 52 +- 24 files changed, 1952 insertions(+), 27 deletions(-) create mode 100644 include/Cubed/gameplay/chat_message.hpp create mode 100644 include/Cubed/tools/time_tools.hpp create mode 100644 include/Cubed/ui/chat_box.hpp create mode 100644 include/utf8cpp/.clang-format create mode 100644 include/utf8cpp/utf8.h create mode 100644 include/utf8cpp/utf8/checked.h create mode 100644 include/utf8cpp/utf8/core.h create mode 100644 include/utf8cpp/utf8/cpp11.h create mode 100644 include/utf8cpp/utf8/cpp17.h create mode 100644 include/utf8cpp/utf8/cpp20.h create mode 100644 include/utf8cpp/utf8/unchecked.h create mode 100644 src/ui/chat_box.cpp diff --git a/include/Cubed/gameplay/chat_message.hpp b/include/Cubed/gameplay/chat_message.hpp new file mode 100644 index 0000000..87f6a2e --- /dev/null +++ b/include/Cubed/gameplay/chat_message.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +namespace Cubed { +struct ChatMessage { + std::string player; + std::string text; + uint64_t time; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/world_scene.hpp b/include/Cubed/scene/world_scene.hpp index a48e6be..42370ba 100644 --- a/include/Cubed/scene/world_scene.hpp +++ b/include/Cubed/scene/world_scene.hpp @@ -33,6 +33,8 @@ public: bool pause() const; void set_pause(bool pause); void set_error(std::string_view error); + void set_mouse(bool pause); + void set_chatting(bool chatting); private: bool handle_mouse_move_event(const MouseMoveEvent& e) override; @@ -40,7 +42,7 @@ private: bool handle_window_resize_event(const WindowResizeEvent& e) override; bool handle_mouse_wheel_event(const MouseWheelEvent& e) override; bool handle_key_event(const KeyEvent& e) override; - + bool handle_text_input_event(const TextInputEvent&) override; SceneManager& m_scene_manager; DevPanel m_dev_panel; Camera m_camera; @@ -50,6 +52,7 @@ private: bool m_paused = false; bool m_show_hud = true; bool m_show_dev_pannel = true; + bool m_chatting = false; PauseMenuUIManager m_pasue_menu; WorldUIManager m_hud_ui; ErrorUI m_error_ui; diff --git a/include/Cubed/tools/text_tools.hpp b/include/Cubed/tools/text_tools.hpp index 38b6869..b4d5a21 100644 --- a/include/Cubed/tools/text_tools.hpp +++ b/include/Cubed/tools/text_tools.hpp @@ -1,6 +1,10 @@ #pragma once +#include "Cubed/tools/cubed_assert.hpp" + #include #include +#include +#include namespace Cubed { inline std::string codepoint_to_utf8(uint32_t cp) { std::string out; @@ -38,4 +42,28 @@ inline void utf8_pop_back(std::string& str) { str.erase(i); } +inline std::vector split_utf8(const std::string& str, int size) { + ASSERT(size > 0); + if (!utf8::is_valid(str)) { + return {}; + } + std::vector blocks; + auto cur = str.begin(); + int chat_count = 0; + + for (auto it = str.begin(); it != str.end();) { + if (chat_count >= size) { + blocks.emplace_back(cur, it); + cur = it; + chat_count = 0; + } + utf8::next(it, str.end()); + ++chat_count; + if (it == str.end()) { + blocks.emplace_back(cur, it); + } + } + return blocks; +} + } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/time_tools.hpp b/include/Cubed/tools/time_tools.hpp new file mode 100644 index 0000000..7d4add8 --- /dev/null +++ b/include/Cubed/tools/time_tools.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +namespace Cubed { +namespace Tools { +inline uint64_t get_time_ticks() { return SDL_GetTicks(); } +} // namespace Tools + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/ui/chat_box.hpp b/include/Cubed/ui/chat_box.hpp new file mode 100644 index 0000000..ef46314 --- /dev/null +++ b/include/Cubed/ui/chat_box.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "Cubed/gameplay/chat_message.hpp" +#include "Cubed/ui/text_field.hpp" +#include "Cubed/ui/widget.hpp" + +#include + +namespace Cubed { +class ChatBox : public Widget { +public: + ChatBox(Widget* parent); + + void add_message(ChatMessage& message); + + ChatBox& set_scale(float scale); + ChatBox& set_text_scale(float scale); + ChatBox& set_width(float width); + ChatBox& set_show_lines(int lines); + ChatBox& set_spacing(float spacing); + float width() const override; + float height() const override; + void set_d_image(TextureManager& m); + void set_typing(bool typing); + void set_app(App* app); + template ChatBox& set_on_finish(F&& f) { + m_text_field->set_on_finish(std::forward(f)); + return *this; + } + +private: + static constexpr int MAX_MESSGAES_SUM = 50; + + struct Line { + std::unique_ptr