diff --git a/PROJECT_OPTIMIZATION_ANALYSIS.md b/PROJECT_OPTIMIZATION_ANALYSIS.md index c2d84648..b27d604d 100644 --- a/PROJECT_OPTIMIZATION_ANALYSIS.md +++ b/PROJECT_OPTIMIZATION_ANALYSIS.md @@ -2,7 +2,7 @@ > 基于本地代码静态扫描生成(不含远程仓库信息,不含 `RenderPipelineFile/` 第三方目录)。 -- 分析时间: 2026-02-28 +- 分析时间: 2026-03-01 - 扫描范围: `main.py`, `Start_Run.py`, `core/`, `scene/`, `project/`, `ui/`, `ssbo_component/`, `TransformGizmo/`, `scripts/`, `tools/`, `templates/` ## 0. 执行进展(非 VR) @@ -56,7 +56,12 @@ - `core/selection.py::updateGizmoDrag` 第一轮拆分已完成: - 实测指标:`278 -> 32` 行,近似圈复杂度 `65 -> 10` - 缩放/旋转/平移逻辑已拆到独立 helper - - 下一轮建议转向 `ui/panels/editor_panels_left.py::_draw_resource_manager` +- `ui/LUI/lui_manager_interaction.py::_update_drag` 第一轮拆分已完成: + - 实测指标:`348 -> 28` 行,近似圈复杂度 `79 -> 7` + - Canvas/组件拖拽流程已拆到独立 helper +- `ui/panels/editor_panels_left.py::_draw_resource_manager` 保留布局版拆分已完成: + - 实测指标:`310 -> 23` 行(第一轮 `310 -> 163`,第二轮 `163 -> 23`) + - 目录/文件项渲染、展开内容、右键菜单流程已拆到独立 helper ## 1. 总体画像 @@ -92,13 +97,12 @@ ### 2.2 长函数 Top(优先拆分,非 VR 当前状态) 1. `ui/LUI/lui_function_properties.py::_draw_component_properties` (`1441` 行) -2. `ui/LUI/lui_manager_interaction.py::_update_drag` (`348` 行) -3. `ui/panels/editor_panels_left.py::_draw_resource_manager` (`310` 行) -4. `ui/LUI/lui_manager_interaction.py::_handle_resize_drag` (`257` 行) -5. `ui/LUI/lui_manager_editor.py::_set_parent_child_relationship` (`234` 行) -6. `ui/panels/editor_panels_top.py::draw_menu_bar` (`227` 行) -7. `scene/scene_manager_io_mixin.py::saveScene` (`222` 行) -8. `TransformGizmo/move_gizmo.py::_on_mouse_down` (`222` 行) +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` 行) ### 2.3 异常处理密度高(可观测性风险) @@ -157,10 +161,9 @@ 建议拆分顺序(非 VR 当前阶段): -1. `ui/panels/editor_panels_left.py::_draw_resource_manager` -2. `ui/LUI/lui_manager_interaction.py::_update_drag` -3. `ui/LUI/lui_function_properties.py::_draw_component_properties`(可按“变换/布局/视觉/交互/脚本”分区) -4. `ui/LUI/lui_manager_interaction.py::_handle_resize_drag` +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` 预期收益: @@ -232,20 +235,27 @@ - 主函数长度降到 `<= 120` 行(实际 `32` 行) - 保持当前 gizmo 拖拽行为一致 -### Task F(推荐下一步,1 天) +### Task F(已完成,保留布局版两轮) - 拆分 `ui/panels/editor_panels_left.py::_draw_resource_manager` - 验收: - - 主函数长度降到 `<= 160` 行 - - 文件/目录项渲染逻辑去重 + - 主函数长度降到 `<= 160` 行(第一轮 `163` 行,第二轮 `23` 行) + - 保持原有布局/样式/文案/交互顺序不变 -### Task G(并行候选,1-2 天) +### Task G(已完成,第一轮) - 拆分 `ui/LUI/lui_manager_interaction.py::_update_drag` - 验收: - - 主函数长度降到 `<= 140` 行 + - 主函数长度降到 `<= 140` 行(实际 `28` 行) - 拖拽与缩放交互行为保持一致 +### Task H(推荐下一步,2-3 天) + +- 拆分 `ui/LUI/lui_function_properties.py::_draw_component_properties` +- 验收: + - 主函数长度降到 `<= 260` 行 + - 按“变换/布局/视觉/交互/脚本”子区块拆分并保持渲染逻辑一致 + ## 4.1 本轮深入分析(非 VR,P1 准备) ### A) `scene/scene_manager_io_mixin.py::loadScene`(核心优先,已完成) @@ -269,9 +279,9 @@ - `loadScene` 主体压缩到 `120` 行以内,只保留流程编排。 - 节点恢复行为(位置/材质/脚本/可见性)与当前一致。 - 失败重试逻辑保持语义一致。 -- 当前状态(2026-02-28): +- 当前状态(2026-03-01): - 第二轮已完成(`loadScene` 已达验收目标:`74` 行)。 - - 后续建议转入回归观察,优先推进 `ui/panels/editor_panels_left.py::_draw_resource_manager`。 + - 后续建议转入回归观察,优先推进 `ui/LUI/lui_function_properties.py::_draw_component_properties`。 ### B) `main.py::__init__`(第二优先,已完成第一轮) @@ -311,27 +321,32 @@ - 主流程已收敛为编排入口。 - 已通过 `python -m compileall core/selection.py`。 -### E) `ui/panels/editor_panels_left.py::_draw_resource_manager`(第五优先) +### E) `ui/panels/editor_panels_left.py::_draw_resource_manager`(已完成两轮) -- 函数规模: `310` 行 -- 近似圈复杂度: `88` -- 关键问题: - - 目录树与文件列表 UI 逻辑高度重复(图标、选择、多选、双击、右键菜单)。 - - 文件/目录条目处理缺少统一的 item 渲染与交互抽象。 -- 建议拆分: - - `_draw_resource_toolbar(rm)` - - `_draw_resource_path_and_search(rm)` - - `_draw_directory_entry(rm, path, depth)` - - `_draw_file_entry(rm, path, depth)` - - `_draw_resource_context_popup(rm)` +- 函数规模: `23` 行(重构前 `310` 行) +- 近似圈复杂度: 已由主函数分支链路转移到子流程函数(入口仅保留编排) +- 已落地 helper: + - `_update_resource_manager_window_rect` + - `_draw_resource_toolbar_and_filters` + - `_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` + - `_draw_resource_context_menu` +- 状态: + - 主流程已收敛为编排入口,且保持原布局与交互顺序。 + - 已通过 `python -m compileall ui/panels/editor_panels_left.py`。 ## 4.2 执行顺序建议(非 VR) 1. `loadScene` 已完成两轮拆分(当前建议冻结并转入回归观察)。 2. `main.__init__` 已完成第一轮拆分(建议转入回归观察)。 -3. 下一步处理 `_draw_resource_manager`(提炼重复 UI 渲染分支)。 -4. 然后处理 `_update_drag`(LUI 交互链路收敛)。 -5. 最后处理 `_draw_component_properties`(属性面板分区拆分)。 +3. `_update_drag` 已完成第一轮拆分,建议转入回归观察。 +4. 下一步处理 `_draw_component_properties`(属性面板分区拆分)。 +5. 然后处理 `_handle_resize_drag`(拖拽缩放路径补充拆分)。 ## 5. 与现有文档关系 @@ -341,4 +356,4 @@ --- -如果按此路线继续,建议下一轮直接从 **Task F** 开始,我可以先落地 `_draw_resource_manager` 的第一轮拆分并保持行为不变。 +如果按此路线继续,建议下一轮直接从 **Task H** 开始,先落地 `_draw_component_properties` 的分区拆分并保持行为不变。 diff --git a/PROJECT_TASK_CHECKLIST.md b/PROJECT_TASK_CHECKLIST.md index 43008def..bc33865f 100644 --- a/PROJECT_TASK_CHECKLIST.md +++ b/PROJECT_TASK_CHECKLIST.md @@ -13,7 +13,8 @@ - Qt 运行依赖主路径已清理完成(排除 `RenderPipelineFile`)。 - 项目已进入“非 VR 结构优化阶段”。 -- `_getActor` 与 `updateGizmoDrag` 第一轮拆分已完成,当前最高优先任务转为资源管理器面板拆分。 +- `_getActor`、`updateGizmoDrag`、`_update_drag` 第一轮拆分已完成。 +- `资源管理器面板` 已完成“保留原布局/样式/交互”的第二轮拆分收敛。 ## 已完成(Done) @@ -69,23 +70,42 @@ - 新增 helper:`_validate_gizmo_drag_state`、`_apply_scale_drag`、`_apply_rotate_drag`、`_compute_axis_movement_distance`、`_apply_translate_drag` - 保持目标:gizmo 缩放/旋转/平移行为不变 +### F. LUI 拖拽链路拆分完成(第一轮) + +- [x] `ui/LUI/lui_manager_interaction.py::_update_drag` 第一轮拆分完成: + - 行数:`348 -> 28` + - 近似圈复杂度:`79 -> 7` + - 新增 helper:`_process_canvas_drag`、`_try_start_pending_component_drag`、`_finish_component_drag_if_released`、`_update_active_component_drag` + - 保持目标:Canvas/组件拖拽、吸附与释放语义不变 + +### G. 资源管理器面板拆分完成(保留布局版) + +- [x] 第一轮低侵入优化: + - 行数:`310 -> 163` + - 近似圈复杂度:`88 -> 41` + - 新增 helper:`_draw_resource_toolbar_and_filters`、`_load_resource_icon`、`_handle_resource_item_selection`、`_open_resource_item_context_menu`、`_handle_resource_file_double_click`、`_draw_resource_context_menu` +- [x] 第二轮(按当前标准重启,保持布局/样式/文案/交互顺序): + - 入口函数行数:`163 -> 23`(相对原始 `310 -> 23`) + - 新增 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` + - 保持目标:不改视觉布局,不改交互语义,仅做结构分解 + ## 下一步(Next) -### N1 `资源管理器面板` 拆分(最高优先级,非 VR) +### N1 `属性面板超大函数` 拆分(最高优先级,非 VR) -- 目标文件:`ui/panels/editor_panels_left.py::_draw_resource_manager` -- 当前规模:`310` 行 +- 目标文件:`ui/LUI/lui_function_properties.py::_draw_component_properties` +- 当前规模:`1441` 行 - 验收目标: - - 提取目录/文件项渲染与右键菜单复用逻辑 - - 交互行为保持一致 + - 按“变换/布局/视觉/交互/脚本”拆分为多个子绘制函数 + - 主流程压缩并保持属性面板行为一致 -### N2 `LUI 交互拖拽链路` 拆分(高优先级,非 VR) +### N2 `LUI 缩放链路` 拆分(高优先级,非 VR) -- 目标文件:`ui/LUI/lui_manager_interaction.py::_update_drag` -- 当前规模:`348` 行 +- 目标文件:`ui/LUI/lui_manager_interaction.py::_handle_resize_drag` +- 当前规模:`257` 行 - 验收目标: - - 入口流程收敛到 `<= 140` 行 - - 保持拖拽/缩放/吸附交互一致 + - 入口流程收敛到 `<= 120` 行 + - 保持缩放手柄行为与边界语义一致 ## 验收标准(阶段) diff --git a/ui/panels/editor_panels_left.py b/ui/panels/editor_panels_left.py index 08b81332..cf2ab073 100644 --- a/ui/panels/editor_panels_left.py +++ b/ui/panels/editor_panels_left.py @@ -283,308 +283,293 @@ class EditorPanelsLeftMixin: self.app.showResourceManager = True rm = self.app.resource_manager - window_pos = imgui.get_window_pos() - window_size = imgui.get_window_size() - self.app._resource_manager_window_rect = ( - float(window_pos.x), - float(window_pos.y), - float(window_size.x), - float(window_size.y), - ) - self.app._resource_drop_targets.append(( - float(window_pos.x), - float(window_pos.y), - float(window_size.x), - float(window_size.y), - str(rm.current_path), - )) + self._update_resource_manager_window_rect(rm) imgui.text("文件浏览器") imgui.separator() - if imgui.button("◀"): - rm.navigate_back() - imgui.same_line() - if imgui.button("▶"): - rm.navigate_forward() - imgui.same_line() - if imgui.button("▲"): - rm.navigate_up() - imgui.same_line() - if imgui.button("主页"): - rm.navigate_to(rm.project_root / "Resources") - imgui.same_line() - if imgui.button("刷新"): - rm.force_refresh() - - # 自动刷新开关 - imgui.same_line() - changed, rm.auto_refresh_enabled = imgui.checkbox("自动刷新", rm.auto_refresh_enabled) - if changed: - rm.set_auto_refresh(rm.auto_refresh_enabled) - - imgui.same_line() - imgui.text(" ") - imgui.same_line() - - # 路径输入框 - changed, new_path = imgui.input_text("路径", str(rm.current_path), 256) - if changed: - try: - rm.navigate_to(Path(new_path)) - except: - pass - - # 搜索框 - changed, rm.search_filter = imgui.input_text("搜索", rm.search_filter, 256) + self._draw_resource_toolbar_and_filters(rm) imgui.separator() - if rm.refresh_if_needed(): - pass + rm.refresh_if_needed() dirs, files = rm.get_directory_contents(rm.current_path) + self._draw_resource_directory_entries(rm, dirs) + self._draw_resource_file_entries(rm, files) + self._draw_resource_context_menu(rm) - for dir_path in dirs: - if not rm.should_show_file(dir_path): - continue + def _update_resource_manager_window_rect(self, rm): + """更新资源管理器窗口矩形与根拖拽目标。""" + window_pos = imgui.get_window_pos() + window_size = imgui.get_window_size() + self.app._resource_manager_window_rect = ( + float(window_pos.x), + float(window_pos.y), + float(window_size.x), + float(window_size.y), + ) + self.app._resource_drop_targets.append(( + float(window_pos.x), + float(window_pos.y), + float(window_size.x), + float(window_size.y), + str(rm.current_path), + )) - icon_name = rm.get_file_icon(dir_path.name, is_folder=True) - is_selected = dir_path in rm.selected_files + def _draw_resource_directory_entries(self, rm, dirs): + """绘制当前目录下的文件夹列表。""" + for dir_path in dirs: + if not rm.should_show_file(dir_path): + continue + self._draw_resource_directory_entry(rm, dir_path) - if is_selected: - imgui.push_style_color(imgui.Col_.header, (100/255, 150/255, 200/255, 1.0)) + def _draw_resource_directory_entry(self, rm, dir_path: Path): + """绘制单个文件夹节点(含一层展开内容)。""" + icon_name = rm.get_file_icon(dir_path.name, is_folder=True) + is_selected = dir_path in rm.selected_files - icon_texture = None - try: - icon_texture = self.app.style_manager.load_icon(f"file_types/{icon_name}") - except: - pass + if is_selected: + imgui.push_style_color(imgui.Col_.header, (100 / 255, 150 / 255, 200 / 255, 1.0)) - if icon_texture: - imgui.image(icon_texture, (16, 16)) - imgui.same_line() - node_open = imgui.tree_node(f"{dir_path.name}##dir_{dir_path}") - else: - node_open = imgui.tree_node(f"[{icon_name.upper()}]{dir_path.name}##dir_{dir_path}") + icon_texture = self._load_resource_icon(icon_name) + if icon_texture: + imgui.image(icon_texture, (16, 16)) + imgui.same_line() + node_open = imgui.tree_node(f"{dir_path.name}##dir_{dir_path}") + else: + node_open = imgui.tree_node(f"[{icon_name.upper()}]{dir_path.name}##dir_{dir_path}") - if is_selected: - imgui.pop_style_color() + if is_selected: + imgui.pop_style_color() - self._append_drop_target_from_last_item(dir_path) - self._start_resource_drag_if_needed(rm, dir_path) + self._append_drop_target_from_last_item(dir_path) + self._start_resource_drag_if_needed(rm, dir_path) - if imgui.is_item_clicked(): - if imgui.get_io().key_ctrl: - if is_selected: - rm.selected_files.discard(dir_path) - else: - rm.selected_files.add(dir_path) - else: - rm.selected_files.clear() - rm.selected_files.add(dir_path) - rm.focused_file = dir_path + if imgui.is_item_clicked(): + self._handle_resource_item_selection(rm, dir_path, is_selected) + if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): + rm.navigate_to(dir_path) + if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): + self._open_resource_item_context_menu(rm, dir_path) - if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): - rm.navigate_to(dir_path) + if node_open: + self._draw_resource_directory_children(rm, dir_path) + imgui.tree_pop() - if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): - rm.show_context_menu = True - rm.context_menu_file = dir_path - rm.context_menu_position = (imgui.get_mouse_pos().x, imgui.get_mouse_pos().y) - imgui.open_popup("resource_context_menu") + def _draw_resource_directory_children(self, rm, parent_dir: Path): + """绘制文件夹展开后的子目录与子文件(保持原有顺序)。""" + subdirs, subfiles = rm.get_directory_contents(parent_dir) - if node_open: - subdirs, subfiles = rm.get_directory_contents(dir_path) + for subdir in subdirs: + if not rm.should_show_file(subdir): + continue + self._draw_resource_subdir_entry(rm, subdir) - for subdir in subdirs: - if not rm.should_show_file(subdir): - continue + for subfile in subfiles: + if not rm.should_show_file(subfile): + continue + self._draw_resource_subfile_entry(rm, subfile) - subicon_name = rm.get_file_icon(subdir.name, is_folder=True) - sub_is_selected = subdir in rm.selected_files + def _draw_resource_subdir_entry(self, rm, subdir: Path): + """绘制一级子目录节点。""" + subicon_name = rm.get_file_icon(subdir.name, is_folder=True) + sub_is_selected = subdir in rm.selected_files + subicon_texture = self._load_resource_icon(subicon_name) - subicon_texture = None - try: - subicon_texture = self.app.style_manager.load_icon(f"file_types/{subicon_name}") - except: - pass + if subicon_texture: + imgui.image(subicon_texture, (16, 16)) + imgui.same_line() + sub_node_open = imgui.tree_node(f" {subdir.name}##dir_{subdir}") + else: + sub_node_open = imgui.tree_node(f" [{subicon_name.upper()}]{subdir.name}##dir_{subdir}") - if subicon_texture: - imgui.image(subicon_texture, (16, 16)) - imgui.same_line() - sub_node_open = imgui.tree_node(f" {subdir.name}##dir_{subdir}") - else: - sub_node_open = imgui.tree_node(f" [{subicon_name.upper()}]{subdir.name}##dir_{subdir}") + self._append_drop_target_from_last_item(subdir) + self._start_resource_drag_if_needed(rm, subdir) - self._append_drop_target_from_last_item(subdir) - self._start_resource_drag_if_needed(rm, subdir) + if imgui.is_item_clicked(): + self._handle_resource_item_selection(rm, subdir, sub_is_selected) + if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): + rm.navigate_to(subdir) + if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): + self._open_resource_item_context_menu(rm, subdir) - if imgui.is_item_clicked(): - if imgui.get_io().key_ctrl: - if sub_is_selected: - rm.selected_files.discard(subdir) - else: - rm.selected_files.add(subdir) - else: - rm.selected_files.clear() - rm.selected_files.add(subdir) - rm.focused_file = subdir + if sub_node_open: + imgui.tree_pop() - if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): - rm.navigate_to(subdir) + def _draw_resource_subfile_entry(self, rm, subfile: Path): + """绘制一级子文件项。""" + subicon_name = rm.get_file_icon(subfile.name) + sub_is_selected = subfile in rm.selected_files + subicon_texture = self._load_resource_icon(subicon_name) - if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): - rm.show_context_menu = True - rm.context_menu_file = subdir - rm.context_menu_position = (imgui.get_mouse_pos().x, imgui.get_mouse_pos().y) - imgui.open_popup("resource_context_menu") + if subicon_texture: + imgui.image(subicon_texture, (16, 16)) + imgui.same_line() + selected = imgui.selectable(f" {subfile.name}##file_{subfile}", sub_is_selected) + else: + selected = imgui.selectable(f" [{subicon_name.upper()}] {subfile.name}##file_{subfile}", sub_is_selected) - if sub_node_open: - imgui.tree_pop() + selected_clicked = selected[0] if isinstance(selected, tuple) else bool(selected) + self._start_resource_drag_if_needed(rm, subfile) - for subfile in subfiles: - if not rm.should_show_file(subfile): - continue + if selected_clicked: + self._handle_resource_item_selection(rm, subfile, sub_is_selected) + if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): + self._handle_resource_file_double_click(rm, subfile) + if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): + self._open_resource_item_context_menu(rm, subfile) - subicon_name = rm.get_file_icon(subfile.name) - sub_is_selected = subfile in rm.selected_files + def _draw_resource_file_entries(self, rm, files): + """绘制当前目录下的文件列表。""" + for file_path in files: + if not rm.should_show_file(file_path): + continue + self._draw_resource_file_entry(rm, file_path) - subicon_texture = None - try: - subicon_texture = self.app.style_manager.load_icon(f"file_types/{subicon_name}") - except: - pass + def _draw_resource_file_entry(self, rm, file_path: Path): + """绘制顶层文件项。""" + icon_name = rm.get_file_icon(file_path.name) + is_selected = file_path in rm.selected_files + icon_texture = self._load_resource_icon(icon_name) - if subicon_texture: - imgui.image(subicon_texture, (16, 16)) - imgui.same_line() - selected = imgui.selectable(f" {subfile.name}##file_{subfile}", sub_is_selected) - else: - selected = imgui.selectable(f" [{subicon_name.upper()}] {subfile.name}##file_{subfile}", sub_is_selected) + if icon_texture: + imgui.image(icon_texture, (16, 16)) + imgui.same_line() + selected = imgui.selectable(f"{file_path.name}##file_{file_path}", is_selected) + else: + selected = imgui.selectable(f"[{icon_name.upper()}] {file_path.name}##file_{file_path}", is_selected) - selected_clicked = selected[0] if isinstance(selected, tuple) else bool(selected) - self._start_resource_drag_if_needed(rm, subfile) + selected_clicked = selected[0] if isinstance(selected, tuple) else bool(selected) + self._start_resource_drag_if_needed(rm, file_path) - if selected_clicked: - if imgui.get_io().key_ctrl: - if sub_is_selected: - rm.selected_files.discard(subfile) - else: - rm.selected_files.add(subfile) - else: - rm.selected_files.clear() - rm.selected_files.add(subfile) - rm.focused_file = subfile + if selected_clicked: + self._handle_resource_item_selection(rm, file_path, is_selected) + if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): + self._handle_resource_file_double_click(rm, file_path) + if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): + self._open_resource_item_context_menu(rm, file_path) - if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): - if self._is_model_file(subfile): - self.app._import_model_for_runtime(str(subfile)) - self.app.add_info_message(f"正在导入模型: {subfile.name}") - else: - rm.open_file(subfile) + def _draw_resource_toolbar_and_filters(self, rm): + """绘制资源管理器顶部工具条与筛选输入。保持原有按钮顺序与文案。""" + if imgui.button("◀"): + rm.navigate_back() + imgui.same_line() + if imgui.button("▶"): + rm.navigate_forward() + imgui.same_line() + if imgui.button("▲"): + rm.navigate_up() + imgui.same_line() + if imgui.button("主页"): + rm.navigate_to(rm.project_root / "Resources") + imgui.same_line() + if imgui.button("刷新"): + rm.force_refresh() - if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): - rm.show_context_menu = True - rm.context_menu_file = subfile - rm.context_menu_position = (imgui.get_mouse_pos().x, imgui.get_mouse_pos().y) - imgui.open_popup("resource_context_menu") + # 自动刷新开关 + imgui.same_line() + changed, rm.auto_refresh_enabled = imgui.checkbox("自动刷新", rm.auto_refresh_enabled) + if changed: + rm.set_auto_refresh(rm.auto_refresh_enabled) - imgui.tree_pop() + imgui.same_line() + imgui.text(" ") + imgui.same_line() - for file_path in files: - if not rm.should_show_file(file_path): - continue + # 路径输入框 + changed, new_path = imgui.input_text("路径", str(rm.current_path), 256) + if changed: + try: + rm.navigate_to(Path(new_path)) + except Exception: + pass - icon_name = rm.get_file_icon(file_path.name) - is_selected = file_path in rm.selected_files + # 搜索框 + changed, rm.search_filter = imgui.input_text("搜索", rm.search_filter, 256) - icon_texture = None - try: - icon_texture = self.app.style_manager.load_icon(f"file_types/{icon_name}") - except: - pass + def _load_resource_icon(self, icon_name: str): + """加载资源图标;失败时返回 None。""" + try: + return self.app.style_manager.load_icon(f"file_types/{icon_name}") + except Exception: + return None - if icon_texture: - imgui.image(icon_texture, (16, 16)) - imgui.same_line() - selected = imgui.selectable(f"{file_path.name}##file_{file_path}", is_selected) - else: - selected = imgui.selectable(f"[{icon_name.upper()}] {file_path.name}##file_{file_path}", is_selected) + def _handle_resource_item_selection(self, rm, target_path: Path, is_selected: bool): + """处理资源项选中逻辑(支持 Ctrl 多选)。""" + if imgui.get_io().key_ctrl: + if is_selected: + rm.selected_files.discard(target_path) + else: + rm.selected_files.add(target_path) + else: + rm.selected_files.clear() + rm.selected_files.add(target_path) + rm.focused_file = target_path - selected_clicked = selected[0] if isinstance(selected, tuple) else bool(selected) - self._start_resource_drag_if_needed(rm, file_path) + def _open_resource_item_context_menu(self, rm, target_path: Path): + """打开资源项右键菜单。""" + rm.show_context_menu = True + rm.context_menu_file = target_path + rm.context_menu_position = (imgui.get_mouse_pos().x, imgui.get_mouse_pos().y) + imgui.open_popup("resource_context_menu") - if selected_clicked: - if imgui.get_io().key_ctrl: - if is_selected: - rm.selected_files.discard(file_path) - else: - rm.selected_files.add(file_path) - else: - rm.selected_files.clear() - rm.selected_files.add(file_path) - rm.focused_file = file_path + def _handle_resource_file_double_click(self, rm, file_path: Path): + """处理文件双击:模型导入,其他文件打开。""" + if self._is_model_file(file_path): + self.app.add_info_message(f"正在导入模型: {file_path.name}") + self.app._import_model_for_runtime(str(file_path)) + else: + rm.open_file(file_path) - if imgui.is_item_hovered() and imgui.is_mouse_double_clicked(0): - if self._is_model_file(file_path): - self.app.add_info_message(f"正在导入模型: {file_path.name}") - self.app._import_model_for_runtime(str(file_path)) - else: - rm.open_file(file_path) + def _draw_resource_context_menu(self, rm): + """绘制资源管理器右键菜单。保持原有菜单项与顺序。""" + if rm.context_menu_file: + imgui.set_next_window_pos((rm.context_menu_position[0], rm.context_menu_position[1])) - if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): - rm.show_context_menu = True - rm.context_menu_file = file_path - rm.context_menu_position = (imgui.get_mouse_pos().x, imgui.get_mouse_pos().y) - imgui.open_popup("resource_context_menu") + if imgui.begin_popup("resource_context_menu"): + if rm.context_menu_file and rm.context_menu_file.is_dir(): + if imgui.menu_item("打开")[1]: + rm.navigate_to(rm.context_menu_file) + imgui.separator() + if imgui.menu_item("重命名")[1]: + print(f"重命名文件夹: {rm.context_menu_file.name}") + if imgui.menu_item("删除")[1]: + print(f"删除文件夹: {rm.context_menu_file.name}") + elif rm.context_menu_file: + if imgui.menu_item("打开")[1]: + rm.open_file(rm.context_menu_file) + imgui.separator() + if imgui.menu_item("导入到场景")[1]: + if self._is_model_file(rm.context_menu_file): + self.app.add_info_message(f"正在导入模型: {rm.context_menu_file.name}") + self.app._import_model_for_runtime(str(rm.context_menu_file)) + if imgui.menu_item("重命名")[1]: + print(f"重命名文件: {rm.context_menu_file.name}") + if imgui.menu_item("删除")[1]: + print(f"删除文件: {rm.context_menu_file.name}") if rm.context_menu_file: - imgui.set_next_window_pos((rm.context_menu_position[0], rm.context_menu_position[1])) + imgui.separator() + if imgui.menu_item("复制路径")[1]: + imgui.set_clipboard_text(str(rm.context_menu_file)) + self.app.add_info_message("路径已复制到剪贴板") + if imgui.menu_item("在文件管理器中显示")[1]: + import platform + import subprocess + if platform.system() == "Windows": + subprocess.run(["explorer", "/select,", str(rm.context_menu_file)]) + elif platform.system() == "Darwin": + subprocess.run(["open", "-R", str(rm.context_menu_file)]) + else: + subprocess.run(["xdg-open", str(rm.context_menu_file.parent)]) - if imgui.begin_popup("resource_context_menu"): - if rm.context_menu_file and rm.context_menu_file.is_dir(): - if imgui.menu_item("打开")[1]: - rm.navigate_to(rm.context_menu_file) - imgui.separator() - if imgui.menu_item("重命名")[1]: - print(f"重命名文件夹: {rm.context_menu_file.name}") - if imgui.menu_item("删除")[1]: - print(f"删除文件夹: {rm.context_menu_file.name}") - elif rm.context_menu_file: - if imgui.menu_item("打开")[1]: - rm.open_file(rm.context_menu_file) - imgui.separator() - if imgui.menu_item("导入到场景")[1]: - if self._is_model_file(rm.context_menu_file): - self.app.add_info_message(f"正在导入模型: {rm.context_menu_file.name}") - self.app._import_model_for_runtime(str(rm.context_menu_file)) - if imgui.menu_item("重命名")[1]: - print(f"重命名文件: {rm.context_menu_file.name}") - if imgui.menu_item("删除")[1]: - print(f"删除文件: {rm.context_menu_file.name}") + if imgui.is_mouse_clicked(0) and not imgui.is_window_hovered(): + rm.context_menu_file = None + rm.show_context_menu = False + imgui.close_current_popup() - if rm.context_menu_file: - imgui.separator() - if imgui.menu_item("复制路径")[1]: - imgui.set_clipboard_text(str(rm.context_menu_file)) - self.app.add_info_message("路径已复制到剪贴板") - if imgui.menu_item("在文件管理器中显示")[1]: - import platform - import subprocess - if platform.system() == "Windows": - subprocess.run(["explorer", "/select,", str(rm.context_menu_file)]) - elif platform.system() == "Darwin": - subprocess.run(["open", "-R", str(rm.context_menu_file)]) - else: - subprocess.run(["xdg-open", str(rm.context_menu_file.parent)]) - - if imgui.is_mouse_clicked(0) and not imgui.is_window_hovered(): - rm.context_menu_file = None - rm.show_context_menu = False - imgui.close_current_popup() - - imgui.end_popup() + imgui.end_popup() @staticmethod def _is_model_file(path: Path) -> bool: