优化文本布局

This commit is contained in:
Nanako 2025-04-22 00:49:59 +08:00
parent 12b6d058df
commit cc37be615c
6 changed files with 237 additions and 199 deletions

View File

@ -60,27 +60,27 @@ int main(int argc, char* argv[]) {
ss << "license: " << license << "\n"; ss << "license: " << license << "\n";
// const char*转换为std::u32string // const char*转换为std::u32string
const auto& config_info_str = utf8::utf8to32(ss.str()); // const auto& config_info_str = utf8::utf8to32(ss.str());
// text_block->set_text(U"Hello, World! 你好,世界!\n换行测试1111测试测试测试测试,测试测试😀🐵🙏 😃🐵🙏"); // text_block->set_text(U"Hello, World! 你好,世界!\n换行测试1111测试测试测试测试,测试测试😀🐵🙏 😃🐵🙏");
const auto& window = mwindow::create({ 800, 600 }, L"Hello, World!"); const auto& window = mwindow::create({ 800, 600 }, L"Hello, World!");
window->set_content( window->set_content(
mnew(mv_box) mnew(mv_box)
[ [
mslot(mv_box) // mslot(mv_box)
.horizontal_alignment(horizontal_alignment_t::left) // .horizontal_alignment(horizontal_alignment_t::left)
+ mnew(mbutton) // + mnew(mbutton)
[ // [
mslot(mbutton) // mslot(mbutton)
.margin({ 10 }) // .margin({ 10 })
.visibility(visibility_t::visible) // .visibility(visibility_t::visible)
[ // [
mnew(mtext_block, // mnew(mtext_block,
.text(config_info_str) // .text(config_info_str)
.font_size(24) // .font_size(24)
) // )
] // ]
], // ],
mslot(mv_box) mslot(mv_box)
.horizontal_alignment(horizontal_alignment_t::right) .horizontal_alignment(horizontal_alignment_t::right)
@ -91,7 +91,8 @@ int main(int argc, char* argv[]) {
.visibility(visibility_t::visible) .visibility(visibility_t::visible)
[ [
mnew(mtext_block, mnew(mtext_block,
.text(U"Hello, World!") .text(U"Hello, World! 你好,世界!\n换行测试1111测试测试测试测试,测试测试😀🐵🙏 😃🐵🙏")
// .text(U"😀🐵🙏😀🐵🙏")
.font_size(24) .font_size(24)
) )
] ]

View File

@ -7,14 +7,6 @@ add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol mirage_image) target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol mirage_image)
option(MIRAGE_SVG_EMOJI "Use svg emoji" ON)
if (MIRAGE_SVG_EMOJI)
find_package(nanosvg REQUIRED)
find_package(ZLIB REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC NanoSVG::nanosvg NanoSVG::nanosvgrast ZLIB::ZLIB)
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_NANOSVG)
endif ()
# #
set(MIRAGE_IMAGE_LOAD_BACKEND "STB_IMAGE" CACHE STRING "选择图像加载器") set(MIRAGE_IMAGE_LOAD_BACKEND "STB_IMAGE" CACHE STRING "选择图像加载器")
set_property(CACHE MIRAGE_IMAGE_LOAD_BACKEND PROPERTY STRINGS "STB_IMAGE" "CUSTOM") set_property(CACHE MIRAGE_IMAGE_LOAD_BACKEND PROPERTY STRINGS "STB_IMAGE" "CUSTOM")

View File

@ -1,5 +1,7 @@
#include "font_system.h" #include "font_system.h"
#include <ranges>
#include "emoji_detector.h" #include "emoji_detector.h"
void font_manager::destroy() { void font_manager::destroy() {
@ -21,17 +23,17 @@ void font_manager::load_default_font() {
} }
int font_manager::add_font(const std::filesystem::path& in_font_path, const std::string& in_font_type) { int font_manager::add_font(const std::filesystem::path& in_font_path, const std::string& in_font_type) {
auto font = create_font_face(in_font_path); const auto font = create_font_face(in_font_path);
if (!font) { if (!font) {
return -1; return -1;
} }
int font_id = next_font_id_++; const auto font_id = next_font_id_++;
fonts_[font_id] = font; fonts_[font_id] = font;
// 如果支持彩色表情或明确指定为表情字体,添加到表情字体列表 // 如果支持彩色表情或明确指定为表情字体,添加到表情字体列表
if (font->supports_color_emoji() || in_font_type == "emoji") { if (font->supports_color_emoji() || in_font_type == "emoji") {
emoji_font_ids_.push_back(font_id); emoji_font_ids_.push_back(font);
} }
// 如果是第一个添加的字体,自动设为主字体 // 如果是第一个添加的字体,自动设为主字体
@ -43,38 +45,69 @@ int font_manager::add_font(const std::filesystem::path& in_font_path, const std:
} }
std::shared_ptr<font_face_interface> font_manager::get_font_for_code_point( std::shared_ptr<font_face_interface> font_manager::get_font_for_code_point(
std::shared_ptr<font_face_interface> in_custom_primary_font, uint32_t in_code_point, uint32_t* out_glyph_index) { const std::shared_ptr<font_face_interface>& in_custom_primary_font, const uint32_t in_code_point,
if (in_custom_primary_font) { uint32_t* out_glyph_index) {
uint32_t glyph_id = in_custom_primary_font->find_glyph_index(in_code_point); // **辅助 Lambda检查字体并更新字形索引**
auto check_font = [&](const std::shared_ptr<font_face_interface>& font) -> std::shared_ptr<font_face_interface> {
if (!font)
return nullptr; // 跳过空指针
// **调用字体接口查找字形索引 (Assuming 0 means 'not found')**
const uint32_t glyph_id = font->find_glyph_index(in_code_point);
if (glyph_id > 0) { if (glyph_id > 0) {
if (out_glyph_index) // **如果找到字形,设置输出参数并返回字体**
if (out_glyph_index) {
*out_glyph_index = glyph_id; *out_glyph_index = glyph_id;
return in_custom_primary_font; }
return font; // 找到字形
}
return nullptr; // 在此字体中未找到字形
};
// **1. 尝试自定义主字体 (如果提供了)**
if (auto found_font = check_font(in_custom_primary_font)) {
return found_font; // 在主字体中找到,直接返回
}
// 确定字符类型
const bool is_emoji = emoji_detector::is_emoji(in_code_point);
// **2. 定义字体过滤器**
// 此过滤器排除了空指针字体和已检查过的主字体。
// 注意: 更复杂的过滤器可以基于 is_emoji 和字体元数据 (例如 is_emoji_font())
// 来优先选择特定类型的字体(例如,为表情符号优先选择表情字体)。
auto font_filter = [&](const std::shared_ptr<font_face_interface>& font) {
// **确保字体有效且不是我们已经检查过的主字体**
if (is_emoji) {
return std::ranges::contains(emoji_font_ids_, font);
}
return font != nullptr && font != in_custom_primary_font;
};
// **3. 遍历字体管理器中所有其他适用的字体**
for (const auto& font : std::views::values(fonts_) | std::views::filter(font_filter)) {
// **检查当前遍历到的字体**
if (auto found_font = check_font(font)) {
return found_font; // 在其他受管字体中找到,返回
} }
} }
// 对表情符号使用表情符号字体 // **4. 最终回退逻辑**
if (emoji_detector::is_emoji(in_code_point)) { // 如果代码执行到这里,表示在主字体(如果提供)和所有其他过滤后的字体中都未找到该字形。
for (int emoji_id : emoji_font_ids_) { // (可选) 在这里可以尝试一个全局默认回退字体:
auto emoji_font = fonts_[emoji_id]; // if (m_global_fallback_font) {
if (emoji_font && emoji_font->has_glyph(in_code_point)) { // if (auto found_font = check_font(m_global_fallback_font)) {
return emoji_font; // return found_font;
} // }
} // }
}
// 尝试所有其他字体 // **未能找到任何包含该字形的字体**
for (const auto& [id, font] : fonts_) { // **将输出字形索引设置为 0 (表示未找到)**
uint32_t glyph_id = font->find_glyph_index(in_code_point); if (out_glyph_index) {
if (id != primary_font_id_ && glyph_id > 0) { *out_glyph_index = 0;
if (out_glyph_index)
*out_glyph_index = glyph_id;
return font;
} }
} // **返回 nullptr表示没有找到合适的字体**
return nullptr;
// 回退到主字体
return in_custom_primary_font;
} }
text_layout_t font_manager::layout_text( text_layout_t font_manager::layout_text(
@ -82,8 +115,7 @@ text_layout_t font_manager::layout_text(
const std::shared_ptr<font_face_interface>& in_font, const std::shared_ptr<font_face_interface>& in_font,
float font_size, float font_size,
float max_width, float max_width,
float line_spacing) float line_spacing) {
{
text_layout_t layout; text_layout_t layout;
// 使用指定字体或主字体 // 使用指定字体或主字体
@ -127,6 +159,13 @@ text_layout_t font_manager::layout_text(
} }
}; };
// 设置字体大小
for (const auto& font : fonts_ | std::views::values) {
font->set_font_size(font_size);
}
std::map<std::shared_ptr<font_face_interface>, font_v_metrics_t> font_metrics;
/** /**
* @brief * @brief
* *
@ -150,11 +189,12 @@ text_layout_t font_manager::layout_text(
return; // 如果没有找到支持的字体,跳过该字符 return; // 如果没有找到支持的字体,跳过该字符
} }
// 设置当前字体大小 if (!font_metrics.contains(using_font)) {
using_font->set_font_size(font_size); font_metrics[using_font] = using_font->get_metrics();
}
// 获取当前字体的度量信息 // 获取当前字体的度量信息
const auto& current_metrics = using_font->get_metrics(); const auto& current_metrics = font_metrics[using_font];
// 获取字形度量信息(形状、尺寸、间距等) // 获取字形度量信息(形状、尺寸、间距等)
const auto& glyph_metrics = using_font->shape_glyph(glyph_index); const auto& glyph_metrics = using_font->shape_glyph(glyph_index);
@ -183,7 +223,8 @@ text_layout_t font_manager::layout_text(
// 计算基线位置 - 基于当前行的上升距离 // 计算基线位置 - 基于当前行的上升距离
const auto baseline = cursor_y + current_line.ascent; const auto baseline = cursor_y + current_line.ascent;
const auto size_y_diff = glyph_metrics.rect.size().y() - region->rect.size().y(); // 计算Y轴差异(字形位图高度与区域高度) // 计算Y轴差异(字形位图高度与区域高度)
const auto size_y_diff = glyph_metrics.rect.size().y() - static_cast<float>(region->rect.size().y());
// 计算字形绘制坐标 // 计算字形绘制坐标
auto x = cursor_x + glyph_metrics.offset.x(); auto x = cursor_x + glyph_metrics.offset.x();
@ -237,8 +278,7 @@ std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(
int32_t in_glyph_id, int32_t in_glyph_id,
const std::shared_ptr<font_face_interface>& in_font, const std::shared_ptr<font_face_interface>& in_font,
float in_font_size, float in_font_size,
bool is_emoji) bool is_emoji) {
{
// 验证字体有效性 // 验证字体有效性
if (!in_font) { if (!in_font) {
return std::nullopt; return std::nullopt;

View File

@ -81,11 +81,15 @@ public:
* @param in_code_point Unicode码点 * @param in_code_point Unicode码点
* @return * @return
*/ */
std::shared_ptr<font_face_interface> get_font_for_code_point(uint32_t in_code_point, uint32_t* out_glyph_index = nullptr) { std::shared_ptr<font_face_interface> get_font_for_code_point(uint32_t in_code_point,
uint32_t* out_glyph_index = nullptr) {
auto primary = get_primary_font(); auto primary = get_primary_font();
return get_font_for_code_point(primary, in_code_point, out_glyph_index); return get_font_for_code_point(primary, in_code_point, out_glyph_index);
} }
std::shared_ptr<font_face_interface> get_font_for_code_point(std::shared_ptr<font_face_interface> in_custom_primary_font, uint32_t in_code_point, uint32_t* out_glyph_index = nullptr);
std::shared_ptr<font_face_interface> get_font_for_code_point(
const std::shared_ptr<font_face_interface>& in_custom_primary_font, uint32_t in_code_point,
uint32_t* out_glyph_index = nullptr);
/** /**
* @brief * @brief
@ -155,13 +159,14 @@ public:
[[nodiscard]] const auto& get_emoji_atlases() const { [[nodiscard]] const auto& get_emoji_atlases() const {
return atlas_manager_.get_emoji_atlases(); return atlas_manager_.get_emoji_atlases();
} }
private: private:
font_manager(); font_manager();
std::unordered_map<int, std::shared_ptr<font_face_interface>> fonts_; ///< 字体ID到字体对象的映射 std::unordered_map<int32_t, std::shared_ptr<font_face_interface>> fonts_; ///< 字体ID到字体对象的映射
font_atlas_manager atlas_manager_; ///< 图集管理器 font_atlas_manager atlas_manager_; ///< 图集管理器
std::vector<int> emoji_font_ids_; ///< 表情符号字体ID列表 std::vector<std::shared_ptr<font_face_interface>> emoji_font_ids_; ///< 表情符号字体ID列表
int primary_font_id_ = -1; ///< 主字体ID int32_t primary_font_id_ = -1; ///< 主字体ID
int next_font_id_ = 0; ///< 下一个可用的字体ID int32_t next_font_id_ = 0; ///< 下一个可用的字体ID
}; };

View File

@ -118,7 +118,7 @@ struct text_style_t {
struct text_layout_t { struct text_layout_t {
struct glyph_position_t { struct glyph_position_t {
bool is_emoji; // 是否为表情符号 bool is_emoji; // 是否为表情符号
int32_t glyph_index; // 字符码点 uint32_t glyph_index; // 字符码点
Eigen::Vector2f position; // 屏幕位置 Eigen::Vector2f position; // 屏幕位置
Eigen::Vector2f size; // 字形尺寸 Eigen::Vector2f size; // 字形尺寸
atlas_region_t region; // 纹理图集区域 atlas_region_t region; // 纹理图集区域