diff --git a/imgui.ini b/imgui.ini index 14f7d531..36a15210 100644 --- a/imgui.ini +++ b/imgui.ini @@ -25,25 +25,25 @@ Collapsed=0 [Window][工具栏] Pos=285,20 -Size=1190,62 +Size=1221,62 Collapsed=0 DockId=0x00000009,0 [Window][场景树] Pos=0,20 -Size=283,506 +Size=283,494 Collapsed=0 DockId=0x00000007,0 [Window][属性面板] -Pos=1477,20 -Size=443,1012 +Pos=1508,20 +Size=412,360 Collapsed=0 DockId=0x0000000B,0 [Window][控制台] -Pos=0,528 -Size=283,504 +Pos=0,516 +Size=283,493 Collapsed=0 DockId=0x00000008,0 @@ -60,7 +60,7 @@ Collapsed=0 [Window][WindowOverViewport_11111111] Pos=0,20 -Size=1920,1012 +Size=1920,989 Collapsed=0 [Window][测试窗口1] @@ -84,12 +84,12 @@ Size=400,300 Collapsed=0 [Window][选择路径] -Pos=660,266 +Pos=660,254 Size=600,500 Collapsed=0 [Window][打开项目] -Pos=710,316 +Pos=715,312 Size=500,400 Collapsed=0 @@ -99,8 +99,8 @@ Size=600,500 Collapsed=0 [Window][资源管理器] -Pos=285,727 -Size=1190,305 +Pos=285,788 +Size=1221,221 Collapsed=0 DockId=0x00000004,0 @@ -150,8 +150,8 @@ Size=101,226 Collapsed=0 [Window][LUI编辑器] -Pos=1477,749 -Size=443,283 +Pos=1508,382 +Size=412,627 Collapsed=0 DockId=0x0000000C,0 @@ -212,17 +212,17 @@ Size=460,240 Collapsed=0 [Docking][Data] -DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1920,1012 Split=X +DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1920,989 Split=X DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=283,1012 Split=Y Selected=0xE0015051 DockNode ID=0x00000007 Parent=0x00000005 SizeRef=283,506 Selected=0xE0015051 DockNode ID=0x00000008 Parent=0x00000005 SizeRef=283,504 Selected=0x5428E753 DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1635,1012 Split=X - DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1190,989 Split=Y - DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1321,666 Split=Y + DockNode ID=0x00000001 Parent=0x00000006 SizeRef=1221,989 Split=Y + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1321,789 Split=Y DockNode ID=0x00000009 Parent=0x00000003 SizeRef=1304,62 Selected=0x43A39006 - DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1304,618 CentralNode=1 - DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1321,305 Selected=0x3A2E05C3 - DockNode ID=0x00000002 Parent=0x00000006 SizeRef=443,989 Split=Y Selected=0x5DB6FF37 - DockNode ID=0x0000000B Parent=0x00000002 SizeRef=443,727 Selected=0x5DB6FF37 - DockNode ID=0x0000000C Parent=0x00000002 SizeRef=443,283 Selected=0x1EB923B7 + DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1304,725 CentralNode=1 + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1321,221 Selected=0x3A2E05C3 + DockNode ID=0x00000002 Parent=0x00000006 SizeRef=412,989 Split=Y Selected=0x5DB6FF37 + DockNode ID=0x0000000B Parent=0x00000002 SizeRef=443,368 Selected=0x5DB6FF37 + DockNode ID=0x0000000C Parent=0x00000002 SizeRef=443,642 Selected=0x1EB923B7 diff --git a/project/project_manager.py b/project/project_manager.py index de0cacf6..a521b522 100644 --- a/project/project_manager.py +++ b/project/project_manager.py @@ -299,8 +299,10 @@ class ProjectManager: temp_scene_file = os.path.join(scene_dir, f"{scene_name}.tmp{scene_ext}") final_gui_info_file = scene_file.replace('.bam', '_gui.json') temp_gui_info_file = temp_scene_file.replace('.bam', '_gui.json') + final_lui_info_file = scene_file.replace('.bam', '_lui.json') + temp_lui_info_file = temp_scene_file.replace('.bam', '_lui.json') - for temp_path in (temp_scene_file, temp_gui_info_file): + for temp_path in (temp_scene_file, temp_gui_info_file, temp_lui_info_file): if os.path.exists(temp_path): try: os.remove(temp_path) @@ -316,12 +318,15 @@ class ProjectManager: if os.path.exists(temp_gui_info_file): os.replace(temp_gui_info_file, final_gui_info_file) + if os.path.exists(temp_lui_info_file): + os.replace(temp_lui_info_file, final_lui_info_file) + return True except Exception as e: print(f"原子保存场景失败: {e}") return False finally: - for temp_path in (temp_scene_file, temp_gui_info_file): + for temp_path in (temp_scene_file, temp_gui_info_file, temp_lui_info_file): if os.path.exists(temp_path): try: os.remove(temp_path) diff --git a/scene/scene_manager_io_mixin.py b/scene/scene_manager_io_mixin.py index 4564b0fc..4bad9d8d 100644 --- a/scene/scene_manager_io_mixin.py +++ b/scene/scene_manager_io_mixin.py @@ -62,6 +62,104 @@ class SceneManagerIOMixin: except Exception as e: print(f"清理残留场景节点失败: {e}") + def _get_scene_sidecar_path(self, filename, suffix): + scene_root, _ = os.path.splitext(os.path.normpath(filename)) + return f"{scene_root}{suffix}" + + def _reset_lui_runtime_state(self, lui_manager): + if lui_manager is None or getattr(lui_manager, "lui_enabled", True) is False: + return True + + apply_fn = getattr(lui_manager, "_apply_lui_structure_snapshot", None) + if callable(apply_fn): + try: + apply_fn({}) + return True + except Exception as e: + print(f"清空LUI结构失败: {e}") + return False + + clear_fn = getattr(lui_manager, "_clear_lui_structure_runtime", None) + if callable(clear_fn): + try: + clear_fn() + return True + except Exception as e: + print(f"清空LUI结构失败: {e}") + return False + + return True + + def _save_lui_snapshot_file(self, filename): + lui_manager = getattr(self.world, "lui_manager", None) + if lui_manager is None or getattr(lui_manager, "lui_enabled", True) is False: + print("ℹ️ 当前会话未启用LUI,跳过LUI结构保存") + return True + + capture_fn = getattr(lui_manager, "_capture_lui_structure_snapshot", None) + if not callable(capture_fn): + print("ℹ️ 未找到LUI结构快照接口,跳过LUI结构保存") + return True + + try: + snapshot = capture_fn() or {} + lui_info_file = self._get_scene_sidecar_path(filename, "_lui.json") + with open(lui_info_file, "w", encoding="utf-8") as f: + json.dump(snapshot, f, ensure_ascii=False, indent=4) + + canvas_count = len(snapshot.get("canvases", []) or []) + component_count = len(snapshot.get("components", []) or []) + print( + f"✓ LUI信息已保存: {lui_info_file} " + f"(Canvas: {canvas_count}, Components: {component_count})" + ) + return True + except Exception as e: + print(f"✗ 保存LUI信息失败: {e}") + import traceback + traceback.print_exc() + return False + + def _load_lui_snapshot_file(self, filename): + lui_manager = getattr(self.world, "lui_manager", None) + if lui_manager is None or getattr(lui_manager, "lui_enabled", True) is False: + print("ℹ️ 当前会话未启用LUI,跳过LUI结构恢复") + return True + + lui_info_file = self._get_scene_sidecar_path(filename, "_lui.json") + apply_fn = getattr(lui_manager, "_apply_lui_structure_snapshot", None) + + if not os.path.exists(lui_info_file): + print("ℹ️ 未找到LUI信息文件,清空当前LUI结构") + return self._reset_lui_runtime_state(lui_manager) + + if not callable(apply_fn): + print("ℹ️ 未找到LUI结构恢复接口,跳过LUI结构恢复") + return True + + try: + with open(lui_info_file, "r", encoding="utf-8") as f: + content = f.read().strip() + + snapshot = json.loads(content) if content else {} + apply_fn(snapshot) + + canvas_count = len(snapshot.get("canvases", []) or []) + component_count = len(snapshot.get("components", []) or []) + print( + f"✓ LUI信息已恢复: {lui_info_file} " + f"(Canvas: {canvas_count}, Components: {component_count})" + ) + return True + except json.JSONDecodeError as e: + print(f"✗ LUI信息文件格式错误: {e}") + except Exception as e: + print(f"✗ 加载LUI信息失败: {e}") + import traceback + traceback.print_exc() + + return self._reset_lui_runtime_state(lui_manager) + def _collectGUIElementInfo(self, gui_node): """收集GUI元素的信息用于保存""" try: @@ -594,6 +692,8 @@ class SceneManagerIOMixin: if success: print(f"✓ 场景保存成功: {filename}") + if not self._save_lui_snapshot_file(filename): + return False else: print("✗ 场景保存失败") @@ -1529,6 +1629,8 @@ class SceneManagerIOMixin: else: print("ℹ️ 未找到GUI信息文件") + self._load_lui_snapshot_file(filename) + # 移除临时场景节点 if not scene.isEmpty(): scene.removeNode() diff --git a/ui/LUI/lui_function_properties.py b/ui/LUI/lui_function_properties.py index d4c12d95..9898e27c 100644 --- a/ui/LUI/lui_function_properties.py +++ b/ui/LUI/lui_function_properties.py @@ -324,7 +324,7 @@ class LUIFunctionPropertiesMixin: manager.luiFunction._draw_text_font_size_editor(manager, index, comp_data, comp_obj) draw_color = manager.luiFunction._draw_text_color_editor(manager, index, comp_data) - manager.luiFunction._sync_text_or_button_color(comp_obj, comp_type, draw_color) + manager.luiFunction._sync_text_or_button_color(manager, comp_obj, comp_type, draw_color) if comp_type == 'Button': manager.luiFunction._draw_button_texture_controls(manager, index, comp_data, comp_obj) @@ -387,9 +387,9 @@ class LUIFunctionPropertiesMixin: def _sync_text_or_button_color(manager, comp_obj, comp_type, draw_color): # 保留原行为:每帧同步当前编辑色,避免按钮状态贴图切换后颜色不同步。 if comp_type == 'Button': - manager.luiFunction._apply_button_layout_color(comp_obj, draw_color) + manager.luiFunction._apply_button_layout_color(manager, comp_obj, draw_color) else: - manager.luiFunction._apply_text_color(comp_obj, draw_color) + manager.luiFunction._apply_text_color(manager, comp_obj, draw_color) imgui.separator() imgui.spacing() @@ -436,7 +436,7 @@ class LUIFunctionPropertiesMixin: lambda: manager.luiFunction._clear_button_custom_textures(manager, comp_data, comp_obj), ) - manager.luiFunction._draw_button_texture_path_labels(comp_data) + manager.luiFunction._draw_button_texture_path_labels(manager, comp_data) def _draw_button_texture_pick_button(manager, index, comp_data, comp_obj, button_label, variant): if not imgui.button(button_label, (160, 20)): @@ -1382,7 +1382,7 @@ class LUIFunctionPropertiesMixin: if changed: manager._apply_component_change_with_history( index, - lambda: manager.luiFunction._set_checkbox_checked(comp_data, comp_obj, new_checked), + lambda: manager.luiFunction._set_checkbox_checked(manager, comp_data, comp_obj, new_checked), ) def _set_checkbox_checked(manager, comp_data, comp_obj, new_checked): @@ -1497,7 +1497,7 @@ class LUIFunctionPropertiesMixin: sel_id = opt_ids[new_index] manager._apply_component_change_with_history( index, - lambda: manager.luiFunction._set_selectbox_selected_option(comp_data, comp_obj, sel_id), + lambda: manager.luiFunction._set_selectbox_selected_option(manager, comp_data, comp_obj, sel_id), ) imgui.spacing() @@ -1526,7 +1526,7 @@ class LUIFunctionPropertiesMixin: if dirty: manager._apply_component_change_with_history( index, - lambda: manager.luiFunction._apply_selectbox_options(comp_data, comp_obj, new_labels, current_selected), + lambda: manager.luiFunction._apply_selectbox_options(manager, comp_data, comp_obj, new_labels, current_selected), ) def _set_selectbox_selected_option(manager, comp_data, comp_obj, selected_option_id): @@ -1682,7 +1682,7 @@ class LUIFunctionPropertiesMixin: if imgui.button("清除音频", (90, 24)): manager._apply_component_change_with_history( index, - lambda: manager.luiFunction._clear_audio_for_component(comp_data), + lambda: manager.luiFunction._clear_audio_for_component(manager, comp_data), ) if imgui.button("Select Audio", (110, 24)): @@ -1928,7 +1928,7 @@ class LUIFunctionPropertiesMixin: def _clear_video_source(manager, comp_data, comp_obj=None): if comp_data.get("audio_from_video"): - manager.luiFunction._clear_audio_for_component(comp_data) + manager.luiFunction._clear_audio_for_component(manager, comp_data) manager.luiFunction._clear_video_audio_runtime(manager, comp_data) target_obj = comp_obj or comp_data.get("object") if target_obj is not None: