feat(ui): add word wrap for chat messages with font width measurement

This commit is contained in:
2026-07-20 10:39:20 +08:00
parent ce10290959
commit 5b081c0b0b
4 changed files with 62 additions and 2 deletions

View File

@@ -56,6 +56,7 @@ public:
TextMesh vertices(const std::string& text);
const Texture* text_texture();
static const std::string& font_path();
static float text_width(const std::string& text);
private:
FT_Library m_ft;

View File

@@ -51,5 +51,6 @@ private:
void on_render(Renderer& renderer) override;
bool handle_text_input_event(const TextInputEvent& e) override;
bool handle_key_event(const KeyEvent& e) override;
std::vector<std::string> wrap_message(const std::string& text) const;
};
} // namespace Cubed

View File

@@ -154,6 +154,28 @@ TextMesh Font::vertices(const std::string& text) {
return {std::move(vertices), max_x - min_x, max_y - min_y, 0, 0};
}
float Font::text_width(const std::string& text) {
auto& f = Font::get();
hb_buffer_t* buffer = hb_buffer_create();
hb_buffer_add_utf8(buffer, text.c_str(), text.size(), 0, text.size());
hb_buffer_guess_segment_properties(buffer);
hb_shape(f.m_hb_font, buffer, nullptr, 0);
unsigned int count = 0;
auto* positions = hb_buffer_get_glyph_positions(buffer, &count);
hb_position_t advance = 0;
for (unsigned int i = 0; i < count; ++i) {
advance += positions[i].x_advance;
}
hb_buffer_destroy(buffer);
return static_cast<float>(advance) / 64.0f;
}
const Texture* Font::text_texture() { return m_text_texture.get(); }
const std::string& Font::font_path() { return m_font_path; }

View File

@@ -1,7 +1,9 @@
#include "Cubed/ui/chat_box.hpp"
#include "Cubed/tools/text_tools.hpp"
#include "Cubed/tools/font.hpp"
#include "Cubed/tools/time_tools.hpp"
#include <utf8cpp/utf8.h>
namespace Cubed {
ChatBox::ChatBox(Widget* parent) : Widget(parent) {}
@@ -12,7 +14,7 @@ void ChatBox::add_message(ChatMessage& message) {
std::string str = std::format("<{}>{}", message.player, message.text);
auto split_str = split_utf8(str, 20);
auto split_str = wrap_message(str);
for (auto it = split_str.begin(); it != split_str.end(); ++it) {
auto lable = std::make_unique<Label>(this);
lable->set_text(*it).set_scale(m_text_scale);
@@ -158,5 +160,39 @@ bool ChatBox::handle_key_event(const KeyEvent& e) {
}
return Widget::handle_key_event(e);
}
std::vector<std::string> ChatBox::wrap_message(const std::string& text) const {
std::vector<std::string> lines;
std::string line;
size_t last_space = std::string::npos;
auto it = text.begin();
while (it != text.end()) {
auto begin = it;
utf8::next(it, text.end());
line.append(begin, it);
if (*begin == ' ')
last_space = line.size() - 1;
if (Font::text_width(line) * m_text_scale > text_label_width()) {
if (last_space != std::string::npos) {
lines.emplace_back(line.substr(0, last_space));
line.erase(0, last_space + 1);
last_space = std::string::npos;
} else {
line.erase(line.size() - (it - begin));
lines.emplace_back(line);
line.assign(begin, it);
}
}
}
if (!line.empty())
lines.emplace_back(std::move(line));
return lines;
}
} // namespace Cubed