Implement unified panel styling system with consistent colors and dimensions

This commit is contained in:
Sam 2025-11-08 23:32:56 -06:00
parent 50eafcf808
commit 074d6fdb3c
6 changed files with 120 additions and 33 deletions

View File

@ -42,6 +42,22 @@ HUD_MARGIN = 10
LINE_HEIGHT = 20 LINE_HEIGHT = 20
SELECTION_THRESHOLD = 3 # pixels 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 # Simulation settings
FOOD_SPAWNING = True FOOD_SPAWNING = True
FOOD_OBJECTS_COUNT = 500 FOOD_OBJECTS_COUNT = 500

View File

@ -136,7 +136,6 @@ class InputHandler:
local_x = mouse_x - inspector_rect.x local_x = mouse_x - inspector_rect.x
local_y = mouse_y - inspector_rect.y local_y = mouse_y - inspector_rect.y
event.pos = (local_x, local_y) event.pos = (local_x, local_y)
print("Passing event to tree widget")
self.hud.tree_widget.handle_event(event) self.hud.tree_widget.handle_event(event)
else: else:
# Fallback: always zoom if no HUD reference # Fallback: always zoom if no HUD reference

View File

@ -269,9 +269,11 @@ class SimulationEngine:
# Update tree widget # Update tree widget
self.hud.update_tree_widget(deltatime) self.hud.update_tree_widget(deltatime)
# Draw panel backgrounds first (before pygame_gui UI)
self.hud.render_panel_backgrounds(self.screen)
# Draw UI elements # Draw UI elements
self.hud.manager.draw_ui(self.screen) self.hud.manager.draw_ui(self.screen)
self.hud.draw_splitters(self.screen)
# Render tree widget # Render tree widget
self.hud.render_tree_widget(self.screen) self.hud.render_tree_widget(self.screen)

107
ui/hud.py
View File

@ -10,9 +10,26 @@ from pygame_gui.elements import UIPanel, UIButton, UITextEntryLine, UILabel
from ui.tree_widget import TreeWidget from ui.tree_widget import TreeWidget
import math import math
# Custom HUD colors (preserving existing functionality)
DARK_GRAY = (40, 40, 40) DARK_GRAY = (40, 40, 40)
DARKER_GRAY = (25, 25, 25) 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 # Panel visibility constants
SHOW_CONTROL_BAR = True SHOW_CONTROL_BAR = True
SHOW_INSPECTOR_PANEL = True SHOW_INSPECTOR_PANEL = True
@ -50,13 +67,16 @@ class HUD:
self.world = None # Will be set when world is available self.world = None # Will be set when world is available
self._last_tree_selection = None # Track last selection to avoid unnecessary updates 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_panels()
self._create_simulation_controls() self._create_simulation_controls()
def _create_panels(self): def _create_panels(self):
self.panels = [] self.panels = []
# Top control bar # Top control bar - full width, positioned at top
if SHOW_CONTROL_BAR: if SHOW_CONTROL_BAR:
self.control_bar = UIPanel( self.control_bar = UIPanel(
relative_rect=pygame.Rect(0, 0, self.screen_width, self.control_bar_height), relative_rect=pygame.Rect(0, 0, self.screen_width, self.control_bar_height),
@ -67,34 +87,35 @@ class HUD:
else: else:
self.control_bar = None 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: if SHOW_INSPECTOR_PANEL:
# Create a container panel for the inspector
self.inspector_panel = UIPanel( self.inspector_panel = UIPanel(
relative_rect=pygame.Rect( 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.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, manager=self.manager,
object_id="#inspector_panel", object_id="#inspector_panel",
) )
self.panels.append(self.inspector_panel) self.panels.append(self.inspector_panel)
# Tree widget will be created when world is available
self.tree_widget = None self.tree_widget = None
else: else:
self.inspector_panel = None self.inspector_panel = None
self.tree_widget = None self.tree_widget = None
# Right properties # Right properties panel - edge-to-edge with control bar, no gap
if SHOW_PROPERTIES_PANEL: if SHOW_PROPERTIES_PANEL:
self.properties_panel = UIPanel( self.properties_panel = UIPanel(
relative_rect=pygame.Rect( relative_rect=pygame.Rect(
self.screen_width - self.properties_width, self.screen_width - self.properties_width, # Precisely at right edge
self.control_bar_height if SHOW_CONTROL_BAR else 0, side_panel_top, # Align with control bar
self.properties_width, 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, manager=self.manager,
object_id="#properties_panel", object_id="#properties_panel",
@ -103,13 +124,16 @@ class HUD:
else: else:
self.properties_panel = None self.properties_panel = None
# Bottom console # Bottom console panel - edge-to-edge with side panels, no gap
if SHOW_CONSOLE_PANEL: 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( self.console_panel = UIPanel(
relative_rect=pygame.Rect( relative_rect=pygame.Rect(
self.inspector_width if SHOW_INSPECTOR_PANEL else 0, console_left, # Start right at inspector edge
self.screen_height - self.console_height, self.screen_height - self.console_height, # Exactly at bottom edge
self.screen_width - (self.inspector_width if SHOW_INSPECTOR_PANEL else 0) - (self.properties_width if SHOW_PROPERTIES_PANEL else 0), console_width, # Fill space between side panels
self.console_height self.console_height
), ),
manager=self.manager, manager=self.manager,
@ -139,17 +163,55 @@ class HUD:
self.tree_widget.select_entities(selected_objects) self.tree_widget.select_entities(selected_objects)
self._last_tree_selection = current_selection 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): def _create_simulation_controls(self):
"""Create simulation control buttons in the control bar.""" """Create simulation control buttons in the control bar."""
if not self.control_bar: if not self.control_bar:
return return
# Button layout constants # Button layout constants (using standardized spacing)
button_width = 40 button_width = 40
button_height = 32 button_height = 32
button_spacing = 8 button_spacing = PANEL_TIGHT_SPACING # Using standardized tight spacing
start_x = 20 start_x = PANEL_INTERNAL_PADDING # Using standardized internal padding
start_y = 8 start_y = PANEL_TIGHT_SPACING # Using standardized tight spacing
# Play/Pause button # Play/Pause button
self.play_pause_button = UIButton( self.play_pause_button = UIButton(
@ -444,8 +506,8 @@ class HUD:
self.update_layout(self.screen_width, self.screen_height) self.update_layout(self.screen_width, self.screen_height)
def draw_splitters(self, screen): def draw_splitters(self, screen):
# Draw draggable splitters for visual feedback # Draw draggable splitters for visual feedback with consistent styling
indicator_color = (220, 220, 220) indicator_color = PANEL_ICON_COLOR # Use standardized icon color for consistency
indicator_size = 6 # Length of indicator line indicator_size = 6 # Length of indicator line
indicator_gap = 4 # Gap between indicator lines indicator_gap = 4 # Gap between indicator lines
indicator_count = 3 # Number of indicator lines indicator_count = 3 # Number of indicator lines
@ -985,3 +1047,6 @@ class HUD:
# Create a surface for the tree widget area # Create a surface for the tree widget area
tree_surface = screen.subsurface(self.inspector_panel.rect) tree_surface = screen.subsurface(self.inspector_panel.rect)
self.tree_widget.draw(tree_surface) self.tree_widget.draw(tree_surface)
# Render panel dividers for visual consistency
self.render_panel_dividers(screen)

View File

@ -6,6 +6,7 @@ Provides extensible tree structure for entity inspection.
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import List, Optional, Dict, Any from typing import List, Optional, Dict, Any
import pygame import pygame
from config.constants import PANEL_NODE_HEIGHT, PANEL_INDENTATION
class TreeNode(ABC): class TreeNode(ABC):
@ -18,7 +19,7 @@ class TreeNode(ABC):
self.is_expanded = False self.is_expanded = False
self.is_selected = False self.is_selected = False
self.depth = 0 if parent is None else parent.depth + 1 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: def add_child(self, child: 'TreeNode') -> None:
"""Add a child node to this node.""" """Add a child node to this node."""
@ -77,7 +78,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."""
return self.depth * 20 return self.depth * PANEL_INDENTATION
class SimulationNode(TreeNode): class SimulationNode(TreeNode):

View File

@ -8,6 +8,10 @@ import pygame_gui
from pygame_gui.core import UIElement from pygame_gui.core import UIElement
from typing import List, Optional, Tuple, Any from typing import List, Optional, Tuple, Any
from ui.inspector_tree import TreeNode, SimulationNode, TreeSelectionManager 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): class TreeWidget(UIElement):
@ -29,14 +33,14 @@ class TreeWidget(UIElement):
# Selection management # Selection management
self.selection_manager = TreeSelectionManager() self.selection_manager = TreeSelectionManager()
# Visual properties # Visual properties (using standardized panel styling)
self.node_height = 20 self.node_height = PANEL_NODE_HEIGHT
self.expand_collapse_width = 20 self.expand_collapse_width = PANEL_INDENTATION
self.icon_size = 8 self.icon_size = 8
self.text_color = (200, 200, 200) self.text_color = PANEL_TEXT_COLOR
self.selected_color = (50, 100, 150) self.selected_color = PANEL_SELECTED_COLOR
self.hover_color = (60, 60, 80) self.hover_color = PANEL_HOVER_COLOR
self.expand_icon_color = (150, 150, 150) self.expand_icon_color = PANEL_ICON_COLOR
# Interaction state # Interaction state
self.hovered_node: Optional[TreeNode] = None self.hovered_node: Optional[TreeNode] = None
@ -253,7 +257,7 @@ class TreeWidget(UIElement):
"""Draw the tree widget.""" """Draw the tree widget."""
# Create a clipping surface for the tree area # Create a clipping surface for the tree area
tree_surface = pygame.Surface((self.rect.width, self.rect.height)) 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 # Set up clipping rect to prevent rendering outside bounds
clip_rect = pygame.Rect(0, 0, self.rect.width, self.rect.height) clip_rect = pygame.Rect(0, 0, self.rect.width, self.rect.height)