import pygame # type: ignore import config from utils import truncate_text_middle, wrap_text, load_system_image, truncate_text_end import logging import math from history import load_history # Ajout de l'import logger = logging.getLogger(__name__) OVERLAY = None # Initialisé dans init_display() # 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 def init_display(): """Initialise l'écran et les ressources globales.""" global OVERLAY 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) OVERLAY.fill((0, 0, 0, 150)) # Transparence augmentée logger.debug(f"Écran initialisé avec résolution : {screen_width}x{screen_height}") return screen # Fond d'écran dégradé def draw_gradient(screen, top_color, bottom_color): """Dessine un fond dégradé vertical avec des couleurs vibrantes.""" 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)) # 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): """Affiche une animation de transition fluide pour la sélection d’une plateforme.""" platform_dict = config.platform_dicts[platform_index] image = load_system_image(platform_dict) if not image: return # 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 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() 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: # 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 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)) # 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() # 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 def draw_loading_screen(screen): """Affiche l’écran de chargement avec un style moderne.""" 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.", ] margin_horizontal = int(config.screen_width * 0.025) padding_vertical = int(config.screen_height * 0.0185) padding_between = int(config.screen_height * 0.0074) border_radius = 16 border_width = 3 shadow_offset = 6 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 rect_x = margin_horizontal rect_y = int(config.screen_height * 0.0185) 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) pygame.draw.rect(disclaimer_surface, THEME_COLORS["button_idle"], disclaimer_surface.get_rect(), border_radius=border_radius) screen.blit(disclaimer_surface, disclaimer_rect.topleft) pygame.draw.rect(screen, THEME_COLORS["border"], disclaimer_rect, border_width, border_radius=border_radius) max_text_width = rect_width - 2 * padding_vertical for i, line in enumerate(disclaimer_lines): wrapped_lines = wrap_text(line, config.small_font, max_text_width) for j, wrapped_line in enumerate(wrapped_lines): text_surface = config.small_font.render(wrapped_line, True, THEME_COLORS["title_text"]) 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) 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"]) text_rect = text.get_rect(center=(config.screen_width // 2, loading_y)) screen.blit(text, text_rect) 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))) screen.blit(progress_text, progress_rect) bar_width = int(config.screen_width * 0.2083) bar_height = int(config.screen_height * 0.037) progress_width = (bar_width * config.loading_progress) / 100 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) # Écran d'erreur def draw_error_screen(screen): """Affiche l’écran d’erreur 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) for i, line in enumerate(wrapped_message): 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)) screen.blit(text, text_rect) 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) # Grille des systèmes 3x3 def draw_platform_grid(screen): """Affiche la grille des plateformes avec un style moderne.""" 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}" 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) margin_left = int(config.screen_width * 0.026) margin_right = int(config.screen_width * 0.026) margin_top = int(config.screen_height * 0.140) margin_bottom = int(config.screen_height * 0.0648) num_cols = 3 num_rows = 4 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() max_size = int(min(col_width, row_height) * scale * 1.1) # Légèrement plus grand que la cellule 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: neon_color = THEME_COLORS["neon"] border_radius = 12 padding = 12 rect_width = image_rect.width + 2 * padding rect_height = image_rect.height + 2 * padding neon_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA) 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) screen.blit(neon_surface, (image_rect.left - padding, image_rect.top - padding), special_flags=pygame.BLEND_RGBA_ADD) 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)) screen.blit(image, image_rect) # Liste des jeux def draw_game_list(screen): """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) 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) for i, line in enumerate(lines): 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 margin_top_bottom = 20 extra_margin_top = 20 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}_" 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}" title_surface = config.font.render(filter_text, True, THEME_COLORS["fond_lignes"]) 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)" 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) 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] 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)) 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)}") # 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, THEME_COLORS["fond_lignes"], (x, scrollbar_y, 15, scrollbar_height), border_radius=4) def draw_history_list(screen): # logger.debug(f"Dessin historique, history={config.history}, needs_redraw={config.needs_redraw}") history = config.history if hasattr(config, 'history') else load_history() history_count = len(history) # Define column widths as percentages of available space column_width_percentages = { "platform": 0.25, # platform column "game_name": 0.50, # game name column "status": 0.25 # status column } available_width = int(0.95 * config.screen_width - 60) # Total available width for columns col_platform_width = int(available_width * column_width_percentages["platform"]) col_game_width = int(available_width * column_width_percentages["game_name"]) col_status_width = int(available_width * column_width_percentages["status"]) rect_width = int(0.95 * config.screen_width) line_height = config.small_font.get_height() + 10 header_height = line_height margin_top_bottom = 20 extra_margin_top = 40 extra_margin_bottom = 80 title_height = config.title_font.get_height() + 20 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) 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) for i, line in enumerate(lines): 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 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 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})" 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) 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) headers = ["Système", "Nom du jeu", "État"] 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, rect_x + 20 + col_platform_width + col_game_width + col_status_width // 2 ] for header, x_pos in zip(headers, header_x_positions): text_surface = config.small_font.render(header, True, THEME_COLORS["text"]) text_rect = text_surface.get_rect(center=(x_pos, header_y)) screen.blit(text_surface, text_rect) 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)))): entry = history[i] platform = entry.get("platform", "Inconnu") game_name = entry.get("game_name", "Inconnu") status = entry.get("status", "Inconnu") progress = entry.get("progress", 0) # Personnaliser l'affichage du statut if status in ["Téléchargement", "downloading"]: status_text = f"Téléchargement : {progress:.1f}%" # logger.debug(f"Affichage progression: {progress:.1f}% pour {game_name}, status={status_text}") elif status == "Extracting": status_text = f"Extraction : {progress:.1f}%" # logger.debug(f"Affichage extraction: {progress:.1f}% pour {game_name}, status={status_text}") elif status == "Download_OK": status_text = "Terminé" # logger.debug(f"Affichage terminé: {game_name}, status={status_text}") elif status == "Erreur": status_text = f"Erreur : {entry.get('message', 'Échec')}" logger.debug(f"Affichage erreur: {game_name}, status={status_text}") else: status_text = status logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}") color = THEME_COLORS["fond_lignes"] if i == config.current_history_item else THEME_COLORS["text"] 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_middle(status_text, config.small_font, col_status_width - 10, is_filename=False) 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) 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)) 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)) 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)}") # 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 avec un style moderne.""" if total_items <= visible_items: return 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) # É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 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) rect_width = max_text_width + 150 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) for i, line in enumerate(wrapped_message): 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) 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) # Affichage du clavier virtuel sur non-PC def draw_virtual_keyboard(screen): """Affiche un clavier virtuel avec un style moderne.""" 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'] ] key_width = int(config.screen_width * 0.03125) key_height = int(config.screen_height * 0.0556) key_spacing = int(config.screen_width * 0.0052) 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 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) 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) 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) 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: pygame.draw.rect(screen, THEME_COLORS["fond_lignes"] + (150,), key_rect, border_radius=8) else: 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"]) text_rect = text.get_rect(center=key_rect.center) screen.blit(text, text_rect) # Écran de progression de téléchargement/extraction def draw_progress_screen(screen): """Affiche l'écran de progression des téléchargements avec un style moderne.""" 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)) 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 margin_top_bottom = 20 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) bar_width = max_text_width rect_width = max_text_width + 80 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) for i, line in enumerate(title_lines): 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)) screen.blit(title_render, title_rect) bar_y = rect_y + text_height + margin_top_bottom progress_width = 0 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)) 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}" 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): 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) # Écran popup résultat téléchargement def draw_popup_result_download(screen, message, is_error): """Affiche une popup flottante centrée avec un message de résultat.""" if message is None: message = "Téléchargement annulé par l'utilisateur." logger.debug(f"Message popup : {message}, is_error={is_error}") # Réduire la largeur maximale pour le wrapping max_popup_width = config.screen_width // 3 # Popup prend 1/3 de la largeur de l'écran wrapped_message = wrap_text(message, config.small_font, max_popup_width - 40) # 40 pixels de marge interne line_height = config.small_font.get_height() + 5 text_height = len(wrapped_message) * line_height margin_top_bottom = 15 margin_sides = 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=200) rect_width = min(max_text_width + 2 * margin_sides, max_popup_width) # Positionner la popup au centre de l'écran rect_x = (config.screen_width - rect_width) // 2 rect_y = (config.screen_height - rect_height) // 2 # Fond semi-transparent pour un effet flottant popup_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA) # Utiliser button_idle sans modifier son alpha (déjà à 150) pygame.draw.rect(popup_surface, THEME_COLORS["button_idle"], (0, 0, rect_width, rect_height), border_radius=10) # Bordure sans alpha modifié pygame.draw.rect(popup_surface, THEME_COLORS["border"], (0, 0, rect_width, rect_height), 2, border_radius=10) # Afficher le texte for i, line in enumerate(wrapped_message): text = config.small_font.render(line, True, THEME_COLORS["error_text"] if is_error else THEME_COLORS["text"]) text_rect = text.get_rect(center=(rect_width // 2, margin_top_bottom + i * line_height + line_height // 2)) popup_surface.blit(text, text_rect) # Appliquer la surface de la popup sur l'écran screen.blit(popup_surface, (rect_x, rect_y)) # Écran 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.""" if not config.pending_download: logger.error("config.pending_download est None ou vide dans extension_warning") 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}") 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}") try: line_height = config.font.get_height() + 5 text_height = len(lines) * 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.font.size(line)[0] for line in lines], 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) for i, line in enumerate(lines): 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) 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) except Exception as e: logger.error(f"Erreur lors du rendu de extension_warning : {str(e)}") error_message = "Erreur d'affichage de l'avertissement." 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) 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) for i, line in enumerate(wrapped_error): 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)) screen.blit(error_surface, error_rect) # Affichage des contrôles en bas de page def draw_controls(screen, menu_state): """Affiche les contrôles sur une seule ligne en bas de l’écran.""" start_button = get_control_display('start', 'START') history_button = get_control_display('history', 'H') filter_button = get_control_display('filter', 'F') control_text = f"RGSX v{config.app_version} - {start_button} : Options - {history_button}: Historique - {filter_button} : Filtrer (bug)" 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 rect_height = len(wrapped_controls) * line_height + 20 rect_y = config.screen_height - rect_height - 5 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) for i, line in enumerate(wrapped_controls): text_surface = config.small_font.render(line, True, THEME_COLORS["text"]) 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) # Menu pause def draw_pause_menu(screen, selected_option): """Dessine le menu pause avec un style moderne.""" screen.blit(OVERLAY, (0, 0)) options = [ "Controls", "Remap controls", "History", "Redownload Games cache", "Quit" ] menu_width = int(config.screen_width * 0.8) line_height = config.font.get_height() + 10 button_height = int(config.screen_height * 0.0463) margin_top_bottom = 20 menu_height = len(options) * (button_height + 10) + 2 * margin_top_bottom menu_x = (config.screen_width - menu_width) // 2 menu_y = (config.screen_height - menu_height) // 2 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) for i, option in enumerate(options): 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 def draw_controls_help(screen, previous_state): """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" } state_controls = { "error": [ common_controls["confirm"]("Retenter"), common_controls["cancel"]("Quitter") ], "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 []) ], "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"), *( [ common_controls["delete"](), common_controls["space"]() ] 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") ] 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 []) ], "download_progress": [ common_controls["cancel"]("Annuler le téléchargement"), common_controls["progress"]("Arrière plan"), common_controls["start"]() ], "download_result": [ common_controls["confirm"]("Retour") ], "confirm_exit": [ common_controls["confirm"]("Confirmer") ], "extension_warning": [ common_controls["confirm"]("Confirmer") ], "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"]() ] } controls = state_controls.get(previous_state, []) if not controls: return screen.blit(OVERLAY, (0, 0)) 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 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(wrapped_controls): text = config.font.render(line, True, THEME_COLORS["text"]) text_rect = text.get_rect(center=(config.screen_width // 2, popup_y + 40 + i * line_height)) screen.blit(text, text_rect) # Menu Quitter Appli def draw_confirm_dialog(screen): """Affiche la boîte de dialogue de confirmation pour quitter.""" 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) OVERLAY.fill((0, 0, 0, 150)) logger.debug("OVERLAY recréé dans draw_confirm_dialog") screen.blit(OVERLAY, (0, 0)) message = "Quitter l'application ?" 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 = 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) rect_width = max_text_width + 150 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) for i, line in enumerate(wrapped_message): 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) 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) OVERLAY.fill((0, 0, 0, 150)) logger.debug("OVERLAY recréé dans draw_redownload_game_cache_dialog") 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 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 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): text = config.small_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) 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) # Popup avec compte à rebours 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) 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 popup_x = (config.screen_width - popup_width) // 2 popup_y = (config.screen_height - popup_height) // 2 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): 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) 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 ''}" 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)