Ameliorations graphique interface
This commit is contained in:
47
__main__.py
47
__main__.py
@@ -4,16 +4,15 @@ import pygame # type: ignore
|
||||
import asyncio
|
||||
import platform
|
||||
import subprocess
|
||||
import math
|
||||
import logging
|
||||
import requests
|
||||
import sys
|
||||
import json
|
||||
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_message, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup
|
||||
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_result_download, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup, THEME_COLORS
|
||||
from network import test_internet, download_rom, check_extension_before_download, extract_zip
|
||||
from controls import handle_controls, validate_menu_state
|
||||
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS
|
||||
from utils import truncate_text_end, load_system_image, load_games
|
||||
from utils import load_games
|
||||
from history import load_history
|
||||
import config
|
||||
|
||||
@@ -77,17 +76,19 @@ clock = pygame.time.Clock()
|
||||
|
||||
# Initialisation des polices
|
||||
try:
|
||||
config.font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 48)
|
||||
config.title_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 60)
|
||||
config.search_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 60)
|
||||
config.font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 36)
|
||||
config.title_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 48)
|
||||
config.search_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 48)
|
||||
config.progress_font = pygame.font.SysFont("arial", 36) # Police pour l'affichage de la progression
|
||||
config.small_font = pygame.font.Font("/userdata/roms/ports/RGSX/assets/Pixel-UniCode.ttf", 28) # Police pour les petits textes
|
||||
logger.debug("Police Pixel-UniCode chargée")
|
||||
except:
|
||||
config.font = pygame.font.SysFont("arial", 48)
|
||||
config.title_font = pygame.font.SysFont("arial", 60)
|
||||
config.search_font = pygame.font.SysFont("arial", 60)
|
||||
config.font = pygame.font.SysFont("arial", 48) # Police fallback
|
||||
config.title_font = pygame.font.SysFont("arial", 60) # Police fallback pour les titres
|
||||
config.search_font = pygame.font.SysFont("arial", 60) # Police fallback pour la recherche
|
||||
config.progress_font = pygame.font.SysFont("arial", 36) # Police fallback pour l'affichage de la progression
|
||||
config.small_font = pygame.font.SysFont("arial", 28) # Police fallback pour les petits textes
|
||||
logger.debug("Police Arial chargée")
|
||||
config.progress_font = pygame.font.SysFont("arial", 36)
|
||||
config.small_font = pygame.font.SysFont("arial", 24)
|
||||
|
||||
# Mise à jour de la résolution dans config
|
||||
config.screen_width, config.screen_height = pygame.display.get_surface().get_size()
|
||||
@@ -436,7 +437,7 @@ async def main():
|
||||
|
||||
# Affichage
|
||||
if config.needs_redraw:
|
||||
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
|
||||
draw_gradient(screen, THEME_COLORS["background_top"], THEME_COLORS["background_bottom"])
|
||||
if config.menu_state == "controls_mapping":
|
||||
draw_controls_mapping(screen, ACTIONS[0], None, False, 0.0)
|
||||
# logger.debug("Rendu initial de draw_controls_mapping")
|
||||
@@ -460,7 +461,7 @@ async def main():
|
||||
draw_progress_screen(screen)
|
||||
# logger.debug("Rendu de draw_progress_screen")
|
||||
elif config.menu_state == "download_result":
|
||||
draw_popup_message(screen, config.download_result_message, config.download_result_error)
|
||||
draw_popup_result_download(screen, config.download_result_message, config.download_result_error)
|
||||
# logger.debug("Rendu de draw_popup_message")
|
||||
elif config.menu_state == "confirm_exit":
|
||||
draw_confirm_dialog(screen)
|
||||
@@ -660,26 +661,6 @@ async def main():
|
||||
pygame.quit()
|
||||
logger.debug("Application terminée")
|
||||
|
||||
# Fonction pour vérifier si un événement correspond à une action
|
||||
def is_input_matched(event, action_name):
|
||||
if not config.controls_config.get(action_name):
|
||||
return False
|
||||
mapping = config.controls_config[action_name]
|
||||
input_type = mapping["type"]
|
||||
input_value = mapping["value"]
|
||||
|
||||
if input_type == "key" and event.type == pygame.KEYDOWN:
|
||||
return event.key == input_value
|
||||
elif input_type == "button" and event.type == pygame.JOYBUTTONDOWN:
|
||||
return event.button == input_value
|
||||
elif input_type == "axis" and event.type == pygame.JOYAXISMOTION:
|
||||
axis, direction = input_value
|
||||
return event.axis == axis and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == direction
|
||||
elif input_type == "hat" and event.type == pygame.JOYHATMOTION:
|
||||
return event.value == input_value
|
||||
elif input_type == "mouse" and event.type == pygame.MOUSEBUTTONDOWN:
|
||||
return event.button == input_value
|
||||
return False
|
||||
|
||||
if platform.system() == "Emscripten":
|
||||
asyncio.ensure_future(main())
|
||||
|
||||
@@ -5,7 +5,7 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "1.8.0"
|
||||
app_version = "1.9.0"
|
||||
|
||||
# Variables d'état
|
||||
platforms = []
|
||||
@@ -60,6 +60,9 @@ popup_message = "" # Message à afficher dans les popups
|
||||
popup_timer = 0 # Temps restant pour le popup en millisecondes (0 = inactif)
|
||||
last_frame_time = pygame.time.get_ticks()
|
||||
|
||||
GRID_COLS = 3 # Number of columns in the platform grid
|
||||
GRID_ROWS = 4 # Number of rows in the platform grid
|
||||
|
||||
# Résolution de l'écran fallback
|
||||
# Utilisée si la résolution définie dépasse les capacités de l'écran
|
||||
SCREEN_WIDTH = 800
|
||||
|
||||
120
controls.py
120
controls.py
@@ -1,14 +1,13 @@
|
||||
import shutil
|
||||
import pygame # type: ignore
|
||||
import config
|
||||
from config import CONTROLS_CONFIG_PATH
|
||||
from config import CONTROLS_CONFIG_PATH , GRID_COLS, GRID_ROWS
|
||||
import asyncio
|
||||
import math
|
||||
import json
|
||||
import os
|
||||
from display import draw_validation_transition
|
||||
from network import download_rom, check_extension_before_download, download_from_1fichier, is_1fichier_url, is_extension_supported,load_extensions_json,sanitize_filename
|
||||
from controls_mapper import get_readable_input_name
|
||||
from utils import load_games
|
||||
from history import load_history, clear_history
|
||||
import logging
|
||||
@@ -22,7 +21,7 @@ JOYHAT_DEBOUNCE = 200 # Délai anti-rebond pour JOYHATMOTION (ms)
|
||||
JOYAXIS_DEBOUNCE = 50 # Délai anti-rebond pour JOYAXISMOTION (ms)
|
||||
REPEAT_ACTION_DEBOUNCE = 50 # Délai anti-rebond pour répétitions up/down/left/right (ms)
|
||||
|
||||
# Liste des états valides (mise à jour)
|
||||
# Liste des états valides
|
||||
VALID_STATES = [
|
||||
"platform", "game", "download_progress", "download_result", "confirm_exit",
|
||||
"extension_warning", "pause_menu", "controls_help", "history", "controls_mapping",
|
||||
@@ -46,17 +45,27 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH):
|
||||
with open(path, "r") as f:
|
||||
config_data = json.load(f)
|
||||
# Vérifier les actions nécessaires
|
||||
required_actions = ["confirm", "cancel", "left", "right"]
|
||||
required_actions = ["confirm", "cancel", "up", "down"]
|
||||
for action in required_actions:
|
||||
if action not in config_data:
|
||||
logger.warning(f"Action {action} manquante dans {path}, utilisation de la valeur par défaut")
|
||||
config_data[action] = {
|
||||
"type": "key",
|
||||
"value": {
|
||||
"confirm": pygame.K_RETURN,
|
||||
"cancel": pygame.K_ESCAPE,
|
||||
"left": pygame.K_LEFT,
|
||||
"right": pygame.K_RIGHT
|
||||
"confirm": {"type": "key", "value": pygame.K_RETURN},
|
||||
"cancel": {"type": "key", "value": pygame.K_ESCAPE},
|
||||
"left": {"type": "key", "value": pygame.K_LEFT},
|
||||
"right": {"type": "key", "value": pygame.K_RIGHT},
|
||||
"up": {"type": "key", "value": pygame.K_UP},
|
||||
"down": {"type": "key", "value": pygame.K_DOWN},
|
||||
"start": {"type": "key", "value": pygame.K_p},
|
||||
"progress": {"type": "key", "value": pygame.K_x},
|
||||
"history": {"type": "key", "value": pygame.K_h},
|
||||
"page_up": {"type": "key", "value": pygame.K_PAGEUP},
|
||||
"page_down": {"type": "key", "value": pygame.K_PAGEDOWN},
|
||||
"filter": {"type": "key", "value": pygame.K_f},
|
||||
"delete": {"type": "key", "value": pygame.K_BACKSPACE},
|
||||
"space": {"type": "key", "value": pygame.K_SPACE}
|
||||
}[action]
|
||||
}
|
||||
return config_data
|
||||
@@ -79,38 +88,25 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH):
|
||||
"space": {"type": "key", "value": pygame.K_SPACE}
|
||||
}
|
||||
|
||||
# Fonction pour vérifier si un événement correspond à une action
|
||||
def is_input_matched(event, action_name):
|
||||
"""Vérifie si l'événement correspond à l'action configurée."""
|
||||
if not config.controls_config.get(action_name):
|
||||
return False
|
||||
mapping = config.controls_config[action_name]
|
||||
input_type = mapping["type"]
|
||||
input_value = mapping["value"]
|
||||
|
||||
event_type = event["type"] if isinstance(event, dict) else event.type
|
||||
event_key = event.get("key") if isinstance(event, dict) else getattr(event, "key", None)
|
||||
event_button = event.get("button") if isinstance(event, dict) else getattr(event, "button", None)
|
||||
event_axis = event.get("axis") if isinstance(event, dict) else getattr(event, "axis", None)
|
||||
event_value = event.get("value") if isinstance(event, dict) else getattr(event, "value", None)
|
||||
|
||||
if input_type == "key" and event_type in (pygame.KEYDOWN, pygame.KEYUP):
|
||||
#logger.debug(f"Vérification key: event_key={event_key}, input_value={input_value}")
|
||||
return event_key == input_value
|
||||
elif input_type == "button" and event_type in (pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP):
|
||||
#logger.debug(f"Vérification button: event_button={event_button}, input_value={input_value}")
|
||||
return event_button == input_value
|
||||
elif input_type == "axis" and event_type == pygame.JOYAXISMOTION:
|
||||
if input_type == "key" and event.type == pygame.KEYDOWN:
|
||||
return event.key == input_value
|
||||
elif input_type == "button" and event.type == pygame.JOYBUTTONDOWN:
|
||||
return event.button == input_value
|
||||
elif input_type == "axis" and event.type == pygame.JOYAXISMOTION:
|
||||
axis, direction = input_value
|
||||
result = event_axis == axis and abs(event_value) > 0.5 and (1 if event_value > 0 else -1) == direction
|
||||
#logger.debug(f"Vérification axis: event_axis={event_axis}, event_value={event_value}, input_value={input_value}, result={result}")
|
||||
return result
|
||||
elif input_type == "hat" and event_type == pygame.JOYHATMOTION:
|
||||
input_value_tuple = tuple(input_value) if isinstance(input_value, list) else input_value
|
||||
#logger.debug(f"Vérification hat: event_value={event_value}, input_value={input_value_tuple}")
|
||||
return event_value == input_value_tuple
|
||||
elif input_type == "mouse" and event_type == pygame.MOUSEBUTTONDOWN:
|
||||
#logger.debug(f"Vérification mouse: event_button={event_button}, input_value={input_value}")
|
||||
return event_button == input_value
|
||||
return event.axis == axis and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == direction
|
||||
elif input_type == "hat" and event.type == pygame.JOYHATMOTION:
|
||||
return event.value == input_value
|
||||
elif input_type == "mouse" and event.type == pygame.MOUSEBUTTONDOWN:
|
||||
return event.button == input_value
|
||||
return False
|
||||
|
||||
def handle_controls(event, sources, joystick, screen):
|
||||
@@ -122,15 +118,11 @@ def handle_controls(event, sources, joystick, screen):
|
||||
|
||||
# Valider previous_menu_state avant tout traitement
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
#logger.debug(f"Validation initiale: previous_menu_state={config.previous_menu_state}")
|
||||
|
||||
# Debounce général
|
||||
if current_time - config.last_state_change_time < config.debounce_delay:
|
||||
return action
|
||||
|
||||
# Log des événements reçus
|
||||
#logger.debug(f"Événement reçu: type={event.type}, value={getattr(event, 'value', None)}")
|
||||
|
||||
# --- CLAVIER, MANETTE, SOURIS ---
|
||||
if event.type in (pygame.KEYDOWN, pygame.JOYBUTTONDOWN, pygame.JOYAXISMOTION, pygame.JOYHATMOTION, pygame.MOUSEBUTTONDOWN):
|
||||
# Débouncer les événements JOYHATMOTION
|
||||
@@ -149,12 +141,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
if event.type == pygame.QUIT:
|
||||
logger.debug("Événement pygame.QUIT détecté")
|
||||
return "quit"
|
||||
|
||||
# Vérification des actions mappées
|
||||
#for action_name in ["up", "down", "left", "right"]:
|
||||
#if is_input_matched(event, action_name):
|
||||
#logger.debug(f"Action mappée détectée: {action_name}, input={get_readable_input_name(event)}")
|
||||
|
||||
|
||||
# Menu pause
|
||||
if is_input_matched(event, "start") and config.menu_state not in ("pause_menu", "controls_help", "controls_mapping", "redownload_game_cache"):
|
||||
config.previous_menu_state = config.menu_state
|
||||
@@ -170,30 +157,33 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.needs_redraw = True
|
||||
logger.debug("Sortie du menu erreur avec Confirm")
|
||||
|
||||
# Plateformes
|
||||
|
||||
#Plateformes
|
||||
elif config.menu_state == "platform":
|
||||
max_index = min(9, len(config.platforms) - config.current_page * 9) - 1
|
||||
current_grid_index = config.selected_platform - config.current_page * 9
|
||||
row = current_grid_index // 3
|
||||
systems_per_page = GRID_COLS * GRID_ROWS
|
||||
max_index = min(systems_per_page, len(config.platforms) - config.current_page * systems_per_page) - 1
|
||||
current_grid_index = config.selected_platform - config.current_page * systems_per_page
|
||||
row = current_grid_index // GRID_COLS
|
||||
col = current_grid_index % GRID_COLS
|
||||
|
||||
if is_input_matched(event, "down"):
|
||||
if current_grid_index + 3 <= max_index:
|
||||
config.selected_platform += 3
|
||||
if current_grid_index + GRID_COLS <= max_index:
|
||||
config.selected_platform += GRID_COLS
|
||||
config.repeat_action = "down"
|
||||
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "up"):
|
||||
if current_grid_index - 3 >= 0:
|
||||
config.selected_platform -= 3
|
||||
if current_grid_index - GRID_COLS >= 0:
|
||||
config.selected_platform -= GRID_COLS
|
||||
config.repeat_action = "up"
|
||||
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "left"):
|
||||
if current_grid_index % 3 != 0:
|
||||
if col > 0:
|
||||
config.selected_platform -= 1
|
||||
config.repeat_action = "left"
|
||||
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||
@@ -202,7 +192,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
elif config.current_page > 0:
|
||||
config.current_page -= 1
|
||||
config.selected_platform = config.current_page * 9 + row * 3 + 2
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS + (GRID_COLS - 1)
|
||||
if config.selected_platform >= len(config.platforms):
|
||||
config.selected_platform = len(config.platforms) - 1
|
||||
config.repeat_action = "left"
|
||||
@@ -211,16 +201,16 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "right"):
|
||||
if current_grid_index % 3 != 2 and current_grid_index < max_index:
|
||||
if col < GRID_COLS - 1 and current_grid_index < max_index:
|
||||
config.selected_platform += 1
|
||||
config.repeat_action = "right"
|
||||
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||
config.repeat_last_action = current_time
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif (config.current_page + 1) * 9 < len(config.platforms):
|
||||
elif (config.current_page + 1) * systems_per_page < len(config.platforms):
|
||||
config.current_page += 1
|
||||
config.selected_platform = config.current_page * 9 + row * 3
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS
|
||||
if config.selected_platform >= len(config.platforms):
|
||||
config.selected_platform = len(config.platforms) - 1
|
||||
config.repeat_action = "right"
|
||||
@@ -229,9 +219,9 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "page_down"):
|
||||
if (config.current_page + 1) * 9 < len(config.platforms):
|
||||
if (config.current_page + 1) * systems_per_page < len(config.platforms):
|
||||
config.current_page += 1
|
||||
config.selected_platform = config.current_page * 9 + row * 3
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS + col
|
||||
if config.selected_platform >= len(config.platforms):
|
||||
config.selected_platform = len(config.platforms) - 1
|
||||
config.repeat_action = None
|
||||
@@ -243,7 +233,19 @@ def handle_controls(event, sources, joystick, screen):
|
||||
elif is_input_matched(event, "page_up"):
|
||||
if config.current_page > 0:
|
||||
config.current_page -= 1
|
||||
config.selected_platform = config.current_page * 9 + row * 3
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS - col
|
||||
if config.selected_platform >= len(config.platforms):
|
||||
config.selected_platform = len(config.platforms) - 1
|
||||
config.repeat_action = None
|
||||
config.repeat_key = None
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
#logger.debug("Page précédente, répétition réinitialisée")
|
||||
elif is_input_matched(event, "page_up"):
|
||||
if config.current_page > 0:
|
||||
config.current_page -= 1
|
||||
config.selected_platform = config.current_page * systems_per_page + row * GRID_COLS + col
|
||||
if config.selected_platform >= len(config.platforms):
|
||||
config.selected_platform = len(config.platforms) - 1
|
||||
config.repeat_action = None
|
||||
|
||||
975
display - Copie.py
Normal file
975
display - Copie.py
Normal file
@@ -0,0 +1,975 @@
|
||||
import pygame # type: ignore
|
||||
import config
|
||||
import math
|
||||
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__)
|
||||
|
||||
OVERLAY = None # Initialisé dans init_display()
|
||||
|
||||
#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, 128)) # Semi-transparent (noir avec alpha 128)
|
||||
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()
|
||||
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))
|
||||
|
||||
#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 d’une 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 = [
|
||||
"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 !"
|
||||
]
|
||||
|
||||
margin_horizontal = int(config.screen_width * 0.025) # 2.5% de la largeur
|
||||
padding_vertical = int(config.screen_height * 0.0185) # ~20px pour 1080p
|
||||
padding_between = int(config.screen_height * 0.0074) # ~8px pour 1080p
|
||||
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) # ~20px pour 1080p
|
||||
|
||||
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, (30, 30, 30, 220), disclaimer_surface.get_rect(), border_radius=border_radius)
|
||||
screen.blit(disclaimer_surface, disclaimer_rect.topleft)
|
||||
|
||||
pygame.draw.rect(screen, (255, 255, 255), 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, (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
|
||||
))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
loading_y = rect_y + rect_height + int(config.screen_height * 0.0926) # ~100px pour 1080p
|
||||
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.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)
|
||||
|
||||
bar_width = int(config.screen_width * 0.2083) # ~400px pour 1920p
|
||||
bar_height = int(config.screen_height * 0.037) # ~40px pour 1080p
|
||||
progress_width = (bar_width * config.loading_progress) / 100
|
||||
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 d’erreur."""
|
||||
error_font = pygame.font.SysFont("arial", 28)
|
||||
wrapped_message = wrap_text(config.error_message, error_font, config.screen_width - 80)
|
||||
line_height = error_font.get_height() + 5
|
||||
for i, line in enumerate(wrapped_message):
|
||||
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)
|
||||
# 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
|
||||
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, (255, 255, 255))
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 10))
|
||||
title_rect_inflated = title_rect.inflate(40, 20)
|
||||
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 0)
|
||||
|
||||
# Dessiner le rectangle de fond du titre
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), title_rect_inflated, border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
# Configuration de la grille
|
||||
margin_left = int(config.screen_width * 0.026) # ~50px pour 1920p
|
||||
margin_right = int(config.screen_width * 0.026)
|
||||
margin_top = int(config.screen_height * 0.140) # ~120px pour 1080p
|
||||
margin_bottom = int(config.screen_height * 0.0648) # ~70px pour 1080p
|
||||
num_cols = 3
|
||||
num_rows = 3
|
||||
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
|
||||
#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):
|
||||
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 * 0.9)
|
||||
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 = (0, 255, 255)
|
||||
border_radius = 24
|
||||
padding = 24
|
||||
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 + (60,),
|
||||
neon_surface.get_rect(),
|
||||
width=1,
|
||||
border_radius=border_radius + 8
|
||||
)
|
||||
pygame.draw.rect(
|
||||
neon_surface,
|
||||
neon_color + (180,),
|
||||
neon_surface.get_rect().inflate(-8, -8),
|
||||
width=2,
|
||||
border_radius=border_radius
|
||||
)
|
||||
screen.blit(neon_surface, (image_rect.left - padding, image_rect.top - padding), special_flags=pygame.BLEND_RGBA_ADD)
|
||||
|
||||
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")
|
||||
|
||||
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 + 40
|
||||
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, (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(lines):
|
||||
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)
|
||||
return
|
||||
|
||||
line_height = config.small_font.get_height() + 10
|
||||
#header_height = line_height
|
||||
margin_top_bottom = 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
|
||||
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, (255, 255, 255))
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 10))
|
||||
title_rect_inflated = title_rect.inflate(40, 20)
|
||||
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 0)
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), title_rect_inflated, border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
||||
screen.blit(title_surface, title_rect)
|
||||
elif config.filter_active:
|
||||
filter_text = f"Filtre actif : {config.search_query}"
|
||||
title_surface = config.small_font.render(filter_text, True, (255, 255, 255))
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 10))
|
||||
title_rect_inflated = title_rect.inflate(40, 20)
|
||||
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 0)
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), title_rect_inflated, border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
||||
screen.blit(title_surface, title_rect)
|
||||
else:
|
||||
title_text = f"{platform_name} ({game_count} jeux)"
|
||||
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))
|
||||
title_rect_inflated = title_rect.inflate(40, 20)
|
||||
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 0)
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), title_rect_inflated, border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
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 + 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_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}")
|
||||
|
||||
# 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."""
|
||||
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
|
||||
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
|
||||
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, (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(lines):
|
||||
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)
|
||||
return
|
||||
|
||||
# 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
|
||||
|
||||
# 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))
|
||||
title_rect_inflated = title_rect.inflate(40, 20)
|
||||
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 0)
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), title_rect_inflated, border_radius=10)
|
||||
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 + 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, (255, 255, 255))
|
||||
text_rect = text_surface.get_rect(center=(x_pos, header_y))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# 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.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_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 + 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))
|
||||
|
||||
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}")
|
||||
|
||||
# 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)}")
|
||||
|
||||
#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_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 = [
|
||||
['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) # ~60px pour 1920p
|
||||
key_height = int(config.screen_height * 0.0556) # ~60px pour 1080p
|
||||
key_spacing = int(config.screen_width * 0.0052) # ~10px pour 1920p
|
||||
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 # ~120px pour 1080p
|
||||
controls_y = config.screen_height - int(config.screen_height * 0.037) # ~40px pour 1080p
|
||||
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, (50, 50, 50, 200), keyboard_rect, border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), keyboard_rect, 2, border_radius=10)
|
||||
|
||||
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, (0, 150, 255, 150), key_rect, border_radius=5)
|
||||
else:
|
||||
pygame.draw.rect(screen, (80, 80, 80, 255), key_rect, border_radius=5)
|
||||
pygame.draw.rect(screen, (255, 255, 255), key_rect, 1, border_radius=5)
|
||||
text = config.font.render(key, True, (255, 255, 255))
|
||||
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")
|
||||
|
||||
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"]
|
||||
#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_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) # ~30px pour 1080p
|
||||
percent_height = line_height
|
||||
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 + 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(title_lines):
|
||||
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()}")
|
||||
|
||||
bar_y = rect_y + text_height + margin_top_bottom
|
||||
progress_width = 0
|
||||
pygame.draw.rect(screen, (100, 100, 100), (rect_x + 20, bar_y, bar_width, bar_height))
|
||||
if total_size > 0:
|
||||
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}")
|
||||
|
||||
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.font, config.screen_width - 80)
|
||||
text_y = bar_y + bar_height + margin_top_bottom
|
||||
for i, line in enumerate(percent_lines):
|
||||
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)
|
||||
|
||||
#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))
|
||||
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.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")
|
||||
|
||||
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 = 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 lines], default=300)
|
||||
rect_width = max_text_width + 40
|
||||
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, (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(lines):
|
||||
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])]}")
|
||||
|
||||
yes_text = "[Oui]" if config.extension_confirm_selection == 1 else "Oui"
|
||||
no_text = "[Non]" if config.extension_confirm_selection == 0 else "Non"
|
||||
yes_surface = config.font.render(yes_text, True, (0, 150, 255) if config.extension_confirm_selection == 1 else (255, 255, 255))
|
||||
no_surface = config.font.render(no_text, True, (0, 150, 255) if config.extension_confirm_selection == 0 else (255, 255, 255))
|
||||
|
||||
button_y = rect_y + text_height + margin_top_bottom + line_height // 2
|
||||
yes_rect = yes_surface.get_rect(center=(config.screen_width // 2 - 100, button_y))
|
||||
no_rect = no_surface.get_rect(center=(config.screen_width // 2 + 100, button_y))
|
||||
|
||||
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}")
|
||||
|
||||
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 + 40
|
||||
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, (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_error):
|
||||
error_surface = config.font.render(line, True, (255, 0, 0))
|
||||
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"{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
|
||||
rect_height = len(wrapped_controls) * line_height + 20
|
||||
rect_y = config.screen_height - rect_height - 5
|
||||
|
||||
for i, line in enumerate(wrapped_controls):
|
||||
text_surface = config.small_font.render(line, True, (255, 255, 255))
|
||||
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 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.8) # ~400px pour 1920p
|
||||
line_height = config.font.get_height() + 10
|
||||
text_height = len(options) * line_height
|
||||
margin_top_bottom = 20
|
||||
menu_height = text_height + 2 * margin_top_bottom
|
||||
menu_x = (config.screen_width - menu_width) // 2
|
||||
menu_y = (config.screen_height - menu_height) // 2
|
||||
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), (menu_x, menu_y, menu_width, menu_height), border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), (menu_x, menu_y, menu_width, menu_height), 2, border_radius=10)
|
||||
|
||||
for i, option in enumerate(options):
|
||||
color = (0, 150, 255) if i == selected_option else (255, 255, 255)
|
||||
text_surface = config.font.render(option, True, color)
|
||||
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)
|
||||
|
||||
#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 = {
|
||||
"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
|
||||
|
||||
for i, line in enumerate(wrapped_controls):
|
||||
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)
|
||||
|
||||
|
||||
#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")
|
||||
# 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_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 = 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_selection == 1 else (255, 255, 255))
|
||||
no_text = config.font.render("Non", True, (0, 150, 255) if config.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)
|
||||
|
||||
#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 = "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.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.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.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)
|
||||
|
||||
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)
|
||||
666
display.py
666
display.py
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,6 @@ import pygame # type: ignore
|
||||
import zipfile
|
||||
import json
|
||||
import time
|
||||
from urllib.parse import urljoin, unquote
|
||||
import asyncio
|
||||
import config
|
||||
from utils import sanitize_filename
|
||||
@@ -346,7 +345,7 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
||||
if os.path.exists(dest_path):
|
||||
os.remove(dest_path)
|
||||
result[0] = False
|
||||
result[1] = str(e)
|
||||
result[1] = f"Erreur téléchargement {game_name}"
|
||||
finally:
|
||||
logger.debug(f"Thread téléchargement terminé pour {url}")
|
||||
with lock:
|
||||
|
||||
@@ -2146,15 +2146,7 @@
|
||||
".elf",
|
||||
".dol",
|
||||
".m3u",
|
||||
".xci"
|
||||
]
|
||||
},
|
||||
{
|
||||
"system": "switch",
|
||||
"folder": "/userdata/roms/switch",
|
||||
"extensions": [
|
||||
".nsp",
|
||||
".xci"
|
||||
".json"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
32
utils.py
32
utils.py
@@ -98,7 +98,7 @@ def sanitize_filename(name):
|
||||
return re.sub(r'[<>:"/\/\\|?*]', '_', name).strip()
|
||||
|
||||
def wrap_text(text, font, max_width):
|
||||
"""Divise le texte en lignes pour respecter la largeur maximale."""
|
||||
"""Divise le texte en lignes pour respecter la largeur maximale, en coupant les mots longs si nécessaire."""
|
||||
if not isinstance(text, str):
|
||||
text = str(text) if text is not None else ""
|
||||
|
||||
@@ -107,15 +107,29 @@ def wrap_text(text, font, max_width):
|
||||
current_line = ''
|
||||
|
||||
for word in words:
|
||||
# Tester si ajouter le mot dépasse la largeur
|
||||
test_line = current_line + (' ' if current_line else '') + word
|
||||
test_surface = font.render(test_line, True, (255, 255, 255))
|
||||
if test_surface.get_width() <= max_width:
|
||||
current_line = test_line
|
||||
# Si le mot seul dépasse max_width, le couper caractère par caractère
|
||||
if font.render(word, True, (255, 255, 255)).get_width() > max_width:
|
||||
temp_line = current_line
|
||||
for char in word:
|
||||
test_line = temp_line + (' ' if temp_line else '') + char
|
||||
test_surface = font.render(test_line, True, (255, 255, 255))
|
||||
if test_surface.get_width() <= max_width:
|
||||
temp_line = test_line
|
||||
else:
|
||||
if temp_line:
|
||||
lines.append(temp_line)
|
||||
temp_line = char
|
||||
current_line = temp_line
|
||||
else:
|
||||
if current_line:
|
||||
lines.append(current_line)
|
||||
current_line = word
|
||||
# Comportement standard pour les mots normaux
|
||||
test_line = current_line + (' ' if current_line else '') + word
|
||||
test_surface = font.render(test_line, True, (255, 255, 255))
|
||||
if test_surface.get_width() <= max_width:
|
||||
current_line = test_line
|
||||
else:
|
||||
if current_line:
|
||||
lines.append(current_line)
|
||||
current_line = word
|
||||
|
||||
if current_line:
|
||||
lines.append(current_line)
|
||||
|
||||
Reference in New Issue
Block a user