Ajout fonction 1fichier avec api + fonction de mise a jour du cache des jeux et corrections de bugs

This commit is contained in:
skymike03
2025-07-09 01:13:55 +02:00
parent c89d7100ef
commit 849f469455
9 changed files with 4794 additions and 442 deletions

View File

@@ -1,15 +1,15 @@
import pygame
import pygame # type: ignore
import config
import math
from utils import truncate_text_end, wrap_text, load_system_image, load_games
from utils import truncate_text_middle, wrap_text, load_system_image, load_games
import logging
from history import load_history # Ajout de l'import
logger = logging.getLogger(__name__)
# Cache global pour l'overlay semi-transparent
OVERLAY = None # Initialisé dans init_display()
#Général, résolution, overlay
def init_display():
"""Initialise l'écran et les ressources globales."""
global OVERLAY
@@ -26,6 +26,7 @@ def init_display():
logger.debug(f"Écran initialisé avec résolution : {screen_width}x{screen_height}")
return screen
#Fond d'ecran dégradé
def draw_gradient(screen, top_color, bottom_color):
"""Dessine un fond dégradé vertical."""
height = screen.get_height()
@@ -36,6 +37,30 @@ def draw_gradient(screen, top_color, bottom_color):
color = top_color.lerp(bottom_color, ratio)
pygame.draw.line(screen, color, (0, y), (screen.get_width(), y))
#Transistion d'image lors de la selection d'un systeme
def draw_validation_transition(screen, platform_index):
"""Affiche une animation de transition pour la sélection dune plateforme."""
platform_dict = config.platform_dicts[platform_index]
image = load_system_image(platform_dict)
if not image:
return
orig_width, orig_height = image.get_width(), image.get_height()
base_size = int(config.screen_width * 0.0781) # ~150px pour 1920p
start_time = pygame.time.get_ticks()
duration = 500
while pygame.time.get_ticks() - start_time < duration:
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
elapsed = pygame.time.get_ticks() - start_time
scale = 2.0 + (2.0 * elapsed / duration) if elapsed < duration / 2 else 3.0 - (2.0 * elapsed / duration)
new_width = int(base_size * scale)
new_height = int(base_size * scale)
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))
screen.blit(scaled_image, image_rect)
pygame.display.flip()
pygame.time.wait(10)
#Ecran de chargement
def draw_loading_screen(screen):
"""Affiche lécran de chargement avec le disclaimer en haut, le texte de chargement et la barre de progression."""
disclaimer_lines = [
@@ -52,7 +77,7 @@ def draw_loading_screen(screen):
border_width = 3
shadow_offset = 6
line_height = config.font.get_height() + padding_between
line_height = config.small_font.get_height() + padding_between
total_height = line_height * len(disclaimer_lines) - padding_between
rect_width = config.screen_width - 2 * margin_horizontal
rect_height = total_height + 2 * padding_vertical
@@ -73,9 +98,9 @@ def draw_loading_screen(screen):
max_text_width = rect_width - 2 * padding_vertical
for i, line in enumerate(disclaimer_lines):
wrapped_lines = wrap_text(line, config.font, max_text_width)
wrapped_lines = wrap_text(line, config.small_font, max_text_width)
for j, wrapped_line in enumerate(wrapped_lines):
text_surface = config.font.render(wrapped_line, True, (255, 255, 255))
text_surface = config.small_font.render(wrapped_line, True, (255, 255, 255))
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
@@ -83,11 +108,11 @@ def draw_loading_screen(screen):
screen.blit(text_surface, text_rect)
loading_y = rect_y + rect_height + int(config.screen_height * 0.0926) # ~100px pour 1080p
text = config.font.render(truncate_text_end(f"{config.current_loading_system}", config.font, config.screen_width - 2 * margin_horizontal), True, (255, 255, 255))
text = config.small_font.render(truncate_text_middle(f"{config.current_loading_system}", config.small_font, config.screen_width - 2 * margin_horizontal), True, (255, 255, 255))
text_rect = text.get_rect(center=(config.screen_width // 2, loading_y))
screen.blit(text, text_rect)
progress_text = config.font.render(f"Progression : {int(config.loading_progress)}%", True, (255, 255, 255))
progress_text = config.small_font.render(f"Progression : {int(config.loading_progress)}%", True, (255, 255, 255))
progress_rect = progress_text.get_rect(center=(config.screen_width // 2, loading_y + int(config.screen_height * 0.0463))) # ~50px pour 1080p
screen.blit(progress_text, progress_rect)
@@ -97,6 +122,7 @@ def draw_loading_screen(screen):
pygame.draw.rect(screen, (100, 100, 100), (config.screen_width // 2 - bar_width // 2, loading_y + int(config.screen_height * 0.0926), bar_width, bar_height))
pygame.draw.rect(screen, (0, 255, 0), (config.screen_width // 2 - bar_width // 2, loading_y + int(config.screen_height * 0.0926), progress_width, bar_height))
#Ecran d'erreur
def draw_error_screen(screen):
"""Affiche lécran derreur."""
error_font = pygame.font.SysFont("arial", 28)
@@ -106,10 +132,20 @@ def draw_error_screen(screen):
text = error_font.render(line, True, (255, 0, 0))
text_rect = text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 - (len(wrapped_message) // 2 - i) * line_height))
screen.blit(text, text_rect)
retry_text = config.font.render(f"{get_control_display('confirm', 'Entrée/A')} : retenter, {get_control_display('cancel', 'Échap/B')} : quitter", True, (255, 255, 255))
retry_rect = retry_text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 + int(config.screen_height * 0.0926))) # ~100px pour 1080p
screen.blit(retry_text, retry_rect)
# Afficher uniquement "Valider"
confirm_text = config.small_font.render("Valider", True, (0, 150, 255))
confirm_rect = confirm_text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 + int(config.screen_height * 0.0926)))
screen.blit(confirm_text, confirm_rect)
#Recuperer les noms d'affichage des controles
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)
#Grille des systemes 3x3
def draw_platform_grid(screen):
"""Affiche la grille des plateformes avec un titre en haut."""
# Configuration du titre
@@ -149,7 +185,7 @@ def draw_platform_grid(screen):
y_positions = [margin_top + row_height * i + row_height // 2 for i in range(num_rows)]
start_idx = config.current_page * systems_per_page
logger.debug(f"Page {config.current_page}, start_idx: {start_idx}, total_platforms: {len(config.platforms)}")
#logger.debug(f"Page {config.current_page}, start_idx: {start_idx}, total_platforms: {len(config.platforms)}")
for idx in range(start_idx, start_idx + systems_per_page):
if idx >= len(config.platforms):
@@ -196,9 +232,10 @@ def draw_platform_grid(screen):
screen.blit(image, image_rect)
#Liste des jeux
def draw_game_list(screen):
"""Affiche la liste des jeux avec défilement et rectangle de fond."""
logger.debug("Début de draw_game_list")
#logger.debug("Début de draw_game_list")
platform = config.platforms[config.current_platform]
platform_name = config.platform_names.get(platform, platform)
@@ -228,25 +265,26 @@ def draw_game_list(screen):
screen.blit(text_surface, text_rect)
return
line_height = config.font.get_height() + 10
line_height = config.small_font.get_height() + 10
#header_height = line_height
margin_top_bottom = 20
extra_margin_top = 5
extra_margin_bottom = 40
title_height = max(config.title_font.get_height(), config.search_font.get_height(), config.small_font.get_height()) + 20
extra_margin_top = 20
extra_margin_bottom = 60 # Aligné sur draw_history_list
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
games_per_page = available_height // line_height
config.visible_games = games_per_page # Mettre à jour config.visible_games
max_text_width = max([config.font.size(truncate_text_end(game[0] if isinstance(game, (list, tuple)) else game, config.font, config.screen_width - 80))[0] for game in games], default=300)
rect_width = max_text_width + 40
rect_height = games_per_page * line_height + 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) - games_per_page)))
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 + games_per_page:
config.scroll_offset = config.current_game - games_per_page + 1
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))
@@ -281,33 +319,68 @@ def draw_game_list(screen):
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i in range(config.scroll_offset, min(config.scroll_offset + games_per_page, len(games))):
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]
color = (0, 150, 255) if i == config.current_game else (255, 255, 255)
game_text = truncate_text_end(game_name, config.font, config.screen_width - 80)
text_surface = config.font.render(game_text, True, color)
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))
screen.blit(text_surface, text_rect)
logger.debug(f"Jeu affiché : texte={game_text}, position={text_rect}, selected={i == config.current_game}")
#logger.debug(f"Jeu affiché : texte={game_text}, position={text_rect}, selected={i == config.current_game}")
draw_scrollbar(screen)
if config.search_mode and config.is_non_pc:
draw_virtual_keyboard(screen)
# Afficher la barre de scroll si besoin
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)}")
#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))
pygame.draw.rect(screen, (255, 255, 255), (x, scrollbar_y, 15, scrollbar_height))
#Liste historique téléchargement
def draw_history_list(screen):
"""Affiche l'historique des téléchargements sous forme de tableau avec système, nom du jeu et état."""
logger.debug("Début de draw_history_list")
history = config.history if hasattr(config, 'history') else load_history()
history_count = len(history)
# Définir les largeurs des colonnes (valeurs fixes pour simplifier, ajustez si nécessaire)
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)
rect_width = int(0.95 * config.screen_width) # 95% de la largeur de l'écran
# Hauteur des lignes et en-tête
line_height = config.small_font.get_height() + 10
header_height = line_height
margin_top_bottom = 20
extra_margin_top = 20
extra_margin_bottom = 60
title_height = config.title_font.get_height() + 20
# Cas où l'historique est vide
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
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)
rect_width = max_text_width + 40
@@ -324,33 +397,26 @@ def draw_history_list(screen):
screen.blit(text_surface, text_rect)
return
line_height = config.small_font.get_height() + 10
margin_top_bottom = 20
extra_margin_top = 5
extra_margin_bottom = 40
title_height = config.title_font.get_height() + 20
# Calculer la hauteur disponible et les éléments par page
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
items_per_page = available_height // line_height
config.visible_history_items = items_per_page # Mettre à jour config.visible_history_items
# Calculer la largeur des colonnes
col_platform_width = config.screen_width // 4 # ~25% pour le système
col_game_width = config.screen_width // 2 # ~50% pour le nom du jeu
col_status_width = config.screen_width // 4 # ~25% pour l'état
max_text_width = col_platform_width + col_game_width + col_status_width
rect_width = max_text_width + 40
rect_height = items_per_page * line_height + 2 * margin_top_bottom
# Calculer les dimensions du rectangle
rect_height = header_height + items_per_page * line_height + 2 * margin_top_bottom
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
# Gestion du défilement
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
# Fond et cadre
screen.blit(OVERLAY, (0, 0))
# Titre
title_text = f"Historique des téléchargements ({history_count})"
title_surface = config.title_font.render(title_text, True, (255, 255, 255))
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 10))
@@ -360,12 +426,13 @@ def draw_history_list(screen):
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
screen.blit(title_surface, title_rect)
# Cadre du tableau
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
# En-têtes du tableau
headers = ["Système", "Nom du jeu", "État"]
header_y = rect_y + margin_top_bottom - line_height // 2
header_y = rect_y + margin_top_bottom + header_height // 2
header_x_positions = [
rect_x + 20 + col_platform_width // 2,
rect_x + 20 + col_platform_width + col_game_width // 2,
@@ -376,18 +443,18 @@ def draw_history_list(screen):
text_rect = text_surface.get_rect(center=(x_pos, header_y))
screen.blit(text_surface, text_rect)
# Lignes du tableau
for i in range(config.history_scroll_offset, min(config.history_scroll_offset + items_per_page, len(history))):
# Afficher les entrées de l'historique
for idx, i in enumerate(range(config.history_scroll_offset, min(config.history_scroll_offset + items_per_page, len(history)))):
entry = history[i]
platform = entry["platform"]
game_name = entry["game_name"]
status = entry["status"]
platform = entry.get("platform", "Inconnu")
game_name = entry.get("game_name", "Inconnu")
status = entry.get("status", "Inconnu")
color = (0, 150, 255) if i == config.current_history_item else (255, 255, 255)
platform_text = truncate_text_end(platform, config.small_font, col_platform_width - 10)
game_text = truncate_text_end(game_name, config.small_font, col_game_width - 10)
status_text = truncate_text_end(status, config.small_font, col_status_width - 10)
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)
y_pos = rect_y + margin_top_bottom + (i - config.history_scroll_offset + 1) * line_height + line_height // 2
y_pos = rect_y + margin_top_bottom + header_height + idx * line_height + line_height // 2
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)
@@ -399,20 +466,68 @@ def draw_history_list(screen):
screen.blit(platform_surface, platform_rect)
screen.blit(game_surface, game_rect)
screen.blit(status_surface, status_rect)
logger.debug(f"Entrée historique affichée : index={i}, platform={platform_text}, game={game_text}, status={status_text}, selected={i == config.current_history_item}")
#logger.debug(f"Entrée historique affichée : index={i}, platform={platform_text}, game={game_text}, status={status_text}, selected={i == config.current_history_item}")
draw_history_scrollbar(screen)
# Scrollbar
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)}")
def draw_history_scrollbar(screen):
"""Affiche la barre de défilement pour l'historique."""
if len(config.history) <= config.visible_history_items:
#Barre de défilement de l'historique
def draw_history_scrollbar(screen, scroll_offset, total_items, visible_items, x, y, height):
"""Affiche la barre de défilement à droite de lécran."""
if len(config.filtered_games) <= config.visible_games:
return
game_area_height = config.screen_height - 150
scrollbar_height = game_area_height * (config.visible_history_items / len(config.history))
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.history_scroll_offset / max(1, len(config.history) - config.visible_history_items))
scrollbar_height = game_area_height * (config.visible_games / len(config.filtered_games))
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (scroll_offset / max(1, len(config.filtered_games) - config.visible_games))
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
#Ecran 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
button_height = line_height + 20
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)
rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, (255, 255, 255))
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)
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_clear_selection == 1 else (255, 255, 255))
no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_clear_selection == 0 else (255, 255, 255))
yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2))
no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2))
screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect)
#Affichage du clavier virtuel sur non pc
def draw_virtual_keyboard(screen):
"""Affiche un clavier virtuel pour la saisie dans search_mode, centré verticalement."""
keyboard_layout = [
@@ -450,9 +565,10 @@ def draw_virtual_keyboard(screen):
text_rect = text.get_rect(center=key_rect.center)
screen.blit(text, text_rect)
#Ecran de progression de téléchargement/extraction
def draw_progress_screen(screen):
"""Affiche l'écran de progression des téléchargements avec taille en Mo."""
logger.debug("Début de draw_progress_screen")
#logger.debug("Début de draw_progress_screen")
if not config.download_tasks:
logger.debug("Aucune tâche de téléchargement active")
@@ -466,11 +582,11 @@ def draw_progress_screen(screen):
downloaded_size = progress["downloaded_size"]
total_size = progress["total_size"]
progress_percent = progress["progress_percent"]
logger.debug(f"Progression : game_name={game_name}, url={url}, status={status}, progress_percent={progress_percent}, downloaded_size={downloaded_size}, total_size={total_size}")
#logger.debug(f"Progression : game_name={game_name}, url={url}, status={status}, progress_percent={progress_percent}, downloaded_size={downloaded_size}, total_size={total_size}")
screen.blit(OVERLAY, (0, 0))
title_text = f"{status} : {truncate_text_end(game_name, config.font, config.screen_width - 200)}"
title_text = f"{status} : {truncate_text_middle(game_name, config.font, config.screen_width - 200)}"
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
@@ -491,7 +607,7 @@ def draw_progress_screen(screen):
title_render = config.font.render(line, True, (255, 255, 255))
title_rect = title_render.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(title_render, title_rect)
logger.debug(f"Titre affiché : texte={line}, position={title_rect}, taille={title_render.get_size()}")
#logger.debug(f"Titre affiché : texte={line}, position={title_rect}, taille={title_render.get_size()}")
bar_y = rect_y + text_height + margin_top_bottom
progress_width = 0
@@ -500,7 +616,7 @@ def draw_progress_screen(screen):
progress_width = int(bar_width * (progress_percent / 100))
pygame.draw.rect(screen, (0, 150, 255), (rect_x + 20, bar_y, progress_width, bar_height))
pygame.draw.rect(screen, (255, 255, 255), (rect_x + 20, bar_y, bar_width, bar_height), 2)
logger.debug(f"Barre de progression affichée : position=({rect_x + 20}, {bar_y}), taille=({bar_width}, {bar_height}), progress_width={progress_width}")
#logger.debug(f"Barre de progression affichée : position=({rect_x + 20}, {bar_y}), taille=({bar_width}, {bar_height}), progress_width={progress_width}")
downloaded_mb = downloaded_size / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
@@ -512,64 +628,25 @@ def draw_progress_screen(screen):
percent_render = config.font.render(line, True, (255, 255, 255))
percent_rect = percent_render.get_rect(center=(config.screen_width // 2, text_y + i * line_height + line_height // 2))
screen.blit(percent_render, percent_rect)
logger.debug(f"Texte de progression affiché : texte={line}, position={percent_rect}, taille={percent_render.get_size()}")
def draw_scrollbar(screen):
"""Affiche la barre de défilement à droite de lécran."""
if len(config.filtered_games) <= config.visible_games:
return
game_area_height = config.screen_height - 150
scrollbar_height = game_area_height * (config.visible_games / len(config.filtered_games))
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.scroll_offset / max(1, len(config.filtered_games) - config.visible_games))
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
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
button_height = line_height + 20
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)
rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, (255, 255, 255))
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)
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_clear_selection == 1 else (255, 255, 255))
no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_clear_selection == 0 else (255, 255, 255))
yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2))
no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2))
screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect)
#Ecran popup resultat téléchargement
def draw_popup_message(screen, message, is_error):
"""Affiche une popup avec un message de résultat."""
screen.blit(OVERLAY, (0, 0))
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
if message is None:
message = "Téléchargement annulé"
logger.debug(f"Message popup : {message}, is_error={is_error}")
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 80)
line_height = config.small_font.get_height() + 5
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, (255, 0, 0) if is_error else (0, 255, 0))
text = config.small_font.render(line, True, (255, 0, 0) if is_error else (0, 255, 0))
text_rect = text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 - (len(wrapped_message) // 2 - i) * line_height))
screen.blit(text, text_rect)
#Ecran avertissement extension non supportée téléchargement
def draw_extension_warning(screen):
"""Affiche un avertissement pour une extension non reconnue ou un fichier ZIP."""
logger.debug("Début de draw_extension_warning")
#logger.debug("Début de draw_extension_warning")
if not config.pending_download:
logger.error("config.pending_download est None ou vide dans extension_warning")
@@ -612,7 +689,7 @@ def draw_extension_warning(screen):
text_surface = config.font.render(line, True, (255, 255, 255))
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)
logger.debug(f"Lignes affichées : {[(rect.center, text_surface.get_size()) for rect, text_surface in zip([text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) for i in range(len(lines))], [config.font.render(line, True, (255, 255, 255)) for line in lines])]}")
#logger.debug(f"Lignes affichées : {[(rect.center, text_surface.get_size()) for rect, text_surface in zip([text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) for i in range(len(lines))], [config.font.render(line, True, (255, 255, 255)) for line in lines])]}")
yes_text = "[Oui]" if config.extension_confirm_selection == 1 else "Oui"
no_text = "[Non]" if config.extension_confirm_selection == 0 else "Non"
@@ -625,7 +702,7 @@ def draw_extension_warning(screen):
screen.blit(yes_surface, yes_rect)
screen.blit(no_surface, no_rect)
logger.debug(f"Boutons affichés : Oui={yes_rect}, Non={no_rect}, selection={config.extension_confirm_selection}")
#logger.debug(f"Boutons affichés : Oui={yes_rect}, Non={no_rect}, selection={config.extension_confirm_selection}")
except Exception as e:
logger.error(f"Erreur lors du rendu de extension_warning : {str(e)}")
@@ -647,10 +724,11 @@ def draw_extension_warning(screen):
error_rect = error_surface.get_rect(center=(config.screen_width // 2, rect_y + 20 + i * line_height + line_height // 2))
screen.blit(error_surface, error_rect)
#Affichage des controles en bas de page
def draw_controls(screen, menu_state):
"""Affiche les contrôles sur une seule ligne en bas de lécran pour tous les états du menu."""
start_button = get_control_display('start', 'START')
control_text = f"Menu {menu_state} - {start_button} : Options - History - Controls"
control_text = f"{start_button} : Options - History - Controls"
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
@@ -662,40 +740,20 @@ def draw_controls(screen, menu_state):
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)
def draw_validation_transition(screen, platform_index):
"""Affiche une animation de transition pour la sélection dune plateforme."""
platform_dict = config.platform_dicts[platform_index]
image = load_system_image(platform_dict)
if not image:
return
orig_width, orig_height = image.get_width(), image.get_height()
base_size = int(config.screen_width * 0.0781) # ~150px pour 1920p
start_time = pygame.time.get_ticks()
duration = 500
while pygame.time.get_ticks() - start_time < duration:
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
elapsed = pygame.time.get_ticks() - start_time
scale = 2.0 + (2.0 * elapsed / duration) if elapsed < duration / 2 else 3.0 - (2.0 * elapsed / duration)
new_width = int(base_size * scale)
new_height = int(base_size * scale)
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))
screen.blit(scaled_image, image_rect)
pygame.display.flip()
pygame.time.wait(10)
#Menu pause
def draw_pause_menu(screen, selected_option):
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Historique, Quitter."""
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Historique, Redownload game cache, Quitter."""
screen.blit(OVERLAY, (0, 0))
options = [
"Controls",
"Remap controls",
"History",
"Redownload Games cache",
"Quit"
]
menu_width = int(config.screen_width * 0.2083) # ~400px pour 1920p
menu_width = int(config.screen_width * 0.8) # ~400px pour 1920p
line_height = config.font.get_height() + 10
text_height = len(options) * line_height
margin_top_bottom = 20
@@ -712,13 +770,7 @@ def draw_pause_menu(screen, selected_option):
text_rect = text_surface.get_rect(center=(config.screen_width // 2, menu_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
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)
#Menu aide controles
def draw_controls_help(screen, previous_state):
"""Affiche la liste des contrôles pour l'état précédent du menu."""
common_controls = {
@@ -731,6 +783,7 @@ def draw_controls_help(screen, previous_state):
"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"
}
@@ -744,11 +797,13 @@ def draw_controls_help(screen, previous_state):
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 [])
],
"game": [
common_controls["confirm"](f"{'Valider' if config.search_mode else 'Télécharger'}"),
common_controls["cancel"](f"{'Annuler' if config.search_mode else 'Retour'}"),
common_controls["history"]("Historique"),
*( [
common_controls["delete"](),
common_controls["space"]()
@@ -814,63 +869,13 @@ def draw_controls_help(screen, previous_state):
text = config.font.render(line, True, (255, 255, 255))
text_rect = text.get_rect(center=(config.screen_width // 2, popup_y + 40 + i * line_height))
screen.blit(text, text_rect)
def draw_history(screen):
"""Affiche la liste de l'historique des téléchargements."""
if not config.history:
text = config.font.render("Aucun téléchargement dans l'historique", True, (255, 255, 255))
screen.blit(text, (config.screen_width // 2 - text.get_width() // 2, config.screen_height // 2))
return
# Calculer le nombre d'éléments visibles
item_height = config.small_font.get_height() + 10
config.visible_history_items = (config.screen_height - 200) // item_height
max_scroll = max(0, len(config.history) - config.visible_history_items)
config.history_scroll_offset = max(0, min(config.history_scroll_offset, max_scroll))
# Cadre semi-transparent
panel_width = config.screen_width - 100
panel_height = config.visible_history_items * item_height + 20
panel_x = (config.screen_width - panel_width) // 2
panel_y = (config.screen_height - panel_height) // 2
panel_surface = pygame.Surface((panel_width, panel_height), pygame.SRCALPHA)
panel_surface.fill((0, 0, 0, 128))
screen.blit(panel_surface, (panel_x, panel_y))
pygame.draw.rect(screen, (255, 255, 255), (panel_x, panel_y, panel_width, panel_height), 2)
# Afficher les colonnes
headers = ["Système", "Nom du jeu", "État"]
col_widths = [panel_width // 4, panel_width // 2, panel_width // 4]
header_y = panel_y + 10
for i, header in enumerate(headers):
text = config.small_font.render(header, True, (255, 255, 255))
screen.blit(text, (panel_x + sum(col_widths[:i]) + 10, header_y))
# Afficher les entrées
start_index = config.history_scroll_offset
end_index = min(start_index + config.visible_history_items, len(config.history))
for i, entry in enumerate(config.history[start_index:end_index]):
y = panel_y + 40 + i * item_height
color = (255, 255, 0) if i + start_index == config.current_history_item else (255, 255, 255)
system = config.platform_names.get(entry["platform"], entry["platform"])
system_text = truncate_text_end(system, config.small_font, col_widths[0] - 20)
game_text = truncate_text_end(entry["game_name"], config.small_font, col_widths[1] - 20)
status_text = entry["status"]
texts = [system_text, game_text, status_text]
for j, text in enumerate(texts):
rendered = config.small_font.render(text, True, color)
screen.blit(rendered, (panel_x + sum(col_widths[:j]) + 10, y))
if i + start_index == config.current_history_item:
pygame.draw.rect(screen, (255, 255, 0), (panel_x + 5, y - 5, panel_width - 10, item_height), 1)
# Barre de défilement
if len(config.history) > config.visible_history_items:
draw_scrollbar(screen, config.history_scroll_offset, len(config.history), config.visible_history_items, panel_x + panel_width - 10, panel_y, panel_height)
#Menu Quitter Appli
def draw_confirm_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour quitter."""
global OVERLAY
logger.debug("Rendu de draw_confirm_dialog")
#logger.debug("Rendu de draw_confirm_dialog")
# Vérifier si OVERLAY est valide, sinon le recréer
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)
@@ -902,34 +907,68 @@ def draw_confirm_dialog(screen):
screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect)
def draw_clear_history_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour vider l'historique."""
#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
#logger.debug("Rendu de draw_redownload_game_cache_dialog")
# Vérifier si OVERLAY est valide, sinon le recréer
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)
OVERLAY.fill((0, 0, 0, 128))
logger.debug("OVERLAY recréé dans draw_redownload_game_cache_dialog")
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
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
text_height = len(wrapped_message) * line_height
button_height = line_height + 20
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)
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=300)
rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, (255, 255, 255))
text = config.small_font.render(line, True, (255, 255, 255))
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)
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_clear_selection == 1 else (255, 255, 255))
no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_clear_selection == 0 else (255, 255, 255))
yes_text = config.small_font.render("Oui", True, (0, 150, 255) if config.redownload_confirm_selection == 1 else (255, 255, 255))
no_text = config.small_font.render("Non", True, (0, 150, 255) if config.redownload_confirm_selection == 0 else (255, 255, 255))
yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2))
no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2))
screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect)
screen.blit(no_text, no_rect)
def draw_popup(screen):
"""Dessine un popup avec un message et un compte à rebours."""
screen.blit(OVERLAY, (0, 0))
popup_width = int(config.screen_width * 0.8) # ~400px pour 1920p
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
popup_height = text_height + 2 * margin_top_bottom + line_height # Espace pour le compte à rebours
popup_x = (config.screen_width - popup_width) // 2
popup_y = (config.screen_height - popup_height) // 2
pygame.draw.rect(screen, (50, 50, 50, 200), (popup_x, popup_y, popup_width, popup_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (popup_x, popup_y, popup_width, popup_height), 2, border_radius=10)
for i, line in enumerate(text_lines):
text_surface = config.small_font.render(line, True, (255, 255, 255))
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)
# Afficher le compte à rebours
remaining_time = max(0, config.popup_timer // 1000) # Convertir ms en secondes
countdown_text = f"Ce message se fermera dans {remaining_time} seconde{'s' if remaining_time != 1 else ''}"
countdown_surface = config.small_font.render(countdown_text, True, (255, 255, 255))
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)