重启N1资源管理器面板拆分(保留布局)

This commit is contained in:
ayuan9957 2026-03-01 12:50:14 +08:00
parent 2c9f512996
commit ea6ff140e9
3 changed files with 319 additions and 299 deletions

View File

@ -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 本轮深入分析(非 VRP1 准备)
### 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` 的分区拆分并保持行为不变。

View File

@ -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` 行
- 保持缩放手柄行为与边界语义一致
## 验收标准(阶段)

View File

@ -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: