ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

如何在 Odin 项目中接入 Clay 绑定?clay-odin 目录导入、MeasureText proc 与渲染命令处理

如何在 Odin 项目中接入 Clay 绑定?clay-odin 目录导入、MeasureText proc 与渲染命令处理 如何在 Odin 项目中接入 Clay 绑定clay-odin 目录导入、MeasureText proc 与渲染命令处理【免费下载链接】clayHigh performance UI layout library in C.项目地址: https://gitcode.com/GitHub_Trending/clay9/clayClay 是一个 C 语言的高性能 UI 布局库仓库在 bindings/odin 目录下提供了官方 Odin 绑定一个clay-odin绑定包按平台链接预编译好的 Clay 库和一个基于 Raylib 渲染的完整示例Clay 官网页面。本文的目标是把 Clay 接入一个已有的 Odin 项目走通「导入 clay-odin 目录 → 初始化内存 → 提供 MeasureText proc → 每帧声明布局 → 处理 RenderCommand 渲染」这条完整路径最终以 Clay 官方示例中的 Raylib 渲染器作为参照实现。准备条件clay-odin 目录与平台预编译库bindings/odin/clay-odin目录下除了clay.odin绑定源码还按平台存放了预编译的 Clay 库平台链接文件Windowswindows/clay.libLinuxlinux/clay.amacOS (x86)macos/clay.amacOS (arm64)macos-arm64/clay.aWASMwasm/clay.oclay.odin 顶部根据ODIN_OS与ODIN_ARCH自动选择其中一份做foreign import所以接入时不需要手动指定库文件只需保证运行平台在上述列表内。Odin 侧绑定通过(link_prefix Clay_, ...)声明全部 C 接口C 端所有公开函数都有对应版本命名规律是CLAY_IDC→clay.IDOdin。准备工作的核心一步来自 bindings/odin/README.md把bindings/odin/clay-odin整个目录拷贝到你的项目中。示例工程里的导入语句是相对路径import clay ../../clay-odin因为示例源码位于examples/clay-official-website/子目录下拷贝到你的项目目录后一般写成import clay clay-odin。如果打算直接复用仓库的渲染参照还需要 Raylib示例通过import vendor:raylib引入并依赖resources/目录下的字体Calistoga-Regular.ttf、Quicksand-Semibold.ttf与图片。只接布局库而不使用 Raylib 渲染器时这部分可以省略。第一步导入并初始化内存初始化链路在 bindings/odin/README.md 的 Quick Start 第 2 步中给出用clay.MinMemorySize()查询所需静态内存大小创建一块内存并用clay.CreateArenaWithCapacityAndMemory包装成clay.Arena最后调用clay.Initialize完成初始化同时传入布局尺寸和错误处理器import clay clay-odin error_handler :: proc c (errorData: clay.ErrorData) { // Do something with the error data. } min_memory_size : clay.MinMemorySize() memory : make([^]u8, min_memory_size) arena: clay.Arena clay.CreateArenaWithCapacityAndMemory(uint(min_memory_size), memory) clay.Initialize(arena, {1080, 720}, { handler error_handler })官方示例 clay-official-website.odin 中同样的链路把尺寸换成了窗口实际分辨率并演示了错误处理器怎么消费错误类型errorHandler :: proc c (errorData: clay.ErrorData) { if (errorData.errorType clay.ErrorType.DuplicateId) { // etc } } minMemorySize: c.size_t cast(c.size_t)clay.MinMemorySize() memory : make([^]u8, minMemorySize) arena: clay.Arena clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory) clay.Initialize(arena, {cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}, {handler errorHandler})错误处理器是后续验证接入是否正常的观察点之一clay.ErrorType枚举见 clay.odin列出了TextMeasurementFunctionNotProvided、ArenaCapacityExceeded、ElementsCapacityExceeded、DuplicateId、UnbalancedOpenClose等类型回调里按errorType分支处理。第二步提供 MeasureText procClay 本身不负责文字测量你需要通过clay.SetMeasureTextFunction(function, userData)注册一个测量回调签名是proc c (text: clay.StringSlice, config: ^clay.TextElementConfig, userData: rawptr) - clay.Dimensions。clay.TextElementConfig携带fontId、fontSize、letterSpacing等字段Clay_MinMemorySize→CreateArenaWithCapacityAndMemory→Initialize→SetMeasureTextFunction是文档给出的启动顺序。README 给出了一份最简占位实现宽度按字符数 × 字号估算measure_text :: proc c ( text: clay.StringSlice, config: ^clay.TextElementConfig, userData: rawptr, ) - clay.Dimensions { // clay.TextElementConfig contains members such as fontId, fontSize, letterSpacing, etc.. // Note: clay.String-chars is not guaranteed to be null terminated return { width f32(text.length * i32(config.fontSize)), height f32(config.fontSize), } } // Tell clay how to measure text clay.SetMeasureTextFunction(measure_text, nil)真实渲染中需要按字体字形逐字符累加宽度。示例的 clay_renderer_raylib.odin 中的measure_text_ascii展示了针对 Raylib 字体的完整做法按config.fontId取出字体遍历字符累加glyph.advanceX再乘以fontSize / font.baseSize的比例并加上letterSpacing总量最后返回{width, height}同文件还提供了一份measure_text_unicode用utf8.grapheme_count计算 Unicode 文本的字符数。注册时clay.SetMeasureTextFunction(measure_text, nil)两个必须注意的限制来自 C 端文档 README.md 中Clay_SetMeasureTextFunction一节传入的StringSlice不保证以 null 结尾渲染端若只支持 C 字符串如 Raylib需要先克隆一份示例用strings.clone_to_cstring完成并且该函数处于布局热路径上文本密集场景会被调用很多次应尽量保持轻量Clay 内部会缓存测量结果。第三步每帧声明布局并拿到渲染命令列表与 C 版 Element Macros 最大的差异是Odin 绑定用if语句来圈定子元素的作用域。C 的写法CLAY({ .id CLAY_ID(Outer), .layout { .padding CLAY_PADDING_ALL(16) } }) { // Child elements here }对应的 Odin 写法if clay.UI(clay.ID(Outer))({ layout { padding clay.PaddingAll(16) }}) { // Child elements here }每帧的调用顺序对照 C 文档Clay_BeginLayout一节的生命周期是SetPointerState可选鼠标交互→UpdateScrollContainers可选滚动容器→SetLayoutDimensions→BeginLayout→ 声明元素 →EndLayout。示例主循环的实际代码for !raylib.WindowShouldClose() { defer free_all(context.temp_allocator) clay.SetPointerState(transmute(clay.Vector2)raylib.GetMousePosition(), raylib.IsMouseButtonDown(raylib.MouseButton.LEFT)) clay.UpdateScrollContainers(false, transmute(clay.Vector2)raylib.GetMouseWheelMoveV(), raylib.GetFrameTime()) clay.SetLayoutDimensions({cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}) renderCommands : createLayout(animationLerpValue 0 ? (animationLerpValue 1) : (1 - animationLerpValue), raylib.GetFrameTime()) raylib.BeginDrawing() clay_raylib_render(renderCommands) raylib.EndDrawing() }其中createLayout内部以clay.BeginLayout()开头、以return clay.EndLayout(frametime)结尾返回clay.ClayArray(clay.RenderCommand)。布局树里可以混用普通 Odin 代码示例中用for循环生成了 5 个侧栏条目也展示了可复用组件就是普通 procsidebar_item_component :: proc(index: u32) { if clay.UI()({ id clay.ID(SidebarBlob, index), layout sidebar_item_layout, backgroundColor COLOR_ORANGE, }) {} }clay.ID(SidebarBlob, index)的第二个参数用于为同一 id 生成带索引的变体避免DuplicateId。第四步处理 RenderCommand 渲染命令EndLayout返回的是一批渲染命令。最小处理骨架来自 bindings/odin/README.md用clay.RenderCommandArray_Get(render_commands, i)逐条取出再按commandType分支调用自己的绘制函数render_commands : create_layout() for i in 0..i32(render_commands.length) { render_command : clay.RenderCommandArray_Get(render_commands, i) switch render_command.commandType { case .Rectangle: // 用 boundingBox 与 renderData.rectangle.backgroundColor 绘制矩形 // ... Implement handling of other command types } }commandType的完整取值定义在RenderCommandType枚举中clay.odinNone、Rectangle、Border、Text、Image、ScissorStart、ScissorEnd、OverlayColorStart、OverlayColorEnd、Custom。具体画什么由你的渲染器决定。Raylib 参照实现 clay_renderer_raylib.odin 中clay_raylib_render展示了几个有代表性的分支clay_raylib_render :: proc(render_commands: ^clay.ClayArray(clay.RenderCommand), allocator : context.temp_allocator) { overlay_colors : make([dynamic]clay.Color, allocator) for i in 0 .. render_commands.length { render_command : clay.RenderCommandArray_Get(render_commands, i) bounds : render_command.boundingBox switch render_command.commandType { case .None: case .Text: config : render_command.renderData.text text : string(config.stringContents.chars[:config.stringContents.length]) // Raylib uses C strings instead of Odin strings, so we need to clone cstr_text : strings.clone_to_cstring(text, allocator) font : raylib_fonts[config.fontId].font rl.DrawTextEx(font, cstr_text, {bounds.x, bounds.y}, f32(config.fontSize), f32(config.letterSpacing), clay_color_to_rl_color(config.textColor)) case .Image: config : render_command.renderData.image imageTexture : (^rl.Texture2D)(config.imageData) rl.DrawTextureEx(imageTexture^, {bounds.x, bounds.y}, 0, bounds.width / f32(imageTexture.width), clay_color_to_rl_color(tint)) case .ScissorStart: rl.BeginScissorMode(i32(math.round(bounds.x)), i32(math.round(bounds.y)), i32(math.round(bounds.width)), i32(math.round(bounds.height))) case .ScissorEnd: rl.EndScissorMode() case .Rectangle: config : render_command.renderData.rectangle // 有圆角走 draw_rect_rounded否则走 draw_rect // ... Border 等其余分支 } } }注意Image命令的imageData是一个rawptr指向什么类型由渲染器自己决定——示例里存的正是*rl.Texture2D。ScissorStart/ScissorEnd对应裁剪区域的进出OverlayColorStart/OverlayColorEnd在示例中用一个颜色栈跟踪叠加色。验证接入是否正常文档没有给出单独的成功日志可用的核对手段有以下几个错误处理器clay.Initialize时注册的errorHandler会在布局出错时收到ErrorData。按errorType分支可以定位问题——例如出现TextMeasurementFunctionNotProvided说明忘记调用clay.SetMeasureTextFunctionDuplicateId说明同一帧声明了重复的clay.IDArenaCapacityExceeded/ElementsCapacityExceeded/TextMeasurementCapacityExceeded则分别提示需要按Clay_MinMemorySize重新计算内存或调大元素/文本缓存上限后重新初始化。调试叠加层示例把D键绑定到clay.SetDebugModeEnabled(debugModeEnabled)见 clay-official-website.odin打开后可以在画面中查看 Clay 的布局调试信息。端到端运行直接运行完整示例Raylib 窗口打开后官网页面由 Clay 布局并绘制确认文本换行、侧栏固定 300px 宽 内容区 Grow 的效果与预期一致说明 MeasureText 与渲染命令处理链路都工作正常。限制与后续绑定只覆盖布局计算文字测量必须自己实现且按 C 文档说明位于热路径文本密集场景下它会成为主要布局开销Clay 内部虽有测量缓存但 DPI 等外部因素变化时需调用clay.ResetMeasureTextCache强制重算。传入 MeasureText 与 Text 渲染命令的字符串均为「指针 长度」切片不保证 null 结尾渲染器只接受 C 字符串时如 Raylib必须自行克隆。调整元素数量上限或文本测量缓存上限时clay.SetMaxElementCount/clay.SetMaxMeasureTextCacheWordCount需要重新调用clay.MinMemorySize()并按新尺寸重新初始化。完整 API 细节以根目录 README.md 的 C 文档为准所有 C 公开函数与宏在 Odin 绑定中都有对应物命名规律为CLAY_IDC→clay.IDOdin。【免费下载链接】clayHigh performance UI layout library in C.项目地址: https://gitcode.com/GitHub_Trending/clay9/clay创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表