Refine tree widget event handling and hover state management

This commit is contained in:
Sam 2025-11-08 22:56:45 -06:00
parent 7af12f7035
commit b7e4c96188
4 changed files with 35 additions and 30 deletions

View File

@ -127,14 +127,17 @@ class InputHandler:
# Zoom in viewport # Zoom in viewport
self.camera.handle_zoom(event.y) self.camera.handle_zoom(event.y)
elif inspector_rect and inspector_rect.collidepoint(mouse_x, mouse_y) and self.hud.tree_widget: 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 # Scroll tree widget in inspector
if not hasattr(event, 'pos'): if not viewport_rect.collidepoint(mouse_x, mouse_y):
event.pos = (mouse_x - inspector_rect.x, mouse_y - inspector_rect.y) # Convert to local coordinates if needed
else: if not hasattr(event, 'pos'):
local_x = mouse_x - inspector_rect.x event.pos = (mouse_x - inspector_rect.x, mouse_y - inspector_rect.y)
local_y = mouse_y - inspector_rect.y else:
event.pos = (local_x, local_y) local_x = mouse_x - inspector_rect.x
self.hud.tree_widget.handle_event(event) 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: else:
# Fallback: always zoom if no HUD reference # Fallback: always zoom if no HUD reference
self.camera.handle_zoom(event.y) self.camera.handle_zoom(event.y)

View File

@ -368,14 +368,19 @@ class HUD:
if (0 <= tree_local_pos[0] < self.tree_widget.rect.width and if (0 <= tree_local_pos[0] < self.tree_widget.rect.width and
0 <= tree_local_pos[1] < self.tree_widget.rect.height): 0 <= tree_local_pos[1] < self.tree_widget.rect.height):
event.pos = tree_local_pos 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): if self.tree_widget.handle_event(event):
selected_entities = self.tree_widget.get_selected_entities() selected_entities = self.tree_widget.get_selected_entities()
return 'tree_selection_changed', selected_entities return 'tree_selection_changed', selected_entities
else:
# Mouse left the tree widget area, clear hover
self.tree_widget.clear_hover()
else: else:
# For non-mouse events, try handling them normally # Handle specific mouse events in tree widget (but not wheel - handled by InputHandler)
if self.tree_widget.handle_event(event): if event.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP):
selected_entities = self.tree_widget.get_selected_entities() if self.tree_widget.handle_event(event):
return 'tree_selection_changed', selected_entities selected_entities = self.tree_widget.get_selected_entities()
return 'tree_selection_changed', selected_entities
# Handle simulation control button events using ID matching # Handle simulation control button events using ID matching
if event.type == pygame_gui.UI_BUTTON_START_PRESS: if event.type == pygame_gui.UI_BUTTON_START_PRESS:

View File

@ -23,11 +23,7 @@ class TreeNode(ABC):
def add_child(self, child: 'TreeNode') -> None: def add_child(self, child: 'TreeNode') -> None:
"""Add a child node to this node.""" """Add a child node to this node."""
child.parent = self child.parent = self
old_depth = child.depth
child.depth = self.depth + 1 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) self.children.append(child)
def remove_child(self, child: 'TreeNode') -> None: def remove_child(self, child: 'TreeNode') -> None:
@ -81,12 +77,7 @@ class TreeNode(ABC):
def get_indent(self) -> int: def get_indent(self) -> int:
"""Get the indentation width for this node.""" """Get the indentation width for this node."""
indent = self.depth * 20 return 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
class SimulationNode(TreeNode): class SimulationNode(TreeNode):
@ -131,9 +122,9 @@ class SimulationNode(TreeNode):
else: else:
# Create new type node # Create new type node
type_node = EntityTypeNode(type_name, entities) 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) 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): class EntityTypeNode(TreeNode):
@ -142,8 +133,6 @@ class EntityTypeNode(TreeNode):
def __init__(self, entity_type: str, entities: List[Any]): def __init__(self, entity_type: str, entities: List[Any]):
super().__init__(entity_type) super().__init__(entity_type)
self.entities = entities 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 # Don't call _update_children() here - parent will call after adding this node
def _update_children(self) -> None: def _update_children(self) -> None:
@ -162,9 +151,6 @@ class EntityTypeNode(TreeNode):
# Update existing node # Update existing node
entity_node = existing_ids[entity_id] entity_node = existing_ids[entity_id]
entity_node.entity = entity 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: else:
# Create new entity node # Create new entity node
entity_node = EntityNode(entity) entity_node = EntityNode(entity)

View File

@ -42,6 +42,7 @@ class TreeWidget(UIElement):
self.hovered_node: Optional[TreeNode] = None self.hovered_node: Optional[TreeNode] = None
self.drag_start_node: Optional[TreeNode] = None self.drag_start_node: Optional[TreeNode] = None
self.is_dragging = False self.is_dragging = False
self.last_mouse_pos: Optional[Tuple[int, int]] = None
# Scrolling # Scrolling
self.scroll_offset = 0 self.scroll_offset = 0
@ -183,6 +184,7 @@ class TreeWidget(UIElement):
if event.type == pygame.MOUSEBUTTONDOWN: if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Left click if event.button == 1: # Left click
# Event position is already converted to local coordinates by HUD # 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) node = self._get_node_at_position_local(event.pos)
if node: if node:
# Check for expand/collapse click # Check for expand/collapse click
@ -197,6 +199,7 @@ class TreeWidget(UIElement):
elif event.type == pygame.MOUSEMOTION: elif event.type == pygame.MOUSEMOTION:
# Event position is already converted to local coordinates by HUD # 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) self.hovered_node = self._get_node_at_position_local(event.pos)
return True return True
@ -218,6 +221,9 @@ class TreeWidget(UIElement):
if self._should_update_tree(): if self._should_update_tree():
self._update_tree_structure() self._update_tree_structure()
self._update_visible_nodes() 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: def _should_update_tree(self) -> bool:
"""Determine if the tree structure needs updating.""" """Determine if the tree structure needs updating."""
@ -317,4 +323,9 @@ class TreeWidget(UIElement):
self.scroll_offset + (node_bottom - self.rect.height) self.scroll_offset + (node_bottom - self.rect.height)
) )
self._update_node_layout() 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