601 lines
26 KiB
Python
601 lines
26 KiB
Python
from imgui_bundle import imgui, imgui_ctx
|
||
from pathlib import Path
|
||
|
||
class EditorPanelsLeftMixin:
|
||
"""Auto-split mixin from editor_panels.py."""
|
||
|
||
def _draw_scene_tree(self):
|
||
"""绘制场景树面板"""
|
||
# 使用更少的限制性标志,允许docking
|
||
flags = (imgui.WindowFlags_.no_collapse)
|
||
|
||
with self.app.style_manager.begin_styled_window("场景树", self.app.showSceneTree, flags):
|
||
self.app.showSceneTree = True # 确保窗口保持打开
|
||
window_pos = imgui.get_window_pos()
|
||
window_size = imgui.get_window_size()
|
||
self.app._scene_tree_window_rect = (
|
||
float(window_pos.x),
|
||
float(window_pos.y),
|
||
float(window_size.x),
|
||
float(window_size.y),
|
||
)
|
||
|
||
imgui.text("场景层级")
|
||
imgui.separator()
|
||
|
||
# 构建动态场景树
|
||
self._build_scene_tree()
|
||
|
||
def _build_scene_tree(self):
|
||
"""构建动态场景树"""
|
||
# 渲染节点
|
||
if imgui.tree_node("渲染"):
|
||
# 环境光
|
||
if hasattr(self.app, 'ambient_light') and self.app.ambient_light:
|
||
self._draw_scene_node(self.app.ambient_light, "环境光", "light")
|
||
|
||
# 聚光灯
|
||
if hasattr(self.app, 'scene_manager') and self.app.scene_manager:
|
||
if hasattr(self.app.scene_manager, 'Spotlight') and self.app.scene_manager.Spotlight:
|
||
for i, spotlight in enumerate(self.app.scene_manager.Spotlight):
|
||
self._draw_scene_node(spotlight, f"聚光灯_{i+1}", "light")
|
||
if hasattr(self.app.scene_manager, 'Pointlight') and self.app.scene_manager.Pointlight:
|
||
for i, pointlight in enumerate(self.app.scene_manager.Pointlight):
|
||
self._draw_scene_node(pointlight, f"点光源_{i+1}", "light")
|
||
|
||
# 地板
|
||
if hasattr(self.app, 'ground') and self.app.ground:
|
||
self._draw_scene_node(self.app.ground, "地板", "geometry")
|
||
|
||
imgui.tree_pop()
|
||
|
||
# 相机节点
|
||
if imgui.tree_node("相机"):
|
||
if hasattr(self.app, 'camera') and self.app.camera:
|
||
self._draw_scene_node(self.app.camera, "主相机", "camera")
|
||
imgui.tree_pop()
|
||
|
||
# 3D模型节点
|
||
if imgui.tree_node("模型"):
|
||
models = []
|
||
if hasattr(self.app, 'scene_manager') and self.app.scene_manager and hasattr(self.app.scene_manager, 'models'):
|
||
models.extend([m for m in self.app.scene_manager.models if m and not m.isEmpty()])
|
||
|
||
# SSBO模式下,模型可能不在 scene_manager.models 中,补充显示 ssbo_editor.model
|
||
ssbo_editor = getattr(self.app, "ssbo_editor", None)
|
||
ssbo_model = getattr(ssbo_editor, "model", None) if ssbo_editor else None
|
||
if ssbo_model and not ssbo_model.isEmpty() and ssbo_model.hasParent() and ssbo_model not in models:
|
||
models.append(ssbo_model)
|
||
|
||
if models:
|
||
for i, model in enumerate(models):
|
||
self._draw_scene_node(model, model.getName() or f"模型_{i+1}", "model")
|
||
else:
|
||
imgui.text("(空)")
|
||
imgui.tree_pop()
|
||
|
||
# if imgui.tree_node("GUI元素"):
|
||
# if hasattr(self,'gui_manager') and self.app.gui_manager and hasattr(self.app.gui_manager,'gui_elements'):
|
||
# if self.app.gui_manager.gui_elements:
|
||
# for gui_element in self.app.gui_manager.gui_elements:
|
||
# if gui_element and hasattr(gui_element,'node'):
|
||
# gui_type = getattr(gui_element,'gui_type','GUI_UNKNOWN')
|
||
# display_name = getattr(gui_element,'name',gui_type)
|
||
# self._draw_scene_node(gui_element.node,display_name,"gui",gui_type)
|
||
# else:
|
||
# imgui.text("(空)")
|
||
# else:
|
||
# imgui.text("(空)")
|
||
# imgui.tree_pop()
|
||
|
||
# LUI元素节点
|
||
if imgui.tree_node("GUI元素"):
|
||
if hasattr(self.app, 'lui_manager') and self.app.lui_manager.lui_enabled:
|
||
self.app.lui_manager.draw_component_tree()
|
||
imgui.tree_pop()
|
||
# if imgui.tree_node("LUI元素"):
|
||
# if hasattr(self.app, 'lui_manager') and self.app.lui_manager.lui_enabled:
|
||
# if self.app.lui_manager.components:
|
||
# for comp in self.app.lui_manager.components:
|
||
# if 'node' in comp:
|
||
# self._draw_scene_node(comp['node'], comp['name'], "ui")
|
||
|
||
# if self.app.lui_manager.canvases:
|
||
# for canvas in self.app.lui_manager.canvases:
|
||
# if imgui.tree_node(f"Canvas: {canvas['name']}"):
|
||
# # 实际上组件已经在 node 下了,可以通过 _draw_scene_node 递归显示
|
||
# # 但为了清晰,我们可以手动列出或者依赖递归
|
||
# self._draw_scene_node(canvas['node'], canvas['name'], "geometry")
|
||
# imgui.tree_pop()
|
||
# else:
|
||
# imgui.text("(空)")
|
||
# else:
|
||
# imgui.text("(空)")
|
||
# imgui.tree_pop()
|
||
|
||
def _draw_scene_node(self, node, name, node_type, gui_subtype=None):
|
||
"""绘制单个场景节点"""
|
||
if not node or node.isEmpty():
|
||
return
|
||
|
||
# 检查是否被选中
|
||
is_selected = (hasattr(self.app, 'selection') and self.app.selection and
|
||
hasattr(self.app.selection, 'selectedNode') and
|
||
self.app.selection.selectedNode == node)
|
||
|
||
# 节点可见性
|
||
is_visible = node.is_hidden() == False
|
||
|
||
# 设置选择颜色
|
||
if is_selected:
|
||
imgui.push_style_color(imgui.Col_.text, (0.2, 0.6, 1.0, 1.0))
|
||
|
||
node_open = False
|
||
try:
|
||
# 显示节点
|
||
node_open = imgui.tree_node(name)
|
||
|
||
# 处理节点选择
|
||
if imgui.is_item_clicked():
|
||
# In SSBO mode, clicking model root should finally bind gizmo to
|
||
# SSBO group proxy (not legacy model root). So run legacy
|
||
# selection first, then force SSBO root selection at the end.
|
||
force_ssbo_root_key = None
|
||
ssbo_editor_ref = None
|
||
try:
|
||
ssbo_editor = getattr(self.app, "ssbo_editor", None)
|
||
controller = getattr(ssbo_editor, "controller", None) if ssbo_editor else None
|
||
ssbo_model = getattr(ssbo_editor, "model", None) if ssbo_editor else None
|
||
root_key = getattr(controller, "tree_root_key", None) if controller else None
|
||
if (
|
||
ssbo_editor and controller and ssbo_model and node == ssbo_model
|
||
and root_key and root_key in controller.tree_nodes
|
||
):
|
||
force_ssbo_root_key = root_key
|
||
ssbo_editor_ref = ssbo_editor
|
||
except Exception:
|
||
pass
|
||
|
||
if hasattr(self.app, 'selection') and self.app.selection:
|
||
self.app.selection.updateSelection(node)
|
||
|
||
if force_ssbo_root_key and ssbo_editor_ref:
|
||
try:
|
||
ssbo_editor_ref.select_node(force_ssbo_root_key)
|
||
except Exception:
|
||
pass
|
||
# Clear LUI selection when a scene node is selected
|
||
if hasattr(self.app, 'lui_manager'):
|
||
self.app.lui_manager.selected_index = -1
|
||
|
||
if self.app.is_dragging and imgui.is_item_hovered():
|
||
self.app._drag_scene_tree_hover_node = node
|
||
|
||
# 右键菜单
|
||
if imgui.is_item_hovered() and imgui.is_mouse_clicked(1):
|
||
self._show_node_context_menu(node, name, node_type)
|
||
|
||
# 显示节点属性
|
||
imgui.same_line()
|
||
if is_visible:
|
||
imgui.text_colored((0.5, 1.0, 0.5, 1.0), "可见")
|
||
else:
|
||
imgui.text_colored((0.5, 0.5, 0.5, 1.0), "隐藏")
|
||
|
||
if node_open:
|
||
# SSBO模型使用虚拟层级显示(避免 flatten 后真实子级丢失)
|
||
if self._draw_ssbo_virtual_children(node):
|
||
pass
|
||
elif node.getNumChildren() > 0:
|
||
for i in range(node.getNumChildren()):
|
||
child = node.getChild(i)
|
||
if not child or child.isEmpty():
|
||
continue
|
||
child_name = child.getName() or f"child_{i+1}"
|
||
# 过滤碰撞辅助节点,避免污染场景树
|
||
if child_name.startswith("modelCollision_"):
|
||
continue
|
||
self._draw_scene_node(child, child_name, node_type)
|
||
# tree_pop moved to finally
|
||
except Exception as e:
|
||
print(f"绘制场景节点时出错: {e}")
|
||
finally:
|
||
if node_open:
|
||
imgui.tree_pop()
|
||
# Ensure style stack is balanced.
|
||
if is_selected:
|
||
imgui.pop_style_color()
|
||
|
||
def _draw_ssbo_virtual_children(self, node):
|
||
"""Draw SSBO controller tree_nodes as virtual children for scene tree."""
|
||
ssbo_editor = getattr(self.app, "ssbo_editor", None)
|
||
if not ssbo_editor:
|
||
return False
|
||
model = getattr(ssbo_editor, "model", None)
|
||
controller = getattr(ssbo_editor, "controller", None)
|
||
if not model or model != node or not controller:
|
||
return False
|
||
|
||
root_key = getattr(controller, "tree_root_key", None)
|
||
if not root_key or root_key not in controller.tree_nodes:
|
||
imgui.text_disabled("(无可用子节点)")
|
||
return True
|
||
|
||
root_node = controller.tree_nodes[root_key]
|
||
if not root_node["children"]:
|
||
imgui.text_disabled("(无可用子节点)")
|
||
return True
|
||
|
||
for child_key in root_node["children"]:
|
||
self._draw_ssbo_virtual_tree_node(ssbo_editor, controller, child_key)
|
||
return True
|
||
|
||
def _draw_ssbo_virtual_tree_node(self, ssbo_editor, controller, key):
|
||
"""Recursively draw SSBO tree_nodes hierarchy in scene tree."""
|
||
node_data = controller.tree_nodes.get(key)
|
||
if not node_data:
|
||
return
|
||
|
||
# Skip redundant wrapper nodes (e.g. ROOT), show their children instead.
|
||
if controller.should_hide_tree_node(key):
|
||
for child_key in node_data["children"]:
|
||
self._draw_ssbo_virtual_tree_node(ssbo_editor, controller, child_key)
|
||
return
|
||
|
||
display = controller.display_names.get(key, key)
|
||
obj_count = len(controller.name_to_ids.get(key, []))
|
||
children = node_data["children"]
|
||
is_selected = (getattr(ssbo_editor, "selected_name", None) == key)
|
||
|
||
if not children:
|
||
# Leaf node: selectable
|
||
label = f"{display} ({obj_count})##{key}"
|
||
if imgui.selectable(label, is_selected)[0]:
|
||
ssbo_editor.select_node(key)
|
||
if hasattr(self.app, "lui_manager"):
|
||
self.app.lui_manager.selected_index = -1
|
||
else:
|
||
# Branch node: tree node
|
||
flags = imgui.TreeNodeFlags_.open_on_arrow
|
||
if is_selected:
|
||
flags |= imgui.TreeNodeFlags_.selected
|
||
label = f"{display} ({obj_count})##{key}"
|
||
opened = imgui.tree_node_ex(label, flags)
|
||
if imgui.is_item_clicked(0):
|
||
ssbo_editor.select_node(key)
|
||
if hasattr(self.app, "lui_manager"):
|
||
self.app.lui_manager.selected_index = -1
|
||
if opened:
|
||
for child_key in children:
|
||
self._draw_ssbo_virtual_tree_node(ssbo_editor, controller, child_key)
|
||
imgui.tree_pop()
|
||
|
||
def _show_node_context_menu(self, node, name, node_type):
|
||
"""显示节点右键菜单"""
|
||
self.app._context_menu_node = True
|
||
self.app._context_menu_target = node
|
||
|
||
def _draw_resource_manager(self):
|
||
"""绘制资源管理器面板"""
|
||
flags = self.app.style_manager.get_window_flags("panel")
|
||
|
||
with self.app.style_manager.begin_styled_window("资源管理器", self.app.showResourceManager, flags):
|
||
self.app.showResourceManager = True
|
||
rm = self.app.resource_manager
|
||
|
||
self._update_resource_manager_window_rect(rm)
|
||
|
||
imgui.text("文件浏览器")
|
||
imgui.separator()
|
||
|
||
self._draw_resource_toolbar_and_filters(rm)
|
||
|
||
imgui.separator()
|
||
|
||
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)
|
||
|
||
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),
|
||
))
|
||
|
||
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)
|
||
|
||
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
|
||
|
||
if is_selected:
|
||
imgui.push_style_color(imgui.Col_.header, (100 / 255, 150 / 255, 200 / 255, 1.0))
|
||
|
||
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()
|
||
|
||
self._append_drop_target_from_last_item(dir_path)
|
||
self._start_resource_drag_if_needed(rm, 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 node_open:
|
||
self._draw_resource_directory_children(rm, dir_path)
|
||
imgui.tree_pop()
|
||
|
||
def _draw_resource_directory_children(self, rm, parent_dir: Path):
|
||
"""绘制文件夹展开后的子目录与子文件(保持原有顺序)。"""
|
||
subdirs, subfiles = rm.get_directory_contents(parent_dir)
|
||
|
||
for subdir in subdirs:
|
||
if not rm.should_show_file(subdir):
|
||
continue
|
||
self._draw_resource_subdir_entry(rm, subdir)
|
||
|
||
for subfile in subfiles:
|
||
if not rm.should_show_file(subfile):
|
||
continue
|
||
self._draw_resource_subfile_entry(rm, subfile)
|
||
|
||
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)
|
||
|
||
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)
|
||
|
||
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 sub_node_open:
|
||
imgui.tree_pop()
|
||
|
||
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 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)
|
||
|
||
selected_clicked = selected[0] if isinstance(selected, tuple) else bool(selected)
|
||
self._start_resource_drag_if_needed(rm, subfile)
|
||
|
||
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)
|
||
|
||
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)
|
||
|
||
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 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, file_path)
|
||
|
||
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)
|
||
|
||
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()
|
||
|
||
# 自动刷新开关
|
||
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 Exception:
|
||
pass
|
||
|
||
# 搜索框
|
||
changed, rm.search_filter = imgui.input_text("搜索", rm.search_filter, 256)
|
||
|
||
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
|
||
|
||
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
|
||
|
||
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")
|
||
|
||
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)
|
||
|
||
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.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.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()
|
||
|
||
@staticmethod
|
||
def _is_model_file(path: Path) -> bool:
|
||
return path.suffix.lower() in ['.gltf', '.glb', '.fbx', '.bam', '.egg', '.obj']
|
||
|
||
def _append_drop_target_from_last_item(self, target_path: Path):
|
||
item_min = imgui.get_item_rect_min()
|
||
item_max = imgui.get_item_rect_max()
|
||
width = float(item_max.x - item_min.x)
|
||
height = float(item_max.y - item_min.y)
|
||
if width <= 0 or height <= 0:
|
||
return
|
||
self.app._resource_drop_targets.append((
|
||
float(item_min.x),
|
||
float(item_min.y),
|
||
width,
|
||
height,
|
||
str(target_path),
|
||
))
|
||
|
||
def _start_resource_drag_if_needed(self, rm, fallback_path: Path):
|
||
if not imgui.is_item_active() or not imgui.is_mouse_dragging(0):
|
||
return
|
||
drag_files = list(rm.selected_files) if rm.selected_files else [fallback_path]
|
||
rm.start_drag(drag_files)
|
||
self.app.is_dragging = True
|
||
self.app.show_drag_overlay = True
|
||
|