aecde67c73
- What: 新增 apps/studio/src/orisys_studio/view_layout.py:把「一个视图是什么」从 app_window 搬进 ViewLayoutController——面板可见性与最小宽度、splitter 尺寸与 stretch、 三个预设(live / replay / calibration)的切换、视图菜单三个勾选项的状态、3D 面板的 开与关(含它看哪个传感器、恢复持久化的显示设置)、设置抽屉的展开状态。 控制器自己拥有 view_preset / show_cal / show_finger_3d 三个状态(初值直接取自 preferences,窗口不再留第二份),并直接驱动面板;`LayoutHost` Protocol 写明它向窗口 借的四件事:_persist_prefs / _update_run_enabled / _refresh_status_rail / _current_source_kind(以及 3D 的 _device_geometry_provider)。 预设切换同时通知 ReplayController(暂停 transport、把曲线图交还实时会话), 这是 B 之后两个控制器之间唯一的方向性依赖,写在构造参数里而不是隐式查找。 - What: 窗口侧保留过渡 shim(_view_preset / _show_cal / _show_finger_3d 可读写属性, _apply_view_preset / _apply_panel_visibility / _shows_3d / _splitter_sizes_for / _set_settings_expanded / _sync_settings_action / _sync_3d_source / _restore_3d_settings / view_preset()),供 tests 与 tools/ui_check.py 继续驱动窗口;窗口内部调用点 (brand_bar 导航、mesh3d_panel.enabledChanged、toolbar.settings_toggled、 _build_menus 里的三个 QAction、_on_sensor_type_changed、_sync_menu_checks)改为直连 控制器。_sync_menu_checks 只保留主题/语言两项(那是窗口的状态),视图三项交给 layout_ctl.sync_view_checks()。 - What: 新增 apps/studio/tests/test_view_layout.py:10 个用例,用假面板/假 splitter/ 假 host 直接测布局规则——预设决定面板组合与 rail 角色、未知预设回落 custom、离开回放 视图会停 transport 并清掉重算标签、3D 面板需要 finger + 开关且隐藏时不建 GL 视图、 预设不给 3D 留宽度时从最宽面板取且总量不变、标定面板开关即视图开关、3D 开关不自我 驱动(no-op 不写偏好)、菜单勾选跟随屏幕且 blockSignals、设置抽屉三处同步、 3D 面板拿到正确的传感器与密钥。 - Why: 「家具」是 app_window 的第三个独立变更驱动。搬完之后「哪个视图显示哪些面板」 只有一处规则,窗口不再同时持有布局状态与面板。布局与回放两个控制器的依赖是显式的 单向调用。 - Verification: pytest shared/tests apps/studio/tests(offscreen)621 passed / 0 failed / 15 skipped(原 611 + 新增 10);apps/studio/tools/ui_check.py 587 项检查 0 失败; .repro/locale_check.py 0 缺失。行为等价(搬运 + 一层转发)。 - Not in this change: C2 显示偏好(单位/量程/箭头/2D 模式/fps)与标定交互仍未拆分; 预览(_start_preview/_stop_preview/_on_preview_tick)留在窗口,它既不是会话也不是布局; docs 的行数/方法数指标待全部切片落定后统一刷新。