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: