资讯详情

资讯详情

microui 使用指南:即时模式 UI 库的窗口、布局系统、样式定制与自定义控件实战

microui 使用指南即时模式 UI 库的窗口、布局系统、样式定制与自定义控件实战【免费下载链接】microuiA tiny immediate-mode UI library项目地址: https://gitcode.com/GitHub_Trending/mi/microuimicroui 是一个用 ANSI C 编写的微型即时模式immediate-modeUI 库本文基于仓库核心文档 doc/usage.md系统讲解其完整使用流程从初始化mu_Context、接入输入事件、构建窗口与控件到掌握基于行/列的布局系统、通过mu_Style定制外观以及利用库暴露的底层原语编写自定义控件。读完本文你将能够基于任何只需绘制矩形与文本的渲染后端独立搭建一套可交互、可换肤、可扩展的即时模式 UI 应用。总体结构一帧 UI 的生命周期microui 的整体使用结构在 doc/usage.md 的 Overview 一节给出其核心是一个每帧全量重建、由用户负责输入与绘制的循环initialise mu_Context main loop: call mu_input_... functions call mu_begin() process ui call mu_end() iterate commands using mu_command_next()关键认知microui 本身不做任何绘制README.md Notes 节明确说明它只负责记录用户的输入状态、维护控件状态focus/hover 等并在mu_begin()与mu_end()之间把 UI 描述转换成一条条绘制命令真正把矩形、文字、图标画到屏幕上的工作由调用方完成。这也是它能与任何能绘制矩形和文本的渲染系统协作的原因。快速开始初始化、输入、窗口与命令渲染初始化 Context 与回调在使用任何功能前先分配并初始化一个mu_Contextmu_Context *ctx malloc(sizeof(mu_Context)); mu_init(ctx);mu_init()在 src/microui.c 中的实现是将整个 context 清零、把默认的draw_frame回调写入ctx-draw_frame并将内部默认样式default_style定义于 src/microui.c拷贝到ctx-_style、令ctx-style指向它。也就是说初始化后所有间距、字号、颜色等样式即有了默认值可直接使用。接下来必须设置两个回调供库在布局和绘制时测量文本尺寸ctx-text_width text_width; ctx-text_height text_height;注意mu_begin()内部通过expect(ctx-text_width ctx-text_height)src/microui.c强制校验这两个回调必须已赋值否则会断言失败。它们的典型实现见 demo/main.ctext_width返回字符串像素宽度len -1时按strlen计算text_height返回行高。输入mu_input_...系列函数在主循环里应首先用mu_input_...函数把用户输入传给库。microui 支持的事件与对应函数如下声明见 src/microui.h输入事件函数底层行为src/microui.c鼠标移动mu_input_mousemove(ctx, x, y)更新ctx-mouse_pos鼠标按下mu_input_mousedown(ctx, x, y, btn)先更新位置再置位mouse_down与mouse_pressed鼠标抬起mu_input_mouseup(ctx, x, y, btn)先更新位置再清除mouse_down对应位滚轮mu_input_scroll(ctx, x, y)累加到scroll_delta供mu_end()作用到滚动目标上按键按下mu_input_keydown(ctx, key)置位key_pressed与key_down按键抬起mu_input_keyup(ctx, key)清除key_down对应位文本输入mu_input_text(ctx, text)追加到input_text容量 32 字节src/microui.h同一帧内对同一输入事件多次调用是安全的库内部以位域累积状态例如mouse_pressed使用位或合并。按钮掩码MU_MOUSE_LEFT/RIGHT/MIDDLEsrc/microui.h与按键掩码MU_KEY_*src/microui.h均为位标志。demo 给出了将 SDL 事件映射到 microui 输入的完整范例demo/main.cSDL_MOUSEMOTION→mu_input_mousemove、滚轮值取反后乘以 -30 传给mu_input_scroll、SDL_TEXTINPUT→mu_input_text并用button_map/key_map两张查表把 SDL 键码翻译为 microui 掩码。帧开始与窗口块输入处理完毕后必须先调用mu_begin(ctx)再处理 UI。mu_begin()会重置命令列表与根容器列表、计算mouse_delta、递增帧计数frame。任何控件都必须放在某个容器内才能使用。容器由mu_begin_window...或mu_begin_popup...系列函数开启。mu_begin_...窗口函数返回真值表示该窗口当前是打开的若返回假例如用户点了关闭按钮就不应继续处理该窗口的 UIif (mu_begin_window(ctx, My Window, mu_rect(10, 10, 300, 400))) { /* process ui here... */ mu_end_window(ctx); }窗口的矩形位置与尺寸通过mu_rect(x, y, w, h)指定mu_rect定义见 src/microui.c。从源码看src/microui.cmu_begin_window_ex除了绘制窗口背景与标题栏外还内置了标题栏拖拽移动!title、关闭按钮!close点击后cnt-open 0、右下角缩放柄!resize最小尺寸 96×64以及滚动条布局。mu_begin_window()调用可以安全嵌套常用于实现上下文菜单等场景——嵌套窗口仍会像普通窗口一样彼此独立渲染。窗口容器在 src/microui.h 的mu_Container中保存通过内部保留池container_pool容量MU_CONTAINERPOOL_SIZE48跨帧保留其rect、open、scroll等状态。控件交互返回值在窗口块内可以安全地处理控件。允许用户交互的控件返回一个MU_RES_...位集取值定义于 src/microui.h标志含义典型来源MU_RES_ACTIVE控件处于激活/展开状态窗口、树节点、headerMU_RES_SUBMIT控件被提交如按钮被点击mu_button、文本框中回车MU_RES_CHANGE控件值发生变化mu_checkbox、mu_slider、mu_textbox输入某些控件如按钮只可能返回单个标志因此返回值可直接当布尔值用if (mu_button(ctx, My Button)) { printf(My Button was pressed\n); }ID 机制与mu_push_id()/mu_pop_id()库内部会为控件生成唯一 ID用于跨帧追踪 focus、hover 等状态。ID 通常由传给控件的名称/标签生成滑杆与复选框则使用其值指针生成见mu_button_ex用标签、mu_checkbox用state、mu_slider_ex用value调用mu_get_idsrc/microui.c。由此产生一个问题如果同一窗口/面板里多个按钮使用相同标签它们的 ID 就会冲突。解决办法是用mu_push_id()/mu_pop_id()把额外数据混入 ID 计算for (int i 0; i 10; i) { mu_push_id(ctx, i, sizeof(i)); if (mu_button(ctx, x)) { printf(Pressed button %d\n, i); } mu_pop_id(ctx); }从实现看src/microui.cID 本质是一个 32 位 FNV-1a 哈希mu_get_id以栈顶或HASH_INITIAL常量2166136261为初值把传入数据逐字节混入并保存到ctx-last_idmu_push_id把mu_get_id的结果压入id_stackmu_pop_id弹出。因此mu_push_id通常应紧跟mu_pop_id成对使用保证后续控件的 ID 计算基址正确。帧结束与命令迭代处理完本帧 UI 后调用mu_end(ctx)src/microui.c。mu_end()会校验各栈已平衡、把滚轮输入应用到scroll_target、在本帧未触碰 focus 时清空焦点、将根容器按 z 序排序并为每个根容器写入跳转命令最后重置本帧的按下/文本输入状态。当需要真正绘制时用mu_next_command()迭代生成的命令列表。该函数接收一个初始化为NULL的mu_Command指针同一命令列表可以安全地迭代任意多次每帧每次渲染都从头遍历即可。命令类型定义于 src/microui.h典型的分发渲染代码如下mu_Command *cmd NULL; while (mu_next_command(ctx, cmd)) { if (cmd-type MU_COMMAND_TEXT) { render_text(cmd-text.font, cmd-text.text, cmd-text.pos.x, cmd-text.pos.y, cmd-text.color); } if (cmd-type MU_COMMAND_RECT) { render_rect(cmd-rect.rect, cmd-rect.color); } if (cmd-type MU_COMMAND_ICON) { render_icon(cmd-icon.id, cmd-icon.rect, cmd-icon.color); } if (cmd-type MU_COMMAND_CLIP) { set_clip_rect(cmd-clip.rect); } }底层实现中src/microui.c命令是一条条变长记录mu_next_command根据每条命令的base.size前进MU_COMMAND_JUMP类型的跳转命令会被自动跟随*cmd (*cmd)-jump.dst这正是mu_end()中按 z 序串联各根容器绘制序列的机制——所以命令列表在逻辑上是按窗口前后顺序排列的。demo 里与渲染器对接的完整渲染循环见 demo/main.c其中MU_COMMAND_CLIP映射到r_set_clip_rect。关于mu_Command的字段结构可对照 src/microui.hmu_TextCommand内嵌str[1]柔性数组存放文本内容mu_JumpCommand.dst指向跳转目标所有命令类型共用mu_Command联合体、首字段type相同因此可以安全地按cmd-type分发。更完整的可运行示例参见仓库 demo 目录入口文件为 demo/main.c渲染后端封装在 demo/renderer.c 与 demo/renderer.h。布局系统行、列与相对尺寸microui 的布局系统以行row为核心每行可包含若干项items或列columns每列内部又可再包含行如此递归嵌套。基础行布局mu_layout_row()用mu_layout_row()初始化一行参数为项数、每项宽度数组、行高/* initialise a row of 3 items: the first item with a width ** of 90 and the remaining two with the width of 100 */ mu_layout_row(ctx, 3, (int[]) { 90, 100, 100 }, 0);行满自动换行一行填满后自动开始下一行。例如上面的代码之后紧跟 6 个按钮就会形成两行每行 3 个。再次调用mu_layout_row()可显式开启新的一行。从源码看src/microui.cmu_layout_row会把宽度数组拷贝进当前mu_Layout的widths容量上限MU_MAX_WIDTHS即 16见 src/microui.h并重置item_index与行内 x 起点。而mu_layout_next()src/microui.c在item_index到达items时自动调用mu_layout_row续行——这就是行满自动换行的机制来源。0 与负值使用样式尺寸或相对右/下边缘宽度和高度除了绝对值外还有两种特殊取值0使用ctx-style-size的值源码中为res.w style-size.x style-padding * 2见 src/microui.c即按样式默认尺寸负数按右/下边缘相对计算res.w layout-body.w - res.x 1src/microui.c负值绝对值越大该项越窄即占据剩余空间减去该值。因此想要左侧一个小按钮、中间一个占满大半的文本框、右侧一个大按钮的一行可写mu_layout_row(ctx, 3, (int[]) { 30, -90, -1 }, 0); mu_button(ctx, X); mu_textbox(ctx, buf, sizeof(buf)); mu_button(ctx, Submit);中间项的-90表示整行剩余宽度再减 90右侧的-1表示占满最后剩余。自由流式布局items 0与mu_layout_width()当mu_layout_row()的items参数为0时widths参数被忽略后续控件会以mu_layout_width()最后一次指定的宽度继续排列若从未调用过mu_layout_width()则使用style.size.xmu_layout_row(ctx, 0, NULL, 0); mu_layout_width(ctx, -90); mu_textbox(ctx, buf, sizeof(buf)); mu_layout_width(ctx, -1); mu_button(ctx, Submit);源码中items 0时宽度取自layout-size.xres.w layout-items 0 ? layout-widths[layout-item_index] : layout-size.xsrc/microui.c而mu_layout_width()正是修改layout-size.xsrc/microui.c。行高同理可由mu_layout_height()修改src/microui.c。这与 demo 中窗口信息区mu_layout_row(ctx, 2, (int[]) { 54, -1 }, 0)的用法互为印证。列mu_layout_begin_column()/mu_layout_end_column()在行内的任意位置可以开启一列mu_layout_begin_column(ctx); /* 列体内的行布局 ... */ mu_layout_end_column(ctx);列开启后其内部的行都限定在列体column body内排布所有负尺寸值此时相对于列体而非外层容器计算。所有新行都会被包含在该列内直到调用mu_layout_end_column()。实现上src/microui.cmu_layout_begin_column把当前下一个矩形位置压入layout_stack开启新的布局上下文mu_layout_end_column弹出子布局并把子布局更大的position、next_row、max合并回父布局——这正是列结束后外层行继续往后排的关键。demo 中 Background Color 区就是列布局的典型用例一行分成两列左列放三组标签滑杆右列放颜色预览块demo/main.c。精确控制mu_layout_next()与mu_layout_set_next()mu_layout_next(ctx)返回下一个屏幕坐标矩形并推进布局系统。自定义控件用它获取自己的绘制区域若只想推进布局而不放控件也可直接调用它。mu_layout_set_next(ctx, r, relative)显式设置下一次mu_layout_next()返回的矩形。relative为假时传入的是屏幕空间矩形不叠加容器偏移为真时该矩形会被加上容器位置与滚动偏移源码中next_type取RELATIVE/ABSOLUTE枚举src/microui.c。偷看下一个矩形而不消费它的惯用写法mu_Rect rect mu_layout_next(ctx); mu_layout_set_next(ctx, rect, 0);想在某容器内任意摆放控件时把relative设为真即可/* place a (40, 40) sized button at (300, 300) inside the container: */ mu_layout_set_next(ctx, mu_rect(300, 300, 40, 40), 1); mu_button(ctx, X);重要副作用以relative true设置的矩形会计入容器的content_size一旦超出容器 body 的宽或高就会触发滚动条doc/usage.md Layout System 节明确说明。demo 中的颜色预览块正是取出矩形 →mu_draw_rect填充 →mu_draw_control_text居中写字的用法demo/main.c。样式定制mu_Style与draw_frame()回调样式定制通过mu_Style结构体实现若想进一步控制外观还可覆写draw_frame()回调。mu_Style结构mu_Style定义于 src/microui.h包含字段类型含义fontmu_Font默认字体void 指针由渲染端解释sizemu_Vec2控件默认尺寸默认 {68, 10}paddingint内边距默认 5spacingint控件间距默认 4indentint树节点缩进量默认 24title_heightint窗口标题栏高度默认 24scrollbar_sizeint滚动条宽度默认 12thumb_sizeint滑杆/滚动条滑块尺寸默认 8colorsmu_Color[MU_COLOR_MAX]颜色表将colorid映射为mu_Color颜色索引枚举MU_COLOR_TEXT到MU_COLOR_SCROLLTHUMB共 14 项定义于 src/microui.h。默认配色见default_stylesrc/microui.c例如文本默认{230,230,230,255}、边框{25,25,25,255}、按钮{75,75,75,255}等。库通过ctx-style指针解析颜色与间距。在任何时刻更改style指针本身或修改其指向结构体的任意字段都是安全的——每帧 UI 处理都会重新读取当前样式值。这意味着可以在运行时动态换肤例如 demo 的 Style Editor 窗口就在运行时实时修改各颜色通道demo/main.c。完整的结构实现参见 src/microui.h。draw_frame()回调除样式结构外context 还保存一个draw_frame()回调每当需要绘制某个控件的**外框frame**时被调用。默认实现src/microui.c为用colorid对应的颜色填充整个矩形对普通控件MU_COLOR_SCROLLBASE/SCROLLTHUMB/TITLEBG除外在边框颜色 alpha 非零时用MU_COLOR_BORDER颜色绘制一圈 1 像素的外边框。覆写ctx-draw_frame即可全局改变所有控件外框的画法圆角、渐变、贴图等。注意mu_draw_control_frame()在调用draw_frame之前会根据控件的 focus/hover 状态自动把colorid偏移2/1src/microui.c因此默认的悬停变亮、聚焦更亮效果正是由颜色表中MU_COLOR_BUTTONHOVER、MU_COLOR_BUTTONFOCUS等相邻色项实现的。自定义控件复用内置控件的底层原语microui 把内置控件使用的底层函数全部暴露给用户方便编写自定义控件。一个自定义控件应当满足以下约定签名第一个参数是mu_Context *返回MU_RES_...位集取位置用mu_layout_next()获取自己的目标矩形并推进布局生成 ID用mu_get_id()结合控件特有的数据通常是指针生成 ID更新状态用mu_update_control()根据鼠标输入状态更新 context 的hover和focus。mu_update_control()的实现见 src/microui.c它先计算鼠标是否悬停mu_mouse_over会同时检查裁剪矩形与 hover root再据此决定是否把该控件设为 hover若该控件已获得 focus 且鼠标按键释放且未设MU_OPT_HOLDFOCUS则释放焦点若鼠标悬停且按下左键则夺取 focus。MU_OPT_HOLDFOCUS让控件在释放鼠标后保持焦点MU_OPT_HOLDFOCUSsrc/microui.h可传给mu_update_control()使控件在鼠标按键释放后继续保持焦点——文本框正是利用这一特性来留住焦点以接收键盘输入mu_textbox_raw以opt | MU_OPT_HOLDFOCUS调用mu_update_controlsrc/microui.c。从mu_update_control源码可见其作用点if (!ctx-mouse_down ~opt MU_OPT_HOLDFOCUS) { mu_set_focus(ctx, 0); }——只有未设该标志时鼠标松开才会清焦点。示例自增计数按钮控件官方文档给出了一个显示整数、点击自增的自定义按钮控件可直接作为模板int incrementer(mu_Context *ctx, int *value) { mu_Id id mu_get_id(ctx, value, sizeof(value)); mu_Rect rect mu_layout_next(ctx); mu_update_control(ctx, id, rect, 0); /* handle input */ int res 0; if (ctx-mouse_pressed MU_MOUSE_LEFT ctx-focus id) { (*value); res | MU_RES_CHANGE; } /* draw */ char buf[32]; sprintf(buf, %d, *value); mu_draw_control_frame(ctx, id, rect, MU_COLOR_BUTTON, 0); mu_draw_control_text(ctx, buf, rect, MU_COLOR_TEXT, MU_OPT_ALIGNCENTER); return res; }这个例子与内置mu_button_ex的结构src/microui.c几乎一一对应可逐段拆解mu_get_id(ctx, value, sizeof(value))——用值指针生成 ID与内置按钮用标签、复选框用状态指针生成 ID 的惯例一致mu_layout_next(ctx)——占据布局系统分配的下一个矩形mu_update_control(ctx, id, rect, 0)——更新 hover/focus若想模仿文本框留住焦点把最后的0换成MU_OPT_HOLDFOCUS输入判断ctx-mouse_pressed MU_MOUSE_LEFT ctx-focus id——本帧左键按下且本控件持有焦点即视为点击与mu_button_ex中的判断完全相同绘制部分mu_draw_control_frame()画按钮外框自动应用 hover/focus 色偏移mu_draw_control_text()在矩形内绘制居中对齐MU_OPT_ALIGNCENTER的文本mu_draw_control_text的居中/右对齐逻辑见 src/microui.c。demo 中uint8_slider()demo/main.c是自定义控件的另一实战范例它用mu_push_id隔离 ID再委托内置mu_slider_ex处理 unsigned char 值展示了包装内置控件 ID 隔离的扩展思路。进阶实践提示树节点缩进mu_begin_treenode_ex展开时会增大布局indent并压入 ID 栈mu_end_treenode恢复src/microui.c因此树节点必须成对调用 begin/end否则缩进会逐帧累积错乱。滚动容器内容尺寸超过 body 时自动出现滚动条滚动条尺寸取自style-scrollbar_size滚轮输入在mu_end()中统一施加到scroll_targetsrc/microui.c。demo 的日志窗口通过panel-scroll.y panel-content_size.y手动滚到底部demo/main.c。弹窗mu_open_popup(ctx, name)会把弹窗定位到鼠标位置并置前mu_begin_popup内部以MU_OPT_POPUP | MU_OPT_AUTOSIZE | ...组合开启窗口src/microui.c点击弹窗外区域会自动关闭MU_OPT_POPUP语义src/microui.c。固定内存microui 的全部状态命令列表MU_COMMANDLIST_SIZE256KB、各栈、容器/树节点池都内嵌在mu_Context中运行期不额外分配内存src/microui.h适合资源受限的嵌入式环境。完整示例仓库 demo 目录提供了一个基于 SDL2 的完整可运行 demodemo/main.c包含窗口信息、按钮、弹窗、树节点、复选框、滑杆、文本输入、样式编辑器与滚动日志等几乎所有内置控件是学习各 API 组合用法的首选参照。【免费下载链接】microuiA tiny immediate-mode UI library项目地址: https://gitcode.com/GitHub_Trending/mi/microui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →