给节点 offset 补上有限数值校验

src-tauri/src/ui_editor/persistence.rs 的 validate_node 现在校验 node.offset.min/max 必须是有限数值,与相邻的 Transform/custom_minimum_size 校验一致;此前只有 Transform 的 anchor/offset 被校验,NaN 或 Infinity 的树级偏移可以存盘,预览按 offset.min 定位时整棵树的渲染会一起失效

同文件新增 rejects_non_finite_node_offset,覆盖「offset 全有限可保存、min 变成 Infinity 后校验失败」
This commit is contained in:
2026-09-24 17:24:47 +08:00
parent 246059af4c
commit ec9a4fcbac
@@ -710,6 +710,11 @@ fn validate_node(
{
return Err("UI 节点 Transform 必须是有限数值".to_string());
}
if !node.offset.min.iter().all(|value| value.is_finite())
|| !node.offset.max.iter().all(|value| value.is_finite())
{
return Err("UI 节点 offset 必须是有限数值".to_string());
}
if node
.layout
.transform
@@ -1321,6 +1326,62 @@ mod tests {
);
}
#[test]
fn rejects_non_finite_node_offset() {
let state: State = serde_json::from_value(serde_json::json!({
"ui_trees": [{
"src_ui_design": "page",
"root": {
"id": "root",
"layout": {
"transform": {
"anchor_min": [0.0, 0.0],
"anchor_max": [1.0, 1.0],
"offset_min": [0.0, 0.0],
"offset_max": [0.0, 0.0]
},
"custom_minimum_size": [0.0, 0.0],
"size_flags_horizontal": 1,
"size_flags_vertical": 1,
"size_flags_stretch_ratio": 1.0,
"container": "None"
},
"metadata": {
"name": "根节点",
"description": "",
"layout_status": "NoProblem",
"component_status": "NoProblem",
"allow_llm_edit_layout": true,
"allow_llm_edit_component": true,
"source": "System"
},
"component": null,
"children_display_mode": "Stack",
"children": [],
"offset": { "min": [0.0, 0.0], "max": [1280.0, 720.0] }
}
}],
"ui_design_images": {
"page": {
"path": "missing.png",
"pixel_size": [1280.0, 720.0],
"pixels_per_unit": 1.0
}
},
"sprite_assets": {},
"font_assets": {}
}))
.expect("deserialize state with ordered offset");
assert_eq!(validate_state(&state), Ok(()));
let mut invalid = state;
invalid.ui_trees[0].root.offset.min[1] = f32::INFINITY;
assert_eq!(
validate_state(&invalid),
Err("UI 节点 offset 必须是有限数值".to_string())
);
}
#[test]
fn recovers_last_valid_state_when_primary_is_corrupt() {
let (directory, asset_id) = fixture();