From e25de9f3b4513b172bdcfb269ff860f5cac21f1c Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Wed, 15 Jul 2026 20:07:16 +0800 Subject: [PATCH] feat(ui): add TextField widget with UTF-8 text input support --- assets/texture/ui/textfield001.png | Bin 0 -> 174 bytes include/Cubed/scene/scene.hpp | 3 + include/Cubed/tools/text_tools.hpp | 41 +++++++ include/Cubed/ui/text_field.hpp | 65 +++++++++++ include/Cubed/ui/ui_manager.hpp | 1 + include/Cubed/ui/widget.hpp | 10 +- src/CMakeLists.txt | 1 + src/app.cpp | 2 + src/ui/text_field.cpp | 170 +++++++++++++++++++++++++++++ src/ui/ui_manager.cpp | 15 ++- src/ui/widget.cpp | 11 ++ 11 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 assets/texture/ui/textfield001.png create mode 100644 include/Cubed/tools/text_tools.hpp create mode 100644 include/Cubed/ui/text_field.hpp create mode 100644 src/ui/text_field.cpp diff --git a/assets/texture/ui/textfield001.png b/assets/texture/ui/textfield001.png new file mode 100644 index 0000000000000000000000000000000000000000..7b5b9568e4e21c61463b571326cd471072221c2e GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^4}n;OgAGVlzlpj4q!^2X+?^QKos)S9TW=Hl^CoUB=mqjPK433HKoPFTzcDrqV*;3BsR_~wpm2bz=^WSeRmSZee sck1XANK$H +#include +namespace Cubed { +inline std::string codepoint_to_utf8(uint32_t cp) { + std::string out; + + if (cp <= 0x7F) { + out.push_back(static_cast(cp)); + } else if (cp <= 0x7FF) { + out.push_back(static_cast(0xC0 | (cp >> 6))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else if (cp <= 0xFFFF) { + out.push_back(static_cast(0xE0 | (cp >> 12))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else { + out.push_back(static_cast(0xF0 | (cp >> 18))); + out.push_back(static_cast(0x80 | ((cp >> 12) & 0x3F))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } + + return out; +} + +inline void utf8_pop_back(std::string& str) { + if (str.empty()) { + return; + } + + std::size_t i = str.size() - 1; + + while (i > 0 && (static_cast(str[i]) & 0xC0) == 0x80) { + --i; + } + + str.erase(i); +} + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/ui/text_field.hpp b/include/Cubed/ui/text_field.hpp new file mode 100644 index 0000000..4610ac5 --- /dev/null +++ b/include/Cubed/ui/text_field.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "Cubed/ui/image.hpp" +#include "Cubed/ui/label.hpp" +#include "Cubed/ui/widget.hpp" +namespace Cubed { +class TextField : public Widget { +public: + TextField(const TextField&) = delete; + TextField(TextField&&) = delete; + TextField& operator=(const TextField&) = delete; + TextField& operator=(TextField&&) = delete; + TextField(Widget* parent); + + TextField& set_scale(float scale); + + float width() const override; + float height() const override; + + TextField& set_width(float width); + TextField& set_height(float height); + TextField& set_show_text(const std::string& text); + TextField& set_background_image(const std::string& path, + TextureManager& texture_manager); + TextField& set_default_image(TextureManager& texture_manager); + TextField& set_auto_scale(bool auto_scale); + bool handle_mouse_move_event(const MouseMoveEvent& e) override; + bool handle_mouse_button_event(const MouseButtonEvent& e) override; + bool handle_text_input_event(const TextInputEvent& e) override; + bool handle_key_event(const KeyEvent& e) override; + + const std::string& input_text() const; + + template TextField& set_on_finish(F&& f) { + m_on_finished = std::forward(f); + return *this; + } + +private: + static constexpr float PADDING = 5.0f; + static constexpr float DEFAULT_SCALE = 3.0f; + static constexpr float TEXT_SCALE = 0.6f; + + static constexpr const char* DEFAULT_TEXT_FIELD_IMAGE = + "texture/ui/textfield001.png"; + std::unique_ptr m_background; + std::unique_ptr