1
0
forked from Mirrors/RGSX
Files
RGSX/display.py

1045 lines
55 KiB
Python
Raw Normal View History

2025-07-10 22:32:35 +02:00
import pygame # type: ignore
2025-07-06 19:47:21 +02:00
import config
2025-07-10 22:32:35 +02:00
from utils import truncate_text_middle, wrap_text, load_system_image
2025-07-06 19:47:21 +02:00
import logging
2025-07-10 22:32:35 +02:00
import math
2025-07-07 02:48:05 +02:00
from history import load_history # Ajout de l'import
2025-07-06 19:47:21 +02:00
logger = logging.getLogger(__name__)
2025-07-06 22:05:20 +02:00
OVERLAY = None # Initialisé dans init_display()
2025-07-10 22:32:35 +02:00
# Couleurs modernes pour le thème
THEME_COLORS = {
# Fond des lignes sélectionnées
"fond_lignes": (0, 255, 0), # vert
# Fond par défaut des images de grille des systèmes
"fond_image": (50, 50, 70), # Bleu sombre métal
# Néon image grille des systèmes
"neon": (0, 134, 179), # bleu
# Dégradé sombre pour le fond
"background_top": (30, 40, 50),
"background_bottom": (60, 80, 100), # noir vers bleu foncé
# Fond des cadres
"button_idle": (50, 50, 70, 150), # Bleu sombre métal
# Fond des boutons sélectionnés dans les popups ou menu
"button_hover": (255, 0, 255, 220), # Rose
# Générique
"text": (255, 255, 255), # blanc
# Erreur
"error_text": (255, 0, 0), # rouge
# Avertissement
"warning_text": (255, 100, 0), # orange
# Titres
"title_text": (200, 200, 200), # gris clair
# Bordures
"border": (150, 150, 150), # Bordures grises subtiles
}
# Général, résolution, overlay
2025-07-06 19:47:21 +02:00
def init_display():
2025-07-07 03:46:28 +02:00
"""Initialise l'écran et les ressources globales."""
global OVERLAY
2025-07-07 03:46:28 +02:00
logger.debug("Initialisation de l'écran")
display_info = pygame.display.Info()
screen_width = display_info.current_w
screen_height = display_info.current_h
screen = pygame.display.set_mode((screen_width, screen_height))
config.screen_width = screen_width
config.screen_height = screen_height
# Initialisation de OVERLAY
OVERLAY = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
2025-07-10 22:32:35 +02:00
OVERLAY.fill((0, 0, 0, 150)) # Transparence augmentée
2025-07-07 03:46:28 +02:00
logger.debug(f"Écran initialisé avec résolution : {screen_width}x{screen_height}")
2025-07-06 19:47:21 +02:00
return screen
2025-07-06 22:05:20 +02:00
2025-07-10 22:32:35 +02:00
# Fond d'écran dégradé
2025-07-06 19:47:21 +02:00
def draw_gradient(screen, top_color, bottom_color):
2025-07-10 22:32:35 +02:00
"""Dessine un fond dégradé vertical avec des couleurs vibrantes."""
2025-07-06 19:47:21 +02:00
height = screen.get_height()
top_color = pygame.Color(*top_color)
bottom_color = pygame.Color(*bottom_color)
for y in range(height):
ratio = y / height
color = top_color.lerp(bottom_color, ratio)
pygame.draw.line(screen, color, (0, y), (screen.get_width(), y))
2025-07-10 22:32:35 +02:00
# Nouvelle fonction pour dessiner un bouton stylisé
def draw_stylized_button(screen, text, x, y, width, height, selected=False):
"""Dessine un bouton moderne avec effet de survol et bordure arrondie."""
button_surface = pygame.Surface((width, height), pygame.SRCALPHA)
button_color = THEME_COLORS["button_hover"] if selected else THEME_COLORS["button_idle"]
pygame.draw.rect(button_surface, button_color, (0, 0, width, height), border_radius=12)
pygame.draw.rect(button_surface, THEME_COLORS["border"], (0, 0, width, height), 2, border_radius=12)
if selected:
glow_surface = pygame.Surface((width + 10, height + 10), pygame.SRCALPHA)
pygame.draw.rect(glow_surface, THEME_COLORS["fond_lignes"] + (50,), (5, 5, width, height), border_radius=12)
screen.blit(glow_surface, (x - 5, y - 5))
screen.blit(button_surface, (x, y))
text_surface = config.font.render(text, True, THEME_COLORS["text"])
text_rect = text_surface.get_rect(center=(x + width // 2, y + height // 2))
screen.blit(text_surface, text_rect)
# Transition d'image lors de la sélection d'un système
def draw_validation_transition(screen, platform_index):
2025-07-10 22:32:35 +02:00
"""Affiche une animation de transition fluide pour la sélection dune plateforme."""
platform_dict = config.platform_dicts[platform_index]
image = load_system_image(platform_dict)
if not image:
return
2025-07-10 22:32:35 +02:00
# Dimensions originales et calcul du ratio pour préserver les proportions
orig_width, orig_height = image.get_width(), image.get_height()
base_size = int(config.screen_width * 0.0781) # ~150px pour 1920p
2025-07-10 22:32:35 +02:00
ratio = min(base_size / orig_width, base_size / orig_height) # Maintenir les proportions
base_width = int(orig_width * ratio)
base_height = int(orig_height * ratio)
# Paramètres de l'animation
start_time = pygame.time.get_ticks()
2025-07-10 22:32:35 +02:00
duration = 1000 # Durée augmentée à 1 seconde
fps = 60
frame_time = 1000 / fps # Temps par frame en ms
while pygame.time.get_ticks() - start_time < duration:
2025-07-10 22:32:35 +02:00
# Fond dégradé
draw_gradient(screen, THEME_COLORS["background_top"], THEME_COLORS["background_bottom"])
# Calcul de l'échelle avec une courbe sinusoïdale pour une transition fluide
elapsed = pygame.time.get_ticks() - start_time
2025-07-10 22:32:35 +02:00
progress = elapsed / duration
# Courbe sinusoïdale pour une montée/descente douce
scale = 1.5 + 1.0 * math.sin(math.pi * progress) # Échelle de 1.5 à 2.5
new_width = int(base_width * scale)
new_height = int(base_height * scale)
# Redimensionner l'image en préservant les proportions
scaled_image = pygame.transform.smoothscale(image, (new_width, new_height))
image_rect = scaled_image.get_rect(center=(config.screen_width // 2, config.screen_height // 2))
2025-07-10 22:32:35 +02:00
# Effet de fondu (opacité de 50% à 100% puis retour à 50%)
alpha = int(128 + 127 * math.cos(math.pi * progress)) # Opacité entre 128 et 255
scaled_image.set_alpha(alpha)
# Effet de glow néon pour l'image sélectionnée
neon_color = THEME_COLORS["neon"] # Cyan vif
padding = 24
neon_surface = pygame.Surface((new_width + 2 * padding, new_height + 2 * padding), pygame.SRCALPHA)
pygame.draw.rect(neon_surface, neon_color + (40,), neon_surface.get_rect(), border_radius=24)
pygame.draw.rect(neon_surface, neon_color + (100,), neon_surface.get_rect().inflate(-10, -10), border_radius=18)
screen.blit(neon_surface, (image_rect.left - padding, image_rect.top - padding), special_flags=pygame.BLEND_RGBA_ADD)
# Afficher l'image
screen.blit(scaled_image, image_rect)
pygame.display.flip()
2025-07-10 22:32:35 +02:00
# Contrôler la fréquence de rendu
pygame.time.wait(int(frame_time))
# Afficher l'image finale sans effet pour une transition propre
draw_gradient(screen, THEME_COLORS["background_top"], THEME_COLORS["background_bottom"])
final_image = pygame.transform.smoothscale(image, (base_width, base_height))
final_image.set_alpha(255) # Opacité complète
final_rect = final_image.get_rect(center=(config.screen_width // 2, config.screen_height // 2))
screen.blit(final_image, final_rect)
pygame.display.flip()
# Écran de chargement
2025-07-06 19:47:21 +02:00
def draw_loading_screen(screen):
2025-07-10 22:32:35 +02:00
"""Affiche lécran de chargement avec un style moderne."""
2025-07-06 19:47:21 +02:00
disclaimer_lines = [
"Bienvenue dans RGSX",
"It's dangerous to go alone, take all you need!",
"Mais ne téléchargez que des jeux",
"dont vous possédez les originaux !",
"RGSX n'est pas responsable des contenus téléchargés,",
"et n'heberge pas de ROMs.",
2025-07-06 19:47:21 +02:00
]
2025-07-10 22:32:35 +02:00
margin_horizontal = int(config.screen_width * 0.025)
padding_vertical = int(config.screen_height * 0.0185)
padding_between = int(config.screen_height * 0.0074)
2025-07-06 19:47:21 +02:00
border_radius = 16
border_width = 3
shadow_offset = 6
line_height = config.small_font.get_height() + padding_between
2025-07-06 19:47:21 +02:00
total_height = line_height * len(disclaimer_lines) - padding_between
rect_width = config.screen_width - 2 * margin_horizontal
rect_height = total_height + 2 * padding_vertical
rect_x = margin_horizontal
2025-07-10 22:32:35 +02:00
rect_y = int(config.screen_height * 0.0185)
2025-07-06 19:47:21 +02:00
shadow_rect = pygame.Rect(rect_x + shadow_offset, rect_y + shadow_offset, rect_width, rect_height)
shadow_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA)
pygame.draw.rect(shadow_surface, (0, 0, 0, 100), shadow_surface.get_rect(), border_radius=border_radius)
screen.blit(shadow_surface, shadow_rect.topleft)
disclaimer_rect = pygame.Rect(rect_x, rect_y, rect_width, rect_height)
disclaimer_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA)
2025-07-10 22:32:35 +02:00
pygame.draw.rect(disclaimer_surface, THEME_COLORS["button_idle"], disclaimer_surface.get_rect(), border_radius=border_radius)
2025-07-06 19:47:21 +02:00
screen.blit(disclaimer_surface, disclaimer_rect.topleft)
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["border"], disclaimer_rect, border_width, border_radius=border_radius)
2025-07-06 19:47:21 +02:00
2025-07-06 22:05:20 +02:00
max_text_width = rect_width - 2 * padding_vertical
2025-07-06 19:47:21 +02:00
for i, line in enumerate(disclaimer_lines):
wrapped_lines = wrap_text(line, config.small_font, max_text_width)
2025-07-06 22:05:20 +02:00
for j, wrapped_line in enumerate(wrapped_lines):
2025-07-10 22:32:35 +02:00
text_surface = config.small_font.render(wrapped_line, True, THEME_COLORS["title_text"])
2025-07-06 22:05:20 +02:00
text_rect = text_surface.get_rect(center=(
config.screen_width // 2,
rect_y + padding_vertical + (i * len(wrapped_lines) + j + 0.5) * line_height - padding_between // 2
))
screen.blit(text_surface, text_rect)
2025-07-10 22:32:35 +02:00
loading_y = rect_y + rect_height + int(config.screen_height * 0.0926)
text = config.small_font.render(truncate_text_middle(f"{config.current_loading_system}", config.small_font, config.screen_width - 2 * margin_horizontal), True, THEME_COLORS["text"])
2025-07-06 19:47:21 +02:00
text_rect = text.get_rect(center=(config.screen_width // 2, loading_y))
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
progress_text = config.small_font.render(f"Progression : {int(config.loading_progress)}%", True, THEME_COLORS["text"])
progress_rect = progress_text.get_rect(center=(config.screen_width // 2, loading_y + int(config.screen_height * 0.0463)))
2025-07-06 19:47:21 +02:00
screen.blit(progress_text, progress_rect)
2025-07-10 22:32:35 +02:00
bar_width = int(config.screen_width * 0.2083)
bar_height = int(config.screen_height * 0.037)
2025-07-06 19:47:21 +02:00
progress_width = (bar_width * config.loading_progress) / 100
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (config.screen_width // 2 - bar_width // 2, loading_y + int(config.screen_height * 0.0926), bar_width, bar_height), border_radius=8)
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], (config.screen_width // 2 - bar_width // 2, loading_y + int(config.screen_height * 0.0926), progress_width, bar_height), border_radius=8)
2025-07-06 19:47:21 +02:00
2025-07-10 22:32:35 +02:00
# Écran d'erreur
2025-07-06 19:47:21 +02:00
def draw_error_screen(screen):
2025-07-10 22:32:35 +02:00
"""Affiche lécran derreur avec un style moderne."""
wrapped_message = wrap_text(config.error_message, config.small_font, config.screen_width - 80)
line_height = config.small_font.get_height() + 5
text_height = len(wrapped_message) * line_height
button_height = int(config.screen_height * 0.0463)
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=300)
rect_width = max_text_width + 80
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
screen.blit(OVERLAY, (0, 0))
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-06 22:05:20 +02:00
for i, line in enumerate(wrapped_message):
2025-07-10 22:32:35 +02:00
text = config.small_font.render(line, True, THEME_COLORS["error_text"])
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
2025-07-06 22:05:20 +02:00
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
draw_stylized_button(screen, "Valider", rect_x + rect_width // 2 - 80, rect_y + text_height + margin_top_bottom, 160, button_height, selected=True)
# Récupérer les noms d'affichage des contrôles
def get_control_display(action, default):
"""Récupère le nom d'affichage d'une action depuis controls_config."""
if not config.controls_config:
logger.warning(f"controls_config vide pour l'action {action}, utilisation de la valeur par défaut")
return default
return config.controls_config.get(action, {}).get('display', default)
2025-07-06 19:47:21 +02:00
2025-07-10 22:32:35 +02:00
# Grille des systèmes 3x3
2025-07-06 19:47:21 +02:00
def draw_platform_grid(screen):
2025-07-10 22:32:35 +02:00
"""Affiche la grille des plateformes avec un style moderne."""
2025-07-07 00:27:08 +02:00
if not config.platforms or config.selected_platform >= len(config.platforms):
platform_name = "Aucune plateforme"
logger.warning("Aucune plateforme ou selected_platform hors limites")
else:
platform = config.platforms[config.selected_platform]
platform_name = config.platform_names.get(platform, platform)
title_text = f"{platform_name}"
2025-07-10 22:32:35 +02:00
title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 20))
title_rect_inflated = title_rect.inflate(60, 30)
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 10)
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_rect_inflated, border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=12)
screen.blit(title_surface, title_rect)
2025-07-10 22:32:35 +02:00
margin_left = int(config.screen_width * 0.026)
2025-07-06 22:05:20 +02:00
margin_right = int(config.screen_width * 0.026)
2025-07-10 22:32:35 +02:00
margin_top = int(config.screen_height * 0.140)
margin_bottom = int(config.screen_height * 0.0648)
2025-07-06 19:47:21 +02:00
num_cols = 3
2025-07-10 22:32:35 +02:00
num_rows = 4
2025-07-06 19:47:21 +02:00
systems_per_page = num_cols * num_rows
available_width = config.screen_width - margin_left - margin_right
available_height = config.screen_height - margin_top - margin_bottom
col_width = available_width // num_cols
row_height = available_height // num_rows
x_positions = [margin_left + col_width * i + col_width // 2 for i in range(num_cols)]
y_positions = [margin_top + row_height * i + row_height // 2 for i in range(num_rows)]
start_idx = config.current_page * systems_per_page
for idx in range(start_idx, start_idx + systems_per_page):
if idx >= len(config.platforms):
break
grid_idx = idx - start_idx
row = grid_idx // num_cols
col = grid_idx % num_cols
x = x_positions[col]
y = y_positions[row]
scale = 1.5 if idx == config.selected_platform else 1.0
platform_dict = config.platform_dicts[idx]
image = load_system_image(platform_dict)
if image:
orig_width, orig_height = image.get_width(), image.get_height()
2025-07-10 22:32:35 +02:00
max_size = int(min(col_width, row_height) * scale * 1.1) # Légèrement plus grand que la cellule
2025-07-06 19:47:21 +02:00
ratio = min(max_size / orig_width, max_size / orig_height)
new_width = int(orig_width * ratio)
new_height = int(orig_height * ratio)
image = pygame.transform.smoothscale(image, (new_width, new_height))
image_rect = image.get_rect(center=(x, y))
if idx == config.selected_platform:
2025-07-10 22:32:35 +02:00
neon_color = THEME_COLORS["neon"]
border_radius = 12
padding = 12
2025-07-06 19:47:21 +02:00
rect_width = image_rect.width + 2 * padding
rect_height = image_rect.height + 2 * padding
neon_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA)
2025-07-10 22:32:35 +02:00
pygame.draw.rect(neon_surface, neon_color + (40,), neon_surface.get_rect(), border_radius=border_radius)
pygame.draw.rect(neon_surface, neon_color + (100,), neon_surface.get_rect().inflate(-10, -10), border_radius=border_radius)
pygame.draw.rect(neon_surface, neon_color + (200,), neon_surface.get_rect().inflate(-20, -20), width=1, border_radius=border_radius)
2025-07-06 19:47:21 +02:00
screen.blit(neon_surface, (image_rect.left - padding, image_rect.top - padding), special_flags=pygame.BLEND_RGBA_ADD)
2025-07-10 22:32:35 +02:00
background_surface = pygame.Surface((image_rect.width + 10, image_rect.height + 10), pygame.SRCALPHA)
pygame.draw.rect(background_surface, THEME_COLORS["fond_image"] + (180,), background_surface.get_rect(), border_radius=12)
screen.blit(background_surface, (image_rect.left - 5, image_rect.top - 5))
2025-07-06 19:47:21 +02:00
screen.blit(image, image_rect)
2025-07-10 22:32:35 +02:00
# Liste des jeux
def draw_game_list(screen):
2025-07-10 22:32:35 +02:00
"""Affiche la liste des jeux avec un style moderne."""
platform = config.platforms[config.current_platform]
platform_name = config.platform_names.get(platform, platform)
games = config.filtered_games if config.filter_active or config.search_mode else config.games
game_count = len(games)
if not games:
logger.debug("Aucune liste de jeux disponible")
message = "Aucun jeu disponible"
lines = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(lines) * line_height
margin_top_bottom = 20
rect_height = text_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in lines], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 80
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
screen.blit(OVERLAY, (0, 0))
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
for i, line in enumerate(lines):
2025-07-10 22:32:35 +02:00
text_surface = config.font.render(line, True, THEME_COLORS["text"])
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
return
line_height = config.small_font.get_height() + 10
2025-07-07 00:27:08 +02:00
margin_top_bottom = 20
extra_margin_top = 20
2025-07-10 22:32:35 +02:00
extra_margin_bottom = 60
title_height = config.title_font.get_height() + 20
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
items_per_page = available_height // line_height
rect_height = items_per_page * line_height + 2 * margin_top_bottom
rect_width = int(0.95 * config.screen_width)
rect_x = (config.screen_width - rect_width) // 2
rect_y = title_height + extra_margin_top + (config.screen_height - title_height - extra_margin_top - extra_margin_bottom - rect_height) // 2
config.scroll_offset = max(0, min(config.scroll_offset, max(0, len(games) - items_per_page)))
if config.current_game < config.scroll_offset:
config.scroll_offset = config.current_game
elif config.current_game >= config.scroll_offset + items_per_page:
config.scroll_offset = config.current_game - items_per_page + 1
screen.blit(OVERLAY, (0, 0))
if config.search_mode:
search_text = f"Filtrer : {config.search_query}_"
2025-07-10 22:32:35 +02:00
title_surface = config.search_font.render(search_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 20))
title_rect_inflated = title_rect.inflate(60, 30)
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 10)
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_rect_inflated, border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=12)
screen.blit(title_surface, title_rect)
elif config.filter_active:
filter_text = f"Filtre actif : {config.search_query}"
2025-07-10 22:32:35 +02:00
title_surface = config.small_font.render(filter_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 20))
title_rect_inflated = title_rect.inflate(60, 30)
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 10)
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_rect_inflated, border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=12)
screen.blit(title_surface, title_rect)
else:
title_text = f"{platform_name} ({game_count} jeux)"
2025-07-10 22:32:35 +02:00
title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 20))
title_rect_inflated = title_rect.inflate(60, 30)
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 10)
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_rect_inflated, border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=12)
screen.blit(title_surface, title_rect)
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
for i in range(config.scroll_offset, min(config.scroll_offset + items_per_page, len(games))):
game_name = games[i][0] if isinstance(games[i], (list, tuple)) else games[i]
2025-07-10 22:32:35 +02:00
color = THEME_COLORS["fond_lignes"] if i == config.current_game else THEME_COLORS["text"]
game_text = truncate_text_middle(game_name, config.small_font, rect_width - 40)
text_surface = config.small_font.render(game_text, True, color)
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + (i - config.scroll_offset) * line_height + line_height // 2))
2025-07-10 22:32:35 +02:00
if i == config.current_game:
glow_surface = pygame.Surface((text_rect.width + 20, text_rect.height + 10), pygame.SRCALPHA)
pygame.draw.rect(glow_surface, THEME_COLORS["fond_lignes"] + (50,), (10, 5, text_rect.width, text_rect.height), border_radius=8)
screen.blit(glow_surface, (text_rect.left - 10, text_rect.top - 5))
screen.blit(text_surface, text_rect)
if len(games) > items_per_page:
try:
draw_game_scrollbar(
screen,
config.scroll_offset,
len(games),
items_per_page,
rect_x + rect_width - 10,
rect_y,
rect_height
)
except NameError as e:
logger.error(f"Erreur : draw_game_scrollbar non défini: {str(e)}")
2025-07-10 22:32:35 +02:00
# Barre de défilement des jeux
def draw_game_scrollbar(screen, scroll_offset, total_items, visible_items, x, y, height):
"""Affiche la barre de défilement pour la liste des jeux."""
if total_items <= visible_items:
return
game_area_height = height
scrollbar_height = game_area_height * (visible_items / total_items)
scrollbar_y = y + (game_area_height - scrollbar_height) * (scroll_offset / max(1, total_items - visible_items))
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], (x, scrollbar_y, 15, scrollbar_height), border_radius=4)
2025-07-07 02:48:05 +02:00
def draw_history_list(screen):
2025-07-10 22:32:35 +02:00
"""Affiche l'historique des téléchargements avec un style moderne."""
2025-07-07 02:48:05 +02:00
history = config.history if hasattr(config, 'history') else load_history()
history_count = len(history)
col_platform_width = int((0.95 * config.screen_width - 60) * 0.33)
col_game_width = int((0.95 * config.screen_width - 60) * 0.50)
col_status_width = int((0.95 * config.screen_width - 60) * 0.17)
2025-07-10 22:32:35 +02:00
rect_width = int(0.95 * config.screen_width)
line_height = config.small_font.get_height() + 10
header_height = line_height
margin_top_bottom = 20
2025-07-10 22:32:35 +02:00
extra_margin_top = 40
extra_margin_bottom = 80
title_height = config.title_font.get_height() + 20
2025-07-07 02:48:05 +02:00
if not history:
logger.debug("Aucun historique disponible")
message = "Aucun téléchargement dans l'historique"
lines = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(lines) * line_height
rect_height = text_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in lines], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 80
2025-07-07 02:48:05 +02:00
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
screen.blit(OVERLAY, (0, 0))
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-07 02:48:05 +02:00
for i, line in enumerate(lines):
2025-07-10 22:32:35 +02:00
text_surface = config.font.render(line, True, THEME_COLORS["text"])
2025-07-07 02:48:05 +02:00
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
return
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
items_per_page = available_height // line_height
rect_height = header_height + items_per_page * line_height + 2 * margin_top_bottom
2025-07-07 02:48:05 +02:00
rect_x = (config.screen_width - rect_width) // 2
rect_y = title_height + extra_margin_top + (config.screen_height - title_height - extra_margin_top - extra_margin_bottom - rect_height) // 2
config.history_scroll_offset = max(0, min(config.history_scroll_offset, max(0, len(history) - items_per_page)))
if config.current_history_item < config.history_scroll_offset:
config.history_scroll_offset = config.current_history_item
elif config.current_history_item >= config.history_scroll_offset + items_per_page:
config.history_scroll_offset = config.current_history_item - items_per_page + 1
screen.blit(OVERLAY, (0, 0))
title_text = f"Historique des téléchargements ({history_count})"
2025-07-10 22:32:35 +02:00
title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"])
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 20))
title_rect_inflated = title_rect.inflate(60, 30)
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 10)
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_rect_inflated, border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=12)
2025-07-07 02:48:05 +02:00
screen.blit(title_surface, title_rect)
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-07 02:48:05 +02:00
headers = ["Système", "Nom du jeu", "État"]
header_y = rect_y + margin_top_bottom + header_height // 2
2025-07-07 02:48:05 +02:00
header_x_positions = [
rect_x + 20 + col_platform_width // 2,
rect_x + 20 + col_platform_width + col_game_width // 2,
rect_x + 20 + col_platform_width + col_game_width + col_status_width // 2
]
for header, x_pos in zip(headers, header_x_positions):
2025-07-10 22:32:35 +02:00
text_surface = config.small_font.render(header, True, THEME_COLORS["text"])
2025-07-07 02:48:05 +02:00
text_rect = text_surface.get_rect(center=(x_pos, header_y))
screen.blit(text_surface, text_rect)
2025-07-10 22:32:35 +02:00
# Ajouter un séparateur sous les en-têtes
separator_y = rect_y + margin_top_bottom + header_height
pygame.draw.line(screen, THEME_COLORS["border"], (rect_x + 20, separator_y), (rect_x + rect_width - 20, separator_y), 2)
for idx, i in enumerate(range(config.history_scroll_offset, min(config.history_scroll_offset + items_per_page, len(history)))):
2025-07-07 02:48:05 +02:00
entry = history[i]
platform = entry.get("platform", "Inconnu")
game_name = entry.get("game_name", "Inconnu")
status = entry.get("status", "Inconnu")
2025-07-10 22:32:35 +02:00
color = THEME_COLORS["fond_lignes"] if i == config.current_history_item else THEME_COLORS["text"]
platform_text = truncate_text_middle(platform, config.small_font, col_platform_width - 10)
game_text = truncate_text_middle(game_name, config.small_font, col_game_width - 10)
status_text = truncate_text_middle(status, config.small_font, col_status_width - 10)
2025-07-10 22:32:35 +02:00
y_pos = rect_y + margin_top_bottom + header_height + idx * line_height + line_height // 2
2025-07-07 02:48:05 +02:00
platform_surface = config.small_font.render(platform_text, True, color)
game_surface = config.small_font.render(game_text, True, color)
status_surface = config.small_font.render(status_text, True, color)
2025-07-10 22:32:35 +02:00
2025-07-07 02:48:05 +02:00
platform_rect = platform_surface.get_rect(center=(header_x_positions[0], y_pos))
game_rect = game_surface.get_rect(center=(header_x_positions[1], y_pos))
status_rect = status_surface.get_rect(center=(header_x_positions[2], y_pos))
2025-07-10 22:32:35 +02:00
if i == config.current_history_item:
glow_surface = pygame.Surface((rect_width - 40, line_height), pygame.SRCALPHA)
pygame.draw.rect(glow_surface, THEME_COLORS["fond_lignes"] + (50,), (0, 0, rect_width - 40, line_height), border_radius=8)
screen.blit(glow_surface, (rect_x + 20, y_pos - line_height // 2))
2025-07-07 02:48:05 +02:00
screen.blit(platform_surface, platform_rect)
screen.blit(game_surface, game_rect)
screen.blit(status_surface, status_rect)
if len(history) > items_per_page:
try:
draw_history_scrollbar(
screen,
config.history_scroll_offset,
len(history),
items_per_page,
rect_x + rect_width - 10,
rect_y,
rect_height
)
except NameError as e:
logger.error(f"Erreur : draw_history_scrollbar non défini: {str(e)}")
2025-07-10 22:32:35 +02:00
# Barre de défilement de l'historique
def draw_history_scrollbar(screen, scroll_offset, total_items, visible_items, x, y, height):
2025-07-10 22:32:35 +02:00
"""Affiche la barre de défilement avec un style moderne."""
if total_items <= visible_items:
2025-07-07 02:48:05 +02:00
return
2025-07-10 22:32:35 +02:00
game_area_height = height
scrollbar_height = game_area_height * (visible_items / total_items) - 10
scrollbar_y = y + (game_area_height - scrollbar_height) * (scroll_offset / max(1, total_items - visible_items)) + 10
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], (x, scrollbar_y, 5, scrollbar_height), border_radius=4)
2025-07-07 02:48:05 +02:00
2025-07-10 22:32:35 +02:00
# Écran confirmation vider historique
def draw_clear_history_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour vider l'historique."""
screen.blit(OVERLAY, (0, 0))
message = "Vider l'historique ?"
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(wrapped_message) * line_height
2025-07-10 22:32:35 +02:00
button_height = int(config.screen_height * 0.0463)
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in wrapped_message], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 150
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
for i, line in enumerate(wrapped_message):
2025-07-10 22:32:35 +02:00
text = config.font.render(line, True, THEME_COLORS["text"])
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
draw_stylized_button(screen, "Oui", rect_x + rect_width // 2 - 180, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_clear_selection == 1)
draw_stylized_button(screen, "Non", rect_x + rect_width // 2 + 20, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_clear_selection == 0)
2025-07-10 22:32:35 +02:00
# Affichage du clavier virtuel sur non-PC
2025-07-06 19:47:21 +02:00
def draw_virtual_keyboard(screen):
2025-07-10 22:32:35 +02:00
"""Affiche un clavier virtuel avec un style moderne."""
2025-07-06 19:47:21 +02:00
keyboard_layout = [
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
['A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
['Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'],
['W', 'X', 'C', 'V', 'B', 'N']
]
2025-07-10 22:32:35 +02:00
key_width = int(config.screen_width * 0.03125)
key_height = int(config.screen_height * 0.0556)
key_spacing = int(config.screen_width * 0.0052)
2025-07-06 19:47:21 +02:00
keyboard_width = len(keyboard_layout[0]) * (key_width + key_spacing) - key_spacing
keyboard_height = len(keyboard_layout) * (key_height + key_spacing) - key_spacing
start_x = (config.screen_width - keyboard_width) // 2
2025-07-10 22:32:35 +02:00
search_bottom_y = int(config.screen_height * 0.111) + (config.search_font.get_height() + 40) // 2
controls_y = config.screen_height - int(config.screen_height * 0.037)
2025-07-06 19:47:21 +02:00
available_height = controls_y - search_bottom_y
start_y = search_bottom_y + (available_height - keyboard_height - 40) // 2
keyboard_rect = pygame.Rect(start_x - 20, start_y - 20, keyboard_width + 40, keyboard_height + 40)
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], keyboard_rect, border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], keyboard_rect, 2, border_radius=12)
2025-07-06 19:47:21 +02:00
for row_idx, row in enumerate(keyboard_layout):
for col_idx, key in enumerate(row):
x = start_x + col_idx * (key_width + key_spacing)
y = start_y + row_idx * (key_height + key_spacing)
key_rect = pygame.Rect(x, y, key_width, key_height)
if (row_idx, col_idx) == config.selected_key:
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"] + (150,), key_rect, border_radius=8)
2025-07-06 19:47:21 +02:00
else:
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], key_rect, border_radius=8)
pygame.draw.rect(screen, THEME_COLORS["border"], key_rect, 1, border_radius=8)
text = config.font.render(key, True, THEME_COLORS["text"])
2025-07-06 19:47:21 +02:00
text_rect = text.get_rect(center=key_rect.center)
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
# Écran de progression de téléchargement/extraction
2025-07-06 19:47:21 +02:00
def draw_progress_screen(screen):
2025-07-10 22:32:35 +02:00
"""Affiche l'écran de progression des téléchargements avec un style moderne."""
2025-07-06 19:47:21 +02:00
if not config.download_tasks:
logger.debug("Aucune tâche de téléchargement active")
return
task = list(config.download_tasks.keys())[0]
game_name = config.download_tasks[task][2]
url = config.download_tasks[task][1]
progress = config.download_progress.get(url, {"downloaded_size": 0, "total_size": 0, "status": "Téléchargement", "progress_percent": 0})
status = progress.get("status", "Téléchargement")
downloaded_size = progress["downloaded_size"]
total_size = progress["total_size"]
progress_percent = progress["progress_percent"]
screen.blit(OVERLAY, (0, 0))
2025-07-06 19:47:21 +02:00
title_text = f"{status} : {truncate_text_middle(game_name, config.font, config.screen_width - 200)}"
2025-07-06 22:05:20 +02:00
title_lines = wrap_text(title_text, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(title_lines) * line_height
margin_top_bottom = 20
2025-07-10 22:32:35 +02:00
bar_height = int(config.screen_height * 0.0278)
percent_height = config.progress_font.get_height() + 5
rect_height = text_height + bar_height + percent_height + 3 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in title_lines], default=300)
2025-07-07 02:48:05 +02:00
bar_width = max_text_width
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 80
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-06 22:05:20 +02:00
for i, line in enumerate(title_lines):
2025-07-10 22:32:35 +02:00
title_render = config.font.render(line, True, THEME_COLORS["text"])
title_rect = title_render.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
2025-07-06 22:05:20 +02:00
screen.blit(title_render, title_rect)
2025-07-06 19:47:21 +02:00
bar_y = rect_y + text_height + margin_top_bottom
progress_width = 0
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x + 20, bar_y, bar_width, bar_height), border_radius=8)
if total_size > 0:
progress_width = int(bar_width * (progress_percent / 100))
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], (rect_x + 20, bar_y, progress_width, bar_height), border_radius=8)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x + 20, bar_y, bar_width, bar_height), 2, border_radius=8)
downloaded_mb = downloaded_size / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
size_text = f"{downloaded_mb:.1f} Mo / {total_mb:.1f} Mo"
percent_text = f"{int(progress_percent)}% {size_text}"
2025-07-10 22:32:35 +02:00
percent_lines = wrap_text(percent_text, config.progress_font, config.screen_width - 80)
text_y = bar_y + bar_height + margin_top_bottom
for i, line in enumerate(percent_lines):
2025-07-10 22:32:35 +02:00
percent_render = config.progress_font.render(line, True, THEME_COLORS["text"])
percent_rect = percent_render.get_rect(center=(config.screen_width // 2, text_y + i * percent_height + percent_height // 2))
screen.blit(percent_render, percent_rect)
2025-07-06 19:47:21 +02:00
2025-07-10 22:32:35 +02:00
# Écran popup résultat téléchargement
def draw_popup_result_download(screen, message, is_error):
2025-07-06 19:47:21 +02:00
"""Affiche une popup avec un message de résultat."""
screen.blit(OVERLAY, (0, 0))
if message is None:
message = "Téléchargement annulé par l'utilisateur."
logger.debug(f"Message popup : {message}, is_error={is_error}")
2025-07-10 22:32:35 +02:00
# Réduire la largeur maximale pour le wrapping
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 160)
# Débogage pour vérifier les lignes wrappées
logger.debug(f"Lignes wrappées : {wrapped_message}")
line_height = config.small_font.get_height() + 5
2025-07-10 22:32:35 +02:00
text_height = len(wrapped_message) * line_height
margin_top_bottom = 20
rect_height = text_height + 2 * margin_top_bottom
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=300)
rect_width = max_text_width + 100 # Augmenter la marge
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-06 22:05:20 +02:00
for i, line in enumerate(wrapped_message):
2025-07-10 22:32:35 +02:00
text = config.small_font.render(line, True, THEME_COLORS["error_text"] if is_error else THEME_COLORS["fond_lignes"])
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
2025-07-06 22:05:20 +02:00
screen.blit(text, text_rect)
2025-07-06 19:47:21 +02:00
2025-07-10 22:32:35 +02:00
# Écran avertissement extension non supportée téléchargement
2025-07-06 19:47:21 +02:00
def draw_extension_warning(screen):
"""Affiche un avertissement pour une extension non reconnue ou un fichier ZIP."""
if not config.pending_download:
logger.error("config.pending_download est None ou vide dans extension_warning")
2025-07-06 19:47:21 +02:00
message = "Erreur : Aucun téléchargement en attente."
is_zip = False
game_name = "Inconnu"
else:
url, platform, game_name, is_zip_non_supported = config.pending_download
logger.debug(f"config.pending_download: url={url}, platform={platform}, game_name={game_name}, is_zip_non_supported={is_zip_non_supported}")
2025-07-06 19:47:21 +02:00
is_zip = is_zip_non_supported
if not game_name:
game_name = "Inconnu"
logger.warning("game_name vide, utilisation de 'Inconnu'")
if is_zip:
message = f"Le fichier '{game_name}' est une archive et Batocera ne prend pas en charge les archives pour ce système. L'extraction automatique du fichier aura lieu après le téléchargement, continuer ?"
else:
message = f"L'extension du fichier '{game_name}' n'est pas supportée par Batocera d'après le fichier info.txt. Voulez-vous continuer ?"
max_width = config.screen_width - 80
lines = wrap_text(message, config.font, max_width)
logger.debug(f"Lignes générées : {lines}")
2025-07-06 19:47:21 +02:00
try:
line_height = config.font.get_height() + 5
text_height = len(lines) * line_height
2025-07-10 22:32:35 +02:00
button_height = int(config.screen_height * 0.0463)
2025-07-06 19:47:21 +02:00
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in lines], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 80
2025-07-06 19:47:21 +02:00
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
screen.blit(OVERLAY, (0, 0))
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
for i, line in enumerate(lines):
2025-07-10 22:32:35 +02:00
text_surface = config.font.render(line, True, THEME_COLORS["warning_text"])
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
2025-07-10 22:32:35 +02:00
draw_stylized_button(screen, "Oui", rect_x + rect_width // 2 - 180, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.extension_confirm_selection == 1)
draw_stylized_button(screen, "Non", rect_x + rect_width // 2 + 20, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.extension_confirm_selection == 0)
2025-07-06 19:47:21 +02:00
except Exception as e:
logger.error(f"Erreur lors du rendu de extension_warning : {str(e)}")
error_message = "Erreur d'affichage de l'avertissement."
2025-07-06 22:05:20 +02:00
wrapped_error = wrap_text(error_message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
rect_height = len(wrapped_error) * line_height + 2 * 20
max_text_width = max([config.font.size(line)[0] for line in wrapped_error], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 80
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
screen.blit(OVERLAY, (0, 0))
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-06 22:05:20 +02:00
for i, line in enumerate(wrapped_error):
2025-07-10 22:32:35 +02:00
error_surface = config.font.render(line, True, THEME_COLORS["error_text"])
error_rect = error_surface.get_rect(center=(config.screen_width // 2, rect_y + 20 + i * line_height + line_height // 2))
2025-07-06 22:05:20 +02:00
screen.blit(error_surface, error_rect)
2025-07-06 19:47:21 +02:00
2025-07-10 22:32:35 +02:00
# Affichage des contrôles en bas de page
2025-07-06 19:47:21 +02:00
def draw_controls(screen, menu_state):
2025-07-10 22:32:35 +02:00
"""Affiche les contrôles sur une seule ligne en bas de lécran."""
start_button = get_control_display('start', 'START')
2025-07-10 22:32:35 +02:00
control_text = f"RGSX v{config.app_version} - {start_button} : Options - History - Help"
2025-07-06 19:47:21 +02:00
max_width = config.screen_width - 40
wrapped_controls = wrap_text(control_text, config.small_font, max_width)
line_height = config.small_font.get_height() + 5
2025-07-06 22:05:20 +02:00
rect_height = len(wrapped_controls) * line_height + 20
2025-07-07 02:48:05 +02:00
rect_y = config.screen_height - rect_height - 5
2025-07-10 22:32:35 +02:00
rect_x = (config.screen_width - max_width) // 2
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, max_width, rect_height), border_radius=8)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, max_width, rect_height), 1, border_radius=8)
2025-07-06 19:47:21 +02:00
2025-07-06 22:05:20 +02:00
for i, line in enumerate(wrapped_controls):
2025-07-10 22:32:35 +02:00
text_surface = config.small_font.render(line, True, THEME_COLORS["text"])
2025-07-06 22:05:20 +02:00
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + 10 + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
2025-07-06 19:47:21 +02:00
2025-07-10 22:32:35 +02:00
# Menu pause
2025-07-06 19:47:21 +02:00
def draw_pause_menu(screen, selected_option):
2025-07-10 22:32:35 +02:00
"""Dessine le menu pause avec un style moderne."""
screen.blit(OVERLAY, (0, 0))
2025-07-06 19:47:21 +02:00
options = [
"Controls",
"Remap controls",
2025-07-07 02:48:05 +02:00
"History",
"Redownload Games cache",
2025-07-06 19:47:21 +02:00
"Quit"
]
2025-07-10 22:32:35 +02:00
menu_width = int(config.screen_width * 0.8)
2025-07-06 22:05:20 +02:00
line_height = config.font.get_height() + 10
2025-07-10 22:32:35 +02:00
button_height = int(config.screen_height * 0.0463)
margin_top_bottom = 20
2025-07-10 22:32:35 +02:00
menu_height = len(options) * (button_height + 10) + 2 * margin_top_bottom
2025-07-06 19:47:21 +02:00
menu_x = (config.screen_width - menu_width) // 2
menu_y = (config.screen_height - menu_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (menu_x, menu_y, menu_width, menu_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (menu_x, menu_y, menu_width, menu_height), 2, border_radius=12)
2025-07-06 19:47:21 +02:00
for i, option in enumerate(options):
2025-07-10 22:32:35 +02:00
draw_stylized_button(
screen,
option,
menu_x + 20,
menu_y + margin_top_bottom + i * (button_height + 10),
menu_width - 40,
button_height,
selected=i == selected_option
)
# Menu aide contrôles
2025-07-06 19:47:21 +02:00
def draw_controls_help(screen, previous_state):
2025-07-10 22:32:35 +02:00
"""Affiche la liste des contrôles avec un style moderne."""
common_controls = {
"confirm": lambda action: f"{get_control_display('confirm', 'Entrée/A')} : {action}",
"cancel": lambda action: f"{get_control_display('cancel', 'Échap/B')} : {action}",
"start": lambda: f"{get_control_display('start', 'Start')} : Menu",
"progress": lambda action: f"{get_control_display('progress', 'X')} : {action}",
"up": lambda action: f"{get_control_display('up', 'Flèche Haut')} : {action}",
"down": lambda action: f"{get_control_display('down', 'Flèche Bas')} : {action}",
"page_up": lambda action: f"{get_control_display('page_up', 'Q/LB')} : {action}",
"page_down": lambda action: f"{get_control_display('page_down', 'E/RB')} : {action}",
"filter": lambda action: f"{get_control_display('filter', 'Select')} : {action}",
"history": lambda action: f"{get_control_display('history', 'H')} : {action}",
"delete": lambda: f"{get_control_display('delete', 'Retour Arrière')} : Supprimer",
"space": lambda: f"{get_control_display('space', 'Espace')} : Espace"
}
2025-07-06 19:47:21 +02:00
state_controls = {
"error": [
common_controls["confirm"]("Retenter"),
common_controls["cancel"]("Quitter")
2025-07-06 19:47:21 +02:00
],
"platform": [
common_controls["confirm"]("Sélectionner"),
common_controls["cancel"]("Quitter"),
common_controls["start"](),
common_controls["history"]("Historique"),
*( [common_controls["progress"]("Progression")] if config.download_tasks else [])
2025-07-06 19:47:21 +02:00
],
"game": [
common_controls["confirm"](f"{'Selectionner' if config.search_mode else 'Télécharger'}"),
common_controls["filter"]("Filtrer"),
common_controls["cancel"](f"{'Annuler' if config.search_mode else 'Retour'}"),
common_controls["history"]("Historique"),
2025-07-06 19:47:21 +02:00
*( [
common_controls["delete"](),
common_controls["space"]()
2025-07-06 19:47:21 +02:00
] if config.search_mode and config.is_non_pc else []),
*( [
f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}",
f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}",
common_controls["filter"]("Filtrer")
2025-07-06 19:47:21 +02:00
] if not config.is_non_pc or not config.search_mode else []),
common_controls["start"](),
*( [common_controls["progress"]("Progression")] if config.download_tasks and not config.search_mode else [])
2025-07-06 19:47:21 +02:00
],
"download_progress": [
common_controls["cancel"]("Annuler le téléchargement"),
common_controls["progress"]("Arrière plan"),
common_controls["start"]()
2025-07-06 19:47:21 +02:00
],
"download_result": [
common_controls["confirm"]("Retour")
2025-07-06 19:47:21 +02:00
],
"confirm_exit": [
common_controls["confirm"]("Confirmer")
2025-07-06 19:47:21 +02:00
],
"extension_warning": [
common_controls["confirm"]("Confirmer")
2025-07-07 02:48:05 +02:00
],
"history": [
common_controls["confirm"]("Retélécharger"),
common_controls["cancel"]("Retour"),
common_controls["progress"]("Vider l'historique"),
f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}",
f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}",
common_controls["start"]()
2025-07-06 19:47:21 +02:00
]
}
controls = state_controls.get(previous_state, [])
if not controls:
return
2025-07-06 19:47:21 +02:00
screen.blit(OVERLAY, (0, 0))
2025-07-06 19:47:21 +02:00
max_width = config.screen_width - 80
wrapped_controls = []
current_line = ""
for control in controls:
test_line = f"{current_line} | {control}" if current_line else control
if config.font.size(test_line)[0] <= max_width:
current_line = test_line
else:
wrapped_controls.append(current_line)
current_line = control
if current_line:
wrapped_controls.append(current_line)
line_height = config.font.get_height() + 10
popup_width = max_width + 40
popup_height = len(wrapped_controls) * line_height + 60
popup_x = (config.screen_width - popup_width) // 2
popup_y = (config.screen_height - popup_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (popup_x, popup_y, popup_width, popup_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (popup_x, popup_y, popup_width, popup_height), 2, border_radius=12)
2025-07-06 19:47:21 +02:00
for i, line in enumerate(wrapped_controls):
2025-07-10 22:32:35 +02:00
text = config.font.render(line, True, THEME_COLORS["text"])
2025-07-06 19:47:21 +02:00
text_rect = text.get_rect(center=(config.screen_width // 2, popup_y + 40 + i * line_height))
2025-07-07 02:48:05 +02:00
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
# Menu Quitter Appli
2025-07-07 02:48:05 +02:00
def draw_confirm_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour quitter."""
2025-07-07 03:46:28 +02:00
global OVERLAY
if OVERLAY is None or OVERLAY.get_size() != (config.screen_width, config.screen_height):
OVERLAY = pygame.Surface((config.screen_width, config.screen_height), pygame.SRCALPHA)
2025-07-10 22:32:35 +02:00
OVERLAY.fill((0, 0, 0, 150))
2025-07-07 03:46:28 +02:00
logger.debug("OVERLAY recréé dans draw_confirm_dialog")
2025-07-10 22:32:35 +02:00
2025-07-07 02:48:05 +02:00
screen.blit(OVERLAY, (0, 0))
2025-07-07 03:46:28 +02:00
message = "Quitter l'application ?"
2025-07-07 02:48:05 +02:00
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(wrapped_message) * line_height
2025-07-10 22:32:35 +02:00
button_height = int(config.screen_height * 0.0463)
2025-07-07 02:48:05 +02:00
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in wrapped_message], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 150
2025-07-07 02:48:05 +02:00
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-07 02:48:05 +02:00
for i, line in enumerate(wrapped_message):
2025-07-10 22:32:35 +02:00
text = config.font.render(line, True, THEME_COLORS["text"])
2025-07-07 02:48:05 +02:00
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
draw_stylized_button(screen, "Oui", rect_x + rect_width // 2 - 180, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_selection == 1)
draw_stylized_button(screen, "Non", rect_x + rect_width // 2 + 20, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.confirm_selection == 0)
# draw_redownload_game_cache_dialog
def draw_redownload_game_cache_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour retélécharger le cache des jeux."""
global OVERLAY
if OVERLAY is None or OVERLAY.get_size() != (config.screen_width, config.screen_height):
OVERLAY = pygame.Surface((config.screen_width, config.screen_height), pygame.SRCALPHA)
2025-07-10 22:32:35 +02:00
OVERLAY.fill((0, 0, 0, 150))
logger.debug("OVERLAY recréé dans draw_redownload_game_cache_dialog")
2025-07-10 22:32:35 +02:00
2025-07-07 02:48:05 +02:00
screen.blit(OVERLAY, (0, 0))
message = "Retélécharger le cache des jeux ?"
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 80)
line_height = config.small_font.get_height() + 5
2025-07-07 02:48:05 +02:00
text_height = len(wrapped_message) * line_height
2025-07-10 22:32:35 +02:00
button_height = int(config.screen_height * 0.0463)
2025-07-07 02:48:05 +02:00
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=300)
2025-07-10 22:32:35 +02:00
rect_width = max_text_width + 80
2025-07-07 02:48:05 +02:00
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
2025-07-07 02:48:05 +02:00
for i, line in enumerate(wrapped_message):
2025-07-10 22:32:35 +02:00
text = config.small_font.render(line, True, THEME_COLORS["text"])
2025-07-07 02:48:05 +02:00
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect)
2025-07-10 22:32:35 +02:00
draw_stylized_button(screen, "Oui", rect_x + rect_width // 2 - 180, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.redownload_confirm_selection == 1)
draw_stylized_button(screen, "Non", rect_x + rect_width // 2 + 20, rect_y + text_height + margin_top_bottom, 160, button_height, selected=config.redownload_confirm_selection == 0)
2025-07-10 22:32:35 +02:00
# Popup avec compte à rebours
def draw_popup(screen):
"""Dessine un popup avec un message et un compte à rebours."""
screen.blit(OVERLAY, (0, 0))
2025-07-10 22:32:35 +02:00
popup_width = int(config.screen_width * 0.8)
line_height = config.small_font.get_height() + 10
text_lines = config.popup_message.split('\n')
text_height = len(text_lines) * line_height
margin_top_bottom = 20
2025-07-10 22:32:35 +02:00
popup_height = text_height + 2 * margin_top_bottom + line_height
popup_x = (config.screen_width - popup_width) // 2
popup_y = (config.screen_height - popup_height) // 2
2025-07-10 22:32:35 +02:00
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (popup_x, popup_y, popup_width, popup_height), border_radius=12)
pygame.draw.rect(screen, THEME_COLORS["border"], (popup_x, popup_y, popup_width, popup_height), 2, border_radius=12)
for i, line in enumerate(text_lines):
2025-07-10 22:32:35 +02:00
text_surface = config.small_font.render(line, True, THEME_COLORS["text"])
text_rect = text_surface.get_rect(center=(config.screen_width // 2, popup_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
2025-07-10 22:32:35 +02:00
remaining_time = max(0, config.popup_timer // 1000)
countdown_text = f"Ce message se fermera dans {remaining_time} seconde{'s' if remaining_time != 1 else ''}"
2025-07-10 22:32:35 +02:00
countdown_surface = config.small_font.render(countdown_text, True, THEME_COLORS["text"])
countdown_rect = countdown_surface.get_rect(center=(config.screen_width // 2, popup_y + margin_top_bottom + len(text_lines) * line_height + line_height // 2))
screen.blit(countdown_surface, countdown_rect)