"""LUIManager tree/editor/layout mixin."""

import struct
from pathlib import Path

import panda3d.core as p3d
from imgui_bundle import imgui, imgui_ctx


class LUIManagerEditorMixin:
    def _add_to_scene_tree(self, comp_data):
        """Add a virtual node to represent the LUI component in the scene tree"""
        from panda3d.core import NodePath
        node_name = f"UI_{comp_data['type']}_{len(self.components)}"
        ui_node = NodePath(node_name)
        
        # Reparent to current canvas node if available
        if self.current_canvas_index >= 0 and self.current_canvas_index < len(self.canvases):
            canvas_node = self.canvases[self.current_canvas_index]['node']
            ui_node.reparent_to(canvas_node)
            comp_data['canvas_index'] = self.current_canvas_index
        else:
            ui_node.reparent_to(self.world.render)
            comp_data['canvas_index'] = None
        
        comp_data['ui_node'] = ui_node
        comp_data['parent_index'] = None
        comp_data['children_indices'] = []

    def _set_parent_child_relationship(self, child_index, parent_index, anchor_position=None, keep_world=False):
        # Set parent-child relationship
        if (child_index < 0 or child_index >= len(self.components) or
            parent_index < 0 or parent_index >= len(self.components)):
            print("Error: invalid component index")
            return

        child_data = self.components[child_index]
        parent_data = self.components[parent_index]

        if parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout']:
            child_data['draggable'] = False

        if child_data.get('parent_index') == parent_index:
            print(f"Component {child_index} already under {parent_index}")
            return

        current_abs_left, current_abs_top = self._get_component_accumulated_pos(child_index)
        
        print(f"[_set_parent_child_relationship] Child #{child_index} -> Parent #{parent_index}")
        print(f"  Current absolute position: ({current_abs_left:.1f}, {current_abs_top:.1f})")

        old_parent_index = child_data.get('parent_index')
        if old_parent_index is not None and old_parent_index >= 0:
            old_parent_data = self.components[old_parent_index]
            if child_index in old_parent_data.get('children_indices', []):
                old_parent_data['children_indices'].remove(child_index)

        child_data['parent_index'] = parent_index
        if 'children_indices' not in parent_data:
            parent_data['children_indices'] = []
        if child_index not in parent_data['children_indices']:
            parent_data['children_indices'].append(child_index)

        parent_abs_left, parent_abs_top = self._get_component_accumulated_pos(parent_index)
        new_local_left = current_abs_left - parent_abs_left
        new_local_top = current_abs_top - parent_abs_top
        
        print(f"  Parent absolute position: ({parent_abs_left:.1f}, {parent_abs_top:.1f})")
        print(f"  Calculated local position: ({new_local_left:.1f}, {new_local_top:.1f})")

        # 在数据结构中存储局部坐标（相对于父组件）
        child_data['left'] = new_local_left
        child_data['top'] = new_local_top

        child_obj = child_data['object']
        parent_obj = parent_data['object']
        parent_type = parent_data.get('type')
        visual_container_types = [
            'Frame', 'Plane', 'Video', 'HttpText',
            'ScrollableRegion', 'TabbedFrame',
            'VerticalLayout', 'HorizontalLayout'
        ]
        if parent_data.get('type') == 'ScrollableRegion':
            parent_obj = parent_data['object'].content_node
        elif parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout'] and parent_data.get('layout_obj'):
            if parent_data.get('layout_wrap', True):
                parent_obj = parent_data.get('object')
            else:
                parent_obj = parent_data.get('layout_obj').cell()

        try:
            if parent_type in visual_container_types:
                child_data['visual_parent_canvas'] = False
                # 关键修改：不要调用 reparent_to()，避免破坏内部结构
                # 只更新数据结构，保持原有的父对象关系
                print(f"[_set_parent_child_relationship] Setting parent relationship (data only, no reparenting)")
            else:
                # Keep visual parent on canvas to avoid clipping, but keep logical parent
                child_data['visual_parent_canvas'] = True

            # If not preserving world position, clamp into parent bounds for visibility
            if not keep_world:
                try:
                    parent_w = parent_data.get('width')
                    parent_h = parent_data.get('height')
                    if parent_w is None and hasattr(parent_obj, 'width'):
                        parent_w = parent_obj.width
                    if parent_h is None and hasattr(parent_obj, 'height'):
                        parent_h = parent_obj.height
                    child_w = child_data.get('width')
                    child_h = child_data.get('height')
                    if child_w is None and hasattr(child_obj, 'width'):
                        child_w = child_obj.width
                    if child_h is None and hasattr(child_obj, 'height'):
                        child_h = child_obj.height
                    if parent_w and parent_h and child_w and child_h:
                        max_x = max(0.0, float(parent_w) - float(child_w))
                        max_y = max(0.0, float(parent_h) - float(child_h))
                        new_local_left = max(0.0, min(float(new_local_left), max_x))
                        new_local_top = max(0.0, min(float(new_local_top), max_y))
                        child_data['left'] = new_local_left
                        child_data['top'] = new_local_top
                except Exception:
                    pass

            # 关键修改：由于我们不实际重新父化对象，所有组件都保持相对于Canvas的绝对位置
            # 只在数据结构中记录相对于父组件的局部坐标
            if child_data.get('visual_parent_canvas'):
                # Keep world position
                try:
                    child_data['canvas_index'] = parent_data.get('canvas_index', self.current_canvas_index)
                except Exception:
                    pass
            
            # 根据keep_world参数决定是否保持世界位置
            if keep_world:
                # 保持世界位置：使用绝对坐标
                if hasattr(child_obj, 'left'):
                    child_obj.left = current_abs_left
                if hasattr(child_obj, 'top'):
                    child_obj.top = current_abs_top
                print(f"[_set_parent_child_relationship] Child position kept at absolute: ({current_abs_left:.1f}, {current_abs_top:.1f})")
            else:
                # 不保持世界位置：使用局部坐标（但由于没有实际重新父化，这会导致位置跳变）
                # 为了避免跳变，我们仍然使用绝对坐标
                if hasattr(child_obj, 'left'):
                    child_obj.left = current_abs_left
                if hasattr(child_obj, 'top'):
                    child_obj.top = current_abs_top
                print(f"[_set_parent_child_relationship] Child position set to absolute: ({current_abs_left:.1f}, {current_abs_top:.1f})")

            # Ensure child visuals are visible above parent
            if hasattr(child_obj, 'z_offset'):
                try:
                    parent_z = getattr(parent_obj, 'z_offset', 0)
                    child_obj.z_offset = float(parent_z) + 2.0
                except Exception:
                    pass

            # Special handling for Button: raise internal sprites/label above parent
            if child_data.get('type') == 'Button':
                try:
                    layout = getattr(child_obj, '_layout', None)
                    if layout is not None:
                        for attr in ('_sprite_left', '_sprite_mid', '_sprite_right'):
                            spr = getattr(layout, attr, None)
                            if spr is not None and hasattr(spr, 'z_offset'):
                                spr.z_offset = float(getattr(child_obj, 'z_offset', 0)) + 1.0
                    lbl = getattr(child_obj, '_label', None)
                    if lbl is not None and hasattr(lbl, 'z_offset'):
                        lbl.z_offset = float(getattr(child_obj, 'z_offset', 0)) + 2.0
                    # Ensure label visible
                    if lbl is not None and hasattr(lbl, 'show'):
                        lbl.show()
                except Exception:
                    pass
            
            # Special handling for Frame: ensure visibility
            elif child_data.get('type') == 'Frame':
                try:
                    if hasattr(child_obj, 'show'):
                        child_obj.show()
                    if hasattr(child_obj, 'visible'):
                        child_obj.visible = True
                    print(f"[_set_parent_child_relationship] Frame visibility ensured")
                except Exception as e:
                    print(f"[_set_parent_child_relationship] Error ensuring Frame visibility: {e}")
            
            # Special handling for Plane, Image, Video: ensure sprite visibility
            elif child_data.get('type') in ['Plane', 'Image', 'Video', 'HttpText']:
                try:
                    spr = child_data.get('sprite')
                    if spr is not None:
                        if hasattr(spr, 'show'):
                            spr.show()
                        if hasattr(spr, 'visible'):
                            spr.visible = True
                        
                        # Ensure sprite color is not transparent
                        if child_data.get('type') == 'Plane':
                            color = child_data.get('color', (1, 1, 1, 1))
                            if hasattr(spr, 'color'):
                                spr.color = color
                    
                    print(f"[_set_parent_child_relationship] {child_data.get('type')} sprite visibility ensured")
                except Exception as e:
                    print(f"[_set_parent_child_relationship] Error ensuring {child_data.get('type')} visibility: {e}")

            child_sprite = child_data.get('sprite')
            if child_sprite is not None:
                try:
                    if hasattr(child_sprite, 'show'):
                        child_sprite.show()
                    # Ensure sprite is attached to the child object
                    if hasattr(child_sprite, 'parent') and child_sprite.parent != child_obj:
                        child_sprite.parent = child_obj
                    # Restore sprite color if stored
                    if hasattr(child_sprite, 'color') and 'color' in child_data:
                        child_sprite.color = child_data.get('color', child_sprite.color)
                    if hasattr(child_sprite, 'z_offset'):
                        parent_sprite = parent_data.get('sprite')
                        parent_sprite_z = 0.0
                        if parent_sprite is not None and hasattr(parent_sprite, 'z_offset'):
                            parent_sprite_z = float(parent_sprite.z_offset)
                        child_sprite.z_offset = max(parent_sprite_z + 1.0, float(getattr(child_obj, 'z_offset', 0)) + 1.0)
                except Exception:
                    pass
            # Keep child visible above parent visuals
            if hasattr(child_obj, 'z_offset'):
                parent_z = getattr(parent_obj, 'z_offset', 0)
                try:
                    child_obj.z_offset = float(parent_z) + 2.0
                except Exception:
                    pass
            # Ensure child sprite renders above parent visuals
            child_sprite = child_data.get('sprite')
            if child_sprite is not None and hasattr(child_sprite, 'z_offset'):
                parent_sprite_z = 0
                try:
                    parent_sprite = parent_data.get('sprite')
                    if parent_sprite is not None and hasattr(parent_sprite, 'z_offset'):
                        parent_sprite_z = float(parent_sprite.z_offset)
                except Exception:
                    parent_sprite_z = 0
                try:
                    child_sprite.z_offset = max(float(parent_sprite_z) + 1.0, float(getattr(child_obj, 'z_offset', 0)) + 1.0)
                except Exception:
                    pass
            if hasattr(child_obj, 'show'):
                child_obj.show()

        except Exception as e:
            print(f"Reparenting error: {e}")

        # Ensure canvas_index follows parent canvas (visibility)
        parent_canvas = parent_data.get('canvas_index')
        if parent_canvas is not None:
            self._update_canvas_index_recursive(child_index, parent_canvas)

        if parent_data.get('type') in ['HorizontalLayout', 'VerticalLayout'] and parent_data.get('layout_wrap', True):
            self._apply_wrap_layout(parent_index)

        print(f"Set parent: child #{child_index} -> parent #{parent_index}")

    def _set_parent_root(self, child_index, keep_world=False):
        """Detach a component from its parent and move it to root (canvas)."""
        if child_index < 0 or child_index >= len(self.components):
            print("Error: invalid component index")
            return

        child_data = self.components[child_index]
        
        # 保存当前的累积位置（相对于Canvas的绝对位置）
        current_abs_left, current_abs_top = self._get_component_accumulated_pos(child_index)
        
        print(f"[_set_parent_root] Component #{child_index} current accumulated pos: ({current_abs_left:.1f}, {current_abs_top:.1f})")

        old_parent_index = child_data.get('parent_index')
        if old_parent_index is not None and old_parent_index >= 0:
            old_parent_data = self.components[old_parent_index]
            if child_index in old_parent_data.get('children_indices', []):
                old_parent_data['children_indices'].remove(child_index)

        # 更新数据结构
        child_data['parent_index'] = None
        child_data['visual_parent_canvas'] = False
        child_data['draggable'] = True

        # 获取Canvas panel
        parent_obj = None
        canvas_index = self.current_canvas_index
        if canvas_index is not None and canvas_index >= 0 and canvas_index < len(self.canvases):
            parent_obj = self.canvases[canvas_index]['panel']
            child_data['canvas_index'] = canvas_index
        else:
            parent_obj = getattr(self, 'overlay_root', None) or getattr(self.world, 'render', None)

        child_obj = child_data.get('object')
        
        # 关键修改：对于 Button 等复杂组件，不要重新父化！
        # 只更新位置和数据结构
        if child_obj is not None:
            # 更新位置数据
            if keep_world:
                child_data['left'] = current_abs_left
                child_data['top'] = current_abs_top
                
                # 直接设置对象位置，不改变父对象
                if hasattr(child_obj, 'set_pos'):
                    child_obj.set_pos(current_abs_left, current_abs_top)
                else:
                    if hasattr(child_obj, 'left'):
                        child_obj.left = current_abs_left
                    if hasattr(child_obj, 'top'):
                        child_obj.top = current_abs_top
                        
                print(f"[_set_parent_root] Set position to: ({current_abs_left:.1f}, {current_abs_top:.1f}) without reparenting")
            else:
                # 不保持世界位置：使用原有的局部坐标
                if hasattr(child_obj, 'left'):
                    child_obj.left = child_data.get('left', 0)
                if hasattr(child_obj, 'top'):
                    child_obj.top = child_data.get('top', 0)

        # 确保可见性
        if child_obj is not None:
            try:
                if hasattr(child_obj, 'show'):
                    child_obj.show()
                elif hasattr(child_obj, 'visible'):
                    child_obj.visible = True
            except Exception:
                pass

        # 对于 Button，确保内部组件可见
        comp_type = child_data.get('type')
        if comp_type == 'Button' and child_obj is not None:
            try:
                layout = getattr(child_obj, '_layout', None)
                lbl = getattr(child_obj, '_label', None)
                
                if layout is not None:
                    if hasattr(layout, 'show'):
                        layout.show()
                    if hasattr(layout, 'visible'):
                        layout.visible = True
                
                if lbl is not None:
                    if hasattr(lbl, 'show'):
                        lbl.show()
                    if hasattr(lbl, 'visible'):
                        lbl.visible = True
                    
                    # 重新设置文本
                    current_text = child_data.get('text', 'Button')
                    if hasattr(lbl, 'set_text'):
                        lbl.set_text(current_text)
                    elif hasattr(lbl, 'text'):
                        lbl.text = current_text
                
                print(f"[_set_parent_root] Button components visibility ensured")
            except Exception as e:
                print(f"[_set_parent_root] Error ensuring button visibility: {e}")
        
        # 对于 Plane, Image, Video - 确保内部 sprite 可见
        elif comp_type in ['Plane', 'Image', 'Video', 'HttpText'] and child_obj is not None:
            try:
                spr = child_data.get('sprite')
                if spr is not None:
                    if hasattr(spr, 'show'):
                        spr.show()
                    if hasattr(spr, 'visible'):
                        spr.visible = True
                    
                    # 确保 sprite 的颜色不是透明的
                    if comp_type == 'Plane':
                        color = child_data.get('color', (1, 1, 1, 1))
                        if hasattr(spr, 'color'):
                            spr.color = color
                
                print(f"[_set_parent_root] {comp_type} sprite visibility ensured")
            except Exception as e:
                print(f"[_set_parent_root] Error ensuring {comp_type} visibility: {e}")
        
        # 对于 Frame - 直接确保可见
        elif comp_type == 'Frame' and child_obj is not None:
            try:
                if hasattr(child_obj, 'show'):
                    child_obj.show()
                if hasattr(child_obj, 'visible'):
                    child_obj.visible = True
                print(f"[_set_parent_root] Frame visibility ensured")
            except Exception as e:
                print(f"[_set_parent_root] Error ensuring Frame visibility: {e}")

        # Update canvas index recursively
        self._update_canvas_index_recursive(child_index, child_data.get('canvas_index'))
        
        # 调试输出：确认组件状态
        print(f"[_set_parent_root] Component #{child_index} ({child_data.get('name', 'Unknown')}) moved to root (data only)")
        print(f"  - Position: ({child_data.get('left', 0):.1f}, {child_data.get('top', 0):.1f})")
        print(f"  - Size: ({child_data.get('width', 0):.1f}, {child_data.get('height', 0):.1f})")
        print(f"  - Canvas index: {child_data.get('canvas_index')}")
        print(f"  - Visible: {getattr(child_obj, 'visible', 'N/A') if child_obj else 'N/A'}")
        print(f"  - Parent (unchanged): {getattr(child_obj, 'parent', 'N/A') if child_obj else 'N/A'}")

    def create_child_component(self, parent_index, comp_type, anchor_position=None):
        # Create child component under parent
        print("\n=== Create child component ===")
        print(f"Parent index: {parent_index}, Type: {comp_type}")

        if parent_index < 0 or parent_index >= len(self.components):
            print("Error: invalid parent index")
            return None

        parent_data = self.components[parent_index]
        parent_obj = parent_data['object']
        if parent_data.get('type') == 'ScrollableRegion':
            parent_obj = parent_data['object'].content_node
        elif parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout'] and parent_data.get('layout_obj'):
            if parent_data.get('layout_wrap', True):
                parent_obj = parent_data.get('object')
            else:
                parent_obj = parent_data.get('layout_obj').cell()

        parent_width = parent_data.get('width', 100)
        parent_height = parent_data.get('height', 30)

        comp_sizes = {
            'Button': (100, 30),
            'Text': (80, 20),
            'Label': (80, 20),
            'CheckBox': (120, 20),
            'InputField': (200, 24),
            'Slider': (200, 16),
            'Frame': (300, 200),
            'Plane': (100, 100),
            'Image': (100, 100),
            'Video': (320, 240),
            'HttpText': (320, 120),
            'Progressbar': (200, 30),
            'Selectbox': (200, 30),
            'ScrollableRegion': (200, 200),
            'TabbedFrame': (300, 200),
            'VerticalLayout': (300, 200),
            'HorizontalLayout': (300, 200),
        }

        child_w, child_h = comp_sizes.get(comp_type, (80, 20))
        child_x = (parent_width - child_w) / 2
        child_y = (parent_height - child_h) / 2

        if parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout']:
            child_x = 0
            child_y = 0

        child_obj = None
        try:
            if comp_type == 'Button':
                child_obj = self.luiFunction.create_button(self, text="ChildButton", x=child_x, y=child_y, parent=parent_obj)
            elif comp_type in ['Text', 'Label']:
                child_obj = self.luiFunction.create_label(self, text="ChildText", x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'CheckBox':
                child_obj = self.luiFunction.create_checkbox(self, label="ChildCheckbox", x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'InputField':
                child_obj = self.luiFunction.create_input_field(self, text="ChildInput", x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Slider':
                child_obj = self.luiFunction.create_slider(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Frame':
                child_obj = self.luiFunction.create_frame(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Plane':
                child_obj = self.luiFunction.create_plane(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Image':
                child_obj = self.luiFunction.create_image(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Video':
                child_obj = self.luiFunction.create_video(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'HttpText':
                child_obj = self.luiFunction.create_http_text(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Progressbar':
                child_obj = self.luiFunction.create_progressbar(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'Selectbox':
                child_obj = self.luiFunction.create_selectbox(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'ScrollableRegion':
                child_obj = self.luiFunction.create_scrollable_region(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'TabbedFrame':
                child_obj = self.luiFunction.create_tabbed_frame(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'VerticalLayout':
                child_obj = self.luiFunction.create_vertical_layout(self, x=child_x, y=child_y, parent=parent_obj)
            elif comp_type == 'HorizontalLayout':
                child_obj = self.luiFunction.create_horizontal_layout(self, x=child_x, y=child_y, parent=parent_obj)
            else:
                print(f"Error: unsupported component type {comp_type}")
                return None

        except Exception as e:
            print(f"Error: create component failed: {e}")
            import traceback
            traceback.print_exc()
            return None

        if child_obj:
            child_index = -1
            for i, comp in enumerate(self.components):
                if comp.get('object') == child_obj:
                    child_index = i
                    break

            if child_index >= 0:
                child_data = self.components[child_index]
                if parent_data.get('type') in ['HorizontalLayout', 'VerticalLayout'] and parent_data.get('layout_wrap', True):
                    self._apply_wrap_layout(parent_index)

                if parent_data.get('type') in ['VerticalLayout', 'HorizontalLayout']:
                    child_data['draggable'] = False

                child_data['parent_index'] = parent_index
                if 'children_indices' not in parent_data:
                    parent_data['children_indices'] = []
                if child_index not in parent_data['children_indices']:
                    parent_data['children_indices'].append(child_index)

                print(f"Linked child #{child_index} -> parent #{parent_index}")
                return child_index
        else:
            print("Child creation failed")
        return None

    def _get_descendants(self, idx):
        """Return set of all descendant indices for a component."""
        desc = set()
        if idx < 0 or idx >= len(self.components):
            return desc
        comp = self.components[idx]
        for c_idx in comp.get('children_indices', []):
            if c_idx in desc:
                continue
            desc.add(c_idx)
            desc.update(self._get_descendants(c_idx))
        return desc

    def _update_canvas_index_recursive(self, idx, canvas_index):
        """Update canvas_index for component and its descendants."""
        if idx < 0 or idx >= len(self.components):
            return
        comp = self.components[idx]
        comp['canvas_index'] = canvas_index
        for c_idx in comp.get('children_indices', []):
            self._update_canvas_index_recursive(c_idx, canvas_index)

    def _reorder_list(self, items, src, target):
        """Move src before target within items list."""
        if src == target:
            return items
        if src not in items or target not in items:
            return items
        items = list(items)
        items.remove(src)
        insert_at = items.index(target)
        items.insert(insert_at, src)
        return items

    def _sync_canvas_children(self, parent_index):
        """Update positions for children that keep visual parent on canvas."""
        if parent_index < 0 or parent_index >= len(self.components):
            return
        
        # Get parent's absolute position
        parent_abs_left, parent_abs_top = self._get_component_accumulated_pos(parent_index)
        parent_data = self.components[parent_index]
        parent_obj = parent_data.get('object')

        for child_idx in parent_data.get('children_indices', []):
            if child_idx < 0 or child_idx >= len(self.components):
                continue
            child = self.components[child_idx]
            child_obj = child.get('object')
            
            if child_obj is None:
                continue

            # Check actual parenting
            child_parent = getattr(child_obj, 'parent', None)
            is_scene_parented = (child_parent is not None and child_parent == parent_obj)

            if not is_scene_parented:
                # If child is NOT physically parented to the parent object (e.g. it is on Canvas),
                # we MUST manually update its absolute position to follow the parent.
                abs_left = parent_abs_left + float(child.get('left', 0))
                abs_top = parent_abs_top + float(child.get('top', 0))
                
                if hasattr(child_obj, 'left'):
                    child_obj.left = abs_left
                if hasattr(child_obj, 'top'):
                    child_obj.top = abs_top
                if hasattr(child_obj, 'set_pos'):
                    child_obj.set_pos(abs_left, abs_top)
            
            # Recurse for grandchildren
            self._sync_canvas_children(child_idx)

    def draw_editor(self):
        """Draw the LUI Editor Window"""
        if not self.lui_enabled or not self.show_editor:
            return

        imgui.set_next_window_size((350, 600), imgui.Cond_.first_use_ever)
        with imgui_ctx.begin("LUI编辑器", True) as (opened, show):
            self.show_editor = opened
            if not show:
                return

            # Play/Stop
            if imgui.button("Play" if not self.play_mode else "Stop", (120, 25)):
                self.play_mode = not self.play_mode
                if self.play_mode:
                    # Clear editor-only state when entering play mode
                    self.selected_index = -1
                    self.dragging_comp = None
                    self.pending_drag_comp = None
                    self.pending_drag_index = -1
                    self.pending_drag_start_mouse = None
                    self.pending_drag_start_abs = None
                    self.pending_drag_start_parent = None
                    self._hide_resize_handles()
                # Keep LUI input enabled for runtime UI interaction
                self.set_input_enabled(True)
            imgui.same_line()
            imgui.text_colored((0.2, 1.0, 0.2, 1.0) if self.play_mode else (1.0, 0.8, 0.2, 1.0),
                               "Running" if self.play_mode else "Editing")
            imgui.separator()

            # In play mode, show toolbar only
            if self.play_mode:
                return
            
            # Canvas Management
            imgui.text("Canvas 管理")
            imgui.separator()
            
            if imgui.button("创建新Canvas", (150, 25)):
                self.luiFunction.create_canvas(self)
            
            imgui.same_line()
            
            # 显示当前Canvas信息
            if self.current_canvas_index >= 0 and self.current_canvas_index < len(self.canvases):
                current_canvas_name = self.canvases[self.current_canvas_index]['name']
                imgui.text_colored((0.0, 1.0, 0.0, 1.0), f"当前: {current_canvas_name}")
            else:
                imgui.text_colored((1.0, 0.0, 0.0, 1.0), "无Canvas")
            
            if len(self.canvases) > 0:
                canvas_names = [c['name'] for c in self.canvases]
                if self.current_canvas_index >= len(canvas_names):
                    self.current_canvas_index = len(canvas_names) - 1
                
                changed, new_index = imgui.combo("选择Canvas", self.current_canvas_index, canvas_names)
                if changed:
                    self.switch_canvas(new_index)
                
                # Canvas Properties
                if self.current_canvas_index >= 0:
                    canvas = self.canvases[self.current_canvas_index]
                    panel = canvas['panel']
                    imgui.indent()
                    
                    # Visibility
                    is_visible = canvas.get('visible', True)
                    changed, visible = imgui.checkbox("显示Canvas", is_visible)
                    if changed:
                        canvas['visible'] = visible
                        if visible:
                            panel.show()
                        else:
                            panel.hide()
                            
                    
                    # Fill Mode & Margins
                    is_fill_mode = canvas.get('fill_mode', False)
                    imgui.separator()
                    changed, fill_mode = imgui.checkbox("场景填充模式", is_fill_mode)
                    if changed:
                        canvas['fill_mode'] = fill_mode
                        if fill_mode:
                            # 刚切换到填充模式时，更新一次
                            self._update_canvas_geometry(canvas)
                            
                    if fill_mode:
                        imgui.text("场景边距设置 (调整以匹配视口)")
                        margins = canvas.get('margins', {'left':0,'right':0,'top':0,'bottom':0})
                        geometry_changed = False
                        
                        changed, margins['left'] = imgui.drag_float("左边距", margins['left'], 1.0, 0, 1000)
                        if changed: geometry_changed = True
                            
                        changed, margins['right'] = imgui.drag_float("右边距", margins['right'], 1.0, 0, 1000)
                        if changed: geometry_changed = True
                            
                        changed, margins['top'] = imgui.drag_float("顶边距", margins['top'], 1.0, 0, 500)
                        if changed: geometry_changed = True
                            
                        changed, margins['bottom'] = imgui.drag_float("底边距", margins['bottom'], 1.0, 0, 500)
                        if changed: geometry_changed = True
                            
                        if geometry_changed:
                            canvas['margins'] = margins
                            self._update_canvas_geometry(canvas)
                    else:
                        # Manual Position
                        curr_pos_val = panel.get_pos()
                        curr_pos = (curr_pos_val.x, curr_pos_val.y)
                        
                        # 固定开关
                        is_fixed = canvas.get('fixed', False)
                        changed, fixed = imgui.checkbox("锁定位置 (禁止拖动)", is_fixed)
                        if changed:
                            canvas['fixed'] = fixed
                            
                        if not is_fixed:
                            changed, pos = imgui.drag_float2("Canvas位置", curr_pos)
                            if changed:
                                panel.set_pos(pos[0], pos[1])
                            
                        
                        # Manual Size
                        curr_size = (panel.get_width(), panel.get_height())
                        changed, size = imgui.drag_float2("Canvas尺寸", curr_size, 1, 100, 4000)
                        if changed:
                            panel.width = size[0]
                            panel.height = size[1]
                            # 同时更新背景尺寸
                            if 'background' in canvas:
                                bg = canvas['background']
                                bg.width = size[0]
                                bg.height = size[1]
                                
                    # Canvas Color
                    if 'background' in canvas:
                        imgui.separator()
                        bg = canvas['background']
                        # 获取当前颜色 (RGBA)
                        current_color = bg.color
                        # 转换为可编辑的格式 (list)
                        edit_color = [current_color[0], current_color[1], current_color[2], current_color[3]]
                        
                        changed, new_color = imgui.color_edit4("背景颜色/透明度", edit_color)
                        if changed:
                            bg.color = (new_color[0], new_color[1], new_color[2], new_color[3])
                                
                    # Title settings
                    if 'title_label' in canvas:
                        imgui.separator()
                        imgui.text("标题设置")
                        title = canvas['title_label']
                        
                        # Visible
                        is_title_visible = title.visible
                        changed, title_visible = imgui.checkbox("显示标题", is_title_visible)
                        if changed:
                            try:
                                if title_visible:
                                    title.show()
                                else:
                                    title.hide()
                            except:
                                title.visible = title_visible

                        # Text content
                        curr_text = title.text
                        changed, new_text = imgui.input_text("标题文本", curr_text, 128)
                        if changed:
                            title.text = new_text

                        # Position
                        curr_pos_val = title.get_pos()
                        curr_pos = (curr_pos_val.x, curr_pos_val.y)
                        changed, new_pos = imgui.drag_float2("标题位置", curr_pos)
                        if changed:
                            title.set_pos(new_pos[0], new_pos[1])
                            
                    imgui.unindent()
            else:
                imgui.text_colored((1, 1, 0, 1), "请先创建一个Canvas")
            
            imgui.spacing()
            imgui.text("创建组件")
            imgui.separator()
            
            if len(self.canvases) == 0:
                imgui.begin_disabled()
            
            # Component Type Selection
            component_types = ["按钮 (Button)", "文本 (Text)", "复选框 (CheckBox)", "输入框 (InputField)",
                               "滑块 (Slider)", "框架 (Frame)","面板 (Plane)","图片 (Image)","视频 (Video)", "HTTP文本 (HttpText)",
                               "进度条 (Progressbar)", "下拉框 (Selectbox)", "滚动区域 (ScrollableRegion)", "标签页 (TabbedFrame)",
                               "垂直布局组 (VerticalLayout)", "水平布局组 (HorizontalLayout)"]
            
            changed, self._selected_type_index = imgui.combo("组件类型", self._selected_type_index, component_types)
            
            if imgui.button("创建组件", (120, 25)):
                self._create_component_by_type(self._selected_type_index)
            
            if len(self.canvases) == 0:
                imgui.end_disabled()
                
            imgui.spacing()
            imgui.text(f"已创建组件 ({len(self.components)})")
            imgui.separator()
            
            # Component Tree Helper
            # self.draw_component_tree()
            
            imgui.spacing()
            imgui.separator()
            
            # Anchor Popup Handling
            self._handle_anchor_popup()

    def draw_component_tree(self):
        """Draw the component tree (can be used in other windows)"""
        # Track tree item rects for drop hit-testing (allows drop on earlier items)
        self._tree_item_rects = {}

        def _store_item_rect(item_key):
            try:
                rmin = imgui.get_item_rect_min()
                rmax = imgui.get_item_rect_max()
                self._tree_item_rects[item_key] = (rmin, rmax)
            except Exception:
                pass

        def _hit_test_tree_item(mx, my):
            for key, (rmin, rmax) in self._tree_item_rects.items():
                try:
                    min_x = rmin.x
                    min_y = rmin.y
                    max_x = rmax.x
                    max_y = rmax.y
                except Exception:
                    min_x = rmin[0]
                    min_y = rmin[1]
                    max_x = rmax[0]
                    max_y = rmax[1]
                if min_x <= mx <= max_x and min_y <= my <= max_y:
                    return key
            return None

        # Root drop target
        if imgui.selectable("ROOT", self.selected_index < 0):
            # ????????
            pass
        _store_item_rect(-1)

        def render_component_tree(idx):
            if idx >= len(self.components): return True
            comp = self.components[idx]
            comp_type = comp.get('type', 'Unknown')
            
            label = f"{comp.get('name', 'N/A')}"
            
            # Determine flags
            flags = imgui.TreeNodeFlags_.open_on_arrow | imgui.TreeNodeFlags_.open_on_double_click | imgui.TreeNodeFlags_.default_open
            if self.selected_index == idx:
                flags |= imgui.TreeNodeFlags_.selected
            
            # Check for children
            children = list(comp.get('children_indices', []))
            if not children:
                flags |= imgui.TreeNodeFlags_.leaf
            else:
                label += f" ({len(children)})"
            
            # Draw tree node
            is_open = imgui.tree_node_ex(f"##node_{idx}", flags, label)
            _store_item_rect(idx)

            # Custom drag/drop (avoid payload capsule issues)
            if imgui.is_item_active() and imgui.is_mouse_dragging(0):
                self._tree_drag_src = idx
                imgui.begin_tooltip()
                imgui.text(f"Move: {label}")
                imgui.end_tooltip()

            # Handle selection
            if imgui.is_item_clicked():
                self.selected_index = idx
                # Clear 3D scene selection
                if hasattr(self.world, 'selection') and self.world.selection:
                    self.world.selection.selectedNode = None
                    self.world.selection.clearSelectionBox()
                    self.world.selection.clearGizmo()
            
            # Right-click menu
            if imgui.begin_popup_context_item(f"##comp_ctx_{idx}"):
                if imgui.menu_item("删除", "", False)[0]:
                    self.luiFunction.delete_component(self, idx)
                    imgui.end_popup()
                    if is_open: imgui.tree_pop()
                    return False
                
                imgui.separator()
                if imgui.begin_menu("创建子组件"):
                    child_types = ['Button', 'Text', 'CheckBox', 'InputField', 'Slider', 'Frame', 'Plane','Image', 'Video', 'HttpText', 'Progressbar','Selectbox','ScrollableRegion','TabbedFrame', 'VerticalLayout', 'HorizontalLayout']
                    cn_names = ['按钮', '标签', '复选框', '输入框', '滑块', '框架', '面板', '图片', '视频', 'HTTP文本', '进度条','下拉框','滚动区域','标签页', '垂直布局组', '水平布局组']
                    for ct_idx, ct in enumerate(child_types):
                        if imgui.menu_item(f"{cn_names[ct_idx]}", "", False)[0]:
                            self.create_child_component(idx, ct)
                    imgui.end_menu()
                imgui.end_popup()
            
            # Draw children if open
            if is_open:
                for child_idx in children:
                    # After a deletion, the list might have shifted, 
                    # so we check bounds again or just return False to stop this frame's recursion
                    if child_idx < len(self.components):
                        if not render_component_tree(child_idx):
                            # If a child (or sub-child) was deleted, indices shifted. 
                            # Stopping current rendering frame's branch is safest.
                            imgui.tree_pop()
                            return False
                imgui.tree_pop()
            return True

        # Find root components
        roots = []
        for i, comp in enumerate(self.components):
            p_idx = comp.get('parent_index')
            if p_idx is None or p_idx < 0:
                roots.append(i)

        # Maintain root order for sorting
        if not self.root_order:
            self.root_order = list(roots)
        else:
            self.root_order = [r for r in self.root_order if r in roots]
            for r in roots:
                if r not in self.root_order:
                    self.root_order.append(r)

        # Render roots
        for root_idx in self.root_order:
            if not render_component_tree(root_idx):
                break

        # Handle tree drop after full render (allows drop on earlier items)
        if self._tree_drag_src is not None and imgui.is_mouse_released(0):
            src_idx = self._tree_drag_src
            self._tree_drag_src = None
            mx, my = imgui.get_mouse_pos()
            target_idx = _hit_test_tree_item(mx, my)
            if target_idx is None:
                target_idx = -1
            if 0 <= src_idx < len(self.components):
                if target_idx == -1:
                    self._set_parent_root(src_idx, keep_world=True)
                elif target_idx != src_idx:
                    if target_idx in self._get_descendants(src_idx):
                        print("Invalid drop: cannot parent to descendant")
                    else:
                        src_parent = self.components[src_idx].get('parent_index', -1)
                        tgt_parent = self.components[target_idx].get('parent_index', -1)
                        io = imgui.get_io()
                        want_reorder = bool(getattr(io, 'key_shift', False))
                        if want_reorder and src_parent == tgt_parent:
                            if src_parent is None or src_parent < 0:
                                if not self.root_order:
                                    self.root_order = [i for i, c in enumerate(self.components) if c.get('parent_index') is None or c.get('parent_index') < 0]
                                self.root_order = self._reorder_list(self.root_order, src_idx, target_idx)
                            else:
                                parent_data = self.components[src_parent]
                                children = parent_data.get('children_indices', [])
                                parent_data['children_indices'] = self._reorder_list(children, src_idx, target_idx)
                        else:
                            self._set_parent_child_relationship(src_idx, target_idx, keep_world=True)

        # Clear drag state if released outside any item
        if self._tree_drag_src is not None and imgui.is_mouse_released(0):
            self._tree_drag_src = None
            
            imgui.spacing()
            imgui.separator()
            
            # Property Editing
            if self.selected_index >= 0 and self.selected_index < len(self.components):
                imgui.text("属性编辑")
                imgui.separator()
                self.luiFunction._draw_component_properties(self,self.selected_index)
            else:
                imgui.text_disabled("未选中任何组件")
            
            # Anchor popup
            if self.anchor_popup_open:
                imgui.open_popup("选择锚点位置")
            
            if imgui.begin_popup("选择锚点位置"):
                imgui.text("选择锚点位置:")
                
                # 中文显示名称
                anchor_names = {
                    'top-left': '左上', 'top-center': '中上', 'top-right': '右上',
                    'middle-left': '左中', 'center': '中心', 'middle-right': '右中',
                    'bottom-left': '左下', 'bottom-center': '中下', 'bottom-right': '右下'
                }
                
                anchor_positions = [
                    ['top-left', 'top-center', 'top-right'],
                    ['middle-left', 'center', 'middle-right'],
                    ['bottom-left', 'bottom-center', 'bottom-right']
                ]
                
                for row in anchor_positions:
                    for i, pos in enumerate(row):
                        button_text = anchor_names.get(pos, pos)
                        if imgui.button(f"{button_text}##popup_{pos}", (50, 25)):
                            # 使用新的锚点更新方法
                            if hasattr(self, '_temp_selected_index_for_anchor'):
                                selected_idx = self._temp_selected_index_for_anchor
                                self._update_component_anchor_position(selected_idx, pos)
                            
                            self.anchor_popup_open = False
                            if hasattr(self, '_temp_selected_index_for_anchor'):
                                delattr(self, '_temp_selected_index_for_anchor')
                            imgui.close_current_popup()
                        if i < len(row) - 1:
                            imgui.same_line()
                
                imgui.spacing()
                if imgui.button("取消"):
                    self.anchor_popup_open = False
                    if hasattr(self, '_temp_selected_index_for_anchor'):
                        delattr(self, '_temp_selected_index_for_anchor')
                    imgui.close_current_popup()
                
                imgui.end_popup()

    def _create_component_by_type(self, type_index):
        """Create LUI component by index - 默认生成在Canvas中心"""
        
        # 1. 确定生成位置 (默认 Canvas 中心)
        canvas_relative_x = 100
        canvas_relative_y = 100
        
        if self.current_canvas_index >= 0 and self.current_canvas_index < len(self.canvases):
            canvas_panel = self.canvases[self.current_canvas_index]['panel']
            canvas_width = canvas_panel.get_width()
            canvas_height = canvas_panel.get_height()
            
            # 根据组件类型预测尺寸，以便进行中心点对齐
            # 注意：这些是 lui_function.py 中定义的默认值
            comp_sizes = {
                0: (100, 30),  # Button
                1: (80, 20),   # Text
                2: (120, 20),  # Checkbox
                3: (200, 24),  # InputField
                4: (200, 16),  # Slider
                5: (300, 200), # Frame
                6: (100, 100), # Plane
                7: (100, 100), # Image
                8: (320, 240),  # Video
                9: (320, 120),  # HttpText
                10: (200, 30),  # Progressbar
                11: (200, 30),  # Selectbox
                12: (200, 200), # ScrollableRegion
                13: (300, 200), # TabbedFrame
                14: (300, 200), # VerticalLayout
                15: (300, 200)  # HorizontalLayout
            }

            
            w, h = comp_sizes.get(type_index, (100, 100))
            
            # 计算中心位置
            canvas_relative_x = (canvas_width - w) / 2
            canvas_relative_y = (canvas_height - h) / 2
            
            # 确保不超出合理范围 (防止 Canvas 太小时位置为负)
            canvas_relative_x = max(10, canvas_relative_x)
            canvas_relative_y = max(10, canvas_relative_y)
        
        try:
            if type_index == 0:  # Button
                self.luiFunction.create_button(self, text="New Button", x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 1:  # Text
                self.luiFunction.create_label(self, text="New Text", x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 2:  # Checkbox
                self.luiFunction.create_checkbox(self, label="Check box", x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 3:  # InputField
                self.luiFunction.create_input_field(self, text="", x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 4:  # Slider
                self.luiFunction.create_slider(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 5:  # Frame
                self.luiFunction.create_frame(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 6:  # Plane
                self.luiFunction.create_plane(self, x=canvas_relative_x, y=canvas_relative_y, color=(0.8, 0.8, 0.8, 1))
            elif type_index == 7:  # Image
                self.luiFunction.create_image(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 8:  # Video
                self.luiFunction.create_video(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 9:  # HttpText
                self.luiFunction.create_http_text(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 10:  # Progressbar
                self.luiFunction.create_progressbar(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 11: # Selectbox
                self.luiFunction.create_selectbox(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 12: # ScrollableRegion
                self.luiFunction.create_scrollable_region(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 13: # TabbedFrame
                self.luiFunction.create_tabbed_frame(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 14: # VerticalLayout
                self.luiFunction.create_vertical_layout(self, x=canvas_relative_x, y=canvas_relative_y)
            elif type_index == 15: # HorizontalLayout
                self.luiFunction.create_horizontal_layout(self, x=canvas_relative_x, y=canvas_relative_y)
            
            print(f"✓ 创建组件成功，已重定向至 Canvas 中心: ({canvas_relative_x:.1f}, {canvas_relative_y:.1f})")
            if hasattr(self.world, 'updateSceneTree'):
                self.world.updateSceneTree()
        except Exception as e:
            print(f"创建组件失败: {str(e)}")

    def set_component_anchor(self, comp_index, anchor_position):
        """设置组件锚点 - 保持向后兼容"""
        self._update_component_anchor_position(comp_index, anchor_position)

    def _update_anchored_children(self, parent_index):
        """Recursively update anchored/fill children."""
        if parent_index < 0 or parent_index >= len(self.components):
            return

        print(f"[_update_anchored_children] Updating children of #{parent_index}")
        parent_data = self.components[parent_index]
        children = parent_data.get('children_indices', [])
        for child_idx in children:
            if child_idx < len(self.components):
                child_data = self.components[child_idx]
                # Fill layout has priority over anchored positioning
                if child_data.get('layout_mode') == 'fill':
                    print(f"  -> Child #{child_idx} is FILL mode")
                    self._apply_fill_layout(child_idx)
                elif child_data.get('anchored_to_parent'):
                    anchor_pos = child_data.get('anchor_position')
                    manual_offset = (
                        child_data.get('anchor_manual_offset_x', 0.0),
                        child_data.get('anchor_manual_offset_y', 0.0)
                    )
                    if anchor_pos:
                        self._update_component_anchor_position(child_idx, anchor_pos, manual_offset)

                # Recurse into children
                self._update_anchored_children(child_idx)

    def _apply_fill_layout(self, comp_index):
        """Apply fill layout to component: match parent/canvas size with margins."""
        if comp_index < 0 or comp_index >= len(self.components):
            return

        comp_data = self.components[comp_index]
        comp_obj = comp_data.get('object')
        comp_type = comp_data.get('type', '')

        parent_width = None
        parent_height = None

        parent_index = comp_data.get('parent_index')
        if parent_index is not None and parent_index >= 0 and parent_index < len(self.components):
            parent_data = self.components[parent_index]
            parent_width = parent_data.get('width')
            parent_height = parent_data.get('height')
            parent_obj = parent_data.get('object')
            if parent_width is None and parent_obj is not None and hasattr(parent_obj, 'width'):
                parent_width = parent_obj.width
            if parent_height is None and parent_obj is not None and hasattr(parent_obj, 'height'):
                parent_height = parent_obj.height
        else:
            canvas_index = comp_data.get('canvas_index', self.current_canvas_index)
            if canvas_index is not None and canvas_index >= 0 and canvas_index < len(self.canvases):
                canvas_panel = self.canvases[canvas_index].get('panel')
                if canvas_panel is not None:
                    if hasattr(canvas_panel, 'width'):
                        parent_width = canvas_panel.width
                    elif hasattr(canvas_panel, 'get_width'):
                        parent_width = canvas_panel.get_width()
                    if hasattr(canvas_panel, 'height'):
                        parent_height = canvas_panel.height
                    elif hasattr(canvas_panel, 'get_height'):
                        parent_height = canvas_panel.get_height()

        # DEBUG: Trace fill layout calculations
        # print(f"[_apply_fill_layout] Comp #{comp_index} Parent #{parent_index}: PW={parent_width} PH={parent_height}")
        
        if parent_width is None or parent_height is None:
            # print(f"[_apply_fill_layout] Failed to get parent dimensions for Comp #{comp_index}")
            return

        margin_left = float(comp_data.get('fill_margin_left', 0.0))
        margin_right = float(comp_data.get('fill_margin_right', 0.0))
        margin_top = float(comp_data.get('fill_margin_top', 0.0))
        margin_bottom = float(comp_data.get('fill_margin_bottom', 0.0))

        new_left = margin_left
        new_top = margin_top
        new_width = float(parent_width) - (margin_left + margin_right)
        new_height = float(parent_height) - (margin_top + margin_bottom)

        if new_width < 1.0:
            new_width = 1.0
        if new_height < 1.0:
            new_height = 1.0

        comp_data['left'] = new_left
        comp_data['top'] = new_top
        comp_data['width'] = new_width
        comp_data['height'] = new_height

        if comp_obj is not None:
            # Handle visual_parent_canvas case: convert local to absolute
            abs_left = new_left
            abs_top = new_top
            
            if parent_index is not None and parent_index >= 0 and comp_data.get('visual_parent_canvas'):
                 p_abs_x, p_abs_y = self._get_component_accumulated_pos(parent_index)
                 abs_left = p_abs_x + new_left
                 abs_top = p_abs_y + new_top
            
            if hasattr(comp_obj, 'set_pos'):
                 comp_obj.set_pos(abs_left, abs_top)
            else:
                 if hasattr(comp_obj, 'left'):
                     comp_obj.left = abs_left
                 if hasattr(comp_obj, 'top'):
                     comp_obj.top = abs_top
            if hasattr(comp_obj, 'width'):
                comp_obj.width = new_width
            if hasattr(comp_obj, 'height'):
                comp_obj.height = new_height

        if comp_type in ['Plane', 'Image', 'Video', 'HttpText'] and 'sprite' in comp_data:
            comp_data['sprite'].width = new_width
            comp_data['sprite'].height = new_height
            if comp_type == 'HttpText':
                self.luiFunction.sync_http_text_layout(self, comp_data)
        elif comp_type == 'Button':
            if hasattr(comp_obj, 'set_width'):
                comp_obj.set_width(new_width)
            if hasattr(comp_obj, 'set_height'):
                comp_obj.set_height(new_height)
            
            # Force update internal layout for Button to ensure fill works
            try:
                layout = getattr(comp_obj, '_layout', None)
                if layout:
                    if hasattr(layout, 'width'): layout.width = new_width 
                    if hasattr(layout, 'height'): layout.height = new_height
                    
                    # Inner layout
                    inner = getattr(layout, '_layout', None)
                    if inner:
                        if hasattr(inner, 'width'): inner.width = new_width 
                        if hasattr(inner, 'height'): inner.height = new_height
                        
                    # Mid sprite usually stretches
                    spr_mid = getattr(layout, '_sprite_mid', None)
                    if spr_mid:
                        spr_mid.width = new_width
                        spr_mid.height = new_height
            except Exception:
                pass
        elif comp_type == 'InputField':
            if hasattr(comp_obj, 'set_width'):
                comp_obj.set_width(new_width)
        elif comp_type == 'Slider':
            if hasattr(comp_obj, 'set_width'):
                comp_obj.set_width(new_width)

    def _apply_layout_spacing(self, layout_obj, spacing):
        try:
            if hasattr(layout_obj, 'set_spacing'):
                layout_obj.set_spacing(spacing)
            elif hasattr(layout_obj, 'setSpacing'):
                layout_obj.setSpacing(spacing)
            elif hasattr(layout_obj, 'spacing'):
                layout_obj.spacing = spacing
            elif hasattr(layout_obj, '_spacing'):
                layout_obj._spacing = spacing
        except Exception:
            pass

    def _apply_layout_alignment(self, layout_obj, align):
        try:
            if hasattr(layout_obj, 'set_alignment'):
                layout_obj.set_alignment(align)
            elif hasattr(layout_obj, 'alignment'):
                layout_obj.alignment = align
            elif hasattr(layout_obj, 'align'):
                layout_obj.align = align
        except Exception:
            pass

    def _update_layout_inner(self, comp_index):
        if comp_index < 0 or comp_index >= len(self.components):
            return

        comp_data = self.components[comp_index]
        layout_obj = comp_data.get('layout_obj')
        if layout_obj is None:
            return

        width = float(comp_data.get('width', 0.0))
        height = float(comp_data.get('height', 0.0))

        pad_left = float(comp_data.get('layout_padding_left', 0.0))
        pad_right = float(comp_data.get('layout_padding_right', 0.0))
        pad_top = float(comp_data.get('layout_padding_top', 0.0))
        pad_bottom = float(comp_data.get('layout_padding_bottom', 0.0))

        inner_w = max(1.0, width - (pad_left + pad_right))
        inner_h = max(1.0, height - (pad_top + pad_bottom))

        try:
            if hasattr(layout_obj, 'left'):
                layout_obj.left = pad_left
            if hasattr(layout_obj, 'top'):
                layout_obj.top = pad_top
            if hasattr(layout_obj, 'width'):
                layout_obj.width = inner_w
            if hasattr(layout_obj, 'height'):
                layout_obj.height = inner_h
            if hasattr(layout_obj, 'set_size'):
                layout_obj.set_size(inner_w, inner_h)
        except Exception:
            pass

        spacing = float(comp_data.get('layout_spacing', 0.0))
        self._apply_layout_spacing(layout_obj, spacing)

        align = comp_data.get('layout_align', None)
        if align:
            self._apply_layout_alignment(layout_obj, align)


        if comp_data.get('type') in ['HorizontalLayout', 'VerticalLayout']:
            self._apply_wrap_layout(comp_index)

        try:
            if hasattr(layout_obj, 'update'):
                layout_obj.update()
        except Exception:
            pass

    def _reparent_scrollable_children(self, parent_index):
        if parent_index < 0 or parent_index >= len(self.components):
            return
        parent_data = self.components[parent_index]
        if parent_data.get('type') != 'ScrollableRegion':
            return
        parent_obj = parent_data['object']
        try:
            content_node = parent_obj.content_node
        except Exception:
            return

        for child_idx in parent_data.get('children_indices', []):
            if child_idx < 0 or child_idx >= len(self.components):
                continue
            child_data = self.components[child_idx]
            child_obj = child_data.get('object')
            if child_obj is None:
                continue
            try:
                if hasattr(child_obj, 'reparent_to'):
                    child_obj.reparent_to(content_node)
                elif hasattr(child_obj, 'parent'):
                    child_obj.parent = content_node
            except Exception:
                pass

    def _apply_wrap_layout(self, parent_index):
        if parent_index < 0 or parent_index >= len(self.components):
            return

        parent_data = self.components[parent_index]
        layout_type = parent_data.get('type')
        if layout_type not in ['HorizontalLayout', 'VerticalLayout']:
            return
        if not parent_data.get('layout_wrap', True):
            return

        # Get parent absolute position for coordinate conversion
        parent_abs_left, parent_abs_top = self._get_component_accumulated_pos(parent_index)

        width = float(parent_data.get('width', 0.0))
        height = float(parent_data.get('height', 0.0))

        pad_left = float(parent_data.get('layout_padding_left', 0.0))
        pad_right = float(parent_data.get('layout_padding_right', 0.0))
        pad_top = float(parent_data.get('layout_padding_top', 0.0))
        pad_bottom = float(parent_data.get('layout_padding_bottom', 0.0))
        spacing = float(parent_data.get('layout_spacing', 0.0))
        line_spacing = float(parent_data.get('layout_line_spacing', 0.0))

        inner_w = max(1.0, width - (pad_left + pad_right))
        inner_h = max(1.0, height - (pad_top + pad_bottom))

        if layout_type == 'HorizontalLayout':
            x = pad_left
            y = pad_top
            row_h = 0.0

            for child_idx in parent_data.get('children_indices', []):
                if child_idx < 0 or child_idx >= len(self.components):
                    continue
                child = self.components[child_idx]
                child_obj = child.get('object')

                cw = float(child.get('width', 0.0))
                ch = float(child.get('height', 0.0))
                # Allow layout even if size is 0, but usually skip
                # if cw <= 0 or ch <= 0: continue 

                if x > pad_left and (x + cw) > (pad_left + inner_w):
                    x = pad_left
                    y += row_h + line_spacing
                    row_h = 0.0

                # Data stores relative position (to parent)
                child['left'] = x
                child['top'] = y
                
                # Update visual object
                try:
                    child_parent = getattr(child_obj, 'parent', None)
                    parent_obj = parent_data.get('object')
                    
                    if child_parent is not None and child_parent == parent_obj:
                         # Child is parented to layout object: use relative coordinates
                         if hasattr(child_obj, 'left'):
                             child_obj.left = x
                         if hasattr(child_obj, 'top'):
                             child_obj.top = y
                         if hasattr(child_obj, 'set_pos'):
                             child_obj.set_pos(x, y)
                    else:
                        # Child is NOT parented to layout object (e.g. at root): use absolute coordinates
                        abs_x = parent_abs_left + x
                        abs_y = parent_abs_top + y
                        
                        if hasattr(child_obj, 'left'):
                            child_obj.left = abs_x
                        if hasattr(child_obj, 'top'):
                            child_obj.top = abs_y
                        if hasattr(child_obj, 'set_pos'):
                            child_obj.set_pos(abs_x, abs_y)
                except Exception:
                    pass
                
                # Recursively update child layout/visuals
                if child.get('type') in ['HorizontalLayout', 'VerticalLayout'] and child.get('layout_wrap', True):
                    self._apply_wrap_layout(child_idx)
                elif child.get('children_indices'):
                    self._sync_canvas_children(child_idx)

                row_h = max(row_h, ch)
                x += cw + spacing
        else:
            # Vertical wrap: flow top->bottom, then next column
            x = pad_left
            y = pad_top
            col_w = 0.0

            for child_idx in parent_data.get('children_indices', []):
                if child_idx < 0 or child_idx >= len(self.components):
                    continue
                child = self.components[child_idx]
                child_obj = child.get('object')

                cw = float(child.get('width', 0.0))
                ch = float(child.get('height', 0.0))
                
                if y > pad_top and (y + ch) > (pad_top + inner_h):
                    y = pad_top
                    x += col_w + line_spacing
                    col_w = 0.0

                child['left'] = x
                child['top'] = y
                
                try:
                    child_parent = getattr(child_obj, 'parent', None)
                    parent_obj = parent_data.get('object')
                    
                    if child_parent is not None and child_parent == parent_obj:
                         # Child is parented to layout object: use relative coordinates
                         if hasattr(child_obj, 'left'):
                             child_obj.left = x
                         if hasattr(child_obj, 'top'):
                             child_obj.top = y
                         if hasattr(child_obj, 'set_pos'):
                             child_obj.set_pos(x, y)
                    else:
                        # Child is NOT parented to layout object (e.g. at root): use absolute coordinates
                        abs_x = parent_abs_left + x
                        abs_y = parent_abs_top + y
                        
                        if hasattr(child_obj, 'left'):
                            child_obj.left = abs_x
                        if hasattr(child_obj, 'top'):
                            child_obj.top = abs_y
                        if hasattr(child_obj, 'set_pos'):
                            child_obj.set_pos(abs_x, abs_y)
                except Exception:
                    pass
                
                # If child is also a layout, recursively update it
                if child.get('type') in ['HorizontalLayout', 'VerticalLayout'] and child.get('layout_wrap', True):
                    self._apply_wrap_layout(child_idx)
                # If child is NOT a layout but has children (like a Frame with children), update their visual text
                elif child.get('children_indices'):
                    self._sync_canvas_children(child_idx)

                col_w = max(col_w, cw)
                y += ch + spacing

    def get_selected_component(self):
        """获取当前选中的组件"""
        if self.selected_index >= 0 and self.selected_index < len(self.components):
            return self.components[self.selected_index]
        return None

    def select_component(self, index):
        """选中指定索引的组件"""
        if 0 <= index < len(self.components):
            self.selected_index = index
            print(f"✓ 选中组件 #{index}: {self.components[index]['type']}")
        else:
            self.selected_index = -1

    def deselect_all(self):
        """取消选中所有组件"""
        if getattr(self, '_force_keep_selection_frames', 0) > 0:
            return
        self.selected_index = -1
        print("✓ 已取消选中所有组件")

    def _change_image_texture(self, title = "选择文件",filetypes=None,initialdir=None):
        """更改图像组件的纹理"""
        try:
            import tkinter as tk
            from tkinter import filedialog

            root = tk.Tk()
            root.withdraw()
            root.attributes('-topmost',True)

            if filetypes is None:
                filetypes = [("所有文件","*.*")]

            file_path = filedialog.askopenfilename(
                title = title,
                filetypes = filetypes,
                initialdir = initialdir
            )
            root.destroy()

            if file_path:
                return file_path
            return None
            
        except Exception as e:
            print(f"更改纹理失败: {e}")

    def _update_component_anchor_position(self, comp_index, anchor_position, manual_offset=(0.0, 0.0)):
        """更新组件的锚点位置"""
        if comp_index < 0 or comp_index >= len(self.components):
            return
        

        
        comp_data = self.components[comp_index]
        comp_obj = comp_data['object']
        
        # Update data
        comp_data['anchor_position'] = anchor_position
        comp_data['anchored_to_parent'] = True
        comp_data['anchor_manual_offset_x'] = manual_offset[0]
        comp_data['anchor_manual_offset_y'] = manual_offset[1]
        
        # Determine strict parent reference
        parent_index = comp_data.get('parent_index')
        parent_width = 0
        parent_height = 0
        
        if parent_index is not None and parent_index >= 0:
             parent_data = self.components[parent_index]
             parent_width = parent_data.get('width', 100)
             parent_height = parent_data.get('height', 30)
             # Try to get actual object size if available
             p_obj = parent_data.get('object')
             if p_obj:
                 if hasattr(p_obj, 'width') and p_obj.width > 0: parent_width = p_obj.width
                 if hasattr(p_obj, 'height') and p_obj.height > 0: parent_height = p_obj.height
        elif self.current_canvas_index >= 0:
             # Canvas anchor
             canvas = self.canvases[self.current_canvas_index]['panel']
             if hasattr(canvas, 'get_width'):
                 parent_width = canvas.get_width()
                 parent_height = canvas.get_height()
             else:
                 parent_width = canvas.width
                 parent_height = canvas.height
             
        # Calculate anchor point in parent local space
        anchor_ratios = {
            'top-left': (0, 0), 'top-center': (0.5, 0), 'top-right': (1, 0),
            'middle-left': (0, 0.5), 'center': (0.5, 0.5), 'middle-right': (1, 0.5),
            'bottom-left': (0, 1), 'bottom-center': (0.5, 1), 'bottom-right': (1, 1)
        }
        rx, ry = anchor_ratios.get(anchor_position, (0, 0))
        
        anchor_x = parent_width * rx
        anchor_y = parent_height * ry
        
        child_width = comp_data.get('width', 0)
        child_height = comp_data.get('height', 0)
        # Try to get actual object size
        if hasattr(comp_obj, 'width') and comp_obj.width > 0: child_width = comp_obj.width
        if hasattr(comp_obj, 'height') and comp_obj.height > 0: child_height = comp_obj.height
        
        # Center child on anchor (pivot logic)
        child_left = anchor_x - (child_width * rx) + manual_offset[0]
        child_top = anchor_y - (child_height * ry) + manual_offset[1]
        
        # Update logical position data
        comp_data['left'] = child_left
        comp_data['top'] = child_top
        
        # Update visual position
        if parent_index is not None and parent_index >= 0:
             # Check if visually parented to canvas (absolute positioning needed)
             if comp_data.get('visual_parent_canvas'):
                 p_abs_x, p_abs_y = self._get_component_accumulated_pos(parent_index)
                 abs_left = p_abs_x + child_left
                 abs_top = p_abs_y + child_top
                 
                 # Prefer left/top properties for LUI objects
                 if hasattr(comp_obj, 'left') and hasattr(comp_obj, 'top'):
                     comp_obj.left = abs_left
                     comp_obj.top = abs_top
                 elif hasattr(comp_obj, 'set_pos'):
                     try:
                         comp_obj.set_pos(abs_left, abs_top)
                     except TypeError:
                         # Fallback for NodePath-like objects requiring 3 args
                         comp_obj.set_pos(abs_left, abs_top, 0)
             else:
                 # Standard local parenting
                 if hasattr(comp_obj, 'left') and hasattr(comp_obj, 'top'):
                     comp_obj.left = child_left
                     comp_obj.top = child_top
                 elif hasattr(comp_obj, 'set_pos'):
                     try:
                         comp_obj.set_pos(child_left, child_top)
                     except TypeError:
                         comp_obj.set_pos(child_left, child_top, 0)
        else:
             # Canvas root
             if hasattr(comp_obj, 'left') and hasattr(comp_obj, 'top'):
                 comp_obj.left = child_left
                 comp_obj.top = child_top
             elif hasattr(comp_obj, 'set_pos'):
                 try:
                     comp_obj.set_pos(child_left, child_top)
                 except TypeError:
                     comp_obj.set_pos(child_left, child_top, 0)
                     
        print(f"✓ 已更新锚点位置: {anchor_position} -> ({child_left:.1f}, {child_top:.1f})")
        
        # Update children positions (for visual_parent_canvas or anchored children)
        self._sync_canvas_children(comp_index)
        self._update_anchored_children(comp_index)

    def _handle_anchor_popup(self):
        """Handle component anchor selection popup"""
        if imgui.begin_popup("选择锚点位置"):
            imgui.text("选择锚点位置:")
            
            anchor_names = {
                'top-left': '左上', 'top-center': '中上', 'top-right': '右上',
                'middle-left': '左中', 'center': '中心', 'middle-right': '右中',
                'bottom-left': '左下', 'bottom-center': '中下', 'bottom-right': '右下'
            }
            
            anchor_positions = [
                ['top-left', 'top-center', 'top-right'],
                ['middle-left', 'center', 'middle-right'],
                ['bottom-left', 'bottom-center', 'bottom-right']
            ]
            
            for row in anchor_positions:
                for i, pos in enumerate(row):
                    button_text = anchor_names.get(pos, pos)
                    if imgui.button(f"{button_text}##popup_{pos}", (50, 25)):
                        if hasattr(self, '_temp_selected_index_for_anchor'):
                            selected_idx = self._temp_selected_index_for_anchor
                            # Reset manual offset by passing (0,0) explicitly to match original behavior logic
                            self._update_component_anchor_position(selected_idx, pos, manual_offset=(0.0, 0.0))
                            
                            # Clean up temp attribute
                            delattr(self, '_temp_selected_index_for_anchor')
                        
                        # Reset flag if used elsewhere
                        self.anchor_popup_open = False
                        imgui.close_current_popup()
                        
                    if i < len(row) - 1:
                        imgui.same_line()
            
            imgui.spacing()
            if imgui.button("取消", (160, 25)):
                self.anchor_popup_open = False
                if hasattr(self, '_temp_selected_index_for_anchor'):
                    delattr(self, '_temp_selected_index_for_anchor')
                imgui.close_current_popup()
            
            imgui.end_popup()
