from imgui_bundle import imgui, imgui_ctx
import os
import io
import tempfile
import time
from pathlib import Path
from panda3d.core import Filename
try:
    from PIL import Image
except Exception:  # pragma: no cover - pillow may be missing in minimal env
    Image = None


class EditorPanels:
    """Centralized UI panel renderer for top-level editor panels."""

    def __init__(self, app):
        self.app = app


    def __getattr__(self, name):
        return getattr(self.app, name)

    def __setattr__(self, name, value):
        if name == "app" or name in self.__dict__ or hasattr(type(self), name):
            object.__setattr__(self, name, value)
        else:
            setattr(self.app, name, value)

    def _on_create_web_panel(self):
        """创建或激活 ImGui Web 面板。"""
        self._ensure_web_panel_state()
        self.app.showWebPanel = True

        webview = getattr(self.app, "_imgui_webview", None)
        if webview and getattr(webview, "_running", False):
            return True

        return self._start_imgui_webview(self.app.web_panel_url_input)

    def _ensure_web_panel_state(self):
        if not hasattr(self.app, "web_panel_url_input") or not self.app.web_panel_url_input:
            self.app.web_panel_url_input = "https://www.example.com"
        if not hasattr(self.app, "_imgui_webview"):
            self.app._imgui_webview = None
        if not hasattr(self.app, "_imgui_webview_tex_id"):
            self.app._imgui_webview_tex_id = None
        if not hasattr(self.app, "_imgui_webview_texture_path"):
            self.app._imgui_webview_texture_path = None

    def _start_imgui_webview(self, url):
        self._ensure_web_panel_state()
        self._stop_imgui_webview()
        try:
            target_url = (url or "").strip()
            if not target_url:
                target_url = "https://www.example.com"
            if not target_url.startswith(("http://", "https://", "file://")):
                target_url = "https://" + target_url

            from core.imgui_webview import ImGuiWebView
            webview = ImGuiWebView(width=1280, height=720)
            webview.start(target_url)
            self.app._imgui_webview = webview
            return True
        except Exception as e:
            self.app.add_error_message(f"启动Web视图失败: {e}")
            return False

    def _stop_imgui_webview(self):
        webview = getattr(self.app, "_imgui_webview", None)
        if webview:
            try:
                webview.stop()
            except Exception:
                pass
        self.app._imgui_webview = None

        tex_id = getattr(self.app, "_imgui_webview_tex_id", None)
        if tex_id:
            try:
                self.app.imgui.removeTexture(tex_id)
            except Exception:
                pass
        self.app._imgui_webview_tex_id = None
        texture_path = getattr(self.app, "_imgui_webview_texture_path", None)
        if texture_path:
            try:
                Path(texture_path).unlink(missing_ok=True)
            except Exception:
                pass
        self.app._imgui_webview_texture_path = None

    def _navigate_web_panel(self):
        self._ensure_web_panel_state()
        url = (self.app.web_panel_url_input or "").strip()
        if not url:
            return

        webview = getattr(self.app, "_imgui_webview", None)
        if not webview or not getattr(webview, "_running", False):
            self._start_imgui_webview(url)
            return

        webview.navigate(url)

    def _update_web_panel_texture(self):
        webview = getattr(self.app, "_imgui_webview", None)
        if not webview:
            return
        if not webview.tex_dirty:
            return

        jpeg_bytes = webview.get_screenshot_bytes()
        if not jpeg_bytes:
            return

        try:
            if Image is None:
                self.app.add_warning_message("缺少 Pillow，无法更新Web纹理")
                return
            img = Image.open(io.BytesIO(jpeg_bytes)).convert("RGBA")
            # p3dimgui 纹理坐标系与网页截图存在Y轴方向差异，先在像素层修正
            img = img.transpose(Image.FLIP_TOP_BOTTOM)

            temp_dir = Path(tempfile.gettempdir()) / "eg_imgui_webpanel"
            temp_dir.mkdir(parents=True, exist_ok=True)
            temp_path = temp_dir / f"webview_{time.time_ns()}.png"
            img.save(temp_path, format="PNG")

            panda_path = Filename.fromOsSpecific(str(temp_path)).getFullpath()
            new_tex_id = self.app.imgui.loadTexture(panda_path)
            old_tex_id = getattr(self.app, "_imgui_webview_tex_id", None)
            if old_tex_id:
                try:
                    self.app.imgui.removeTexture(old_tex_id)
                except Exception:
                    pass

            old_texture_path = getattr(self.app, "_imgui_webview_texture_path", None)
            if old_texture_path:
                try:
                    Path(old_texture_path).unlink(missing_ok=True)
                except Exception:
                    pass

            self.app._imgui_webview_tex_id = new_tex_id
            self.app._imgui_webview_texture_path = str(temp_path)
        except Exception as e:
            self.app.add_warning_message(f"Web纹理更新失败: {e}")
        finally:
            webview.tex_dirty = False

    @staticmethod
    def _clamp01(value):
        return 0.0 if value < 0.0 else 1.0 if value > 1.0 else value

    def draw_menu_bar(self):
            """绘制菜单栏"""
            with imgui_ctx.begin_main_menu_bar() as main_menu:
                if main_menu:
                    # 文件菜单
                    with imgui_ctx.begin_menu("文件") as file_menu:
                        if file_menu:
                            if imgui.menu_item("新建项目", "Ctrl+N", False, True)[1]:
                                self.app._on_new_project()
                            if imgui.menu_item("打开项目", "Ctrl+O", False, True)[1]:
                                self.app._on_open_project()
                            imgui.separator()
                            if imgui.menu_item("保存", "Ctrl+S", False, True)[1]:
                                self.app._on_save_project()
                            if imgui.menu_item("另存为", "", False, True)[1]:
                                self.app._on_save_as_project()
                            imgui.separator()
                            if imgui.menu_item("退出", "Alt+F4", False, True)[1]:
                                self.app._on_exit()
            
                    # 编辑菜单
                    with imgui_ctx.begin_menu("编辑") as edit_menu:
                        if edit_menu:
                            if imgui.menu_item("撤销", "Ctrl+Z", False, True)[1]:
                                self.app._on_undo()
                            if imgui.menu_item("重做", "Ctrl+Y", False, True)[1]:
                                self.app._on_redo()
                            imgui.separator()
                            if imgui.menu_item("剪切", "Ctrl+X", False, True)[1]:
                                self.app._on_cut()
                            if imgui.menu_item("复制", "Ctrl+C", False, True)[1]:
                                self.app._on_copy()
                            if imgui.menu_item("粘贴", "Ctrl+V", False, True)[1]:
                                self.app._on_paste()
                            imgui.separator()
                            if imgui.menu_item("删除", "Del", False, True)[1]:
                                self.app._on_delete()
            
                    # 创建菜单
                    with imgui_ctx.begin_menu("创建") as create_menu:
                        if create_menu:
                            if imgui.menu_item("导入模型", "", False, True)[1]:
                                self.app._on_import_model()
                    
                            imgui.separator()
                    
                            if imgui.menu_item("空对象", "", False, True)[1]:
                                self.app._on_create_empty_object()
                    
                            # 3D对象子菜单
                            with imgui_ctx.begin_menu("3D对象") as three_d_menu:
                                if three_d_menu:
                                    if imgui.menu_item("立方体", "", False, True)[1]:
                                        self.app._on_create_cube()
                                    if imgui.menu_item("球体", "", False, True)[1]:
                                        self.app._on_create_sphere()
                                    if imgui.menu_item("圆柱体", "", False, True)[1]:
                                        self.app._on_create_cylinder()
                                    if imgui.menu_item("平面", "", False, True)[1]:
                                        self.app._on_create_plane()
                    
                            # 3D GUI子菜单
                            with imgui_ctx.begin_menu("3D GUI") as three_d_gui_menu:
                                if three_d_gui_menu:
                                    if imgui.menu_item("3D文本", "", False, True)[1]:
                                        self.app._on_create_3d_text()
                                    if imgui.menu_item("3D图片", "", False, True)[1]:
                                        self.app._on_create_3d_image()
                    
                            # GUI子菜单
                            with imgui_ctx.begin_menu("GUI") as gui_menu:
                                if gui_menu:
                                    if imgui.menu_item("创建按钮", "", False, True)[1]:
                                        self.app._on_create_gui_button()
                                    if imgui.menu_item("创建标签", "", False, True)[1]:
                                        self.app._on_create_gui_label()
                                    if imgui.menu_item("创建输入框", "", False, True)[1]:
                                        self.app._on_create_gui_entry()
                                    if imgui.menu_item("创建图片", "", False, True)[1]:
                                        self.app._on_create_gui_image()
                                    imgui.separator()
                                    if imgui.menu_item("创建视频屏幕", "", False, True)[1]:
                                        self.app._on_create_video_screen()
                                    if imgui.menu_item("创建2D视频屏幕", "", False, True)[1]:
                                        self.app._on_create_2d_video_screen()
                                    if imgui.menu_item("创建球形视频", "", False, True)[1]:
                                        self.app._on_create_spherical_video()
                                    if imgui.menu_item("创建虚拟屏幕", "", False, True)[1]:
                                        self.app._on_create_virtual_screen()
                    
                            # 光源子菜单
                            with imgui_ctx.begin_menu("光源") as light_menu:
                                if light_menu:
                                    if imgui.menu_item("聚光灯", "", False, True)[1]:
                                        self.app._on_create_spot_light()
                                    if imgui.menu_item("点光源", "", False, True)[1]:
                                        self.app._on_create_point_light()
                    
                            # 地形子菜单
                            with imgui_ctx.begin_menu("地形") as terrain_menu:
                                if terrain_menu:
                                    if imgui.menu_item("创建平面地形", "", False, True)[1]:
                                        self.app._on_create_flat_terrain()
                                    if imgui.menu_item("从高度图创建地形", "", False, True)[1]:
                                        self.app._on_create_heightmap_terrain()
                    
                            # 脚本子菜单
                            with imgui_ctx.begin_menu("脚本") as script_menu:
                                if script_menu:
                                    if imgui.menu_item("创建脚本...", "", False, True)[1]:
                                        self.app._on_create_script()
                                    if imgui.menu_item("加载脚本文件...", "", False, True)[1]:
                                        self.app._on_load_script()
                                    imgui.separator()
                                    if imgui.menu_item("重载所有脚本", "", False, True)[1]:
                                        self.app._on_reload_all_scripts()
                                    _, self.app.hotReloadEnabled = imgui.menu_item("启用热重载", "", self.app.hotReloadEnabled, True)
                                    if imgui.menu_item("脚本管理器", "", False, True)[1]:
                                        self.app._on_open_scripts_manager()
                    
                            # 信息面板子菜单
                            with imgui_ctx.begin_menu("信息面板") as info_panel_menu:
                                if info_panel_menu:
                                    if imgui.menu_item("创建2D示例面板", "", False, True)[1]:
                                        self.app._on_create_2d_sample_panel()
                                    if imgui.menu_item("创建3D实例面板", "", False, True)[1]:
                                        self.app._on_create_3d_sample_panel()
                                    if imgui.menu_item("Web面板", "", False, True)[1]:
                                        self.app._on_create_web_panel()
            
                    # 视图菜单
                    with imgui_ctx.begin_menu("视图") as view_menu:
                        if view_menu:
                            _, self.app.showToolbar = imgui.menu_item("工具栏", "", self.app.showToolbar, True)
                            _, self.app.showSceneTree = imgui.menu_item("场景树", "", self.app.showSceneTree, True)
                            _, self.app.showResourceManager = imgui.menu_item("资源管理器", "", self.app.showResourceManager, True)
                            _, self.app.showPropertyPanel = imgui.menu_item("属性面板", "", self.app.showPropertyPanel, True)
                            _, self.app.showConsole = imgui.menu_item("控制台", "", self.app.showConsole, True)
                            _, self.app.showScriptPanel = imgui.menu_item("脚本管理", "", self.app.showScriptPanel, True)
                            _, self.app.showLUIEditor = imgui.menu_item("LUI编辑器", "", self.app.showLUIEditor, True)
                            prev_show_web_panel = self.app.showWebPanel
                            _, self.app.showWebPanel = imgui.menu_item("Web面板", "", self.app.showWebPanel, True)
                            if prev_show_web_panel and not self.app.showWebPanel:
                                self._stop_imgui_webview()
                            elif (not prev_show_web_panel) and self.app.showWebPanel:
                                self._on_create_web_panel()
            
                    # 工具菜单
                    with imgui_ctx.begin_menu("工具") as tools_menu:
                        if tools_menu:
                            # 工具切换选项
                            if imgui.menu_item("选择工具", "", False, True)[1]:
                                self.app.tool_manager.setCurrentTool("选择")
                            if imgui.menu_item("移动工具", "", False, True)[1]:
                                self.app.tool_manager.setCurrentTool("移动")
                            if imgui.menu_item("旋转工具", "", False, True)[1]:
                                self.app.tool_manager.setCurrentTool("旋转")
                            if imgui.menu_item("缩放工具", "", False, True)[1]:
                                self.app.tool_manager.setCurrentTool("缩放")
                    
                            imgui.separator()
                    
                            # 编辑工具
                            if imgui.menu_item("光照编辑", "", False, True)[1]:
                                self.app.tool_manager.setCurrentTool("光照编辑")
                            if imgui.menu_item("图形编辑", "", False, True)[1]:
                                self.app.tool_manager.setCurrentTool("图形编辑")
                    
                            imgui.separator()
                    
                            # VR子菜单
                            with imgui_ctx.begin_menu("VR") as vr_menu:
                                if vr_menu:
                                    if imgui.menu_item("进入VR模式", "", False, True)[1]:
                                        self.app._toggle_vr_mode()
                                    if imgui.menu_item("退出VR模式", "", False, True)[1]:
                                        self.app._exit_vr_mode()
                            
                                    imgui.separator()
                            
                                    if imgui.menu_item("VR状态", "", False, True)[1]:
                                        self.app._show_vr_status()
                                    if imgui.menu_item("VR设置", "", False, True)[1]:
                                        self.app._show_vr_settings()
                            
                                    imgui.separator()
                            
                                    # VR调试子菜单
                                    with imgui_ctx.begin_menu("VR调试") as vr_debug_menu:
                                        if vr_debug_menu:
                                            _, self.app.vr_debug_enabled = imgui.menu_item("启用调试输出", "", self.app.vr_debug_enabled, True)
                                    
                                            if imgui.menu_item("立即显示性能报告", "", False, True)[1]:
                                                self.app._show_vr_performance_report()
                                    
                                            imgui.separator()
                                    
                                            # 输出模式
                                            with imgui_ctx.begin_menu("输出模式") as output_menu:
                                                if output_menu:
                                                    if imgui.menu_item("简短模式", "", not self.app.vr_detailed_mode, True)[1]:
                                                        self.app.vr_detailed_mode = False
                                                    if imgui.menu_item("详细模式", "", self.app.vr_detailed_mode, True)[1]:
                                                        self.app.vr_detailed_mode = True
                                    
                                            imgui.separator()
                                    
                                            _, self.app.vr_performance_monitor = imgui.menu_item("启用性能监控", "", self.app.vr_performance_monitor, True)
            
                    # 窗口菜单 - 已隐藏
                    # with imgui_ctx.begin_menu("窗口") as window_menu:
                    #     if window_menu:
                    #         _, self.app.showDemoWindow = imgui.menu_item("ImGui演示", "", self.app.showDemoWindow, True)
                    #         if self.app.testTexture:
                    #             imgui.menu_item("关闭纹理测试", "", False, True)
                    #         else:
                    #             imgui.menu_item("显示纹理测试", "", False, True)
            
                    # 帮助菜单
                    with imgui_ctx.begin_menu("帮助") as help_menu:
                        if help_menu:
                            imgui.menu_item("关于", "", False, True)
                            imgui.menu_item("文档", "", False, True)
            
                    # 右侧显示FPS
                    imgui.set_cursor_pos_x(imgui.get_window_size().x - 140)
                    imgui.text("%.2f FPS (%.2f ms)" % (imgui.get_io().framerate, 1000.0 / imgui.get_io().framerate))


    def draw_toolbar(self):
            """绘制工具栏"""
            # 工具栏可以保持无标题栏，但允许移动和调整大小
            flags = self.app.style_manager.get_window_flags("toolbar")
    
            with self.app.style_manager.begin_styled_window("工具栏", self.app.showToolbar, flags):
                self.app.showToolbar = True  # 确保窗口保持打开
        
                # 选择工具按钮
                select_active = self.app.tool_manager.isSelectionTool()
                if self.app.icons.get('select'):
                    tint_col = (1.0, 1.0, 0.0, 1.0) if select_active else (1.0, 1.0, 1.0, 1.0)
                    if self.app.style_manager.image_button(self.app.icons['select'], (24, 24), tint_col=tint_col):
                        self.app.tool_manager.setCurrentTool("选择")
                    if imgui.is_item_hovered():
                        imgui.set_tooltip("选择工具 (Q)")
                    imgui.same_line()
                else:
                    if imgui.button("选择##select_tool"):
                        self.app.tool_manager.setCurrentTool("选择")
                    if select_active:
                        draw_list = imgui.get_window_draw_list()
                        button_min = imgui.get_item_rect_min()
                        button_max = imgui.get_item_rect_max()
                        draw_list.add_rect_filled(button_min, button_max, imgui.get_color_u32((0.3, 0.6, 1.0, 0.3)))
                    imgui.same_line()
        
                # 移动工具按钮
                move_active = self.app.tool_manager.isMoveTool()
                if self.app.icons.get('move'):
                    # 使用不同颜色表示活动状态
                    tint_col = (1.0, 1.0, 0.0, 1.0) if move_active else (1.0, 1.0, 1.0, 1.0)  # 活动时显示黄色
                    if self.app.style_manager.image_button(self.app.icons['move'], (24, 24), tint_col=tint_col):
                        self.app.tool_manager.setCurrentTool("移动")
                    if imgui.is_item_hovered():
                        imgui.set_tooltip("移动工具 (W)")
                    imgui.same_line()
                else:
                    if imgui.button("移动##move_tool"):
                        self.app.tool_manager.setCurrentTool("移动")
                    if move_active:
                        # 为活动按钮添加背景色
                        draw_list = imgui.get_window_draw_list()
                        button_min = imgui.get_item_rect_min()
                        button_max = imgui.get_item_rect_max()
                        draw_list.add_rect_filled(button_min, button_max, imgui.get_color_u32((0.3, 0.6, 1.0, 0.3)))
                    imgui.same_line()
        
                # 旋转工具按钮
                rotate_active = self.app.tool_manager.isRotateTool()
                if self.app.icons.get('rotate'):
                    tint_col = (1.0, 1.0, 0.0, 1.0) if rotate_active else (1.0, 1.0, 1.0, 1.0)
                    if self.app.style_manager.image_button(self.app.icons['rotate'], (24, 24), tint_col=tint_col):
                        self.app.tool_manager.setCurrentTool("旋转")
                    if imgui.is_item_hovered():
                        imgui.set_tooltip("旋转工具 (E)")
                    imgui.same_line()
                else:
                    if imgui.button("旋转##rotate_tool"):
                        self.app.tool_manager.setCurrentTool("旋转")
                    if rotate_active:
                        draw_list = imgui.get_window_draw_list()
                        button_min = imgui.get_item_rect_min()
                        button_max = imgui.get_item_rect_max()
                        draw_list.add_rect_filled(button_min, button_max, imgui.get_color_u32((0.3, 0.6, 1.0, 0.3)))
                    imgui.same_line()
        
                # 缩放工具按钮
                scale_active = self.app.tool_manager.isScaleTool()
                if self.app.icons.get('scale'):
                    tint_col = (1.0, 1.0, 0.0, 1.0) if scale_active else (1.0, 1.0, 1.0, 1.0)
                    if self.app.style_manager.image_button(self.app.icons['scale'], (24, 24), tint_col=tint_col):
                        self.app.tool_manager.setCurrentTool("缩放")
                    if imgui.is_item_hovered():
                        imgui.set_tooltip("缩放工具 (R)")
                else:
                    if imgui.button("缩放##scale_tool"):
                        self.app.tool_manager.setCurrentTool("缩放")
                    if scale_active:
                        draw_list = imgui.get_window_draw_list()
                        button_min = imgui.get_item_rect_min()
                        button_max = imgui.get_item_rect_max()
                        draw_list.add_rect_filled(button_min, button_max, imgui.get_color_u32((0.3, 0.6, 1.0, 0.3)))
        
                imgui.same_line()
                imgui.separator()
                imgui.same_line()
        
                # 工具按钮已移除（导入、保存、播放）


    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():
                if hasattr(self.app, 'selection') and self.app.selection:
                    self.app.selection.updateSelection(node)
                # 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 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

        tree_root = controller.get_virtual_hierarchy() if hasattr(controller, "get_virtual_hierarchy") else None
        if not tree_root or not tree_root.get("children"):
            imgui.text_disabled("(无可用子节点)")
            return True

        for name in sorted(tree_root["children"].keys()):
            child = tree_root["children"][name]
            self._draw_ssbo_virtual_tree_node(ssbo_editor, child, "ssbo_root")
        return True

    def _draw_ssbo_virtual_tree_node(self, ssbo_editor, tree_node, unique_id_prefix, depth=0):
        """Recursively draw virtual SSBO hierarchy in scene tree."""
        if not tree_node:
            return
        path = tree_node.get("path", "")
        display = tree_node.get("display_name") or tree_node.get("name") or path
        leaf_key = tree_node.get("leaf_key")
        group_key = tree_node.get("group_key")
        children = tree_node.get("children", {}) or {}

        label = f"{display}##{unique_id_prefix}_{path}"

        # Leaf: selectable to trigger SSBO selection.
        if not children and leaf_key:
            is_selected = (getattr(ssbo_editor, "selected_name", None) == leaf_key)
            if imgui.selectable(label, is_selected)[0]:
                ssbo_editor.select_node(leaf_key)
                if hasattr(self.app, "lui_manager"):
                    self.app.lui_manager.selected_index = -1
            return

        # Non-leaf: tree node only for hierarchy display.
        opened = imgui.tree_node(label)
        # Clicking non-leaf row selects its aggregate group so parent transform affects children.
        if group_key and imgui.is_item_clicked(0):
            ssbo_editor.select_node(group_key)
            if hasattr(self.app, "lui_manager"):
                self.app.lui_manager.selected_index = -1
        if opened:
            # If this node is also a selectable leaf, render selectable entry first.
            if group_key:
                is_group_selected = (getattr(ssbo_editor, "selected_name", None) == group_key)
                if imgui.selectable(f"[整体] {display}##group_{unique_id_prefix}_{path}", is_group_selected)[0]:
                    ssbo_editor.select_node(group_key)
                    if hasattr(self.app, "lui_manager"):
                        self.app.lui_manager.selected_index = -1
            if leaf_key:
                is_selected = (getattr(ssbo_editor, "selected_name", None) == leaf_key)
                if imgui.selectable(f"[节点] {display}##leaf_{unique_id_prefix}_{path}", is_selected)[0]:
                    ssbo_editor.select_node(leaf_key)
                    if hasattr(self.app, "lui_manager"):
                        self.app.lui_manager.selected_index = -1
            for child_name in sorted(children.keys()):
                self._draw_ssbo_virtual_tree_node(
                    ssbo_editor,
                    children[child_name],
                    unique_id_prefix,
                    depth + 1,
                )
            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

            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),
            ))

            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)

            imgui.separator()

            if rm.refresh_if_needed():
                pass

            dirs, files = rm.get_directory_contents(rm.current_path)

            for dir_path in dirs:
                if not rm.should_show_file(dir_path):
                    continue

                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 = None
                try:
                    icon_texture = self.app.style_manager.load_icon(f"file_types/{icon_name}")
                except:
                    pass

                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():
                    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_hovered() and imgui.is_mouse_double_clicked(0):
                    rm.navigate_to(dir_path)

                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")

                if node_open:
                    subdirs, subfiles = rm.get_directory_contents(dir_path)

                    for subdir in subdirs:
                        if not rm.should_show_file(subdir):
                            continue

                        subicon_name = rm.get_file_icon(subdir.name, is_folder=True)
                        sub_is_selected = subdir in rm.selected_files

                        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}")

                        self._append_drop_target_from_last_item(subdir)
                        self._start_resource_drag_if_needed(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 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):
                            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 sub_node_open:
                            imgui.tree_pop()

                    for subfile in subfiles:
                        if not rm.should_show_file(subfile):
                            continue

                        subicon_name = rm.get_file_icon(subfile.name)
                        sub_is_selected = subfile in rm.selected_files

                        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()
                            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:
                            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 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)

                        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.tree_pop()

            for file_path in files:
                if not rm.should_show_file(file_path):
                    continue

                icon_name = rm.get_file_icon(file_path.name)
                is_selected = file_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 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:
                    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

                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)

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

    def _draw_web_panel(self):
        """绘制 Web 面板（ImGui + 后台浏览器截图）。"""
        self._ensure_web_panel_state()
        if not self.app.showWebPanel:
            self._stop_imgui_webview()
            return

        flags = self.app.style_manager.get_window_flags("panel")
        with self.app.style_manager.begin_styled_window("Web面板", self.app.showWebPanel, flags) as (_, opened):
            if not opened:
                self.app.showWebPanel = False
                self._stop_imgui_webview()
                return

            self.app.showWebPanel = True

            changed, self.app.web_panel_url_input = imgui.input_text(
                "URL", self.app.web_panel_url_input, 1024
            )
            if changed:
                self.app.web_panel_url_input = self.app.web_panel_url_input.strip()

            imgui.same_line()
            if imgui.button("访问"):
                self._navigate_web_panel()

            webview = getattr(self.app, "_imgui_webview", None)
            if not webview:
                if not self._start_imgui_webview(self.app.web_panel_url_input):
                    imgui.text_colored((1.0, 0.4, 0.4, 1.0), "Web视图启动失败")
                    return
                webview = self.app._imgui_webview

            imgui.same_line()
            if imgui.button("后退"):
                webview.go_back()
            imgui.same_line()
            if imgui.button("前进"):
                webview.go_forward()
            imgui.same_line()
            if imgui.button("刷新"):
                webview.reload()

            current_url = webview.current_url or self.app.web_panel_url_input
            if current_url:
                imgui.text_colored((0.7, 0.7, 0.7, 1.0), current_url)
            if webview.error:
                imgui.text_colored((1.0, 0.4, 0.4, 1.0), webview.error)

            imgui.separator()

            self._update_web_panel_texture()
            tex_id = getattr(self.app, "_imgui_webview_tex_id", None)
            available = imgui.get_content_region_avail()
            display_w = max(float(available.x), 64.0)
            display_h = max(float(available.y), 64.0)

            if tex_id:
                imgui.image(tex_id, (display_w, display_h))

                if imgui.is_item_hovered():
                    mouse_wheel = imgui.get_io().mouse_wheel
                    if abs(mouse_wheel) > 1e-4:
                        webview.scroll(-mouse_wheel * 120.0)

                    if imgui.is_mouse_clicked(0):
                        item_min = imgui.get_item_rect_min()
                        item_max = imgui.get_item_rect_max()
                        item_w = max(float(item_max.x - item_min.x), 1.0)
                        item_h = max(float(item_max.y - item_min.y), 1.0)
                        mouse_pos = imgui.get_mouse_pos()

                        x_ratio = self._clamp01((float(mouse_pos.x) - float(item_min.x)) / item_w)
                        y_ratio = self._clamp01((float(mouse_pos.y) - float(item_min.y)) / item_h)
                        webview.click(x_ratio, y_ratio)
            elif webview.is_loading:
                imgui.text("网页加载中...")
            else:
                imgui.text("正在初始化Web视图...")


    def _draw_property_panel(self):
        """绘制属性面板"""
        # 使用面板类型的窗口标志，支持docking
        flags = self.app.style_manager.get_window_flags("panel")
    
        with self.app.style_manager.begin_styled_window("属性面板", self.app.showPropertyPanel, flags):
            self.app.showPropertyPanel = True  # 确保窗口保持打开
        
            # --- LUI Component Properties ---
            # 优先检查 LUI 组件选择
            if hasattr(self.app, 'lui_manager'):
                lui_selected_index = getattr(self.app.lui_manager, "selected_index", -1)
                if lui_selected_index >= 0 and self.app.lui_manager.luiFunction:
                    self.app.lui_manager.luiFunction._draw_component_properties(
                        self.app.lui_manager,
                        lui_selected_index
                    )
                    return

            # --- Scene Node Properties ---
            # 获取当前选中的节点
            selected_node = None
            if hasattr(self.app, 'selection') and self.app.selection and hasattr(self.app.selection, 'selectedNode'):
                selected_node = self.app.selection.selectedNode

            # SSBO mode may select a proxy node for gizmo operations.
            # Resolve proxy back to a real scene node so property panel stays meaningful.
            try:
                if (selected_node and not selected_node.isEmpty() and
                        selected_node.hasTag("is_ssbo_proxy")):
                    ssbo_editor = getattr(self.app, "ssbo_editor", None)
                    controller = getattr(ssbo_editor, "controller", None) if ssbo_editor else None
                    if ssbo_editor and controller:
                        resolved = None
                        if getattr(ssbo_editor, "selected_ids", None):
                            first_gid = ssbo_editor.selected_ids[0]
                            key = controller.id_to_name.get(first_gid)
                            if key:
                                resolved = controller.key_to_node.get(key)
                        if (resolved is None or resolved.isEmpty()) and getattr(ssbo_editor, "selected_name", None):
                            resolved = controller.key_to_node.get(ssbo_editor.selected_name)
                        if resolved and not resolved.isEmpty():
                            selected_node = resolved
            except Exception:
                pass
        
            if selected_node and not selected_node.isEmpty():
                self._draw_node_properties(selected_node)
            else:
                # 无选中对象时显示提示（模仿Qt版本的空状态样式）
                imgui.spacing()
                imgui.spacing()
            
                # 居中显示提示信息
                window_width = imgui.get_window_width()
                text_width = 200  # 估算文本宽度
                text_pos_x = (window_width - text_width) / 2
            
                imgui.set_cursor_pos_x(text_pos_x)
                imgui.text_colored((0.5, 0.5, 0.5, 1.0), "🔍 未选择任何对象")
            
                imgui.set_cursor_pos_x(text_pos_x - 20)
                imgui.text("请从场景树中选择一个对象")
            
                imgui.set_cursor_pos_x(text_pos_x + 10)
                imgui.text("以查看其属性")
            
                imgui.spacing()
                imgui.spacing()
            
                # 添加一些分隔线和装饰
                imgui.separator()
            
                # 显示快速提示
                imgui.text("💡 快速提示:")
                imgui.bullet_text("单击场景树中的对象进行选择")
                imgui.bullet_text("使用 F 键快速聚焦到选中对象")
                imgui.bullet_text("使用 Delete 键删除选中对象")


    def _draw_node_properties(self, node):
        """绘制节点属性"""
        if not node or node.isEmpty():
            # 停止变换监控
            self.app.stop_transform_monitoring()
            return
    
        # 检查是否需要重新启动变换监控
        if self.app._monitored_node != node:
            self.app.stop_transform_monitoring()
            self.app.start_transform_monitoring(node)
    
        # 获取节点基本信息
        node_name = node.getName() or "未命名节点"
        node_type = self.app._get_node_type_from_node(node)
    
        # 添加一些间距，模仿Qt版本的布局
        imgui.spacing()
    
        # 物体名称组（使用Qt版本的样式）
        if imgui.collapsing_header("物体名称"):
            # 第一行：可见性复选框和名称输入
            user_visible = node.getPythonTag("user_visible")
            if user_visible is None:
                user_visible = True
                node.setPythonTag("user_visible", True)
        
            # 可见性复选框（模仿Qt版本的样式）
            changed, is_visible = imgui.checkbox("##visibility", user_visible)
            if changed:
                node.setPythonTag("user_visible", is_visible)
                if is_visible:
                    node.show()
                else:
                    node.hide()
        
            imgui.same_line()
            imgui.text("可见")
            imgui.same_line()
            imgui.spacing()
            imgui.same_line()
        
            # 名称输入框（模仿Qt版本的样式）
            imgui.text("名称:")
            imgui.same_line()
            changed, new_name = imgui.input_text("##name", node_name, 256)
            if changed and hasattr(self.app, 'selection'):
                # 更新场景树中的名称
                self.app._update_node_name(node, new_name)
        
            # 添加分隔线
            imgui.separator()
        
            # 状态徽章（模仿Qt版本的徽章样式）
            self.app._draw_status_badges(node)
    
        imgui.spacing()
    
        # 变换属性组
        if imgui.collapsing_header("变换 Transform"):
            self.app._draw_transform_properties(node)
    
        # 根据节点类型显示特定属性组
        if node_type == "GUI元素":
            if imgui.collapsing_header("GUI信息"):
                self.app._draw_gui_properties(node)
        elif node_type == "光源":
            if imgui.collapsing_header("光源属性"):
                self.app._draw_light_properties(node)
        elif node_type == "模型":
            if imgui.collapsing_header("模型属性"):
                self.app._draw_model_properties(node)
        
            # 动画控制组（只对模型显示）
            if imgui.collapsing_header("动画控制"):
                self.app._draw_animation_properties(node)
    
        # 外观属性组（通用）
        if imgui.collapsing_header("外观属性"):
            self.app._draw_appearance_properties(node)
    
        # 碰撞检测组
        if imgui.collapsing_header("碰撞检测"):
            self.app._draw_collision_properties(node)
    
        # 操作按钮组
        if imgui.collapsing_header("操作"):
            self.app._draw_property_actions(node)

    def _draw_status_badges(self, node):
            """绘制状态徽章（模仿Qt版本的徽章样式）"""
            imgui.text("状态标签: ")
            
            # 可见性状态徽章
            is_visible = not node.is_hidden()
            visibility_color = (0.176, 1.0, 0.769, 1.0) if is_visible else (0.953, 0.616, 0.471, 1.0)
            visibility_text = "可见" if is_visible else "隐藏"
            
            imgui.same_line()
            imgui.text_colored(visibility_color, f"[{visibility_text}]")
            
            # 节点类型徽章
            node_type = self._get_node_type_from_node(node)
            type_colors = {
                "GUI元素": (0.188, 0.404, 0.753, 1.0),  # 主题蓝色
                "光源": (1.0, 0.8, 0.2, 1.0),  # 黄色
                "模型": (0.6, 0.8, 1.0, 1.0),  # 浅蓝色
                "相机": (0.8, 0.8, 0.2, 1.0),  # 橙色
                "几何体": (0.5, 0.5, 0.5, 1.0),  # 灰色
            }
            
            if node_type in type_colors:
                imgui.same_line()
                imgui.text_colored(type_colors[node_type], f"[{node_type}]")
            
            # 功能性徽章
            badges = []
            
            # 碰撞体徽章
            has_collision = hasattr(node, 'getChild') and any('Collision' in child.getName() for child in node.getChildren() if child.getName())
            if has_collision:
                badges.append(("碰撞", (0.2, 0.4, 0.8, 1.0)))  # 蓝色
            
            # 脚本徽章
            has_script = hasattr(node, 'getPythonTag') and node.getPythonTag('script')
            if has_script:
                badges.append(("脚本", (0.8, 0.4, 0.8, 1.0)))  # 紫色
            
            # 动画徽章（优化检测逻辑，避免重复创建Actor）
            has_animation = False
            if node_type == "模型":  # 只对模型类型进行动画检测
                model_path = node.getTag("model_path") if node.hasTag("model_path") else ""
                likely_anim_format = bool(model_path and model_path.lower().endswith(('.glb', '.gltf', '.fbx', '.bam', '.egg')))

                # 优先使用场景标签（导入/加载时会写入）
                if node.hasTag("has_animations"):
                    has_animation = node.getTag("has_animations").lower() == "true"

                # 再做轻量结构检测（不依赖 Actor）
                if not has_animation:
                    try:
                        has_character = node.findAllMatches("**/+Character").getNumPaths() > 0
                        has_bundle = node.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0
                        has_animation = has_character or has_bundle
                        if has_animation:
                            node.setTag("has_animations", "true")
                            node.setTag("can_create_actor_from_memory", "true")
                    except Exception:
                        pass

                # 最后才尝试 Actor 检测（只缓存“有动画”，避免把失败结果永久缓存）
                cached_result = node.getPythonTag('animation')
                if cached_result is True:
                    has_animation = True
                elif not has_animation and likely_anim_format:
                    try:
                        actor = self._getActor(node)
                        if actor and actor.getAnimNames():
                            has_animation = True
                            node.setTag("has_animations", "true")
                            node.setPythonTag('animation', True)
                            print(f"[动画检测] {node.getName()}: 有动画")
                    except Exception as e:
                        print(f"动画检测失败: {e}")
                elif cached_result is False and not likely_anim_format:
                    has_animation = False
            else:
                # 对于非模型类型，检查已有的动画标签
                has_animation = hasattr(node, 'getPythonTag') and node.getPythonTag('animation')
                
            if has_animation:
                badges.append(("动画", (0.4, 0.8, 0.4, 1.0)))  # 绿色
            
            # 材质徽章
            has_material = hasattr(node, 'getMaterial') and node.getMaterial()
            if has_material:
                badges.append(("材质", (0.8, 0.6, 0.2, 1.0)))  # 金色
            
            # 绘制功能性徽章
            for badge_text, badge_color in badges:
                imgui.same_line()
                imgui.text_colored(badge_color, f"[{badge_text}]")
            
            # 如果没有特殊徽章，显示默认状态
            if not badges:
                imgui.same_line()
                imgui.text_colored((0.5, 0.5, 0.5, 1.0), "[标准对象]")
        

    def _draw_transform_properties(self, node):
        """绘制变换属性"""
        # 位置组
        if imgui.collapsing_header("位置 Position"):
            # 相对位置
            imgui.text("相对位置")
            pos = node.getPos()
            
            # X坐标
            changed, new_x = imgui.input_float("X##pos_x", pos.x, 0.1, 1.0, "%.3f")
            if changed: node.setX(new_x)
            
            # Y坐标
            changed, new_y = imgui.input_float("Y##pos_y", pos.y, 0.1, 1.0, "%.3f")
            if changed: node.setY(new_y)
            
            # Z坐标
            changed, new_z = imgui.input_float("Z##pos_z", pos.z, 0.1, 1.0, "%.3f")
            if changed: node.setZ(new_z)
            
            # 世界位置
            imgui.text("世界位置")
            world_pos = node.getPos(self.render)
            
            imgui.text(f"世界 X: {world_pos.x:.3f}")
            imgui.text(f"世界 Y: {world_pos.y:.3f}")
            imgui.text(f"世界 Z: {world_pos.z:.3f}")
            
            # 位置操作按钮
            if imgui.button("重置位置##reset_pos"):
                node.setPos(0, 0, 0)
            imgui.same_line()
            if imgui.button("复制位置##copy_pos"):
                self._clipboard_pos = (pos.x, pos.y, pos.z)
            imgui.same_line()
            if imgui.button("粘贴位置##paste_pos") and hasattr(self, '_clipboard_pos'):
                node.setPos(self._clipboard_pos[0], self._clipboard_pos[1], self._clipboard_pos[2])
        
        # 旋转组
        if imgui.collapsing_header("旋转 Rotation"):
            hpr = node.getHpr()
            
            # HPR旋转
            imgui.text("HPR 旋转 (度)")
            changed, new_h = imgui.input_float("H##rot_h", hpr.x, 1.0, 10.0, "%.1f")
            if changed: node.setH(new_h)
            
            changed, new_p = imgui.input_float("P##rot_p", hpr.y, 1.0, 10.0, "%.1f")
            if changed: node.setP(new_p)
            
            changed, new_r = imgui.input_float("R##rot_r", hpr.z, 1.0, 10.0, "%.1f")
            if changed: node.setR(new_r)
            
            # 旋转操作按钮
            if imgui.button("重置旋转##reset_rot"):
                node.setHpr(0, 0, 0)
            imgui.same_line()
            if imgui.button("随机旋转##random_rot"):
                import random
                node.setHpr(random.randint(0, 360), random.randint(0, 360), random.randint(0, 360))
        
        # 缩放组
        if imgui.collapsing_header("缩放 Scale"):
            scale = node.getScale()
            
            # XYZ缩放
            imgui.text("XYZ 缩放")
            changed, new_sx = imgui.input_float("X##scale_x", scale.x, 0.1, 1.0, "%.3f")
            if changed: node.setSx(new_sx)
            
            changed, new_sy = imgui.input_float("Y##scale_y", scale.y, 0.1, 1.0, "%.3f")
            if changed: node.setSy(new_sy)
            
            changed, new_sz = imgui.input_float("Z##scale_z", scale.z, 0.1, 1.0, "%.3f")
            if changed: node.setSz(new_sz)
            
            # 统一缩放
            if imgui.button("统一缩放##uniform_scale"):
                uniform_scale = (scale.x + scale.y + scale.z) / 3.0
                node.setScale(uniform_scale, uniform_scale, uniform_scale)
            imgui.same_line()
            if imgui.button("重置缩放##reset_scale"):
                node.setScale(1, 1, 1)
            imgui.same_line()
            if imgui.button("翻倍##double_scale"):
                node.setScale(scale.x * 2, scale.y * 2, scale.z * 2)
    

    def _draw_gui_properties(self, node):
        """绘制GUI元素属性"""
        # 获取GUI元素
        gui_element = None
        if hasattr(node, 'getPythonTag'):
            gui_element = node.getPythonTag('gui_element')
        
        if not gui_element:
            imgui.text("无GUI元素数据")
            return
        
        # GUI类型信息
        gui_type = getattr(gui_element, 'gui_type', 'UNKNOWN')
        imgui.text(f"GUI类型: {gui_type}")
        
        # 基本属性
        if imgui.collapsing_header("基本属性"):
            # 文本内容 (适用于按钮、标签等)
            if hasattr(gui_element, 'text'):
                changed, new_text = imgui.input_text("文本内容", gui_element.text, 256)
                if changed and hasattr(self, 'gui_manager'):
                    self.gui_manager.editGUIElement(gui_element, 'text', new_text)
            
            # GUI ID
            gui_id = getattr(gui_element, 'id', '')
            changed, new_id = imgui.input_text("GUI ID", gui_id, 64)
            if changed and hasattr(self, 'gui_manager'):
                gui_element.id = new_id
        
        # 变换属性
        if imgui.collapsing_header("变换属性"):
            # 位置
            pos = gui_element.getPos()
            imgui.text("位置")
            
            if gui_type in ["button", "label", "entry", "2d_image"]:
                # 2D GUI组件使用屏幕坐标
                imgui.text("屏幕坐标")
                logical_x = pos.getX() / 0.1
                logical_z = pos.getZ() / 0.1
                
                changed, new_x = imgui.input_float("X##gui_pos_x", logical_x, 1.0, 10.0, "%.1f")
                if changed and hasattr(self, 'gui_manager'):
                    gui_element.setX(new_x * 0.1)
                
                changed, new_z = imgui.input_float("Y##gui_pos_y", logical_z, 1.0, 10.0, "%.1f")
                if changed and hasattr(self, 'gui_manager'):
                    gui_element.setZ(new_z * 0.1)
            else:
                # 3D GUI组件使用世界坐标
                imgui.text("世界坐标")
                changed, new_x = imgui.input_float("X##gui_world_x", pos.getX(), 0.1, 1.0, "%.3f")
                if changed and hasattr(self, 'gui_manager'):
                    gui_element.setX(new_x)
                
                changed, new_y = imgui.input_float("Y##gui_world_y", pos.getY(), 0.1, 1.0, "%.3f")
                if changed and hasattr(self, 'gui_manager'):
                    gui_element.setY(new_y)
                
                changed, new_z = imgui.input_float("Z##gui_world_z", pos.getZ(), 0.1, 1.0, "%.3f")
                if changed and hasattr(self, 'gui_manager'):
                    gui_element.setZ(new_z)
            
            # 缩放
            scale = gui_element.getScale()
            imgui.text("缩放")
            changed, new_sx = imgui.input_float("X##gui_scale_x", scale.getX(), 0.1, 1.0, "%.3f")
            if changed and hasattr(self, 'gui_manager'):
                gui_element.setSx(new_sx)
            
            changed, new_sy = imgui.input_float("Y##gui_scale_y", scale.getY(), 0.1, 1.0, "%.3f")
            if changed and hasattr(self, 'gui_manager'):
                gui_element.setSy(new_sy)
            
            changed, new_sz = imgui.input_float("Z##gui_scale_z", scale.getZ(), 0.1, 1.0, "%.3f")
            if changed and hasattr(self, 'gui_manager'):
                gui_element.setSz(new_sz)
            
            # 旋转
            hpr = gui_element.getHpr()
            imgui.text("旋转")
            changed, new_h = imgui.input_float("H##gui_rot_h", hpr.getX(), 1.0, 10.0, "%.1f")
            if changed and hasattr(self, 'gui_manager'):
                gui_element.setH(new_h)
            
            changed, new_p = imgui.input_float("P##gui_rot_p", hpr.getY(), 1.0, 10.0, "%.1f")
            if changed and hasattr(self, 'gui_manager'):
                gui_element.setP(new_p)
            
            changed, new_r = imgui.input_float("R##gui_rot_r", hpr.getZ(), 1.0, 10.0, "%.1f")
            if changed and hasattr(self, 'gui_manager'):
                gui_element.setR(new_r)
        
        # 外观属性
        if imgui.collapsing_header("外观属性"):
            # 大小
            if hasattr(gui_element, 'size'):
                size = gui_element.size
                imgui.text("大小")
                changed, new_w = imgui.input_float("宽度", size[0], 1.0, 10.0, "%.1f")
                if changed and hasattr(self, 'gui_manager'):
                    new_size = (new_w, size[1])
                    self.gui_manager.editGUIElement(gui_element, 'size', new_size)
                
                changed, new_h = imgui.input_float("高度", size[1], 1.0, 10.0, "%.1f")
                if changed and hasattr(self, 'gui_manager'):
                    new_size = (size[0], new_h)
                    self.gui_manager.editGUIElement(gui_element, 'size', new_size)
            
            # 颜色
            if hasattr(gui_element, 'getColor'):
                try:
                    color = gui_element.getColor()
                    # 确保颜色是有效的
                    if not color or (hasattr(color, '__len__') and len(color) < 3):
                        color = (1.0, 1.0, 1.0, 1.0)  # 默认白色
                except:
                    color = (1.0, 1.0, 1.0, 1.0)  # 默认白色
                
                imgui.text("颜色")
                # 获取颜色值
                if hasattr(color, 'getX'):
                    # 如果是Panda3D的Vec4对象
                    r, g, b = color.getX(), color.getY(), color.getZ()
                else:
                    # 如果是元组或其他格式
                    r, g, b = color[0], color[1], color[2]
            
            changed, new_r = imgui.slider_float("R##gui_color_r", r, 0.0, 1.0)
            if changed: gui_element.setColor(new_r, g, b, 1.0)
            
            changed, new_g = imgui.slider_float("G##gui_color_g", g, 0.0, 1.0)
            if changed: gui_element.setColor(r, new_g, b, 1.0)
            
            changed, new_b = imgui.slider_float("B##gui_color_b", b, 0.0, 1.0)
            if changed: gui_element.setColor(r, g, new_b, 1.0)            
            # 透明度
            imgui.text("透明度")
            current_alpha = getattr(gui_element, 'alpha', 1.0)
            changed, new_alpha = imgui.slider_float("Alpha", current_alpha, 0.0, 1.0)
            if changed:
                gui_element.alpha = new_alpha
                if hasattr(gui_element, 'setTransparency'):
                    # 将0.0-1.0范围转换为Panda3D的透明度格式
                    panda_transparency = int((1.0 - new_alpha) * 255)
                    gui_element.setTransparency(panda_transparency)
            
            # 渲染顺序
            imgui.text("渲染顺序")
            current_sort = getattr(gui_element, 'sort', 0)
            changed, new_sort = imgui.input_int("Sort Order", current_sort)
            if changed:
                gui_element.sort = new_sort
                if hasattr(gui_element, 'setBin'):
                    gui_element.setBin('fixed', new_sort)
            
            # 字体设置（适用于文本类型的GUI元素）
            if gui_type in ["button", "label", "entry"]:
                imgui.text("字体设置")
                
                # 字体选择
                current_font = getattr(gui_element, 'font_path', '')
                if imgui.button(f"字体: {Path(current_font).name if current_font else '默认'}##font_select"):
                    self.show_font_selector(
                        gui_element, 
                        'font_path', 
                        current_font,
                        lambda font_path: self._apply_gui_font(gui_element, font_path)
                    )
                
                # 字体大小
                current_size = getattr(gui_element, 'font_size', 12)
                changed, new_size = imgui.slider_float("字体大小", current_size, 8.0, 72.0)
                if changed:
                    gui_element.font_size = new_size
                    self._apply_gui_font_size(gui_element, new_size)
                
                # 字体样式
                imgui.text("字体样式")
                is_bold = getattr(gui_element, 'font_bold', False)
                changed, new_bold = imgui.checkbox("粗体", is_bold)
                if changed:
                    gui_element.font_bold = new_bold
                    self._apply_gui_font_style(gui_element)
                
                imgui.same_line()
                is_italic = getattr(gui_element, 'font_italic', False)
                changed, new_italic = imgui.checkbox("斜体", is_italic)
                if changed:
                    gui_element.font_italic = new_italic
                    self._apply_gui_font_style(gui_element)
    

    def _draw_light_properties(self, node):
        """绘制光源属性"""
        imgui.text("光源属性")
        
        # 光源颜色
        if hasattr(node, 'getColor'):
            try:
                color = node.getColor()
                # 确保颜色是有效的
                if not color or len(color) < 3:
                    color = (1.0, 1.0, 1.0, 1.0)  # 默认白色
            except:
                color = (1.0, 1.0, 1.0, 1.0)  # 默认白色
            
            changed, new_r = imgui.drag_float("颜色 R", color[0], 0.01, 0.0, 1.0)
            if changed: node.setColor(new_r, color[1], color[2], color[3] if len(color) > 3 else 1.0)
            
            changed, new_g = imgui.drag_float("颜色 G", color[1], 0.01, 0.0, 1.0)
            if changed: node.setColor(color[0], new_g, color[2], color[3] if len(color) > 3 else 1.0)
            
            changed, new_b = imgui.drag_float("颜色 B", color[2], 0.01, 0.0, 1.0)
            if changed: node.setColor(color[0], color[1], new_b, color[3] if len(color) > 3 else 1.0)
        
        # 光源强度
        imgui.text("光源强度: (暂不支持编辑)")
    

    def _draw_model_properties(self, node):
        """绘制模型属性"""
        # 获取模型信息
        model_path = node.getTag("model_path") if node.hasTag("model_path") else "未知"
        
        imgui.text("模型路径:")
        imgui.same_line()
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), model_path)
        
        # 模型基本信息
        imgui.text("模型名称:")
        imgui.same_line()
        model_name = node.getName() or "未命名模型"
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), model_name)
        
        # 模型位置信息
        imgui.text("位置:")
        imgui.same_line()
        pos = node.getPos()
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"X:{pos.x:.2f} Y:{pos.y:.2f} Z:{pos.z:.2f}")
        
        # 模型缩放信息
        imgui.text("缩放:")
        imgui.same_line()
        scale = node.getScale()
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"X:{scale.x:.2f} Y:{scale.y:.2f} Z:{scale.z:.2f}")
        
        # 模型旋转信息
        imgui.text("旋转:")
        imgui.same_line()
        hpr = node.getHpr()
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), f"H:{hpr.x:.1f}° P:{hpr.y:.1f}° R:{hpr.z:.1f}°")
    

    def _draw_animation_properties(self, node):
        """绘制动画控制属性面板（优化版本，使用缓存避免重复计算）"""
        anim_node = node
        try:
            if hasattr(self, "_resolve_animation_owner_model"):
                resolved = self._resolve_animation_owner_model(node)
                if resolved and not resolved.isEmpty():
                    anim_node = resolved
        except Exception:
            pass

        # 路径兜底：当 anim_node 缺少路径时，从 scene_manager.models 中反查祖先模型
        try:
            needs_path = (not anim_node.hasTag("model_path")) or (not anim_node.getTag("model_path"))
            if needs_path and hasattr(self, "scene_manager") and self.scene_manager:
                models = getattr(self.scene_manager, "models", [])
                for model in list(models):
                    try:
                        if not model or model.isEmpty():
                            continue
                        if (model == anim_node or model.isAncestorOf(anim_node) or anim_node.isAncestorOf(model)):
                            if model.hasTag("model_path") and model.getTag("model_path"):
                                anim_node.setTag("model_path", model.getTag("model_path"))
                                if model.hasTag("original_path") and model.getTag("original_path"):
                                    anim_node.setTag("original_path", model.getTag("original_path"))
                                break
                            if model.hasTag("saved_model_path") and model.getTag("saved_model_path"):
                                anim_node.setTag("model_path", model.getTag("saved_model_path"))
                                break
                    except Exception:
                        continue
        except Exception:
            pass

        # 先刷新一次模型动画标签，避免“导入后未初始化”导致误判
        try:
            if hasattr(self, "scene_manager") and self.scene_manager and hasattr(self.scene_manager, "_processModelAnimations"):
                self.scene_manager._processModelAnimations(anim_node)
        except Exception:
            pass

        has_animation_tag = anim_node.hasTag("has_animations") and anim_node.getTag("has_animations").lower() == "true"
        has_animation_nodes = False
        try:
            has_animation_nodes = (
                anim_node.findAllMatches("**/+Character").getNumPaths() > 0 or
                anim_node.findAllMatches("**/+AnimBundleNode").getNumPaths() > 0
            )
        except Exception:
            pass

        # 检查是否已经缓存了动画信息
        cached_anim_info = anim_node.getPythonTag("cached_anim_info")
        cached_processed_names = anim_node.getPythonTag("cached_processed_names")

        # 如果之前缓存的是“格式未知”，但现在已有路径，强制重建缓存
        try:
            now_has_path = anim_node.hasTag("model_path") and bool(anim_node.getTag("model_path"))
            if now_has_path and isinstance(cached_anim_info, str) and "格式: 未知" in cached_anim_info:
                cached_anim_info = None
                cached_processed_names = None
                anim_node.setPythonTag("cached_anim_info", None)
                anim_node.setPythonTag("cached_processed_names", None)
                anim_node.setPythonTag("animation", None)
                self._clear_animation_cache(anim_node)
        except Exception:
            pass

        # 如果节点已被检测为有动画，但缓存是“无动画”，强制重新检测一次
        if (has_animation_tag or has_animation_nodes) and (cached_anim_info == "无动画" or cached_processed_names == []):
            cached_anim_info = None
            cached_processed_names = None
            anim_node.setPythonTag("cached_anim_info", None)
            anim_node.setPythonTag("cached_processed_names", None)
            anim_node.setPythonTag("animation", None)
        
        # 只有在没有缓存时才进行完整的动画检测和处理
        if cached_anim_info is None or cached_processed_names is None:
            # 获取Actor
            actor = self._getActor(anim_node)
            if not actor:
                if has_animation_tag or has_animation_nodes:
                    imgui.text_colored((1.0, 0.7, 0.3, 1.0), "检测到动画结构，但当前未成功绑定Actor")
                else:
                    imgui.text_colored((0.7, 0.7, 0.7, 1.0), "此模型不包含动画")
                return
            
            # 获取和分析动画名称
            anim_names = actor.getAnimNames()
            processed_names = self._processAnimationNames(anim_node, anim_names)
            
            if not processed_names:
                imgui.text_colored((0.7, 0.7, 0.7, 1.0), "未检测到动画序列")
                # 只在明确无动画时缓存空结果，避免误缓存导致后续无法重试
                if not (has_animation_tag or has_animation_nodes):
                    anim_node.setPythonTag("cached_processed_names", [])
                    anim_node.setPythonTag("cached_anim_info", "无动画")
                return

            anim_node.setTag("has_animations", "true")
            anim_node.setPythonTag("animation", True)
            
            # 计算并缓存动画信息
            format_info = self._getModelFormat(anim_node)
            animation_info = self._analyzeAnimationQuality(actor, anim_names, format_info)
            info_text = f"格式: {format_info} | 动画数量: {len(processed_names)}"
            if animation_info:
                info_text += f" | {animation_info}"
            
            # 缓存结果
            anim_node.setPythonTag("cached_anim_info", info_text)
            anim_node.setPythonTag("cached_processed_names", processed_names)
            
        else:
            # 使用缓存的数据
            info_text = cached_anim_info
            processed_names = cached_processed_names
            
            # 如果缓存的空结果，直接返回
            if not processed_names:
                imgui.text_colored((0.7, 0.7, 0.7, 1.0), "未检测到动画序列")
                return
        
        # 显示动画信息（使用缓存的数据）
        imgui.text("信息:")
        imgui.same_line()
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), info_text)
        
        imgui.spacing()
        
        # 动画选择下拉框
        imgui.text("动画名称:")
        imgui.same_line()
        
        # 获取当前选中的动画
        current_anim = anim_node.getPythonTag("selected_animation")
        valid_original_names = [original_name for _, original_name in processed_names]
        if current_anim is None or current_anim not in valid_original_names:
            current_anim = processed_names[0][1] if processed_names else ""
            anim_node.setPythonTag("selected_animation", current_anim)
        
        # 查找当前动画的索引
        current_index = 0
        for i, (display_name, original_name) in enumerate(processed_names):
            if original_name == current_anim:
                current_index = i
                break
        
        # 创建下拉框选项
        animation_options = [display_name for display_name, _ in processed_names]
        changed, new_index = imgui.combo("##animation_combo", current_index, animation_options)
        
        if changed and new_index < len(processed_names):
            selected_display, selected_original = processed_names[new_index]
            anim_node.setPythonTag("selected_animation", selected_original)
            print(f"选择动画: {selected_display} (原始名称: {selected_original})")
        
        imgui.spacing()
        
        # 控制按钮组
        imgui.text("控制:")
        
        # 播放按钮
        if imgui.button("播放##play_animation"):
            self._playAnimation(anim_node)
        imgui.same_line()
        
        # 暂停按钮
        if imgui.button("暂停##pause_animation"):
            self._pauseAnimation(anim_node)
        imgui.same_line()
        
        # 停止按钮
        if imgui.button("停止##stop_animation"):
            self._stopAnimation(anim_node)
        imgui.same_line()
        
        # 循环按钮
        if imgui.button("循环##loop_animation"):
            self._loopAnimation(anim_node)
        
        imgui.spacing()
        
        # 播放速度控制
        imgui.text("播放速度:")
        imgui.same_line()
        
        # 获取当前速度
        current_speed = anim_node.getPythonTag("anim_speed")
        if current_speed is None:
            current_speed = 1.0
            anim_node.setPythonTag("anim_speed", current_speed)
        
        # 速度滑块
        changed, new_speed = imgui.slider_float("##anim_speed", current_speed, 0.1, 5.0, "%.1f")
        if changed:
            anim_node.setPythonTag("anim_speed", new_speed)
            self._setAnimationSpeed(anim_node, new_speed)
        
        imgui.same_line()
        imgui.text_colored((0.7, 0.7, 0.7, 1.0), "倍速")
    

    def _draw_collision_properties(self, node):
        """绘制碰撞检测属性"""
        if not node or node.isEmpty():
            return
        
        try:
            # 检查节点是否已有碰撞
            has_collision = self._has_collision(node)
            
            # 碰撞状态徽章
            imgui.text("状态:")
            imgui.same_line()
            if has_collision:
                imgui.text_colored((0.0, 0.8, 0.0, 1.0), "🟢 已启用")
            else:
                imgui.text_colored((0.8, 0.0, 0.0, 1.0), "🔴 未启用")
            
            imgui.separator()
            
            # 碰撞形状选择
            imgui.text("碰撞形状:")
            imgui.same_line()
            
            # 碰撞形状选项
            collision_shapes = ["球形 (Sphere)", "盒型 (Box)", "胶囊体 (Capsule)", "平面 (Plane)", "自动选择 (Auto)"]
            
            # 获取当前形状
            current_shape = self._get_current_collision_shape(node) if has_collision else getattr(self.app, '_selected_collision_shape', "球形 (Sphere)")
            if has_collision: self.app._selected_collision_shape = current_shape
            
            # 形状选择下拉框
            current_index = collision_shapes.index(current_shape) if current_shape in collision_shapes else 0
            changed, selected_index = imgui.combo("##collision_shape", current_index, collision_shapes)
            if changed:
                # 始终更新选择的形状
                selected_shape = collision_shapes[selected_index]
                self.app._selected_collision_shape = selected_shape
                # 如果已经有碰撞体，询问用户是否要重新创建
                if has_collision:
                    self._remove_collision_from_node(node)
                    self._add_collision_to_node(node)
            
            imgui.separator()
            
            # 位置偏移控件
            imgui.text("位置偏移:")
            
            # 获取当前位置偏移
            pos_offset = self._get_collision_position_offset(node)
            
            # X位置
            changed, new_x = imgui.drag_float("X##collision_pos_x", pos_offset[0], 0.1, -100.0, 100.0, "%.2f")
            if changed and has_collision:
                self._update_collision_position(node, 'x', new_x)
            
            # Y位置
            changed, new_y = imgui.drag_float("Y##collision_pos_y", pos_offset[1], 0.1, -100.0, 100.0, "%.2f")
            if changed and has_collision:
                self._update_collision_position(node, 'y', new_y)
            
            # Z位置
            changed, new_z = imgui.drag_float("Z##collision_pos_z", pos_offset[2], 0.1, -100.0, 100.0, "%.2f")
            if changed and has_collision:
                self._update_collision_position(node, 'z', new_z)
            
            # 形状特定参数（始终显示，但根据状态启用/禁用）
            shape_type = self._get_current_collision_shape_type(node)
            self._draw_shape_specific_parameters(node, shape_type, has_collision)
            
            imgui.separator()
            
            # 操作按钮
            if has_collision:
                # 显示/隐藏碰撞体按钮
                is_visible = self._is_collision_visible(node)
                visibility_text = "隐藏碰撞体" if is_visible else "显示碰撞体"
                if imgui.button(visibility_text):
                    self._toggle_collision_visibility(node)
                
                imgui.same_line()
                
                # 移除碰撞按钮
                if imgui.button("移除碰撞"):
                    self._remove_collision_from_node(node)
            else:
                # 添加碰撞按钮
                if imgui.button("添加碰撞"):
                    self._add_collision_to_node(node)
            
            imgui.separator()
            
            # 碰撞检测触发模式
            imgui.text("触发模式:")
            
            # 自动检测开关
            auto_enabled = self.collision_manager.model_collision_enabled if hasattr(self, 'collision_manager') else False
            changed, new_auto = imgui.checkbox("自动检测", auto_enabled)
            if changed and hasattr(self, 'collision_manager'):
                self.collision_manager.enableModelCollisionDetection(new_auto, 0.1, 0.5)
            
            imgui.same_line()
            
            # 手动检测按钮
            if imgui.button("立即检测"):
                if hasattr(self, 'collision_manager'):
                    self._manual_collision_detection()
            
        except Exception as e:
            print(f"绘制碰撞属性失败: {e}")
            import traceback
            traceback.print_exc()
    

    def _draw_shape_specific_parameters(self, node, shape_type, has_collision=True):
        """绘制形状特定参数"""
        try:
            if shape_type == "sphere":
                self._draw_sphere_parameters(node, has_collision)
            elif shape_type == "box":
                self._draw_box_parameters(node, has_collision)
            elif shape_type == "capsule":
                self._draw_capsule_parameters(node, has_collision)
            elif shape_type == "plane":
                self._draw_plane_parameters(node, has_collision)
        except Exception as e:
            print(f"绘制形状参数失败: {e}")
    

    def _draw_sphere_parameters(self, node, has_collision=True):
        """绘制球形参数"""
        try:
            imgui.text("球形参数:")
            imgui.same_line()
            
            # 获取当前半径
            radius = self._get_sphere_radius(node)
            
            # 半径调整
            changed, new_radius = imgui.drag_float("半径##sphere_radius", radius, 0.1, 0.1, 100.0, "%.2f")
            if changed and has_collision:
                self._update_sphere_radius(node, new_radius)
                
        except Exception as e:
            print(f"绘制球形参数失败: {e}")
    

    def _draw_box_parameters(self, node, has_collision=True):
        """绘制盒型参数"""
        try:
            imgui.text("盒型参数:")
            
            # 获取当前尺寸
            size = self._get_box_size(node)
            
            # 尺寸调整
            changed, new_x = imgui.drag_float("长度##box_length", size[0], 0.1, 0.1, 100.0, "%.2f")
            if changed and has_collision:
                self._update_box_size(node, 'x', new_x)
            
            changed, new_y = imgui.drag_float("宽度##box_width", size[1], 0.1, 0.1, 100.0, "%.2f")
            if changed and has_collision:
                self._update_box_size(node, 'y', new_y)
            
            changed, new_z = imgui.drag_float("高度##box_height", size[2], 0.1, 0.1, 100.0, "%.2f")
            if changed and has_collision:
                self._update_box_size(node, 'z', new_z)
                
        except Exception as e:
            print(f"绘制盒型参数失败: {e}")
    

    def _draw_capsule_parameters(self, node, has_collision=True):
        """绘制胶囊体参数"""
        try:
            imgui.text("胶囊体参数:")
            
            # 获取当前参数
            radius = self._get_capsule_radius(node)
            height = self._get_capsule_height(node)
            
            # 半径调整
            changed, new_radius = imgui.drag_float("半径##capsule_radius", radius, 0.1, 0.1, 100.0, "%.2f")
            if changed and has_collision:
                self._update_capsule_radius(node, new_radius)
            
            # 高度调整
            changed, new_height = imgui.drag_float("高度##capsule_height", height, 0.1, 0.1, 100.0, "%.2f")
            if changed and has_collision:
                self._update_capsule_height(node, new_height)
                
        except Exception as e:
            print(f"绘制胶囊体参数失败: {e}")
    

    def _draw_plane_parameters(self, node, has_collision=True):
        """绘制平面参数"""
        try:
            imgui.text("平面参数:")
            
            # 获取当前法向量
            normal = self._get_plane_normal(node)
            
            # 法向量调整
            changed, new_x = imgui.drag_float("法向量 X##plane_normal_x", normal[0], 0.1, -1.0, 1.0, "%.2f")
            if changed and has_collision:
                self._update_plane_normal(node, 'x', new_x)
            
            changed, new_y = imgui.drag_float("法向量 Y##plane_normal_y", normal[1], 0.1, -1.0, 1.0, "%.2f")
            if changed and has_collision:
                self._update_plane_normal(node, 'y', new_y)
            
            changed, new_z = imgui.drag_float("法向量 Z##plane_normal_z", normal[2], 0.1, -1.0, 1.0, "%.2f")
            if changed and has_collision:
                self._update_plane_normal(node, 'z', new_z)
                
        except Exception as e:
            print(f"绘制平面参数失败: {e}")
    

    def _draw_property_actions(self, node):
        """绘制属性操作按钮"""
        # 重置变换
        if imgui.button("重置变换"):
            node.setPos(0, 0, 0)
            node.setHpr(0, 0, 0)
            node.setScale(1, 1, 1)
        
        imgui.same_line()
        
        # 切换可见性
        is_visible = not node.is_hidden()
        visibility_text = "隐藏" if is_visible else "显示"
        if imgui.button(visibility_text):
            if is_visible:
                node.hide()
            else:
                node.show()
        
        imgui.same_line()
        
        # 聚焦到对象
        if imgui.button("聚焦"):
            if hasattr(self, 'selection') and self.selection:
                self.selection.focusCameraOnSelectedNodeAdvanced()
        
        # 删除对象
        imgui.same_line()
        if imgui.button("删除"):
            if hasattr(self, 'selection') and self.selection:
                self.selection.deleteSelectedNode()
    

    def _draw_appearance_properties(self, node):
        """绘制外观属性"""
        # 颜色属性
        if hasattr(node, 'getColor'):
            imgui.text("颜色")
            try:
                color = node.getColor()
                # 确保颜色是有效的
                if not color or len(color) < 3:
                    color = (1.0, 1.0, 1.0, 1.0)  # 默认白色
            except:
                color = (1.0, 1.0, 1.0, 1.0)  # 默认白色
            
            # 颜色滑块
            changed, new_r = imgui.slider_float("R##color_r", color[0], 0.0, 1.0)
            if changed: 
                new_color = (new_r, color[1], color[2], color[3] if len(color) > 3 else 1.0)
                node.setColor(new_color)
                color = new_color
            
            changed, new_g = imgui.slider_float("G##color_g", color[1], 0.0, 1.0)
            if changed: 
                new_color = (color[0], new_g, color[2], color[3] if len(color) > 3 else 1.0)
                node.setColor(new_color)
                color = new_color
            
            changed, new_b = imgui.slider_float("B##color_b", color[2], 0.0, 1.0)
            if changed: 
                new_color = (color[0], color[1], new_b, color[3] if len(color) > 3 else 1.0)
                node.setColor(new_color)
                color = new_color
            
            # 只有当颜色有alpha通道时才显示alpha滑块
            if len(color) > 3:
                changed, new_a = imgui.slider_float("A##color_a", color[3], 0.0, 1.0)
                if changed: 
                    new_color = (color[0], color[1], color[2], new_a)
                    node.setColor(new_color)
                    color = new_color
            
            # 颜色预览和选择器
            imgui.text("颜色预览")
            color_with_alpha = (color[0], color[1], color[2], color[3] if len(color) > 3 else 1.0)
            if imgui.color_button("颜色预览##preview", color_with_alpha, 0, (100, 30)):
                # 点击颜色按钮打开颜色选择器
                self.show_color_picker(node, 'color', color_with_alpha)
            
            imgui.same_line()
            if imgui.button("选择颜色##color_picker_btn"):
                self.show_color_picker(node, 'color', (color.x, color.y, color.z, color.w))
        
        # 透明度
        if hasattr(node, 'setTransparency') and hasattr(node, 'getTransparency'):
            imgui.text("透明度")
            current_transparency = node.getTransparency()
            # 将当前的透明度值转换为0.0-1.0范围用于显示
            display_transparency = 1.0 - current_transparency if current_transparency <= 1 else 0.0
            changed, new_transparency = imgui.slider_float("透明度", display_transparency, 0.0, 1.0)
            if changed:
                # 将0.0-1.0范围转换回Panda3D的透明度格式
                panda_transparency = int((1.0 - new_transparency) * 255)
                node.setTransparency(panda_transparency)
        
        # 材质属性
        self._draw_material_properties(node)
        
        # 渲染状态
        imgui.text("渲染状态")
        if imgui.button("应用材质"):
            self._apply_material_to_node(node)
        
        imgui.same_line()
        if imgui.button("重置材质"):
            self._reset_material(node)
    

    def _draw_material_properties(self, node):
        """绘制材质属性"""
        materials = node.find_all_materials()
        
        if not materials:
            imgui.text_colored((0.5, 0.5, 0.5, 1.0), "无材质")
            return
        
        for i, material in enumerate(materials):
            material_name = material.get_name() if hasattr(material, 'get_name') and material.get_name() else f"材质{i + 1}"
            
            if imgui.collapsing_header(f"材质: {material_name}"):
                # 材质基础颜色
                base_color = self._get_material_base_color(material)
                if base_color:
                    imgui.text("基础颜色")
                    changed, new_r = imgui.slider_float(f"R##mat_r_{i}", base_color[0], 0.0, 1.0)
                    if changed:
                        self._update_material_base_color(material, 'r', new_r)
                        base_color = (new_r, base_color[1], base_color[2], base_color[3])
                    
                    changed, new_g = imgui.slider_float(f"G##mat_g_{i}", base_color[1], 0.0, 1.0)
                    if changed:
                        self._update_material_base_color(material, 'g', new_g)
                        base_color = (base_color[0], new_g, base_color[2], base_color[3])
                    
                    changed, new_b = imgui.slider_float(f"B##mat_b_{i}", base_color[2], 0.0, 1.0)
                    if changed:
                        self._update_material_base_color(material, 'b', new_b)
                        base_color = (base_color[0], base_color[1], new_b, base_color[3])
                    
                    changed, new_a = imgui.slider_float(f"A##mat_a_{i}", base_color[3], 0.0, 1.0)
                    if changed:
                        self._update_material_base_color(material, 'a', new_a)
                        base_color = (base_color[0], base_color[1], base_color[2], new_a)
                
                # PBR属性
                if hasattr(material, 'roughness') and material.roughness is not None:
                    imgui.text("PBR属性")
                    try:
                        roughness_value = float(material.roughness)
                        changed, new_roughness = imgui.slider_float(f"粗糙度##rough_{i}", roughness_value, 0.0, 1.0)
                        if changed:
                            self._update_material_roughness(material, new_roughness)
                    except:
                        imgui.text_colored((0.7, 0.7, 0.7, 1.0), "粗糙度: 不可用")
                
                if hasattr(material, 'metallic') and material.metallic is not None:
                    try:
                        metallic_value = float(material.metallic)
                        changed, new_metallic = imgui.slider_float(f"金属性##metal_{i}", metallic_value, 0.0, 1.0)
                        if changed:
                            self._update_material_metallic(material, new_metallic)
                    except:
                        imgui.text_colored((0.7, 0.7, 0.7, 1.0), "金属性: 不可用")
                
                if hasattr(material, 'refractive_index') and material.refractive_index is not None:
                    try:
                        ior_value = float(material.refractive_index)
                        changed, new_ior = imgui.slider_float(f"折射率##ior_{i}", ior_value, 1.0, 3.0)
                        if changed:
                            self._update_material_ior(material, new_ior)
                    except:
                        imgui.text_colored((0.7, 0.7, 0.7, 1.0), "折射率: 不可用")
                
                # 材质预设
                imgui.text("材质预设")
                presets = ["默认", "金属", "塑料", "玻璃", "木材", "混凝土"]
                current_preset = 0  # 默认选择
                
                if imgui.begin_combo(f"预设##preset_{i}", presets[current_preset]):
                    for j, preset_name in enumerate(presets):
                        if imgui.selectable(preset_name, j == current_preset):
                            self._apply_material_preset(material, preset_name)
                    imgui.end_combo()
                
                # 纹理信息
                imgui.text("纹理贴图")
                if imgui.button(f"选择漫反射贴图##diffuse_{i}"):
                    self._select_texture_for_material(node, material, "diffuse")
                
                imgui.same_line()
                if imgui.button(f"选择法线贴图##normal_{i}"):
                    self._select_texture_for_material(node, material, "normal")
                
                imgui.same_line()
                if imgui.button(f"选择粗糙度贴图##roughness_{i}"):
                    self._select_texture_for_material(node, material, "roughness")
                
                if imgui.button(f"选择金属性贴图##metallic_{i}"):
                    self._select_texture_for_material(node, material, "metallic")
                
                imgui.same_line()
                if imgui.button(f"选择自发光贴图##emission_{i}"):
                    self._select_texture_for_material(node, material, "emission")
                
                imgui.same_line()
                if imgui.button(f"清除所有贴图##clear_{i}"):
                    self._clear_all_textures(node)
                
                # 着色模型选择
                self._draw_shading_model_panel(material, i)
                
                # 显示当前纹理信息
                self._display_current_textures(node, material)
    

    def _draw_shading_model_panel(self, material, material_index):
        """绘制着色模型选择面板"""
        try:
            imgui.text("着色模型")
            
            # RenderPipeline支持的着色模型
            shading_models = ["默认", "自发光", "透明"]
            current_model = 0  # 默认选择
            
            # 安全地获取当前着色模型
            try:
                if hasattr(material, 'emission') and material.emission is not None:
                    current_model = int(material.emission.x)
            except:
                current_model = 0
            
            # 着色模型选择
            if imgui.begin_combo(f"着色模型##shading_{material_index}", shading_models[current_model]):
                for j, model_name in enumerate(shading_models):
                    if imgui.selectable(model_name, j == current_model):
                        self._update_shading_model(material, j)
                imgui.end_combo()
            
            # 如果是透明着色模型，添加透明度控制
            if current_model == 3:  # 透明着色模型
                imgui.text("透明度设置")
                try:
                    if hasattr(material, 'shading_model_param0'):
                        current_opacity = material.shading_model_param0
                    else:
                        current_opacity = 1.0
                    
                    changed, new_opacity = imgui.slider_float(f"不透明度##opacity_{material_index}", current_opacity, 0.0, 1.0)
                    if changed:
                        self._update_transparency(material, new_opacity)
                except:
                    imgui.text_colored((0.7, 0.7, 0.7, 1.0), "透明度控制不可用")
        
        except Exception as e:
            print(f"绘制着色模型面板失败: {e}")


