From b7e4c9618834904cdfd7f60be86528612a363672 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 8 Nov 2025 22:56:45 -0600 Subject: [PATCH] Refine tree widget event handling and hover state management --- core/input_handler.py | 19 +++++++++++-------- ui/hud.py | 13 +++++++++---- ui/inspector_tree.py | 20 +++----------------- ui/tree_widget.py | 13 ++++++++++++- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/core/input_handler.py b/core/input_handler.py index 561fb5f..9ec3978 100644 --- a/core/input_handler.py +++ b/core/input_handler.py @@ -127,14 +127,17 @@ class InputHandler: # Zoom in viewport self.camera.handle_zoom(event.y) elif inspector_rect and inspector_rect.collidepoint(mouse_x, mouse_y) and self.hud.tree_widget: - # Scroll tree widget in inspector - convert to local coordinates if needed - if not hasattr(event, 'pos'): - event.pos = (mouse_x - inspector_rect.x, mouse_y - inspector_rect.y) - else: - local_x = mouse_x - inspector_rect.x - local_y = mouse_y - inspector_rect.y - event.pos = (local_x, local_y) - self.hud.tree_widget.handle_event(event) + # Scroll tree widget in inspector + if not viewport_rect.collidepoint(mouse_x, mouse_y): + # Convert to local coordinates if needed + if not hasattr(event, 'pos'): + event.pos = (mouse_x - inspector_rect.x, mouse_y - inspector_rect.y) + else: + local_x = mouse_x - inspector_rect.x + local_y = mouse_y - inspector_rect.y + event.pos = (local_x, local_y) + print("Passing event to tree widget") + self.hud.tree_widget.handle_event(event) else: # Fallback: always zoom if no HUD reference self.camera.handle_zoom(event.y) diff --git a/ui/hud.py b/ui/hud.py index 2a5ee89..8e212a2 100644 --- a/ui/hud.py +++ b/ui/hud.py @@ -368,14 +368,19 @@ class HUD: if (0 <= tree_local_pos[0] < self.tree_widget.rect.width and 0 <= tree_local_pos[1] < self.tree_widget.rect.height): event.pos = tree_local_pos + # This is the handle_event call that is being run even when the mouse is not over the tree widget if self.tree_widget.handle_event(event): selected_entities = self.tree_widget.get_selected_entities() return 'tree_selection_changed', selected_entities + else: + # Mouse left the tree widget area, clear hover + self.tree_widget.clear_hover() else: - # For non-mouse events, try handling them normally - if self.tree_widget.handle_event(event): - selected_entities = self.tree_widget.get_selected_entities() - return 'tree_selection_changed', selected_entities + # Handle specific mouse events in tree widget (but not wheel - handled by InputHandler) + if event.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP): + if self.tree_widget.handle_event(event): + selected_entities = self.tree_widget.get_selected_entities() + return 'tree_selection_changed', selected_entities # Handle simulation control button events using ID matching if event.type == pygame_gui.UI_BUTTON_START_PRESS: diff --git a/ui/inspector_tree.py b/ui/inspector_tree.py index 3482687..9dd447c 100644 --- a/ui/inspector_tree.py +++ b/ui/inspector_tree.py @@ -23,11 +23,7 @@ class TreeNode(ABC): def add_child(self, child: 'TreeNode') -> None: """Add a child node to this node.""" child.parent = self - old_depth = child.depth child.depth = self.depth + 1 - # Debug: check if depth changed incorrectly - if hasattr(child, 'entity') and isinstance(child, EntityNode) and old_depth != child.depth: - print(f"EntityNode depth changed from {old_depth} to {child.depth} when added to {self.label}") self.children.append(child) def remove_child(self, child: 'TreeNode') -> None: @@ -81,12 +77,7 @@ class TreeNode(ABC): def get_indent(self) -> int: """Get the indentation width for this node.""" - indent = self.depth * 20 - # Debug: print inconsistent depths (only for EntityNodes) - if hasattr(self, 'entity') and isinstance(self, EntityNode): - if self.depth != 2: # EntityNodes should always be depth 2 (Simulation -> EntityType -> Entity) - print(f"Entity {self.entity_id} has inconsistent depth: {self.depth}, indent: {indent}") - return indent + return self.depth * 20 class SimulationNode(TreeNode): @@ -131,9 +122,9 @@ class SimulationNode(TreeNode): else: # Create new type node type_node = EntityTypeNode(type_name, entities) - print(f"SimulationNode: adding {type_name} with current depth {type_node.depth}, my depth is {self.depth}") self.add_child(type_node) - print(f"SimulationNode: after adding, {type_name} has depth {type_node.depth}") + # Now update children after this node has correct depth + type_node._update_children() class EntityTypeNode(TreeNode): @@ -142,8 +133,6 @@ class EntityTypeNode(TreeNode): def __init__(self, entity_type: str, entities: List[Any]): super().__init__(entity_type) self.entities = entities - # Debug: check depth - print(f"EntityTypeNode {entity_type} created with depth {self.depth}") # Don't call _update_children() here - parent will call after adding this node def _update_children(self) -> None: @@ -162,9 +151,6 @@ class EntityTypeNode(TreeNode): # Update existing node entity_node = existing_ids[entity_id] entity_node.entity = entity - # Debug: verify depth is correct for updated nodes - if entity_node.depth != self.depth + 1: - print(f"Updated EntityNode {entity_id} has wrong depth: {entity_node.depth}, should be {self.depth + 1}") else: # Create new entity node entity_node = EntityNode(entity) diff --git a/ui/tree_widget.py b/ui/tree_widget.py index 1334674..f425d17 100644 --- a/ui/tree_widget.py +++ b/ui/tree_widget.py @@ -42,6 +42,7 @@ class TreeWidget(UIElement): self.hovered_node: Optional[TreeNode] = None self.drag_start_node: Optional[TreeNode] = None self.is_dragging = False + self.last_mouse_pos: Optional[Tuple[int, int]] = None # Scrolling self.scroll_offset = 0 @@ -183,6 +184,7 @@ class TreeWidget(UIElement): if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Left click # Event position is already converted to local coordinates by HUD + self.last_mouse_pos = event.pos node = self._get_node_at_position_local(event.pos) if node: # Check for expand/collapse click @@ -197,6 +199,7 @@ class TreeWidget(UIElement): elif event.type == pygame.MOUSEMOTION: # Event position is already converted to local coordinates by HUD + self.last_mouse_pos = event.pos self.hovered_node = self._get_node_at_position_local(event.pos) return True @@ -218,6 +221,9 @@ class TreeWidget(UIElement): if self._should_update_tree(): self._update_tree_structure() self._update_visible_nodes() + # Recalculate hover state based on last mouse position after structure changes + if self.last_mouse_pos: + self.hovered_node = self._get_node_at_position_local(self.last_mouse_pos) def _should_update_tree(self) -> bool: """Determine if the tree structure needs updating.""" @@ -317,4 +323,9 @@ class TreeWidget(UIElement): self.scroll_offset + (node_bottom - self.rect.height) ) - self._update_node_layout() \ No newline at end of file + self._update_node_layout() + + def clear_hover(self) -> None: + """Clear the hover state (called when mouse leaves tree widget).""" + self.hovered_node = None + self.last_mouse_pos = None \ No newline at end of file