feat: 完善一级关系编辑 UI 与视图体验

This commit is contained in:
2026-08-06 18:15:14 +08:00
parent 57d75541c3
commit eef9efcc1e
29 changed files with 3907 additions and 178 deletions
+63 -3
View File
@@ -8,7 +8,7 @@ PROJECT_ROOT = Path(__file__).resolve().parent.parent
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from step_editor.window_state import WindowStateMixin
from step_editor.window_state import WindowStateMixin, _property_table_column_widths
from step_editor.ui_helpers import INFO_LABELS, _format_value
@@ -102,6 +102,12 @@ def _assert_current_text_contains(
raise SystemExit(f"{label} {key} text missing {missing}: {text!r}")
def _assert_spans_value_columns(specs: list[dict[str, object]], key: str) -> None:
spec = _spec(specs, key)
if not bool(spec.get("span_value_columns")):
raise SystemExit(f"{key} should span the value/intent/target/action columns: {spec}")
def _assert_hard_range(specs: list[dict[str, object]], key: str, low: float, high: float) -> None:
spec = _spec(specs, key)
actual_low = spec.get("min_value")
@@ -312,6 +318,24 @@ def _assert_target_change_detection() -> None:
raise SystemExit("changed vector Face target was not detected")
def _assert_property_table_column_widths() -> None:
for width in (320, 340, 360, 400, 520):
columns = _property_table_column_widths(width)
if len(columns) != 5:
raise SystemExit(f"property table should have five column widths, got {columns}")
if sum(columns) != width:
raise SystemExit(f"property table widths should fill viewport {width}, got {columns} sum={sum(columns)}")
label_width, current_width, scope_width, target_width, action_width = columns
if current_width < label_width + 40:
raise SystemExit(f"current value column should be wider than parameter-name column at {width}: {columns}")
if action_width < 70:
raise SystemExit(f"operation column should keep row buttons visible at {width}: {columns}")
if target_width < 56:
raise SystemExit(f"target value column should stay usable at {width}: {columns}")
if scope_width > label_width + 8:
raise SystemExit(f"modeling-intent column should not take space from current values at {width}: {columns}")
def _assert_holed_plane_local_scopes_disabled() -> None:
specs = _specs(
{
@@ -362,7 +386,15 @@ def _assert_holed_plane_local_scopes_disabled() -> None:
def main() -> int:
if INFO_LABELS.get("face_id") != "当前拓扑 Face ID":
raise SystemExit("raw face_id label should make clear that it is the current topological Face ID")
for key in ("selection_title", "selection_display_id", "selection_topological_face_id"):
for key in (
"selection_title",
"selection_display_id",
"selection_topological_face_id",
"feature_detection_level",
"associated_feature_count",
"associated_feature_face_ids",
"feature_context_note",
):
if key not in INFO_LABELS:
raise SystemExit(f"{key} should have a user-facing label")
@@ -393,6 +425,7 @@ def main() -> int:
plane_specs = _specs(plane_info)
plane_keys = {str(spec.get("key", "")) for spec in plane_specs}
_assert_label(plane_specs, "cad_modeling_form", "建模形式")
_assert_spans_value_columns(plane_specs, "cad_modeling_form")
_assert_current_text_contains(
plane_specs,
"cad_modeling_form",
@@ -400,6 +433,7 @@ def main() -> int:
"plane Face",
)
_assert_label(plane_specs, "cad_recommended_operation", "推荐操作")
_assert_spans_value_columns(plane_specs, "cad_recommended_operation")
_assert_current_text_contains(
plane_specs,
"cad_recommended_operation",
@@ -443,11 +477,13 @@ def main() -> int:
plane_feature_rows = plane_feature_probe._feature_property_specs(plane_feature_specs, plane_info)
plane_feature_keys = {str(spec.get("key", "")) for spec in plane_feature_rows}
topology_spec = _spec(plane_feature_rows, "face_first_level_topology")
_assert_spans_value_columns(plane_feature_rows, "face_first_level_topology")
topology_text = str(topology_spec.get("current_text") or "")
for fragment in ("Face 区域 1 个", "边界 Edge 4 条", "共享边相邻 Face 4 个"):
if fragment not in topology_text:
raise SystemExit(f"plane feature topology row should explain first-level counts, got {topology_spec}")
_assert_label(plane_feature_rows, "face_edit_semantics", "建模意图")
_assert_spans_value_columns(plane_feature_rows, "face_edit_semantics")
_assert_label(plane_feature_rows, "cad_modeling_form", "建模形式")
_assert_label(plane_feature_rows, "cad_recommended_operation", "推荐操作")
if str(plane_feature_rows[0].get("key", "")) != "cad_modeling_form":
@@ -889,6 +925,15 @@ def main() -> int:
"start_point": (0.0, 0.0, 0.0),
"end_point": (10.0, 0.0, 0.0),
"length_center": (5.0, 0.0, 0.0),
"topology_relation_depth": 1,
"selected_edge_count": 1,
"first_level_vertex_count": 2,
"first_level_adjacent_edge_count": 4,
"first_level_adjacent_face_count": 2,
"first_level_edge_count": 5,
"first_level_topology_note": "Edge first-level topology is ready.",
"first_level_fact_summary": "一级事实=当前 Edge、端点、共享端点相邻 Edge、直接相邻 Face。",
"topology_ignored_relation_note": "当前阶段只传播一级关系;二级、三级拓扑暂不自动递归编辑。",
}
)
_assert_current_text_contains(
@@ -903,7 +948,21 @@ def main() -> int:
("优先改长度", "移动端面", "只改当前Edge"),
"line Edge",
)
_assert_contains({str(spec.get("key", "")) for spec in edge_specs}, {"length"}, "line Edge")
_assert_label(edge_specs, "edge_first_level_topology", "一级关系")
_assert_current_text_contains(
edge_specs,
"edge_first_level_topology",
("端点 Vertex 2 个", "相邻 Edge 4 条", "直接相邻 Face 2 个"),
"line Edge",
)
edge_topology_tip = str(_spec(edge_specs, "edge_first_level_topology").get("disabled_tip") or "")
if "二级、三级" not in edge_topology_tip:
raise SystemExit(f"line Edge topology tip should explain ignored deeper topology: {edge_topology_tip}")
_assert_contains(
{str(spec.get("key", "")) for spec in edge_specs},
{"length", "edge_first_level_topology"},
"line Edge",
)
low_recognition_specs = _specs(
{
@@ -928,6 +987,7 @@ def main() -> int:
raise SystemExit(f"{key} disabled tip should explain the recognition blocker: {spec}")
_assert_target_change_detection()
_assert_property_table_column_widths()
_assert_holed_plane_local_scopes_disabled()
_assert_no_legacy_face_source_terms()
print("property editor specs ok")