diff --git a/config/constants.py b/config/constants.py index 06ea318..7a74fed 100644 --- a/config/constants.py +++ b/config/constants.py @@ -42,6 +42,22 @@ HUD_MARGIN = 10 LINE_HEIGHT = 20 SELECTION_THRESHOLD = 3 # pixels +# Unified Panel Styling System (based on tree widget design) +PANEL_BACKGROUND_COLOR = (30, 30, 40) # Dark blue-gray background +PANEL_SELECTED_COLOR = (50, 100, 150) # Blue highlight for selected elements +PANEL_HOVER_COLOR = (60, 60, 80) # Dark blue highlight for interactive elements +PANEL_TEXT_COLOR = (200, 200, 200) # Light gray text +PANEL_ICON_COLOR = (150, 150, 150) # Medium gray icons +PANEL_BORDER_COLOR = (220, 220, 220) # Light gray borders/dividers + +# Panel spacing and dimensions +PANEL_DIVIDER_WIDTH = 0 # No divider lines between panels +PANEL_BORDER_WIDTH = 2 # Border width for emphasis elements +PANEL_INTERNAL_PADDING = 8 # Standard padding inside panels +PANEL_TIGHT_SPACING = 4 # Tight spacing between components +PANEL_NODE_HEIGHT = 20 # Height for list/grid items +PANEL_INDENTATION = 20 # Indentation per hierarchy level + # Simulation settings FOOD_SPAWNING = True FOOD_OBJECTS_COUNT = 500 diff --git a/core/input_handler.py b/core/input_handler.py index 9ec3978..5eed0fd 100644 --- a/core/input_handler.py +++ b/core/input_handler.py @@ -136,7 +136,6 @@ class InputHandler: 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 diff --git a/core/simulation_engine.py b/core/simulation_engine.py index 8811ab5..32ce8b7 100644 --- a/core/simulation_engine.py +++ b/core/simulation_engine.py @@ -269,9 +269,11 @@ class SimulationEngine: # Update tree widget self.hud.update_tree_widget(deltatime) + # Draw panel backgrounds first (before pygame_gui UI) + self.hud.render_panel_backgrounds(self.screen) + # Draw UI elements self.hud.manager.draw_ui(self.screen) - self.hud.draw_splitters(self.screen) # Render tree widget self.hud.render_tree_widget(self.screen) diff --git a/ui/hud.py b/ui/hud.py index 8e212a2..42dd35a 100644 --- a/ui/hud.py +++ b/ui/hud.py @@ -10,9 +10,26 @@ from pygame_gui.elements import UIPanel, UIButton, UITextEntryLine, UILabel from ui.tree_widget import TreeWidget import math +# Custom HUD colors (preserving existing functionality) DARK_GRAY = (40, 40, 40) DARKER_GRAY = (25, 25, 25) + +def create_panel_style(manager: pygame_gui.UIManager) -> dict: + """Create unified styling dictionary for panels.""" + return { + 'panel_background': PANEL_BACKGROUND_COLOR, + 'border_color': PANEL_BORDER_COLOR, + 'text_color': PANEL_TEXT_COLOR, + 'internal_padding': PANEL_INTERNAL_PADDING, + 'border_width': PANEL_BORDER_WIDTH + } + + +def render_panel_divider(surface: pygame.Surface, rect: pygame.Rect): + """Render a thin divider line between panels.""" + pygame.draw.rect(surface, PANEL_BORDER_COLOR, rect, PANEL_DIVIDER_WIDTH) + # Panel visibility constants SHOW_CONTROL_BAR = True SHOW_INSPECTOR_PANEL = True @@ -50,13 +67,16 @@ class HUD: self.world = None # Will be set when world is available self._last_tree_selection = None # Track last selection to avoid unnecessary updates + # Initialize unified panel styling + self.panel_style = create_panel_style(self.manager) + self._create_panels() self._create_simulation_controls() def _create_panels(self): self.panels = [] - # Top control bar + # Top control bar - full width, positioned at top if SHOW_CONTROL_BAR: self.control_bar = UIPanel( relative_rect=pygame.Rect(0, 0, self.screen_width, self.control_bar_height), @@ -67,34 +87,35 @@ class HUD: else: self.control_bar = None - # Left inspector with tree widget + # Calculate vertical position for side panels (edge-to-edge with control bar) + side_panel_top = self.control_bar_height if SHOW_CONTROL_BAR else 0 + side_panel_height = self.screen_height - side_panel_top + + # Left inspector panel - edge-to-edge with control bar, no gap if SHOW_INSPECTOR_PANEL: - # Create a container panel for the inspector self.inspector_panel = UIPanel( relative_rect=pygame.Rect( - 0, self.control_bar_height if SHOW_CONTROL_BAR else 0, + 0, side_panel_top, # Start right at control bar edge self.inspector_width, - self.screen_height - (self.control_bar_height if SHOW_CONTROL_BAR else 0) + side_panel_height # Extend to bottom edge ), manager=self.manager, object_id="#inspector_panel", ) self.panels.append(self.inspector_panel) - - # Tree widget will be created when world is available self.tree_widget = None else: self.inspector_panel = None self.tree_widget = None - # Right properties + # Right properties panel - edge-to-edge with control bar, no gap if SHOW_PROPERTIES_PANEL: self.properties_panel = UIPanel( relative_rect=pygame.Rect( - self.screen_width - self.properties_width, - self.control_bar_height if SHOW_CONTROL_BAR else 0, + self.screen_width - self.properties_width, # Precisely at right edge + side_panel_top, # Align with control bar self.properties_width, - self.screen_height - (self.control_bar_height if SHOW_CONTROL_BAR else 0) + side_panel_height # Extend to bottom edge ), manager=self.manager, object_id="#properties_panel", @@ -103,13 +124,16 @@ class HUD: else: self.properties_panel = None - # Bottom console + # Bottom console panel - edge-to-edge with side panels, no gap if SHOW_CONSOLE_PANEL: + console_left = self.inspector_width if SHOW_INSPECTOR_PANEL else 0 + console_width = self.screen_width - console_left - (self.properties_width if SHOW_PROPERTIES_PANEL else 0) + self.console_panel = UIPanel( relative_rect=pygame.Rect( - self.inspector_width if SHOW_INSPECTOR_PANEL else 0, - self.screen_height - self.console_height, - self.screen_width - (self.inspector_width if SHOW_INSPECTOR_PANEL else 0) - (self.properties_width if SHOW_PROPERTIES_PANEL else 0), + console_left, # Start right at inspector edge + self.screen_height - self.console_height, # Exactly at bottom edge + console_width, # Fill space between side panels self.console_height ), manager=self.manager, @@ -139,17 +163,55 @@ class HUD: self.tree_widget.select_entities(selected_objects) self._last_tree_selection = current_selection + def render_panel_backgrounds(self, screen: pygame.Surface): + """Render panel backgrounds with consistent colors before UI elements.""" + + # Render control bar background to match inspector panel + if SHOW_CONTROL_BAR and self.control_bar: + control_bg_rect = pygame.Rect( + 0, 0, + self.screen_width, + self.control_bar_height + ) + pygame.draw.rect(screen, PANEL_BACKGROUND_COLOR, control_bg_rect) + + # Render console panel background to match inspector panel + if SHOW_CONSOLE_PANEL and self.console_panel: + console_bg_rect = pygame.Rect( + self.console_panel.rect.x, + self.console_panel.rect.y, + self.console_panel.rect.width, + self.console_panel.rect.height + ) + pygame.draw.rect(screen, PANEL_BACKGROUND_COLOR, console_bg_rect) + + # Render properties panel background to match inspector panel + if SHOW_PROPERTIES_PANEL and self.properties_panel: + properties_bg_rect = pygame.Rect( + self.properties_panel.rect.x, + self.properties_panel.rect.y, + self.properties_panel.rect.width, + self.properties_panel.rect.height + ) + pygame.draw.rect(screen, PANEL_BACKGROUND_COLOR, properties_bg_rect) + + def render_panel_dividers(self, screen: pygame.Surface): + """Render consistent panel sliders with proper rendering order.""" + + # Render consistent panel sliders + self.draw_splitters(screen) + def _create_simulation_controls(self): """Create simulation control buttons in the control bar.""" if not self.control_bar: return - # Button layout constants + # Button layout constants (using standardized spacing) button_width = 40 button_height = 32 - button_spacing = 8 - start_x = 20 - start_y = 8 + button_spacing = PANEL_TIGHT_SPACING # Using standardized tight spacing + start_x = PANEL_INTERNAL_PADDING # Using standardized internal padding + start_y = PANEL_TIGHT_SPACING # Using standardized tight spacing # Play/Pause button self.play_pause_button = UIButton( @@ -444,8 +506,8 @@ class HUD: self.update_layout(self.screen_width, self.screen_height) def draw_splitters(self, screen): - # Draw draggable splitters for visual feedback - indicator_color = (220, 220, 220) + # Draw draggable splitters for visual feedback with consistent styling + indicator_color = PANEL_ICON_COLOR # Use standardized icon color for consistency indicator_size = 6 # Length of indicator line indicator_gap = 4 # Gap between indicator lines indicator_count = 3 # Number of indicator lines @@ -985,3 +1047,6 @@ class HUD: # Create a surface for the tree widget area tree_surface = screen.subsurface(self.inspector_panel.rect) self.tree_widget.draw(tree_surface) + + # Render panel dividers for visual consistency + self.render_panel_dividers(screen) diff --git a/ui/inspector_tree.py b/ui/inspector_tree.py index 9dd447c..5a06f09 100644 --- a/ui/inspector_tree.py +++ b/ui/inspector_tree.py @@ -6,6 +6,7 @@ Provides extensible tree structure for entity inspection. from abc import ABC, abstractmethod from typing import List, Optional, Dict, Any import pygame +from config.constants import PANEL_NODE_HEIGHT, PANEL_INDENTATION class TreeNode(ABC): @@ -18,7 +19,7 @@ class TreeNode(ABC): self.is_expanded = False self.is_selected = False self.depth = 0 if parent is None else parent.depth + 1 - self.rect = pygame.Rect(0, 0, 0, 20) # Will be updated during layout + self.rect = pygame.Rect(0, 0, 0, PANEL_NODE_HEIGHT) # Will be updated during layout def add_child(self, child: 'TreeNode') -> None: """Add a child node to this node.""" @@ -77,7 +78,7 @@ class TreeNode(ABC): def get_indent(self) -> int: """Get the indentation width for this node.""" - return self.depth * 20 + return self.depth * PANEL_INDENTATION class SimulationNode(TreeNode): diff --git a/ui/tree_widget.py b/ui/tree_widget.py index f425d17..422d7b4 100644 --- a/ui/tree_widget.py +++ b/ui/tree_widget.py @@ -8,6 +8,10 @@ import pygame_gui from pygame_gui.core import UIElement from typing import List, Optional, Tuple, Any from ui.inspector_tree import TreeNode, SimulationNode, TreeSelectionManager +from config.constants import ( + PANEL_BACKGROUND_COLOR, PANEL_SELECTED_COLOR, PANEL_HOVER_COLOR, + PANEL_TEXT_COLOR, PANEL_ICON_COLOR, PANEL_NODE_HEIGHT, PANEL_INDENTATION +) class TreeWidget(UIElement): @@ -29,14 +33,14 @@ class TreeWidget(UIElement): # Selection management self.selection_manager = TreeSelectionManager() - # Visual properties - self.node_height = 20 - self.expand_collapse_width = 20 + # Visual properties (using standardized panel styling) + self.node_height = PANEL_NODE_HEIGHT + self.expand_collapse_width = PANEL_INDENTATION self.icon_size = 8 - self.text_color = (200, 200, 200) - self.selected_color = (50, 100, 150) - self.hover_color = (60, 60, 80) - self.expand_icon_color = (150, 150, 150) + self.text_color = PANEL_TEXT_COLOR + self.selected_color = PANEL_SELECTED_COLOR + self.hover_color = PANEL_HOVER_COLOR + self.expand_icon_color = PANEL_ICON_COLOR # Interaction state self.hovered_node: Optional[TreeNode] = None @@ -253,7 +257,7 @@ class TreeWidget(UIElement): """Draw the tree widget.""" # Create a clipping surface for the tree area tree_surface = pygame.Surface((self.rect.width, self.rect.height)) - tree_surface.fill((30, 30, 40)) # Background color + tree_surface.fill(PANEL_BACKGROUND_COLOR) # Using standardized background color # Set up clipping rect to prevent rendering outside bounds clip_rect = pygame.Rect(0, 0, self.rect.width, self.rect.height)