refactor(lui): complete batch split for interaction/editor/property flows
This commit is contained in:
parent
a1f6fe6097
commit
c8eb4cef4d
@ -62,13 +62,91 @@
|
||||
- `ui/panels/editor_panels_left.py::_draw_resource_manager` 保留布局版拆分已完成:
|
||||
- 实测指标:`310 -> 23` 行(第一轮 `310 -> 163`,第二轮 `163 -> 23`)
|
||||
- 目录/文件项渲染、展开内容、右键菜单流程已拆到独立 helper
|
||||
- `ui/LUI/lui_function_properties.py::_draw_component_properties` 第一轮编排拆分已完成:
|
||||
- 实测指标:`1441 -> 3` 行(入口)
|
||||
- 新增编排/子流程 helper:
|
||||
- `_draw_component_properties_core`(`34`)
|
||||
- `_draw_text_and_button_properties`(`216`)
|
||||
- `_draw_layout_transform_properties`(`200`)
|
||||
- `_draw_type_specific_properties`(`835`)
|
||||
- `_draw_anchor_and_hierarchy_properties`(`168`)
|
||||
- `ui/LUI/lui_manager_interaction.py::_handle_resize_drag` 第二轮拆分已完成:
|
||||
- 实测指标:`257 -> 3` 行(入口)
|
||||
- 新增编排/子流程 helper:
|
||||
- `_handle_resize_drag_core`(`44`)
|
||||
- `_finish_resize_drag_if_released`(`19`)
|
||||
- `_compute_resize_drag_bounds`(`109`)
|
||||
- `_apply_resize_drag_updates`(`110`)
|
||||
- `ui/LUI/lui_manager_interaction.py::_compute_resize_drag_bounds` / `_apply_resize_drag_updates` 第三轮细拆已完成:
|
||||
- 实测指标:`109 -> 27` / `110 -> 21`
|
||||
- 新增细粒度 helper(边界计算与组件应用分层):
|
||||
- `_get_resize_start_values`(`7`)
|
||||
- `_apply_resize_handle_delta`(`36`)
|
||||
- `_apply_resize_min_size_clamp`(`15`)
|
||||
- `_apply_shift_keep_ratio_resize`(`17`)
|
||||
- `_apply_alt_center_resize`(`22`)
|
||||
- `_resolve_resize_parent_context`(`13`)
|
||||
- `_apply_resize_component_size_by_type`(`24`)
|
||||
- `_sync_input_field_layout_stretch`(`22`)
|
||||
- `ui/LUI/lui_function_properties.py::_draw_type_specific_properties` 第二轮拆分已完成:
|
||||
- 实测指标:`835 -> 25` 行(类型分发入口)
|
||||
- 新增类型 helper:
|
||||
- `_draw_type_props_input_field`(`91`)
|
||||
- `_draw_type_props_slider`(`11`)
|
||||
- `_draw_type_props_checkbox`(`19`)
|
||||
- `_draw_type_props_plane_image`(`152`)
|
||||
- `_draw_type_props_frame`(`14`)
|
||||
- `_draw_type_props_selectbox`(`84`)
|
||||
- `_draw_type_props_http_text`(`72`)
|
||||
- `_draw_type_props_video`(`10`)
|
||||
- `_draw_video_source_and_audio_controls`(`4`,第三轮后为编排入口)
|
||||
- `_draw_video_playback_controls`(`147`)
|
||||
- `ui/LUI/lui_function_properties.py::_draw_video_source_and_audio_controls` 第三轮拆分已完成:
|
||||
- 实测指标:`231 -> 4` 行(入口)
|
||||
- 新增子流程 helper:
|
||||
- `_draw_video_audio_controls`(`26`)
|
||||
- `_draw_video_source_controls`(`19`)
|
||||
- `_process_pending_video_source`(`22`)
|
||||
- `_load_video_texture_from_source`(`21`)
|
||||
- `_apply_loaded_video_texture`(`14`)
|
||||
- `_sync_video_size_and_sprite`(`28`)
|
||||
- `_replace_video_sprite`(`23`)
|
||||
- `_refresh_video_keep_alive_node`(`22`)
|
||||
- `_load_audio_for_component`(`22`)
|
||||
- `_start_video_texture_playback`(`7`)
|
||||
- `ui/LUI/lui_function_properties.py::_draw_text_and_button_properties` 第四轮拆分已完成:
|
||||
- 实测指标:`216 -> 14` 行(入口)
|
||||
- 新增子流程 helper(文本编辑/颜色同步/按钮贴图):
|
||||
- `_draw_text_content_editor`(`13`)
|
||||
- `_draw_text_font_size_editor`(`18`)
|
||||
- `_draw_text_color_editor`(`11`)
|
||||
- `_sync_text_or_button_color`(`10`)
|
||||
- `_draw_button_texture_controls`(`19`)
|
||||
- `_apply_button_texture_variant`(`28`)
|
||||
- `ui/LUI/lui_manager_editor.py::_set_parent_child_relationship` 第四轮拆分已完成:
|
||||
- 实测指标:`234 -> 43` 行(入口)
|
||||
- 新增子流程 helper(关系校验/索引同步/可见层级):
|
||||
- `_is_valid_relationship_index`(`5`)
|
||||
- `_prepare_parent_child_link`(`8`)
|
||||
- `_remove_child_from_old_parent`(`8`)
|
||||
- `_attach_child_to_new_parent`(`6`)
|
||||
- `_compute_new_child_local_position`(`7`)
|
||||
- `_ensure_child_visibility_and_layer`(`19`)
|
||||
- `ui/LUI/lui_function_properties.py::_draw_layout_transform_properties` 第四轮拆分已完成:
|
||||
- 实测指标:`200 -> 10` 行(入口)
|
||||
- 新增子流程 helper(填充模式/尺寸联动/Layout Group/Z-Offset):
|
||||
- `_draw_fill_layout_controls`(`37`)
|
||||
- `_draw_position_controls`(`18`)
|
||||
- `_draw_size_controls`(`17`)
|
||||
- `_draw_layout_group_controls`(`53`)
|
||||
- `_draw_z_offset_controls`(`27`)
|
||||
|
||||
## 1. 总体画像
|
||||
|
||||
- Python 文件: `147`
|
||||
- 代码总行数: `58,341`
|
||||
- `except Exception` / `except:` 总计: `949`
|
||||
- 裸 `except:` 总计: `62`
|
||||
- 代码总行数: `58,479`
|
||||
- `except Exception` / `except:` 总计: `993`
|
||||
- 裸 `except:` 总计: `52`
|
||||
- 旧上下文关键词引用总量:
|
||||
- `interface_manager`: `20`
|
||||
- `treeWidget`: `2`
|
||||
@ -85,24 +163,27 @@
|
||||
|
||||
### 2.1 超大文件 Top(非 VR)
|
||||
|
||||
1. `core/selection.py` (`2941` 行)
|
||||
2. `core/InfoPanelManager.py` (`1725` 行)
|
||||
3. `ui/LUI/lui_manager_editor.py` (`1724` 行)
|
||||
4. `ui/panels/property_helpers.py` (`1711` 行)
|
||||
5. `ui/LUI/lui_function_properties.py` (`1707` 行)
|
||||
6. `TransformGizmo/rotate_gizmo.py` (`1587` 行)
|
||||
7. `ui/panels/animation_tools.py` (`1579` 行)
|
||||
8. `templates/main_template.py` (`1571` 行)
|
||||
1. `core/selection.py` (`2933` 行)
|
||||
2. `ui/LUI/lui_function_properties.py` (`1731` 行)
|
||||
3. `core/InfoPanelManager.py` (`1726` 行)
|
||||
4. `ui/LUI/lui_manager_editor.py` (`1725` 行)
|
||||
5. `ui/panels/property_helpers.py` (`1712` 行)
|
||||
6. `TransformGizmo/rotate_gizmo.py` (`1588` 行)
|
||||
7. `templates/main_template.py` (`1572` 行)
|
||||
8. `ui/panels/animation_tools.py` (`1563` 行)
|
||||
|
||||
### 2.2 长函数 Top(优先拆分,非 VR 当前状态)
|
||||
|
||||
1. `ui/LUI/lui_function_properties.py::_draw_component_properties` (`1441` 行)
|
||||
2. `ui/LUI/lui_manager_interaction.py::_handle_resize_drag` (`257` 行)
|
||||
3. `ui/LUI/lui_manager_editor.py::_set_parent_child_relationship` (`234` 行)
|
||||
4. `ui/panels/editor_panels_top.py::draw_menu_bar` (`227` 行)
|
||||
5. `scene/scene_manager_io_mixin.py::saveScene` (`222` 行)
|
||||
6. `TransformGizmo/move_gizmo.py::_on_mouse_down` (`222` 行)
|
||||
7. `ui/LUI/lui_manager_editor.py::draw_editor` (`217` 行)
|
||||
1. `ui/panels/editor_panels_top.py::draw_menu_bar` (`227` 行)
|
||||
2. `scene/scene_manager_io_mixin.py::saveScene` (`222` 行)
|
||||
3. `TransformGizmo/move_gizmo.py::_on_mouse_down` (`222` 行)
|
||||
4. `ui/LUI/lui_manager_editor.py::draw_editor` (`217` 行)
|
||||
5. `ui/LUI/lui_manager_editor.py::draw_component_tree` (`217` 行)
|
||||
6. `core/InfoPanelManager.py::add_methods_to_property_panel` (`209` 行)
|
||||
7. `ui/panels/editor_panels_right.py::_draw_animation_properties` (`201` 行)
|
||||
8. `ui/panels/editor_panels_right.py::_draw_gui_properties` (`191` 行)
|
||||
9. `core/event_handler.py::mousePressEventLeft` (`186` 行)
|
||||
10. `scene/scene_manager_model_mixin.py::importModel` (`177` 行)
|
||||
|
||||
### 2.3 异常处理密度高(可观测性风险)
|
||||
|
||||
@ -161,9 +242,9 @@
|
||||
|
||||
建议拆分顺序(非 VR 当前阶段):
|
||||
|
||||
1. `ui/LUI/lui_function_properties.py::_draw_component_properties`(可按“变换/布局/视觉/交互/脚本”分区)
|
||||
2. `ui/LUI/lui_manager_interaction.py::_handle_resize_drag`
|
||||
3. `ui/panels/editor_panels_top.py::draw_menu_bar`
|
||||
1. `ui/panels/editor_panels_top.py::draw_menu_bar`
|
||||
2. `scene/scene_manager_io_mixin.py::saveScene`
|
||||
3. `core/InfoPanelManager.py::add_methods_to_property_panel`
|
||||
|
||||
预期收益:
|
||||
|
||||
@ -249,12 +330,65 @@
|
||||
- 主函数长度降到 `<= 140` 行(实际 `28` 行)
|
||||
- 拖拽与缩放交互行为保持一致
|
||||
|
||||
### Task H(推荐下一步,2-3 天)
|
||||
### Task H(已完成,第一轮)
|
||||
|
||||
- 拆分 `ui/LUI/lui_function_properties.py::_draw_component_properties`
|
||||
- 验收:
|
||||
- 主函数长度降到 `<= 260` 行
|
||||
- 按“变换/布局/视觉/交互/脚本”子区块拆分并保持渲染逻辑一致
|
||||
- 主函数长度降到 `<= 260` 行(实际 `3` 行,编排核心 `34` 行)
|
||||
- 已完成第一轮分区拆分并保持渲染逻辑一致
|
||||
|
||||
### Task I(已完成,第二轮)
|
||||
|
||||
- 拆分 `ui/LUI/lui_manager_interaction.py::_handle_resize_drag`
|
||||
- 验收:
|
||||
- 入口流程收敛到 `<= 120` 行(实际 `3` 行,编排核心 `44` 行)
|
||||
- 缩放手柄行为与边界语义保持一致
|
||||
- 第三轮细拆补充(已完成):
|
||||
- `_compute_resize_drag_bounds`: `109 -> 27`
|
||||
- `_apply_resize_drag_updates`: `110 -> 21`
|
||||
- 继续保持缩放语义一致
|
||||
|
||||
### Task J(已完成,第二轮)
|
||||
|
||||
- 继续拆分 `ui/LUI/lui_function_properties.py::_draw_type_specific_properties`
|
||||
- 验收:
|
||||
- 单函数长度降到 `<= 320` 行(实际 `25` 行)
|
||||
- 按组件类型拆分为独立 helper 并保持行为一致
|
||||
|
||||
### Task K(已完成,第三轮)
|
||||
|
||||
- 继续拆分 `ui/LUI/lui_function_properties.py::_draw_video_source_and_audio_controls`(已完成)
|
||||
- 验收:
|
||||
- 单函数长度降到 `<= 160` 行(实际 `4` 行)
|
||||
- 行为保持一致(本地视频/URL加载/音频联动)
|
||||
|
||||
### Task L(已完成,第四轮)
|
||||
|
||||
- 继续拆分 `ui/LUI/lui_function_properties.py::_draw_text_and_button_properties`
|
||||
- 验收:
|
||||
- 单函数长度降到 `<= 160` 行(实际 `14` 行)
|
||||
- 文本/按钮属性编辑行为保持一致
|
||||
|
||||
### Task M(已完成,第四轮)
|
||||
|
||||
- 继续拆分 `ui/LUI/lui_manager_editor.py::_set_parent_child_relationship`
|
||||
- 验收:
|
||||
- 单函数长度降到 `<= 180` 行(实际 `43` 行)
|
||||
- 父子关系编辑行为保持一致
|
||||
|
||||
### Task N(推荐下一步,1-2 天)
|
||||
|
||||
- 拆分 `ui/panels/editor_panels_top.py::draw_menu_bar`
|
||||
- 验收:
|
||||
- 单函数长度降到 `<= 170` 行
|
||||
- 菜单项顺序与行为保持一致
|
||||
|
||||
### Task O(已完成,第四轮)
|
||||
|
||||
- 继续拆分 `ui/LUI/lui_function_properties.py::_draw_layout_transform_properties`
|
||||
- 验收:
|
||||
- 单函数长度降到 `<= 160` 行(实际 `10` 行)
|
||||
- 布局与尺寸联动行为保持一致
|
||||
|
||||
## 4.1 本轮深入分析(非 VR,P1 准备)
|
||||
|
||||
@ -281,7 +415,7 @@
|
||||
- 失败重试逻辑保持语义一致。
|
||||
- 当前状态(2026-03-01):
|
||||
- 第二轮已完成(`loadScene` 已达验收目标:`74` 行)。
|
||||
- 后续建议转入回归观察,优先推进 `ui/LUI/lui_function_properties.py::_draw_component_properties`。
|
||||
- 后续建议转入回归观察,优先推进 `ui/panels/editor_panels_top.py::draw_menu_bar`。
|
||||
|
||||
### B) `main.py::__init__`(第二优先,已完成第一轮)
|
||||
|
||||
@ -345,8 +479,8 @@
|
||||
1. `loadScene` 已完成两轮拆分(当前建议冻结并转入回归观察)。
|
||||
2. `main.__init__` 已完成第一轮拆分(建议转入回归观察)。
|
||||
3. `_update_drag` 已完成第一轮拆分,建议转入回归观察。
|
||||
4. 下一步处理 `_draw_component_properties`(属性面板分区拆分)。
|
||||
5. 然后处理 `_handle_resize_drag`(拖拽缩放路径补充拆分)。
|
||||
4. `_draw_component_properties` 第一轮已完成,`_draw_type_specific_properties` 二轮与视频段三轮已完成。
|
||||
5. `_handle_resize_drag` 第三轮细拆已完成,`_draw_text_and_button_properties`、`_set_parent_child_relationship`、`_draw_layout_transform_properties` 第四轮已完成。
|
||||
|
||||
## 5. 与现有文档关系
|
||||
|
||||
@ -356,4 +490,4 @@
|
||||
|
||||
---
|
||||
|
||||
如果按此路线继续,建议下一轮直接从 **Task H** 开始,先落地 `_draw_component_properties` 的分区拆分并保持行为不变。
|
||||
如果按此路线继续,建议下一轮直接从 **Task N** 开始,优先落地 `draw_menu_bar` 的流程拆分并保持行为不变。
|
||||
|
||||
@ -15,6 +15,12 @@
|
||||
- 项目已进入“非 VR 结构优化阶段”。
|
||||
- `_getActor`、`updateGizmoDrag`、`_update_drag` 第一轮拆分已完成。
|
||||
- `资源管理器面板` 已完成“保留原布局/样式/交互”的第二轮拆分收敛。
|
||||
- `_draw_component_properties` 已完成第一轮“编排入口 + 子流程”拆分(入口 `1441 -> 3` 行)。
|
||||
- `_handle_resize_drag` 已完成第三轮“边界计算 / 更新应用”细拆(核心 `44` 行)。
|
||||
- `_draw_video_source_and_audio_controls` 已完成第三轮“音频/视频源/加载应用”拆分(`231 -> 4` 行)。
|
||||
- `_draw_text_and_button_properties` 已完成第四轮“文本/颜色/按钮贴图”拆分(`216 -> 14` 行)。
|
||||
- `_set_parent_child_relationship` 已完成第四轮“关系编排 + 可见层级”拆分(`234 -> 43` 行)。
|
||||
- `_draw_layout_transform_properties` 已完成第四轮“布局/尺寸/Z-Offset”拆分(`200 -> 10` 行)。
|
||||
|
||||
## 已完成(Done)
|
||||
|
||||
@ -89,23 +95,173 @@
|
||||
- 新增 helper:`_update_resource_manager_window_rect`、`_draw_resource_directory_entries`、`_draw_resource_directory_entry`、`_draw_resource_directory_children`、`_draw_resource_subdir_entry`、`_draw_resource_subfile_entry`、`_draw_resource_file_entries`、`_draw_resource_file_entry`
|
||||
- 保持目标:不改视觉布局,不改交互语义,仅做结构分解
|
||||
|
||||
### H. LUI 属性面板拆分完成(第一轮编排)
|
||||
|
||||
- [x] `ui/LUI/lui_function_properties.py::_draw_component_properties` 第一轮拆分完成:
|
||||
- 入口函数行数:`1441 -> 3`
|
||||
- 编排主流程函数:`_draw_component_properties_core = 34` 行
|
||||
- 新增 helper:
|
||||
- `_draw_text_and_button_properties`(`216` 行)
|
||||
- `_draw_layout_transform_properties`(`200` 行)
|
||||
- `_draw_type_specific_properties`(`835` 行)
|
||||
- `_draw_anchor_and_hierarchy_properties`(`168` 行)
|
||||
- 保持目标:属性面板行为不变,先完成入口和流程层收敛
|
||||
|
||||
### I. LUI 缩放链路拆分完成(第二轮)
|
||||
|
||||
- [x] `ui/LUI/lui_manager_interaction.py::_handle_resize_drag` 第二轮拆分完成:
|
||||
- 入口函数行数:`257 -> 3`
|
||||
- 编排主流程函数:`_handle_resize_drag_core = 44` 行
|
||||
- 新增 helper:
|
||||
- `_finish_resize_drag_if_released`(`19` 行)
|
||||
- `_compute_resize_drag_bounds`(`109` 行)
|
||||
- `_apply_resize_drag_updates`(`110` 行)
|
||||
- 保持目标:缩放手柄行为与边界语义保持一致
|
||||
|
||||
### J. LUI 属性类型分支拆分完成(第二轮)
|
||||
|
||||
- [x] `ui/LUI/lui_function_properties.py::_draw_type_specific_properties` 第二轮拆分完成:
|
||||
- 主函数行数:`835 -> 25`(类型分发入口)
|
||||
- 新增类型 helper:
|
||||
- `_draw_type_props_input_field`(`91` 行)
|
||||
- `_draw_type_props_slider`(`11` 行)
|
||||
- `_draw_type_props_checkbox`(`19` 行)
|
||||
- `_draw_type_props_plane_image`(`152` 行)
|
||||
- `_draw_type_props_frame`(`14` 行)
|
||||
- `_draw_type_props_selectbox`(`84` 行)
|
||||
- `_draw_type_props_http_text`(`72` 行)
|
||||
- `_draw_type_props_video`(`10` 行)
|
||||
- `_draw_video_source_and_audio_controls`(`231` 行)
|
||||
- `_draw_video_playback_controls`(`147` 行)
|
||||
- 保持目标:各组件类型交互语义保持一致,先完成类型分发层收敛
|
||||
|
||||
### K. LUI 视频属性段拆分完成(第三轮)
|
||||
|
||||
- [x] `ui/LUI/lui_function_properties.py::_draw_video_source_and_audio_controls` 第三轮拆分完成:
|
||||
- 主函数行数:`231 -> 4`(编排入口)
|
||||
- 新增 helper:
|
||||
- `_draw_video_audio_controls`(`26` 行)
|
||||
- `_draw_video_source_controls`(`19` 行)
|
||||
- `_process_pending_video_source`(`22` 行)
|
||||
- `_load_video_texture_from_source`(`21` 行)
|
||||
- `_apply_loaded_video_texture`(`14` 行)
|
||||
- `_sync_video_size_and_sprite`(`28` 行)
|
||||
- `_replace_video_sprite`(`23` 行)
|
||||
- `_refresh_video_keep_alive_node`(`22` 行)
|
||||
- `_load_audio_for_component`(`22` 行)
|
||||
- `_start_video_texture_playback`(`7` 行)
|
||||
- 保持目标:本地视频加载/URL加载回退/音频联动语义保持一致
|
||||
|
||||
### L. LUI 缩放链路细拆完成(第三轮)
|
||||
|
||||
- [x] `ui/LUI/lui_manager_interaction.py::_compute_resize_drag_bounds` / `_apply_resize_drag_updates` 第三轮细拆完成:
|
||||
- 主函数行数:`109 -> 27` / `110 -> 21`
|
||||
- 新增 helper:
|
||||
- `_get_resize_start_values`(`7` 行)
|
||||
- `_apply_resize_handle_delta`(`36` 行)
|
||||
- `_apply_resize_min_size_clamp`(`15` 行)
|
||||
- `_apply_shift_keep_ratio_resize`(`17` 行)
|
||||
- `_apply_alt_center_resize`(`22` 行)
|
||||
- `_cache_resize_canvas_bounds`(`8` 行)
|
||||
- `_sanitize_resize_dimensions`(`6` 行)
|
||||
- `_resolve_resize_parent_context`(`13` 行)
|
||||
- `_write_resize_component_data`(`5` 行)
|
||||
- `_apply_resize_component_position`(`9` 行)
|
||||
- `_apply_resize_component_size_by_type`(`24` 行)
|
||||
- `_apply_button_resize_size`(`7` 行)
|
||||
- `_apply_input_field_resize_size`(`10` 行)
|
||||
- `_sync_input_field_layout_stretch`(`22` 行)
|
||||
- `_post_resize_component_sync`(`7` 行)
|
||||
- `_get_resize_modifier_info`(`7` 行)
|
||||
- 保持目标:缩放手柄行为与边界语义保持一致
|
||||
|
||||
### M. LUI 文本按钮属性段拆分完成(第四轮)
|
||||
|
||||
- [x] `ui/LUI/lui_function_properties.py::_draw_text_and_button_properties` 第四轮拆分完成:
|
||||
- 主函数行数:`216 -> 14`
|
||||
- 新增 helper:
|
||||
- `_draw_text_content_editor`(`13` 行)
|
||||
- `_draw_text_font_size_editor`(`18` 行)
|
||||
- `_draw_text_color_editor`(`11` 行)
|
||||
- `_sync_text_or_button_color`(`10` 行)
|
||||
- `_apply_button_layout_color`(`12` 行)
|
||||
- `_apply_text_color`(`3` 行)
|
||||
- `_draw_button_texture_controls`(`19` 行)
|
||||
- `_draw_button_texture_pick_button`(`22` 行)
|
||||
- `_apply_button_texture_variant`(`28` 行)
|
||||
- `_unpack_button_atlas_result`(`9` 行)
|
||||
- `_apply_button_custom_textures`(`11` 行)
|
||||
- `_fit_button_to_default_texture_size`(`22` 行)
|
||||
- `_clear_button_custom_textures`(`13` 行)
|
||||
- `_draw_button_texture_path_labels`(`7` 行)
|
||||
- 保持目标:文本/按钮编辑行为与贴图切换语义保持一致
|
||||
|
||||
### N. LUI 层级编辑父子关系拆分完成(第四轮)
|
||||
|
||||
- [x] `ui/LUI/lui_manager_editor.py::_set_parent_child_relationship` 第四轮拆分完成:
|
||||
- 主函数行数:`234 -> 43`
|
||||
- 新增 helper:
|
||||
- `_is_valid_relationship_index`(`5` 行)
|
||||
- `_prepare_parent_child_link`(`8` 行)
|
||||
- `_remove_child_from_old_parent`(`8` 行)
|
||||
- `_attach_child_to_new_parent`(`6` 行)
|
||||
- `_compute_new_child_local_position`(`7` 行)
|
||||
- `_resolve_parent_visual_object`(`10` 行)
|
||||
- `_set_child_visual_parent_policy`(`12` 行)
|
||||
- `_clamp_child_local_within_parent`(`27` 行)
|
||||
- `_sync_child_canvas_index_with_parent`(`7` 行)
|
||||
- `_apply_child_absolute_position`(`10` 行)
|
||||
- `_ensure_child_visibility_and_layer`(`19` 行)
|
||||
- `_ensure_button_child_visibility`(`15` 行)
|
||||
- `_ensure_frame_child_visibility`(`9` 行)
|
||||
- `_ensure_media_child_visibility`(`13` 行)
|
||||
- `_ensure_child_sprite_visibility_and_layer`(`24` 行)
|
||||
- `_finalize_parent_child_link`(`9` 行)
|
||||
- 保持目标:逻辑父子关系/可见层级/画布索引同步语义保持一致
|
||||
|
||||
### O. LUI 布局变换属性段拆分完成(第四轮)
|
||||
|
||||
- [x] `ui/LUI/lui_function_properties.py::_draw_layout_transform_properties` 第四轮拆分完成:
|
||||
- 主函数行数:`200 -> 10`
|
||||
- 新增 helper:
|
||||
- `_draw_fill_layout_controls`(`37` 行)
|
||||
- `_draw_position_controls`(`18` 行)
|
||||
- `_draw_size_controls`(`17` 行)
|
||||
- `_draw_width_control`(`23` 行)
|
||||
- `_draw_height_control`(`19` 行)
|
||||
- `_post_size_control_sync`(`9` 行)
|
||||
- `_draw_layout_group_controls`(`53` 行)
|
||||
- `_draw_z_offset_controls`(`27` 行)
|
||||
- 保持目标:填充模式/尺寸联动/Layout Group/Z-Offset 行为一致
|
||||
|
||||
## 下一步(Next)
|
||||
|
||||
### N1 `属性面板超大函数` 拆分(最高优先级,非 VR)
|
||||
### N1 `顶部菜单栏` 拆分(最高优先级,非 VR)
|
||||
|
||||
- 目标文件:`ui/LUI/lui_function_properties.py::_draw_component_properties`
|
||||
- 当前规模:`1441` 行
|
||||
- 目标文件:`ui/panels/editor_panels_top.py::draw_menu_bar`
|
||||
- 当前规模:`227` 行
|
||||
- 验收目标:
|
||||
- 按“变换/布局/视觉/交互/脚本”拆分为多个子绘制函数
|
||||
- 主流程压缩并保持属性面板行为一致
|
||||
- 拆分为“菜单组渲染 / 行为分发 / 状态同步”子流程
|
||||
- 单函数规模收敛到 `<= 170` 行
|
||||
- 保持现有菜单项顺序与功能一致
|
||||
|
||||
### N2 `LUI 缩放链路` 拆分(高优先级,非 VR)
|
||||
### N2 `场景保存链路` 拆分(高优先级,非 VR)
|
||||
|
||||
- 目标文件:`ui/LUI/lui_manager_interaction.py::_handle_resize_drag`
|
||||
- 当前规模:`257` 行
|
||||
- 目标文件:`scene/scene_manager_io_mixin.py::saveScene`
|
||||
- 当前规模:`222` 行
|
||||
- 验收目标:
|
||||
- 入口流程收敛到 `<= 120` 行
|
||||
- 保持缩放手柄行为与边界语义一致
|
||||
- 拆分为“保存前校验 / 节点序列化 / 后处理”子流程
|
||||
- 单函数规模收敛到 `<= 170` 行
|
||||
- 保持保存行为与输出格式一致
|
||||
|
||||
### N3 `属性面板方法注入` 拆分(中优先级,非 VR)
|
||||
|
||||
- 目标文件:`core/InfoPanelManager.py::add_methods_to_property_panel`
|
||||
- 当前规模:`209` 行
|
||||
- 验收目标:
|
||||
- 拆分为“能力绑定 / 兼容兜底 / 日志分层”子流程
|
||||
- 单函数规模收敛到 `<= 170` 行
|
||||
- 保持属性面板行为一致
|
||||
|
||||
## 验收标准(阶段)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -28,78 +28,115 @@ class LUIManagerEditorMixin:
|
||||
comp_data['children_indices'] = []
|
||||
|
||||
def _set_parent_child_relationship(self, child_index, parent_index, anchor_position=None, keep_world=False):
|
||||
# Set parent-child relationship
|
||||
if (child_index < 0 or child_index >= len(self.components) or
|
||||
parent_index < 0 or parent_index >= len(self.components)):
|
||||
if not self._is_valid_relationship_index(child_index, parent_index):
|
||||
print("Error: invalid component index")
|
||||
return
|
||||
|
||||
child_data = self.components[child_index]
|
||||
parent_data = self.components[parent_index]
|
||||
if not self._prepare_parent_child_link(child_index, parent_index, child_data, parent_data):
|
||||
return
|
||||
|
||||
# 编排入口:仅更新逻辑父子关系,不执行真实 reparent,避免破坏复杂组件内部结构。
|
||||
current_abs_left, current_abs_top = self._get_component_accumulated_pos(child_index)
|
||||
print(f"[_set_parent_child_relationship] Child #{child_index} -> Parent #{parent_index}")
|
||||
print(f" Current absolute position: ({current_abs_left:.1f}, {current_abs_top:.1f})")
|
||||
|
||||
self._remove_child_from_old_parent(child_data, child_index)
|
||||
self._attach_child_to_new_parent(child_data, parent_data, child_index, parent_index)
|
||||
|
||||
new_local_left, new_local_top = self._compute_new_child_local_position(
|
||||
child_index, parent_index, current_abs_left, current_abs_top
|
||||
)
|
||||
child_data['left'] = new_local_left
|
||||
child_data['top'] = new_local_top
|
||||
|
||||
child_obj = child_data['object']
|
||||
parent_obj = self._resolve_parent_visual_object(parent_data)
|
||||
|
||||
try:
|
||||
self._set_child_visual_parent_policy(child_data, parent_data)
|
||||
if not keep_world:
|
||||
new_local_left, new_local_top = self._clamp_child_local_within_parent(
|
||||
child_data, parent_data, child_obj, parent_obj, new_local_left, new_local_top
|
||||
)
|
||||
child_data['left'] = new_local_left
|
||||
child_data['top'] = new_local_top
|
||||
|
||||
self._sync_child_canvas_index_with_parent(child_data, parent_data)
|
||||
self._apply_child_absolute_position(child_obj, current_abs_left, current_abs_top, keep_world)
|
||||
self._ensure_child_visibility_and_layer(child_data, parent_data, child_obj, parent_obj)
|
||||
except Exception as e:
|
||||
print(f"Reparenting error: {e}")
|
||||
|
||||
self._finalize_parent_child_link(child_index, parent_index, parent_data)
|
||||
|
||||
def _is_valid_relationship_index(self, child_index, parent_index):
|
||||
return (
|
||||
0 <= child_index < len(self.components)
|
||||
and 0 <= parent_index < len(self.components)
|
||||
)
|
||||
|
||||
def _prepare_parent_child_link(self, child_index, parent_index, child_data, parent_data):
|
||||
if parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout']:
|
||||
child_data['draggable'] = False
|
||||
|
||||
if child_data.get('parent_index') == parent_index:
|
||||
print(f"Component {child_index} already under {parent_index}")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _remove_child_from_old_parent(self, child_data, child_index):
|
||||
old_parent_index = child_data.get('parent_index')
|
||||
if old_parent_index is None or old_parent_index < 0:
|
||||
return
|
||||
|
||||
current_abs_left, current_abs_top = self._get_component_accumulated_pos(child_index)
|
||||
|
||||
print(f"[_set_parent_child_relationship] Child #{child_index} -> Parent #{parent_index}")
|
||||
print(f" Current absolute position: ({current_abs_left:.1f}, {current_abs_top:.1f})")
|
||||
|
||||
old_parent_index = child_data.get('parent_index')
|
||||
if old_parent_index is not None and old_parent_index >= 0:
|
||||
old_parent_data = self.components[old_parent_index]
|
||||
if child_index in old_parent_data.get('children_indices', []):
|
||||
old_parent_data['children_indices'].remove(child_index)
|
||||
|
||||
def _attach_child_to_new_parent(self, child_data, parent_data, child_index, parent_index):
|
||||
child_data['parent_index'] = parent_index
|
||||
if 'children_indices' not in parent_data:
|
||||
parent_data['children_indices'] = []
|
||||
if child_index not in parent_data['children_indices']:
|
||||
parent_data['children_indices'].append(child_index)
|
||||
|
||||
def _compute_new_child_local_position(self, child_index, parent_index, current_abs_left, current_abs_top):
|
||||
parent_abs_left, parent_abs_top = self._get_component_accumulated_pos(parent_index)
|
||||
new_local_left = current_abs_left - parent_abs_left
|
||||
new_local_top = current_abs_top - parent_abs_top
|
||||
|
||||
print(f" Parent absolute position: ({parent_abs_left:.1f}, {parent_abs_top:.1f})")
|
||||
print(f" Calculated local position: ({new_local_left:.1f}, {new_local_top:.1f})")
|
||||
return new_local_left, new_local_top
|
||||
|
||||
# 在数据结构中存储局部坐标(相对于父组件)
|
||||
child_data['left'] = new_local_left
|
||||
child_data['top'] = new_local_top
|
||||
|
||||
child_obj = child_data['object']
|
||||
def _resolve_parent_visual_object(self, parent_data):
|
||||
parent_obj = parent_data['object']
|
||||
parent_type = parent_data.get('type')
|
||||
if parent_type == 'ScrollableRegion':
|
||||
return parent_data['object'].content_node
|
||||
if parent_type in ['VerticalLayout', 'HorizontalLayout'] and parent_data.get('layout_obj'):
|
||||
if parent_data.get('layout_wrap', True):
|
||||
return parent_data.get('object')
|
||||
return parent_data.get('layout_obj').cell()
|
||||
return parent_obj
|
||||
|
||||
def _set_child_visual_parent_policy(self, child_data, parent_data):
|
||||
visual_container_types = [
|
||||
'Frame', 'Plane', 'Video', 'HttpText',
|
||||
'ScrollableRegion', 'TabbedFrame',
|
||||
'VerticalLayout', 'HorizontalLayout'
|
||||
'VerticalLayout', 'HorizontalLayout',
|
||||
]
|
||||
if parent_data.get('type') == 'ScrollableRegion':
|
||||
parent_obj = parent_data['object'].content_node
|
||||
elif parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout'] and parent_data.get('layout_obj'):
|
||||
if parent_data.get('layout_wrap', True):
|
||||
parent_obj = parent_data.get('object')
|
||||
else:
|
||||
parent_obj = parent_data.get('layout_obj').cell()
|
||||
|
||||
try:
|
||||
if parent_type in visual_container_types:
|
||||
if parent_data.get('type') in visual_container_types:
|
||||
child_data['visual_parent_canvas'] = False
|
||||
# 关键修改:不要调用 reparent_to(),避免破坏内部结构
|
||||
# 只更新数据结构,保持原有的父对象关系
|
||||
print(f"[_set_parent_child_relationship] Setting parent relationship (data only, no reparenting)")
|
||||
print("[_set_parent_child_relationship] Setting parent relationship (data only, no reparenting)")
|
||||
else:
|
||||
# Keep visual parent on canvas to avoid clipping, but keep logical parent
|
||||
# 非视觉容器保持画布父级显示,避免被父组件裁剪。
|
||||
child_data['visual_parent_canvas'] = True
|
||||
|
||||
# If not preserving world position, clamp into parent bounds for visibility
|
||||
if not keep_world:
|
||||
def _clamp_child_local_within_parent(
|
||||
self, child_data, parent_data, child_obj, parent_obj, new_local_left, new_local_top
|
||||
):
|
||||
try:
|
||||
parent_w = parent_data.get('width')
|
||||
parent_h = parent_data.get('height')
|
||||
@ -107,49 +144,44 @@ class LUIManagerEditorMixin:
|
||||
parent_w = parent_obj.width
|
||||
if parent_h is None and hasattr(parent_obj, 'height'):
|
||||
parent_h = parent_obj.height
|
||||
|
||||
child_w = child_data.get('width')
|
||||
child_h = child_data.get('height')
|
||||
if child_w is None and hasattr(child_obj, 'width'):
|
||||
child_w = child_obj.width
|
||||
if child_h is None and hasattr(child_obj, 'height'):
|
||||
child_h = child_obj.height
|
||||
|
||||
if parent_w and parent_h and child_w and child_h:
|
||||
max_x = max(0.0, float(parent_w) - float(child_w))
|
||||
max_y = max(0.0, float(parent_h) - float(child_h))
|
||||
new_local_left = max(0.0, min(float(new_local_left), max_x))
|
||||
new_local_top = max(0.0, min(float(new_local_top), max_y))
|
||||
child_data['left'] = new_local_left
|
||||
child_data['top'] = new_local_top
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 关键修改:由于我们不实际重新父化对象,所有组件都保持相对于Canvas的绝对位置
|
||||
# 只在数据结构中记录相对于父组件的局部坐标
|
||||
if child_data.get('visual_parent_canvas'):
|
||||
# Keep world position
|
||||
return new_local_left, new_local_top
|
||||
|
||||
def _sync_child_canvas_index_with_parent(self, child_data, parent_data):
|
||||
if not child_data.get('visual_parent_canvas'):
|
||||
return
|
||||
try:
|
||||
child_data['canvas_index'] = parent_data.get('canvas_index', self.current_canvas_index)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 根据keep_world参数决定是否保持世界位置
|
||||
if keep_world:
|
||||
# 保持世界位置:使用绝对坐标
|
||||
def _apply_child_absolute_position(self, child_obj, current_abs_left, current_abs_top, keep_world):
|
||||
if hasattr(child_obj, 'left'):
|
||||
child_obj.left = current_abs_left
|
||||
if hasattr(child_obj, 'top'):
|
||||
child_obj.top = current_abs_top
|
||||
|
||||
if keep_world:
|
||||
print(f"[_set_parent_child_relationship] Child position kept at absolute: ({current_abs_left:.1f}, {current_abs_top:.1f})")
|
||||
else:
|
||||
# 不保持世界位置:使用局部坐标(但由于没有实际重新父化,这会导致位置跳变)
|
||||
# 为了避免跳变,我们仍然使用绝对坐标
|
||||
if hasattr(child_obj, 'left'):
|
||||
child_obj.left = current_abs_left
|
||||
if hasattr(child_obj, 'top'):
|
||||
child_obj.top = current_abs_top
|
||||
print(f"[_set_parent_child_relationship] Child position set to absolute: ({current_abs_left:.1f}, {current_abs_top:.1f})")
|
||||
|
||||
# Ensure child visuals are visible above parent
|
||||
def _ensure_child_visibility_and_layer(self, child_data, parent_data, child_obj, parent_obj):
|
||||
if hasattr(child_obj, 'z_offset'):
|
||||
try:
|
||||
parent_z = getattr(parent_obj, 'z_offset', 0)
|
||||
@ -157,8 +189,19 @@ class LUIManagerEditorMixin:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Special handling for Button: raise internal sprites/label above parent
|
||||
if child_data.get('type') == 'Button':
|
||||
comp_type = child_data.get('type')
|
||||
if comp_type == 'Button':
|
||||
self._ensure_button_child_visibility(child_obj)
|
||||
elif comp_type == 'Frame':
|
||||
self._ensure_frame_child_visibility(child_obj)
|
||||
elif comp_type in ['Plane', 'Image', 'Video', 'HttpText']:
|
||||
self._ensure_media_child_visibility(child_data)
|
||||
|
||||
self._ensure_child_sprite_visibility_and_layer(child_data, parent_data, child_obj)
|
||||
if hasattr(child_obj, 'show'):
|
||||
child_obj.show()
|
||||
|
||||
def _ensure_button_child_visibility(self, child_obj):
|
||||
try:
|
||||
layout = getattr(child_obj, '_layout', None)
|
||||
if layout is not None:
|
||||
@ -169,25 +212,22 @@ class LUIManagerEditorMixin:
|
||||
lbl = getattr(child_obj, '_label', None)
|
||||
if lbl is not None and hasattr(lbl, 'z_offset'):
|
||||
lbl.z_offset = float(getattr(child_obj, 'z_offset', 0)) + 2.0
|
||||
# Ensure label visible
|
||||
if lbl is not None and hasattr(lbl, 'show'):
|
||||
lbl.show()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Special handling for Frame: ensure visibility
|
||||
elif child_data.get('type') == 'Frame':
|
||||
def _ensure_frame_child_visibility(self, child_obj):
|
||||
try:
|
||||
if hasattr(child_obj, 'show'):
|
||||
child_obj.show()
|
||||
if hasattr(child_obj, 'visible'):
|
||||
child_obj.visible = True
|
||||
print(f"[_set_parent_child_relationship] Frame visibility ensured")
|
||||
print("[_set_parent_child_relationship] Frame visibility ensured")
|
||||
except Exception as e:
|
||||
print(f"[_set_parent_child_relationship] Error ensuring Frame visibility: {e}")
|
||||
|
||||
# Special handling for Plane, Image, Video: ensure sprite visibility
|
||||
elif child_data.get('type') in ['Plane', 'Image', 'Video', 'HttpText']:
|
||||
def _ensure_media_child_visibility(self, child_data):
|
||||
try:
|
||||
spr = child_data.get('sprite')
|
||||
if spr is not None:
|
||||
@ -195,64 +235,38 @@ class LUIManagerEditorMixin:
|
||||
spr.show()
|
||||
if hasattr(spr, 'visible'):
|
||||
spr.visible = True
|
||||
|
||||
# Ensure sprite color is not transparent
|
||||
if child_data.get('type') == 'Plane':
|
||||
color = child_data.get('color', (1, 1, 1, 1))
|
||||
if hasattr(spr, 'color'):
|
||||
spr.color = color
|
||||
|
||||
if child_data.get('type') == 'Plane' and hasattr(spr, 'color'):
|
||||
spr.color = child_data.get('color', (1, 1, 1, 1))
|
||||
print(f"[_set_parent_child_relationship] {child_data.get('type')} sprite visibility ensured")
|
||||
except Exception as e:
|
||||
print(f"[_set_parent_child_relationship] Error ensuring {child_data.get('type')} visibility: {e}")
|
||||
|
||||
def _ensure_child_sprite_visibility_and_layer(self, child_data, parent_data, child_obj):
|
||||
child_sprite = child_data.get('sprite')
|
||||
if child_sprite is not None:
|
||||
if child_sprite is None:
|
||||
return
|
||||
|
||||
try:
|
||||
if hasattr(child_sprite, 'show'):
|
||||
child_sprite.show()
|
||||
# Ensure sprite is attached to the child object
|
||||
if hasattr(child_sprite, 'parent') and child_sprite.parent != child_obj:
|
||||
child_sprite.parent = child_obj
|
||||
# Restore sprite color if stored
|
||||
if hasattr(child_sprite, 'color') and 'color' in child_data:
|
||||
child_sprite.color = child_data.get('color', child_sprite.color)
|
||||
|
||||
if hasattr(child_sprite, 'z_offset'):
|
||||
parent_sprite = parent_data.get('sprite')
|
||||
parent_sprite_z = 0.0
|
||||
if parent_sprite is not None and hasattr(parent_sprite, 'z_offset'):
|
||||
parent_sprite_z = float(parent_sprite.z_offset)
|
||||
child_sprite.z_offset = max(parent_sprite_z + 1.0, float(getattr(child_obj, 'z_offset', 0)) + 1.0)
|
||||
except Exception:
|
||||
pass
|
||||
# Keep child visible above parent visuals
|
||||
if hasattr(child_obj, 'z_offset'):
|
||||
parent_z = getattr(parent_obj, 'z_offset', 0)
|
||||
try:
|
||||
child_obj.z_offset = float(parent_z) + 2.0
|
||||
except Exception:
|
||||
pass
|
||||
# Ensure child sprite renders above parent visuals
|
||||
child_sprite = child_data.get('sprite')
|
||||
if child_sprite is not None and hasattr(child_sprite, 'z_offset'):
|
||||
parent_sprite_z = 0
|
||||
try:
|
||||
parent_sprite = parent_data.get('sprite')
|
||||
if parent_sprite is not None and hasattr(parent_sprite, 'z_offset'):
|
||||
parent_sprite_z = float(parent_sprite.z_offset)
|
||||
except Exception:
|
||||
parent_sprite_z = 0
|
||||
try:
|
||||
child_sprite.z_offset = max(float(parent_sprite_z) + 1.0, float(getattr(child_obj, 'z_offset', 0)) + 1.0)
|
||||
child_sprite.z_offset = max(
|
||||
parent_sprite_z + 1.0,
|
||||
float(getattr(child_obj, 'z_offset', 0)) + 1.0,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(child_obj, 'show'):
|
||||
child_obj.show()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Reparenting error: {e}")
|
||||
|
||||
# Ensure canvas_index follows parent canvas (visibility)
|
||||
def _finalize_parent_child_link(self, child_index, parent_index, parent_data):
|
||||
parent_canvas = parent_data.get('canvas_index')
|
||||
if parent_canvas is not None:
|
||||
self._update_canvas_index_recursive(child_index, parent_canvas)
|
||||
|
||||
@ -1213,26 +1213,16 @@ class LUIManagerInteractionMixin:
|
||||
print("✓ 重新创建了手柄,确保在最前面")
|
||||
|
||||
def _handle_resize_drag(self):
|
||||
"""处理 resize 拖拽(编排入口)。"""
|
||||
self._handle_resize_drag_core()
|
||||
|
||||
def _handle_resize_drag_core(self):
|
||||
"""处理resize拖拽 - 完整功能实现,支持Shift和Alt键操作"""
|
||||
if not hasattr(self.world, 'mouseWatcherNode') or not self.world.mouseWatcherNode.hasMouse():
|
||||
return
|
||||
|
||||
# 检查鼠标左键是否释放
|
||||
import panda3d.core as p3d
|
||||
if not self.world.mouseWatcherNode.is_button_down(p3d.MouseButton.one()):
|
||||
if self.resizing_handle:
|
||||
comp_data = self.components[self.selected_index]
|
||||
width_val = comp_data.get('width', 0)
|
||||
height_val = comp_data.get('height', 0)
|
||||
print(f"✓ 组件调整完成: width={width_val:.1f}, height={height_val:.1f}")
|
||||
|
||||
# 更新锚点位置(如果有锚点)
|
||||
if comp_data.get('anchored_to_parent'):
|
||||
anchor_pos = comp_data.get('anchor_position')
|
||||
if anchor_pos:
|
||||
self._update_component_anchor_position(self.selected_index, anchor_pos)
|
||||
|
||||
self.resizing_handle = None
|
||||
if self._finish_resize_drag_if_released(p3d):
|
||||
return
|
||||
|
||||
# 获取当前鼠标位置
|
||||
@ -1257,24 +1247,106 @@ class LUIManagerInteractionMixin:
|
||||
comp_obj = comp_data['object']
|
||||
comp_type = comp_data['type']
|
||||
|
||||
# 获取初始边界(Canvas相对坐标)
|
||||
new_left, new_top, new_width, new_height = self._compute_resize_drag_bounds(
|
||||
delta_x, delta_y, shift_held, alt_held
|
||||
)
|
||||
|
||||
if self.selected_index < 0:
|
||||
return
|
||||
|
||||
comp_data = self.components[self.selected_index]
|
||||
self._apply_resize_drag_updates(
|
||||
comp_data, comp_obj, comp_type,
|
||||
new_left, new_top, new_width, new_height,
|
||||
shift_held, alt_held,
|
||||
)
|
||||
|
||||
def _finish_resize_drag_if_released(self, p3d):
|
||||
"""鼠标释放时结束缩放并执行收尾同步。"""
|
||||
if self.world.mouseWatcherNode.is_button_down(p3d.MouseButton.one()):
|
||||
return False
|
||||
|
||||
if self.resizing_handle and self.selected_index >= 0 and self.selected_index < len(self.components):
|
||||
comp_data = self.components[self.selected_index]
|
||||
width_val = comp_data.get('width', 0)
|
||||
height_val = comp_data.get('height', 0)
|
||||
print(f"✓ 组件调整完成: width={width_val:.1f}, height={height_val:.1f}")
|
||||
|
||||
# 更新锚点位置(如果有锚点)
|
||||
if comp_data.get('anchored_to_parent'):
|
||||
anchor_pos = comp_data.get('anchor_position')
|
||||
if anchor_pos:
|
||||
self._update_component_anchor_position(self.selected_index, anchor_pos)
|
||||
|
||||
self.resizing_handle = None
|
||||
return True
|
||||
|
||||
def _compute_resize_drag_bounds(self, delta_x, delta_y, shift_held, alt_held):
|
||||
"""根据当前手柄/修饰键计算新的 left/top/width/height。"""
|
||||
start_left, start_top, start_width, start_height, aspect_ratio = self._get_resize_start_values()
|
||||
handle_pos = self.resizing_handle._resize_position
|
||||
|
||||
new_left, new_top, new_width, new_height = self._apply_resize_handle_delta(
|
||||
handle_pos, start_left, start_top, start_width, start_height, delta_x, delta_y
|
||||
)
|
||||
new_left, new_top, new_width, new_height = self._apply_resize_min_size_clamp(
|
||||
handle_pos, start_left, start_top, start_width, start_height,
|
||||
new_left, new_top, new_width, new_height,
|
||||
)
|
||||
|
||||
if shift_held:
|
||||
new_left, new_top, new_width, new_height = self._apply_shift_keep_ratio_resize(
|
||||
handle_pos, start_left, start_top, start_width, start_height,
|
||||
new_left, new_top, new_width, new_height, aspect_ratio,
|
||||
)
|
||||
|
||||
if alt_held:
|
||||
new_left, new_top, new_width, new_height = self._apply_alt_center_resize(
|
||||
start_left, start_top, start_width, start_height,
|
||||
new_left, new_top, new_width, new_height,
|
||||
)
|
||||
|
||||
self._cache_resize_canvas_bounds()
|
||||
return self._sanitize_resize_dimensions(new_left, new_top, new_width, new_height)
|
||||
|
||||
def _apply_resize_drag_updates(
|
||||
self, comp_data, comp_obj, comp_type,
|
||||
new_left, new_top, new_width, new_height,
|
||||
shift_held, alt_held,
|
||||
):
|
||||
"""将缩放结果写回组件数据与渲染对象。"""
|
||||
parent_index, parent_offset_x, parent_offset_y, is_scene_parented = self._resolve_resize_parent_context(
|
||||
comp_data, comp_obj
|
||||
)
|
||||
_ = parent_index
|
||||
|
||||
self._write_resize_component_data(comp_data, new_left, new_top, new_width, new_height)
|
||||
self._apply_resize_component_position(
|
||||
comp_obj, new_left, new_top, parent_offset_x, parent_offset_y, is_scene_parented
|
||||
)
|
||||
|
||||
try:
|
||||
self._apply_resize_component_size_by_type(comp_data, comp_obj, comp_type, new_width, new_height)
|
||||
self._post_resize_component_sync(comp_type, shift_held, alt_held)
|
||||
except Exception as e:
|
||||
print(f"⚠ 设置组件尺寸失败 ({comp_type}): {e}")
|
||||
|
||||
def _get_resize_start_values(self):
|
||||
start_left = self.resize_start_bounds['left']
|
||||
start_top = self.resize_start_bounds['top']
|
||||
start_width = self.resize_start_bounds['width']
|
||||
start_height = self.resize_start_bounds['height']
|
||||
|
||||
# 计算初始宽高比
|
||||
aspect_ratio = start_width / start_height if start_height > 0 else 1
|
||||
return start_left, start_top, start_width, start_height, aspect_ratio
|
||||
|
||||
# 根据手柄位置计算新的边界
|
||||
handle_pos = self.resizing_handle._resize_position
|
||||
|
||||
def _apply_resize_handle_delta(
|
||||
self, handle_pos, start_left, start_top, start_width, start_height, delta_x, delta_y
|
||||
):
|
||||
new_left = start_left
|
||||
new_top = start_top
|
||||
new_width = start_width
|
||||
new_height = start_height
|
||||
|
||||
# 根据手柄位置调整尺寸
|
||||
if handle_pos == 'top-left':
|
||||
new_left = start_left + delta_x
|
||||
new_top = start_top + delta_y
|
||||
@ -1302,7 +1374,12 @@ class LUIManagerInteractionMixin:
|
||||
new_left = start_left + delta_x
|
||||
new_width = start_width - delta_x
|
||||
|
||||
# 应用最小尺寸限制
|
||||
return new_left, new_top, new_width, new_height
|
||||
|
||||
def _apply_resize_min_size_clamp(
|
||||
self, handle_pos, start_left, start_top, start_width, start_height,
|
||||
new_left, new_top, new_width, new_height,
|
||||
):
|
||||
if new_width < self.min_size:
|
||||
if handle_pos in ['top-left', 'left', 'bottom-left']:
|
||||
new_left = start_left + start_width - self.min_size
|
||||
@ -1313,8 +1390,12 @@ class LUIManagerInteractionMixin:
|
||||
new_top = start_top + start_height - self.min_size
|
||||
new_height = self.min_size
|
||||
|
||||
# Shift键:保持宽高比
|
||||
if shift_held:
|
||||
return new_left, new_top, new_width, new_height
|
||||
|
||||
def _apply_shift_keep_ratio_resize(
|
||||
self, handle_pos, start_left, start_top, start_width, start_height,
|
||||
new_left, new_top, new_width, new_height, aspect_ratio,
|
||||
):
|
||||
width_change = abs(new_width - start_width)
|
||||
height_change = abs(new_height - start_height)
|
||||
|
||||
@ -1327,20 +1408,22 @@ class LUIManagerInteractionMixin:
|
||||
if handle_pos in ['top-left', 'left', 'bottom-left']:
|
||||
new_left = start_left + start_width - new_width
|
||||
|
||||
# Alt键:从中心点缩放
|
||||
if alt_held:
|
||||
return new_left, new_top, new_width, new_height
|
||||
|
||||
def _apply_alt_center_resize(
|
||||
self, start_left, start_top, start_width, start_height,
|
||||
new_left, new_top, new_width, new_height,
|
||||
):
|
||||
center_x = start_left + start_width / 2
|
||||
center_y = start_top + start_height / 2
|
||||
|
||||
width_diff = new_width - start_width
|
||||
height_diff = new_height - start_height
|
||||
|
||||
new_width = start_width + width_diff * 2
|
||||
new_height = start_height + height_diff * 2
|
||||
new_left = center_x - new_width / 2
|
||||
new_top = center_y - new_height / 2
|
||||
|
||||
# 再次应用最小尺寸限制
|
||||
if new_width < self.min_size:
|
||||
new_width = self.min_size
|
||||
new_left = center_x - new_width / 2
|
||||
@ -1348,57 +1431,55 @@ class LUIManagerInteractionMixin:
|
||||
new_height = self.min_size
|
||||
new_top = center_y - new_height / 2
|
||||
|
||||
# 获取Canvas边界约束
|
||||
canvas_width = 800 # 默认Canvas宽度
|
||||
canvas_height = 600 # 默认Canvas高度
|
||||
return new_left, new_top, new_width, new_height
|
||||
|
||||
def _cache_resize_canvas_bounds(self):
|
||||
canvas_width = 800
|
||||
canvas_height = 600
|
||||
if self.current_canvas_index >= 0 and self.current_canvas_index < len(self.canvases):
|
||||
canvas_panel = self.canvases[self.current_canvas_index]['panel']
|
||||
canvas_width = canvas_panel.get_width()
|
||||
canvas_height = canvas_panel.get_height()
|
||||
_ = (canvas_width, canvas_height)
|
||||
|
||||
def _sanitize_resize_dimensions(self, new_left, new_top, new_width, new_height):
|
||||
if new_width < 1.0:
|
||||
new_width = 1.0
|
||||
if new_height < 1.0:
|
||||
new_height = 1.0
|
||||
return new_left, new_top, new_width, new_height
|
||||
|
||||
# 最后的安全检查,防止宽高变为负数
|
||||
if new_width < 1.0: new_width = 1.0
|
||||
if new_height < 1.0: new_height = 1.0
|
||||
|
||||
if self.selected_index < 0:
|
||||
return
|
||||
|
||||
comp_data = self.components[self.selected_index]
|
||||
def _resolve_resize_parent_context(self, comp_data, comp_obj):
|
||||
parent_index = comp_data.get('parent_index')
|
||||
parent_offset_x = 0
|
||||
parent_offset_y = 0
|
||||
|
||||
if parent_index is not None and parent_index >= 0:
|
||||
parent_offset_x, parent_offset_y = self._get_component_accumulated_pos(parent_index)
|
||||
|
||||
# Update component data
|
||||
child_parent = getattr(comp_obj, 'parent', None)
|
||||
parent_obj = None
|
||||
if parent_index is not None and parent_index >= 0:
|
||||
parent_obj = self.components[parent_index].get('object')
|
||||
is_scene_parented = (child_parent is not None and child_parent == parent_obj)
|
||||
return parent_index, parent_offset_x, parent_offset_y, is_scene_parented
|
||||
|
||||
def _write_resize_component_data(self, comp_data, new_left, new_top, new_width, new_height):
|
||||
comp_data['left'] = new_left
|
||||
comp_data['top'] = new_top
|
||||
comp_data['width'] = new_width
|
||||
comp_data['height'] = new_height
|
||||
|
||||
# Update component position
|
||||
child_parent = getattr(comp_obj, 'parent', None)
|
||||
parent_obj = None
|
||||
if parent_index is not None and parent_index >= 0:
|
||||
parent_obj = self.components[parent_index].get('object')
|
||||
|
||||
# Check if actually parented in LUI scene graph
|
||||
is_scene_parented = (child_parent is not None and child_parent == parent_obj)
|
||||
|
||||
def _apply_resize_component_position(
|
||||
self, comp_obj, new_left, new_top, parent_offset_x, parent_offset_y, is_scene_parented
|
||||
):
|
||||
if is_scene_parented:
|
||||
# Component is physically parented to its logical parent -> Use Relative coords
|
||||
comp_obj.left = new_left
|
||||
comp_obj.top = new_top
|
||||
else:
|
||||
# Component is physically root/canvas -> Use Absolute coords (Parent Abs + Relative)
|
||||
comp_obj.left = parent_offset_x + new_left
|
||||
comp_obj.top = parent_offset_y + new_top
|
||||
|
||||
# Update component size
|
||||
try:
|
||||
def _apply_resize_component_size_by_type(self, comp_data, comp_obj, comp_type, new_width, new_height):
|
||||
if hasattr(comp_obj, 'width'):
|
||||
comp_obj.width = new_width
|
||||
if hasattr(comp_obj, 'height'):
|
||||
@ -1414,13 +1495,24 @@ class LUIManagerInteractionMixin:
|
||||
if comp_type == 'HttpText':
|
||||
self.luiFunction.sync_http_text_layout(self, comp_data)
|
||||
elif comp_type == 'Button':
|
||||
self._apply_button_resize_size(comp_obj, new_width, new_height)
|
||||
elif comp_type == 'InputField':
|
||||
self._apply_input_field_resize_size(comp_obj, new_width, new_height)
|
||||
elif comp_type == 'Slider':
|
||||
if hasattr(comp_obj, 'set_width'):
|
||||
comp_obj.set_width(new_width)
|
||||
if hasattr(comp_obj, 'set_height'):
|
||||
comp_obj.set_height(new_height)
|
||||
|
||||
def _apply_button_resize_size(self, comp_obj, new_width, new_height):
|
||||
if hasattr(comp_obj, 'set_width'):
|
||||
comp_obj.set_width(new_width)
|
||||
if hasattr(comp_obj, 'set_height'):
|
||||
comp_obj.set_height(new_height)
|
||||
if hasattr(comp_obj, '_apply_stretch_sizes'):
|
||||
comp_obj._apply_stretch_sizes()
|
||||
elif comp_type == 'InputField':
|
||||
|
||||
def _apply_input_field_resize_size(self, comp_obj, new_width, new_height):
|
||||
if hasattr(comp_obj, 'set_width'):
|
||||
comp_obj.set_width(new_width)
|
||||
if hasattr(comp_obj, 'set_height'):
|
||||
@ -1429,6 +1521,9 @@ class LUIManagerInteractionMixin:
|
||||
comp_obj.width = new_width
|
||||
if hasattr(comp_obj, 'height'):
|
||||
comp_obj.height = new_height
|
||||
self._sync_input_field_layout_stretch(comp_obj)
|
||||
|
||||
def _sync_input_field_layout_stretch(self, comp_obj):
|
||||
try:
|
||||
layout = getattr(comp_obj, '_layout', None)
|
||||
if layout is not None:
|
||||
@ -1450,22 +1545,19 @@ class LUIManagerInteractionMixin:
|
||||
spr.width = '100%'
|
||||
except Exception:
|
||||
pass
|
||||
elif comp_type == 'Slider':
|
||||
if hasattr(comp_obj, 'set_width'):
|
||||
comp_obj.set_width(new_width)
|
||||
if hasattr(comp_obj, 'set_height'):
|
||||
comp_obj.set_height(new_height)
|
||||
|
||||
def _post_resize_component_sync(self, comp_type, shift_held, alt_held):
|
||||
if comp_type in ['VerticalLayout', 'HorizontalLayout']:
|
||||
self._update_layout_inner(self.selected_index)
|
||||
|
||||
self._update_anchored_children(self.selected_index)
|
||||
modifier_str = self._get_resize_modifier_info(shift_held, alt_held)
|
||||
_ = modifier_str
|
||||
|
||||
def _get_resize_modifier_info(self, shift_held, alt_held):
|
||||
modifier_info = []
|
||||
if shift_held:
|
||||
modifier_info.append('Shift(keep ratio)')
|
||||
if alt_held:
|
||||
modifier_info.append('Alt(center scale)')
|
||||
modifier_str = ' + '.join(modifier_info) if modifier_info else 'Normal'
|
||||
except Exception as e:
|
||||
print(f"⚠ 设置组件尺寸失败 ({comp_type}): {e}")
|
||||
return ' + '.join(modifier_info) if modifier_info else 'Normal'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user