## 代码格式 遵循 .clang-format 文件中定义的规则,修改文件后需格式化。 ### 缩进 使用八个空格缩进。 ### 代码结构 源文件中的代码结构应该为: 1. 预处理指令 2. 类型 3. 全局变量 4. 函数声明 5. 函数定义 其中“函数定义”应该按照功能类别分组、按依赖关系从基础到高级的顺序排列,例如: ```c static int var1; static int var1; static void func1(); static void func2(); // object 1 void object1_func1() { // .. } void object1_func2() { // ... object1_func1(); } // object 2 void object2_func1() { // .. } void object2_func2() { // ... object2_func1(); } ``` 注意!优先通过调整函数定义代码块的顺序来解决声明问题,而不是前置声明函数。 ### 未使用参数 未使用的函数参数(包括回调签名里必须存在的 `void *arg`、事件回调的 `ui_event_t *e` 等) **不要写 `(void)xxx;`**。GCC、Clang、MSVC 都默认不警告未使用参数名,只有未使用局部变量 才会警告。`(void)xxx;` 是冗余噪音,掩盖真正该处理的警告。 ❌ 禁止: ```c static void on_event(ui_widget_t *w, ui_event_t *e, void *arg) { (void)w; (void)e; (void)arg; /* 实际逻辑 */ } ``` ✅ 允许: ```c static void on_event(ui_widget_t *w, ui_event_t *e, void *arg) { /* 直接用到的参数正常使用;用不到的参数名直接保留在签名里 */ } ``` > 例外:C++ 模式下某些编译器会警告未使用参数,需要按上下文决定。LCUI 是纯 C, > 不适用此例外。 ### 事件处理器参数 `ui_widget_on(widget, event, handler, listener_data)` 的第四个参数是监听器上下文, 事件分发时通过 `e->data` 传给处理函数: ```c static void on_click(ui_widget_t *w, ui_event_t *e, void *emit_arg) { app_state_t *state = e->data; } ui_widget_on(button, "click", on_click, state); ``` 处理函数的第三个参数 `emit_arg` 来自 `ui_widget_emit_event(widget, event, emit_arg)`,不是 `ui_widget_on()` 的第四个参数。常规监听器必须从 `e->data` 获取通过 `ui_widget_on()` 绑定的上下文;只有显式发射事件传入的上下文才使用 处理函数的第三个参数。 ## 测试用例 ### 归属规则 按下面优先级判定一个测试归属: 1. case 实际 `#include` 的库头文件集合(含传递依赖)。 2. 调用的运行时入口(`lcui_init` / `ui_init` / `pd_*_init` 等)。 3. xmake target 上需要的 `add_deps`。 仅触达单 lib(外加 yutil/ctest/标准库)的测试放 `lib//tests/`;触达两个及以上同级 lib,或依赖 `src/widgets/` 注册的 widget 类型的测试放 `tests/integration/`。 ### 文件与函数命名 - 文件名:`test_.c` - 套件入口(lib 内):`void test__(void)`,如 `test_ui_xml_parser`、`test_pandagl_image_reader` - 套件入口(顶层集成):`void test_(void)`,无前缀,如 `test_settings` - 套件入口必须在两个位置都注册:所属 lib 的 `tests/main.c` 与顶层 `tests/main.c` 中的 `suites[]` 表 - 内部分组用 `static void <动词>_(void)`,由 `ctest_describe` 注册 ### 描述文本风格 - `ctest_describe(name, fn)` 的 `name` 是名词性主题,全小写空格分词,无 `test` 前缀。例:`"widget opacity"`、`"flex layout"`、`"settings.fps_cap"` - `ctest_equal_*(name, ...)` 的 `name` 用 `should ...` 行为陈述。例:`"should default fps_cap to 120"`、`"should match parent border color"` - 当上下文清晰(例如 layout case 中描述某 selector 对应的 box)时,可保留 jQuery 选择器风格的描述,无需强行加 should ### 资源文件 - 跨 lib 共享的 fixture 放 `tests/fixtures/` - 仅本 lib 用的 fixture 也建议复制到 `tests/fixtures/`(顶层 lcui-tests 与单 lib binary 共用同一 rundir) - xmake target 的 `set_rundir` 指向 `tests/fixtures/` - 测试代码加载资源时直接用文件名,不带目录前缀 ### 三种文件职责 - `tests/integration/test_.c`:跨 lib 集成测试,自动断言。不调用 `lcui_main`,必要时由 `tests/previews/preview_.c` 提供可视诊断 - `tests/scenes/_scene.{c,h}`:可视化场景搭建模块,签名 `void _scene_build(...)`。只构造 widget 树和应用样式,不做断言、不调用 `ctest_*`、不调用 `lcui_main`/`lcui_quit`。给 cases 与未来的 examples demo 共用 - `lib//tests/test_.c`:纯 lib 测试,仅断言 ### 编写示例 ```c #include void test_my_case(void) { ctest_equal_int("should add two numbers", 1 + 1, 2); } ``` 注册: ```c /* lib//tests/main.c 或 tests/main.c */ extern void test_my_case(void); static const ctest_suite_t suites[] = { { "my case", test_my_case }, { NULL, NULL } }; CTEST_MAIN(suites) ``` ### 运行 - 全量:`xmake test` - 按 pattern 过滤:`xmake test "*/widget*"`(匹配 target/test 名) - 按 group 过滤:`xmake test -g tests` - 单 binary 跑全部 suite:`xmake run -tests`,例如 `xmake run lcui-tests` - 单 binary 内细粒度过滤:`xmake run lcui-tests --grep=""`,子串匹配 suite 名 - 不要使用 `xmake test "lcui-tests/*" -- --grep ""`:当前参数转发不会让 `--grep` 在 `lcui-tests` 中生效,实际会运行整个 binary - 单 binary 列出 suite:`xmake run lcui-tests --list` - 内存检查:`xmake f --memcheck=y && xmake test`,调用 drmemory(Windows)或 valgrind(Linux);恢复正常运行:`xmake f --memcheck=n` ### 不要触碰 - `lib/yutil/tests/`:使用旧 libtest 框架自管理,不并入 ctest 体系,不被 lcui-tests 收集 ## 重构约定 ### 合并重复分支 当存在两个分支仅输入不同但后续处理相同(如选择 obs->root 或 ui_root),用局部变量合并公共逻辑,避免重复代码。 ```c /* before */ if (ctx->logger) { write_log(ctx->logger, msg); } else { logger_t *logger = get_default_logger(); write_log(logger, msg); } /* after */ logger_t *logger = ctx->logger ? ctx->logger : get_default_logger(); write_log(logger, msg); ``` ### 提取公共逻辑 若公共逻辑较长,可提取为独立函数,并保持调用路径一致,减少分叉实现。 ## LCUI CSS 引擎约束 ### 选择器 - 仅支持:通配符 `*`、类型 `type`、类 `.cls`、ID `#id`、后代空格 `A B` - 不支持:子代 `>`、相邻兄弟 `+`、通用兄弟 `~` - 不支持:属性选择器 `[attr]` `[data-x]` - 不支持:功能性伪类 `:not()` `:is()` `:where()` `:has()` `:nth-child()`(解析器无 `(` 语法) - 不支持:伪元素 `::before` `::after` ### 优先级 - Rank:`GENERAL=0`,`TYPE=1`,`CLASS=10`,`PCLASS=10`,`ID=100` - 同 rank 用 `batch_num`(声明顺序)决胜,后声明覆盖先声明 - 无 `!important` 机制 - class 与 pclass rank 相同(都是 10) ### 属性与值 - `position`:仅 `static` / `relative` / `absolute`,无 `fixed` - `white-space`:仅 `normal` / `nowrap` - `border-style`:仅 `none` / `solid` - 无 `calc()` / `var()` / CSS 自定义属性 - 无 `calc()` / `var()` / `em` / `rem` / `vh` / `vw` 单位(可用 `dp` / `px` / `pt` / `%`) - 无 `overflow` 属性 ## 指令 ### gen-commit 用于生成符合 Angular 规范的提交信息,scope 应为 lib 目录下的任意目录名(例如:ui、css),标题长度限制在 80 字符以内,应结合本次会话内容生成,无需读取实际改动文件内容。