forked from Mirrors/RGSX
ajout d'une fonction historique
This commit is contained in:
98
__main__.py
98
__main__.py
@@ -9,11 +9,12 @@ import logging
|
|||||||
import requests
|
import requests
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_scrollbar, draw_confirm_dialog, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_message, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list
|
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_scrollbar, draw_confirm_dialog, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_message, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history, draw_clear_history_dialog
|
||||||
from network import test_internet, download_rom, check_extension_before_download, extract_zip
|
from network import test_internet, download_rom, check_extension_before_download, extract_zip
|
||||||
from controls import handle_controls
|
from controls import handle_controls, validate_menu_state
|
||||||
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS
|
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 truncate_text_end, load_system_image, load_games
|
||||||
|
from history import load_history
|
||||||
import config
|
import config
|
||||||
|
|
||||||
# Configuration du logging
|
# Configuration du logging
|
||||||
@@ -104,6 +105,10 @@ config.repeat_key = None
|
|||||||
config.repeat_start_time = 0
|
config.repeat_start_time = 0
|
||||||
config.repeat_last_action = 0
|
config.repeat_last_action = 0
|
||||||
|
|
||||||
|
# Chargement de l'historique
|
||||||
|
config.history = load_history()
|
||||||
|
logger.debug(f"Historique chargé: {len(config.history)} entrées")
|
||||||
|
|
||||||
# Vérification et chargement de la configuration des contrôles
|
# Vérification et chargement de la configuration des contrôles
|
||||||
config.controls_config = load_controls_config()
|
config.controls_config = load_controls_config()
|
||||||
if not config.controls_config:
|
if not config.controls_config:
|
||||||
@@ -287,10 +292,10 @@ async def main():
|
|||||||
(event.type == pygame.KEYDOWN and start_config.get("type") == "key" and event.key == start_config.get("value")) or
|
(event.type == pygame.KEYDOWN and start_config.get("type") == "key" and event.key == start_config.get("value")) or
|
||||||
(event.type == pygame.JOYBUTTONDOWN and start_config.get("type") == "button" and event.button == start_config.get("value")) or
|
(event.type == pygame.JOYBUTTONDOWN and start_config.get("type") == "button" and event.button == start_config.get("value")) or
|
||||||
(event.type == pygame.JOYAXISMOTION and start_config.get("type") == "axis" and event.axis == start_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == start_config.get("value")[1]) or
|
(event.type == pygame.JOYAXISMOTION and start_config.get("type") == "axis" and event.axis == start_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == start_config.get("value")[1]) or
|
||||||
(event.type == pygame.JOYHATMOTION and start_config.get("type") == "hat" and event.value == start_config.get("value")) or
|
(event.type == pygame.JOYHATMOTION and start_config.get("type") == "hat" and event.value == tuple(start_config.get("value"))) or
|
||||||
(event.type == pygame.MOUSEBUTTONDOWN and start_config.get("type") == "mouse" and event.button == start_config.get("value"))
|
(event.type == pygame.MOUSEBUTTONDOWN and start_config.get("type") == "mouse" and event.button == start_config.get("value"))
|
||||||
):
|
):
|
||||||
if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping"]:
|
if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping", "history", "confirm_clear_history"]:
|
||||||
config.previous_menu_state = config.menu_state
|
config.previous_menu_state = config.menu_state
|
||||||
config.menu_state = "pause_menu"
|
config.menu_state = "pause_menu"
|
||||||
config.selected_pause_option = 0
|
config.selected_pause_option = 0
|
||||||
@@ -313,7 +318,7 @@ async def main():
|
|||||||
(event.type == pygame.KEYDOWN and up_config and event.key == up_config.get("value")) or
|
(event.type == pygame.KEYDOWN and up_config and event.key == up_config.get("value")) or
|
||||||
(event.type == pygame.JOYBUTTONDOWN and up_config and up_config.get("type") == "button" and event.button == up_config.get("value")) or
|
(event.type == pygame.JOYBUTTONDOWN and up_config and up_config.get("type") == "button" and event.button == up_config.get("value")) or
|
||||||
(event.type == pygame.JOYAXISMOTION and up_config and up_config.get("type") == "axis" and event.axis == up_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == up_config.get("value")[1]) or
|
(event.type == pygame.JOYAXISMOTION and up_config and up_config.get("type") == "axis" and event.axis == up_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == up_config.get("value")[1]) or
|
||||||
(event.type == pygame.JOYHATMOTION and up_config and up_config.get("type") == "hat" and event.value == up_config.get("value"))
|
(event.type == pygame.JOYHATMOTION and up_config and up_config.get("type") == "hat" and event.value == tuple(up_config.get("value")))
|
||||||
):
|
):
|
||||||
config.selected_pause_option = max(0, config.selected_pause_option - 1)
|
config.selected_pause_option = max(0, config.selected_pause_option - 1)
|
||||||
config.repeat_action = "up"
|
config.repeat_action = "up"
|
||||||
@@ -326,9 +331,9 @@ async def main():
|
|||||||
(event.type == pygame.KEYDOWN and down_config and event.key == down_config.get("value")) or
|
(event.type == pygame.KEYDOWN and down_config and event.key == down_config.get("value")) or
|
||||||
(event.type == pygame.JOYBUTTONDOWN and down_config and down_config.get("type") == "button" and event.button == down_config.get("value")) or
|
(event.type == pygame.JOYBUTTONDOWN and down_config and down_config.get("type") == "button" and event.button == down_config.get("value")) or
|
||||||
(event.type == pygame.JOYAXISMOTION and down_config and down_config.get("type") == "axis" and event.axis == down_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == down_config.get("value")[1]) or
|
(event.type == pygame.JOYAXISMOTION and down_config and down_config.get("type") == "axis" and event.axis == down_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == down_config.get("value")[1]) or
|
||||||
(event.type == pygame.JOYHATMOTION and down_config and down_config.get("type") == "hat" and event.value == down_config.get("value"))
|
(event.type == pygame.JOYHATMOTION and down_config and down_config.get("type") == "hat" and event.value == tuple(down_config.get("value")))
|
||||||
):
|
):
|
||||||
config.selected_pause_option = min(2, config.selected_pause_option + 1)
|
config.selected_pause_option = min(3, config.selected_pause_option + 1)
|
||||||
config.repeat_action = "down"
|
config.repeat_action = "down"
|
||||||
config.repeat_start_time = current_time + REPEAT_DELAY
|
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||||
config.repeat_last_action = current_time
|
config.repeat_last_action = current_time
|
||||||
@@ -339,15 +344,17 @@ async def main():
|
|||||||
(event.type == pygame.KEYDOWN and confirm_config and event.key == confirm_config.get("value")) or
|
(event.type == pygame.KEYDOWN and confirm_config and event.key == confirm_config.get("value")) or
|
||||||
(event.type == pygame.JOYBUTTONDOWN and confirm_config and confirm_config.get("type") == "button" and event.button == confirm_config.get("value")) or
|
(event.type == pygame.JOYBUTTONDOWN and confirm_config and confirm_config.get("type") == "button" and event.button == confirm_config.get("value")) or
|
||||||
(event.type == pygame.JOYAXISMOTION and confirm_config and confirm_config.get("type") == "axis" and event.axis == confirm_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == confirm_config.get("value")[1]) or
|
(event.type == pygame.JOYAXISMOTION and confirm_config and confirm_config.get("type") == "axis" and event.axis == confirm_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == confirm_config.get("value")[1]) or
|
||||||
(event.type == pygame.JOYHATMOTION and confirm_config and confirm_config.get("type") == "hat" and event.value == confirm_config.get("value"))
|
(event.type == pygame.JOYHATMOTION and confirm_config and confirm_config.get("type") == "hat" and event.value == tuple(confirm_config.get("value")))
|
||||||
):
|
):
|
||||||
if config.selected_pause_option == 0:
|
if config.selected_pause_option == 0:
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
config.menu_state = "controls_help"
|
config.menu_state = "controls_help"
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Menu pause: Aide sélectionnée")
|
logger.debug("Menu pause: Aide sélectionnée")
|
||||||
elif config.selected_pause_option == 1:
|
elif config.selected_pause_option == 1:
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
if map_controls(screen):
|
if map_controls(screen):
|
||||||
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning"] else "platform"
|
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning", "history"] else "platform"
|
||||||
config.controls_config = load_controls_config()
|
config.controls_config = load_controls_config()
|
||||||
logger.debug(f"Mappage des contrôles terminé, retour à {config.menu_state}")
|
logger.debug(f"Mappage des contrôles terminé, retour à {config.menu_state}")
|
||||||
else:
|
else:
|
||||||
@@ -356,15 +363,25 @@ async def main():
|
|||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Échec du mappage des contrôles")
|
logger.debug("Échec du mappage des contrôles")
|
||||||
elif config.selected_pause_option == 2:
|
elif config.selected_pause_option == 2:
|
||||||
running = False
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "history"
|
||||||
|
config.current_history_item = 0
|
||||||
|
config.history_scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug("Menu pause: Historique sélectionné")
|
||||||
|
elif config.selected_pause_option == 3:
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "confirm_exit"
|
||||||
|
config.confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
logger.debug("Menu pause: Quitter sélectionné")
|
logger.debug("Menu pause: Quitter sélectionné")
|
||||||
elif (
|
elif (
|
||||||
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
|
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
|
||||||
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or
|
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or
|
||||||
(event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or
|
(event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or
|
||||||
(event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == cancel_config.get("value"))
|
(event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == tuple(cancel_config.get("value")))
|
||||||
):
|
):
|
||||||
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning"] else "platform"
|
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning", "history"] else "platform"
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug(f"Menu pause: Annulation, retour à {config.menu_state}")
|
logger.debug(f"Menu pause: Annulation, retour à {config.menu_state}")
|
||||||
|
|
||||||
@@ -388,7 +405,7 @@ async def main():
|
|||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug(f"Menu pause: Répétition haut, selected_option={config.selected_pause_option}")
|
logger.debug(f"Menu pause: Répétition haut, selected_option={config.selected_pause_option}")
|
||||||
elif config.repeat_action == "down":
|
elif config.repeat_action == "down":
|
||||||
config.selected_pause_option = min(2, config.selected_pause_option + 1)
|
config.selected_pause_option = min(3, config.selected_pause_option + 1)
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug(f"Menu pause: Répétition bas, selected_option={config.selected_pause_option}")
|
logger.debug(f"Menu pause: Répétition bas, selected_option={config.selected_pause_option}")
|
||||||
config.repeat_start_time = current_time + REPEAT_INTERVAL
|
config.repeat_start_time = current_time + REPEAT_INTERVAL
|
||||||
@@ -399,14 +416,24 @@ async def main():
|
|||||||
cancel_config = config.controls_config.get("cancel", {})
|
cancel_config = config.controls_config.get("cancel", {})
|
||||||
if (
|
if (
|
||||||
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
|
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
|
||||||
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value"))
|
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or
|
||||||
|
(event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or
|
||||||
|
(event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == tuple(cancel_config.get("value")))
|
||||||
):
|
):
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
config.menu_state = "pause_menu"
|
config.menu_state = "pause_menu"
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Controls_help: Annulation, retour à pause_menu")
|
logger.debug("Controls_help: Annulation, retour à pause_menu")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "extension_warning"]:
|
# Gérer confirm_clear_history explicitement
|
||||||
|
if config.menu_state == "confirm_clear_history":
|
||||||
|
action = handle_controls(event, sources, joystick, screen)
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Événement transmis à handle_controls dans confirm_clear_history: {event.type}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "extension_warning", "history"]:
|
||||||
action = handle_controls(event, sources, joystick, screen)
|
action = handle_controls(event, sources, joystick, screen)
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
if action == "quit":
|
if action == "quit":
|
||||||
@@ -430,8 +457,31 @@ async def main():
|
|||||||
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||||
config.download_tasks[task] = (task, url, game_name, platform)
|
config.download_tasks[task] = (task, url, game_name, platform)
|
||||||
config.menu_state = "download_progress"
|
config.menu_state = "download_progress"
|
||||||
|
config.pending_download = None # Réinitialiser après démarrage du téléchargement
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug(f"Téléchargement démarré pour {game_name}, passage à download_progress")
|
logger.debug(f"Téléchargement démarré pour {game_name}, passage à download_progress")
|
||||||
|
elif action == "redownload" and config.menu_state == "history" and config.history:
|
||||||
|
entry = config.history[config.current_history_item]
|
||||||
|
platform = entry["platform"]
|
||||||
|
game_name = entry["game_name"]
|
||||||
|
for game in config.games:
|
||||||
|
if game[0] == game_name and config.platforms[config.current_platform] == platform:
|
||||||
|
url = game[1]
|
||||||
|
is_supported, message, is_zip_non_supported = check_extension_before_download(url, platform, game_name)
|
||||||
|
if not is_supported:
|
||||||
|
config.pending_download = (url, platform, game_name, is_zip_non_supported)
|
||||||
|
config.menu_state = "extension_warning"
|
||||||
|
config.extension_confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Extension non reconnue pour retéléchargement, passage à extension_warning pour {game_name}")
|
||||||
|
else:
|
||||||
|
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||||
|
config.download_tasks[task] = (task, url, game_name, platform)
|
||||||
|
config.menu_state = "download_progress"
|
||||||
|
config.pending_download = None # Réinitialiser après démarrage du téléchargement
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retéléchargement démarré pour {game_name}, passage à download_progress")
|
||||||
|
break
|
||||||
|
|
||||||
# Gestion des téléchargements
|
# Gestion des téléchargements
|
||||||
if config.download_tasks:
|
if config.download_tasks:
|
||||||
@@ -444,6 +494,7 @@ async def main():
|
|||||||
config.download_result_start_time = pygame.time.get_ticks()
|
config.download_result_start_time = pygame.time.get_ticks()
|
||||||
config.menu_state = "download_result"
|
config.menu_state = "download_result"
|
||||||
config.download_progress.clear() # Réinitialiser download_progress
|
config.download_progress.clear() # Réinitialiser download_progress
|
||||||
|
config.pending_download = None # Réinitialiser après téléchargement
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
del config.download_tasks[task_id]
|
del config.download_tasks[task_id]
|
||||||
logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}")
|
logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}")
|
||||||
@@ -453,14 +504,16 @@ async def main():
|
|||||||
config.download_result_start_time = pygame.time.get_ticks()
|
config.download_result_start_time = pygame.time.get_ticks()
|
||||||
config.menu_state = "download_result"
|
config.menu_state = "download_result"
|
||||||
config.download_progress.clear() # Réinitialiser download_progress
|
config.download_progress.clear() # Réinitialiser download_progress
|
||||||
|
config.pending_download = None # Réinitialiser après téléchargement
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
del config.download_tasks[task_id]
|
del config.download_tasks[task_id]
|
||||||
logger.error(f"Erreur dans tâche de téléchargement: {str(e)}")
|
logger.error(f"Erreur dans tâche de téléchargement: {str(e)}")
|
||||||
|
|
||||||
# Gestion de la fin du popup download_result
|
# Gestion de la fin du popup download_result
|
||||||
if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000:
|
if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000:
|
||||||
config.menu_state = "game"
|
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "history"] else "game"
|
||||||
config.download_progress.clear() # Réinitialiser download_progress
|
config.download_progress.clear() # Réinitialiser download_progress
|
||||||
|
config.pending_download = None # Réinitialiser après affichage du résultat
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug(f"Fin popup download_result, retour à {config.menu_state}")
|
logger.debug(f"Fin popup download_result, retour à {config.menu_state}")
|
||||||
|
|
||||||
@@ -500,7 +553,18 @@ async def main():
|
|||||||
elif config.menu_state == "controls_help":
|
elif config.menu_state == "controls_help":
|
||||||
draw_controls_help(screen, config.previous_menu_state)
|
draw_controls_help(screen, config.previous_menu_state)
|
||||||
logger.debug("Rendu de draw_controls_help")
|
logger.debug("Rendu de draw_controls_help")
|
||||||
|
elif config.menu_state == "history":
|
||||||
|
draw_history(screen)
|
||||||
|
logger.debug("Rendu de draw_history")
|
||||||
|
elif config.menu_state == "confirm_clear_history":
|
||||||
|
draw_clear_history_dialog(screen)
|
||||||
|
logger.debug("Rendu de confirm_clear_history")
|
||||||
|
else:
|
||||||
|
# Gestion des états non valides
|
||||||
|
config.menu_state = "platform"
|
||||||
|
draw_platform_grid(screen)
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.error(f"État de menu non valide détecté: {config.menu_state}, retour à platform")
|
||||||
draw_controls(screen, config.menu_state)
|
draw_controls(screen, config.menu_state)
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
config.needs_redraw = False
|
config.needs_redraw = False
|
||||||
|
|||||||
12
config.py
12
config.py
@@ -45,6 +45,16 @@ pending_download = None
|
|||||||
controls_config = {}
|
controls_config = {}
|
||||||
selected_pause_option = 0
|
selected_pause_option = 0
|
||||||
previous_menu_state = None
|
previous_menu_state = None
|
||||||
|
history = [] # Liste des entrées de l'historique
|
||||||
|
current_history_item = 0 # Index de l'élément sélectionné dans l'historique
|
||||||
|
history_scroll_offset = 0 # Offset pour le défilement de l'historique
|
||||||
|
visible_history_items = 15 # Nombre d'éléments d'historique visibles (ajusté dynamiquement)
|
||||||
|
confirm_clear_selection = 0 # confirmation clear historique
|
||||||
|
last_state_change_time = 0 # Temps du dernier changement d'état pour debounce
|
||||||
|
debounce_delay = 200 # Délai de debounce en millisecondes
|
||||||
|
platform_dicts = [] # Liste des dictionnaires de plateformes
|
||||||
|
selected_key = (0, 0) # Position du curseur dans le clavier virtuel
|
||||||
|
is_non_pc = True # Indicateur pour plateforme non-PC (par exemple, console)
|
||||||
|
|
||||||
# Résolution de l'écran
|
# Résolution de l'écran
|
||||||
screen_width = 800
|
screen_width = 800
|
||||||
@@ -68,6 +78,8 @@ small_font = None
|
|||||||
|
|
||||||
CONTROLS_CONFIG_PATH = "/userdata/saves/ports/rgsx/controls.json"
|
CONTROLS_CONFIG_PATH = "/userdata/saves/ports/rgsx/controls.json"
|
||||||
"""Chemin du fichier de configuration des contrôles."""
|
"""Chemin du fichier de configuration des contrôles."""
|
||||||
|
HISTORY_PATH = "/userdata/saves/ports/rgsx/history.json"
|
||||||
|
"""Chemin du fichier de l'historique des téléchargements."""
|
||||||
|
|
||||||
def init_font():
|
def init_font():
|
||||||
"""Initialise les polices après pygame.init()."""
|
"""Initialise les polices après pygame.init()."""
|
||||||
|
|||||||
805
controls.py
805
controls.py
@@ -3,10 +3,12 @@ import config
|
|||||||
from config import CONTROLS_CONFIG_PATH
|
from config import CONTROLS_CONFIG_PATH
|
||||||
import asyncio
|
import asyncio
|
||||||
import math
|
import math
|
||||||
|
import json
|
||||||
from display import draw_validation_transition
|
from display import draw_validation_transition
|
||||||
from network import download_rom, check_extension_before_download
|
from network import download_rom, check_extension_before_download
|
||||||
from controls_mapper import get_readable_input_name
|
from controls_mapper import get_readable_input_name
|
||||||
from utils import load_games # Ajout de l'import
|
from utils import load_games
|
||||||
|
from history import load_history, clear_history
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -18,15 +20,57 @@ JOYHAT_DEBOUNCE = 200 # Délai anti-rebond pour JOYHATMOTION (ms)
|
|||||||
JOYAXIS_DEBOUNCE = 50 # Délai anti-rebond pour JOYAXISMOTION (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)
|
REPEAT_ACTION_DEBOUNCE = 50 # Délai anti-rebond pour répétitions up/down/left/right (ms)
|
||||||
|
|
||||||
|
# Liste des états valides (mise à jour)
|
||||||
|
VALID_STATES = [
|
||||||
|
"platform", "game", "download_progress", "download_result", "confirm_exit",
|
||||||
|
"extension_warning", "pause_menu", "controls_help", "history", "remap_controls",
|
||||||
|
"error", "loading", "confirm_clear_history" # Ajout du nouvel état
|
||||||
|
]
|
||||||
|
|
||||||
|
def validate_menu_state(state):
|
||||||
|
"""Valide l'état du menu et retourne un état par défaut si non valide."""
|
||||||
|
return state if state in VALID_STATES else "platform"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def load_controls_config(path=CONTROLS_CONFIG_PATH):
|
def load_controls_config(path=CONTROLS_CONFIG_PATH):
|
||||||
"""Charge la configuration des contrôles depuis un fichier JSON."""
|
"""Charge la configuration des contrôles depuis un fichier JSON."""
|
||||||
try:
|
try:
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
return json.load(f)
|
config_data = json.load(f)
|
||||||
|
# Vérifier les actions nécessaires
|
||||||
|
required_actions = ["confirm", "cancel", "left", "right"]
|
||||||
|
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
|
||||||
|
}[action]
|
||||||
|
}
|
||||||
|
return config_data
|
||||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||||
logging.error(f"Erreur lors de la lecture de {path} : {e}")
|
logger.error(f"Erreur lors de la lecture de {path} : {e}, utilisation de la configuration par défaut")
|
||||||
return {}
|
return {
|
||||||
|
"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_t},
|
||||||
|
"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}
|
||||||
|
}
|
||||||
|
|
||||||
def is_input_matched(event, action_name):
|
def is_input_matched(event, action_name):
|
||||||
"""Vérifie si l'événement correspond à l'action configurée."""
|
"""Vérifie si l'événement correspond à l'action configurée."""
|
||||||
if not config.controls_config.get(action_name):
|
if not config.controls_config.get(action_name):
|
||||||
@@ -53,7 +97,6 @@ def is_input_matched(event, action_name):
|
|||||||
logger.debug(f"Vérification axis: event_axis={event_axis}, event_value={event_value}, input_value={input_value}, result={result}")
|
logger.debug(f"Vérification axis: event_axis={event_axis}, event_value={event_value}, input_value={input_value}, result={result}")
|
||||||
return result
|
return result
|
||||||
elif input_type == "hat" and event_type == pygame.JOYHATMOTION:
|
elif input_type == "hat" and event_type == pygame.JOYHATMOTION:
|
||||||
# Convertir input_value en tuple pour comparaison
|
|
||||||
input_value_tuple = tuple(input_value) if isinstance(input_value, list) else input_value
|
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}")
|
logger.debug(f"Vérification hat: event_value={event_value}, input_value={input_value_tuple}")
|
||||||
return event_value == input_value_tuple
|
return event_value == input_value_tuple
|
||||||
@@ -64,11 +107,15 @@ def is_input_matched(event, action_name):
|
|||||||
|
|
||||||
def handle_controls(event, sources, joystick, screen):
|
def handle_controls(event, sources, joystick, screen):
|
||||||
"""Gère un événement clavier/joystick/souris et la répétition automatique.
|
"""Gère un événement clavier/joystick/souris et la répétition automatique.
|
||||||
Retourne 'quit', 'download', ou None.
|
Retourne 'quit', 'download', 'redownload', ou None.
|
||||||
"""
|
"""
|
||||||
action = None
|
action = None
|
||||||
current_time = pygame.time.get_ticks()
|
current_time = pygame.time.get_ticks()
|
||||||
|
|
||||||
|
# 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
|
# Debounce général
|
||||||
if current_time - config.last_state_change_time < config.debounce_delay:
|
if current_time - config.last_state_change_time < config.debounce_delay:
|
||||||
return action
|
return action
|
||||||
@@ -100,14 +147,25 @@ def handle_controls(event, sources, joystick, screen):
|
|||||||
if is_input_matched(event, action_name):
|
if is_input_matched(event, action_name):
|
||||||
logger.debug(f"Action mappée détectée: {action_name}, input={get_readable_input_name(event)}")
|
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", "history", "remap_controls"):
|
||||||
|
config.previous_menu_state = config.menu_state
|
||||||
|
config.menu_state = "pause_menu"
|
||||||
|
config.selected_option = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Passage à pause_menu depuis {config.previous_menu_state}")
|
||||||
|
return action
|
||||||
|
|
||||||
# Erreur
|
# Erreur
|
||||||
if config.menu_state == "error":
|
if config.menu_state == "error":
|
||||||
if is_input_matched(event, "confirm"):
|
if is_input_matched(event, "confirm"):
|
||||||
config.menu_state = "loading"
|
config.menu_state = "loading"
|
||||||
|
config.needs_redraw = True
|
||||||
logger.debug("Sortie erreur avec Confirm")
|
logger.debug("Sortie erreur avec Confirm")
|
||||||
elif is_input_matched(event, "cancel"):
|
elif is_input_matched(event, "cancel"):
|
||||||
config.menu_state = "confirm_exit"
|
config.menu_state = "confirm_exit"
|
||||||
config.confirm_selection = 0
|
config.confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
|
||||||
# Plateformes
|
# Plateformes
|
||||||
elif config.menu_state == "platform":
|
elif config.menu_state == "platform":
|
||||||
@@ -167,17 +225,29 @@ 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.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
|
config.needs_redraw = True
|
||||||
elif is_input_matched(event, "page_down"):
|
elif is_input_matched(event, "page_down"):
|
||||||
if (config.current_page + 1) * 9 < len(config.platforms):
|
if (config.current_page + 1) * 9 < len(config.platforms):
|
||||||
config.current_page += 1
|
config.current_page += 1
|
||||||
config.selected_platform = config.current_page * 9 + row * 3
|
config.selected_platform = config.current_page * 9 + row * 3
|
||||||
if config.selected_platform >= len(config.platforms):
|
if config.selected_platform >= len(config.platforms):
|
||||||
config.selected_platform = len(config.platforms) - 1
|
config.selected_platform = len(config.platforms) - 1
|
||||||
config.repeat_action = None # Réinitialiser la répétition
|
config.repeat_action = None
|
||||||
config.repeat_key = None
|
config.repeat_key = None
|
||||||
config.repeat_start_time = 0
|
config.repeat_start_time = 0
|
||||||
config.repeat_last_action = current_time
|
config.repeat_last_action = current_time
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Page suivante, répétition réinitialisée")
|
logger.debug("Page suivante, 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 * 9 + row * 3
|
||||||
|
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, "progress"):
|
elif is_input_matched(event, "progress"):
|
||||||
if config.download_tasks:
|
if config.download_tasks:
|
||||||
config.menu_state = "download_progress"
|
config.menu_state = "download_progress"
|
||||||
@@ -186,352 +256,445 @@ def handle_controls(event, sources, joystick, screen):
|
|||||||
elif is_input_matched(event, "confirm"):
|
elif is_input_matched(event, "confirm"):
|
||||||
if config.platforms:
|
if config.platforms:
|
||||||
config.current_platform = config.selected_platform
|
config.current_platform = config.selected_platform
|
||||||
config.games = load_games(config.platforms[config.current_platform]) # Appel à load_games depuis utils
|
config.games = load_games(config.platforms[config.current_platform])
|
||||||
config.filtered_games = config.games
|
config.filtered_games = config.games
|
||||||
config.filter_active = False
|
config.filter_active = False
|
||||||
config.current_game = 0
|
config.current_game = 0
|
||||||
config.scroll_offset = 0
|
config.scroll_offset = 0
|
||||||
draw_validation_transition(screen, config.current_platform) # Animation de transition
|
draw_validation_transition(screen, config.current_platform)
|
||||||
config.menu_state = "game"
|
config.menu_state = "game"
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug(f"Plateforme sélectionnée: {config.platforms[config.current_platform]}, {len(config.games)} jeux chargés")
|
logger.debug(f"Plateforme sélectionnée: {config.platforms[config.current_platform]}, {len(config.games)} jeux chargés")
|
||||||
elif is_input_matched(event, "cancel"):
|
elif is_input_matched(event, "cancel"):
|
||||||
config.menu_state = "confirm_exit"
|
config.menu_state = "confirm_exit"
|
||||||
config.confirm_selection = 0
|
config.confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
|
||||||
# Jeux
|
# Jeux
|
||||||
elif config.menu_state == "game":
|
elif config.menu_state == "game":
|
||||||
if config.search_mode:
|
games = config.filtered_games if config.filter_active or config.search_mode else config.games
|
||||||
if config.is_non_pc:
|
if config.search_mode and config.is_non_pc:
|
||||||
keyboard_layout = [
|
keyboard_layout = [
|
||||||
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
||||||
['A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
|
['A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
|
||||||
['Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'],
|
['Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'],
|
||||||
['W', 'X', 'C', 'V', 'B', 'N']
|
['W', 'X', 'C', 'V', 'B', 'N']
|
||||||
]
|
]
|
||||||
row, col = config.selected_key
|
row, col = config.selected_key
|
||||||
max_row = len(keyboard_layout) - 1
|
max_row = len(keyboard_layout) - 1
|
||||||
max_col = len(keyboard_layout[row]) - 1
|
max_col = len(keyboard_layout[row]) - 1
|
||||||
if is_input_matched(event, "up"):
|
if is_input_matched(event, "up"):
|
||||||
if row > 0:
|
if row > 0:
|
||||||
config.selected_key = (row - 1, min(col, len(keyboard_layout[row - 1]) - 1))
|
config.selected_key = (row - 1, min(col, len(keyboard_layout[row - 1]) - 1))
|
||||||
config.repeat_action = "up"
|
config.repeat_action = "up"
|
||||||
config.repeat_start_time = current_time + REPEAT_DELAY
|
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||||
config.repeat_last_action = current_time
|
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.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, "down"):
|
|
||||||
if row < max_row:
|
|
||||||
config.selected_key = (row + 1, min(col, len(keyboard_layout[row + 1]) - 1))
|
|
||||||
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, "left"):
|
|
||||||
if col > 0:
|
|
||||||
config.selected_key = (row, col - 1)
|
|
||||||
config.repeat_action = "left"
|
|
||||||
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, "right"):
|
|
||||||
if col < max_col:
|
|
||||||
config.selected_key = (row, col + 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 is_input_matched(event, "confirm"):
|
|
||||||
key = keyboard_layout[row][col]
|
|
||||||
if len(config.search_query) < 50:
|
|
||||||
config.search_query += key.lower()
|
|
||||||
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif is_input_matched(event, "delete"):
|
|
||||||
config.search_query = config.search_query[:-1]
|
|
||||||
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
elif is_input_matched(event, "space"):
|
elif is_input_matched(event, "down"):
|
||||||
config.search_query += " "
|
if row < max_row:
|
||||||
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
|
config.selected_key = (row + 1, min(col, len(keyboard_layout[row + 1]) - 1))
|
||||||
config.current_game = 0
|
config.repeat_action = "down"
|
||||||
config.scroll_offset = 0
|
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
|
config.needs_redraw = True
|
||||||
elif is_input_matched(event, "cancel"):
|
elif is_input_matched(event, "left"):
|
||||||
config.search_mode = False
|
if col > 0:
|
||||||
config.search_query = ""
|
config.selected_key = (row, col - 1)
|
||||||
config.filtered_games = config.games
|
config.repeat_action = "left"
|
||||||
config.filter_active = False
|
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||||
config.current_game = 0
|
config.repeat_last_action = current_time
|
||||||
config.scroll_offset = 0
|
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
|
config.needs_redraw = True
|
||||||
logger.debug("Filtre annulé")
|
elif is_input_matched(event, "right"):
|
||||||
elif is_input_matched(event, "filter"):
|
if col < max_col:
|
||||||
config.search_mode = False
|
config.selected_key = (row, col + 1)
|
||||||
config.filter_active = bool(config.search_query)
|
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
|
config.needs_redraw = True
|
||||||
else:
|
|
||||||
if is_input_matched(event, "confirm"):
|
|
||||||
config.search_mode = False
|
|
||||||
config.filter_active = bool(config.search_query)
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif is_input_matched(event, "cancel"):
|
|
||||||
config.search_mode = False
|
|
||||||
config.search_query = ""
|
|
||||||
config.filtered_games = config.games
|
|
||||||
config.filter_active = False
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Filtre annulé")
|
|
||||||
elif event.type == pygame.KEYDOWN:
|
|
||||||
if event.key == pygame.K_BACKSPACE:
|
|
||||||
config.search_query = config.search_query[:-1]
|
|
||||||
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif event.key == pygame.K_SPACE:
|
|
||||||
config.search_query += " "
|
|
||||||
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif event.unicode.isprintable() and len(config.search_query) < 50:
|
|
||||||
config.search_query += event.unicode
|
|
||||||
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
else:
|
|
||||||
if is_input_matched(event, "down"):
|
|
||||||
config.current_game = min(config.current_game + 1, len(config.filtered_games) - 1)
|
|
||||||
if config.current_game >= config.scroll_offset + config.visible_games:
|
|
||||||
config.scroll_offset += 1
|
|
||||||
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"):
|
|
||||||
config.current_game = max(config.current_game - 1, 0)
|
|
||||||
if config.current_game < config.scroll_offset:
|
|
||||||
config.scroll_offset -= 1
|
|
||||||
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, "page_up"):
|
|
||||||
config.current_game = max(config.current_game - config.visible_games, 0)
|
|
||||||
config.scroll_offset = max(config.scroll_offset - config.visible_games, 0)
|
|
||||||
config.repeat_action = "page_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, "page_down"):
|
|
||||||
config.current_game = min(config.current_game + config.visible_games, len(config.filtered_games) - 1)
|
|
||||||
config.scroll_offset = min(config.scroll_offset + config.visible_games, len(config.filtered_games) - config.visible_games)
|
|
||||||
config.repeat_action = "page_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, "confirm"):
|
elif is_input_matched(event, "confirm"):
|
||||||
if config.filtered_games:
|
config.search_query += keyboard_layout[row][col]
|
||||||
action = "download"
|
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()]
|
||||||
|
config.current_game = 0
|
||||||
|
config.scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Recherche mise à jour: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
|
||||||
|
elif is_input_matched(event, "delete"):
|
||||||
|
if config.search_query:
|
||||||
|
config.search_query = config.search_query[:-1]
|
||||||
|
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()]
|
||||||
|
config.current_game = 0
|
||||||
|
config.scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Suppression caractère: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
|
||||||
|
elif is_input_matched(event, "space"):
|
||||||
|
config.search_query += " "
|
||||||
|
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()]
|
||||||
|
config.current_game = 0
|
||||||
|
config.scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Espace ajouté: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
|
||||||
|
elif is_input_matched(event, "cancel"):
|
||||||
|
config.search_mode = False
|
||||||
|
config.search_query = ""
|
||||||
|
config.selected_key = (0, 0)
|
||||||
|
config.filtered_games = config.games
|
||||||
|
config.current_game = 0
|
||||||
|
config.scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug("Sortie du mode recherche")
|
||||||
|
else:
|
||||||
|
if is_input_matched(event, "up"):
|
||||||
|
if config.current_game > 0:
|
||||||
|
config.current_game -= 1
|
||||||
|
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, "down"):
|
||||||
|
if config.current_game < len(games) - 1:
|
||||||
|
config.current_game += 1
|
||||||
|
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, "page_up"):
|
||||||
|
config.current_game = max(0, config.current_game - config.visible_games)
|
||||||
|
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 dans la liste des jeux")
|
||||||
|
elif is_input_matched(event, "page_down"):
|
||||||
|
config.current_game = min(len(games) - 1, config.current_game + config.visible_games)
|
||||||
|
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 suivante dans la liste des jeux")
|
||||||
elif is_input_matched(event, "filter"):
|
elif is_input_matched(event, "filter"):
|
||||||
config.search_mode = True
|
config.search_mode = True
|
||||||
config.search_query = ""
|
config.search_query = ""
|
||||||
config.filtered_games = config.games
|
config.filtered_games = config.games
|
||||||
|
config.current_game = 0
|
||||||
|
config.scroll_offset = 0
|
||||||
config.selected_key = (0, 0)
|
config.selected_key = (0, 0)
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Entrée en mode recherche")
|
logger.debug("Entrée en mode recherche")
|
||||||
elif is_input_matched(event, "cancel"):
|
|
||||||
config.menu_state = "platform"
|
|
||||||
config.current_game = 0
|
|
||||||
config.scroll_offset = 0
|
|
||||||
config.filter_active = False
|
|
||||||
config.filtered_games = config.games
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Retour à platform, filtre réinitialisé")
|
|
||||||
elif is_input_matched(event, "progress"):
|
elif is_input_matched(event, "progress"):
|
||||||
if config.download_tasks:
|
if config.download_tasks:
|
||||||
config.menu_state = "download_progress"
|
config.menu_state = "download_progress"
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Retour à download_progress depuis game")
|
logger.debug("Retour à download_progress depuis game")
|
||||||
|
elif is_input_matched(event, "confirm"):
|
||||||
|
if games:
|
||||||
|
config.pending_download = check_extension_before_download(games[config.current_game][0], config.platforms[config.current_platform], games[config.current_game][1])
|
||||||
|
if config.pending_download:
|
||||||
|
url, platform, game_name, is_zip_non_supported = config.pending_download
|
||||||
|
if is_zip_non_supported:
|
||||||
|
config.menu_state = "extension_warning"
|
||||||
|
config.extension_confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Extension non supportée, passage à extension_warning pour {game_name}")
|
||||||
|
else:
|
||||||
|
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||||
|
config.download_tasks[task] = (task, url, game_name, platform) # Stocker tuple de 4 éléments
|
||||||
|
config.menu_state = "download_progress"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Début du téléchargement: {game_name} pour {platform} depuis {url}")
|
||||||
|
config.pending_download = None # Réinitialiser après démarrage
|
||||||
|
action = "download"
|
||||||
|
else:
|
||||||
|
config.menu_state = "error"
|
||||||
|
config.error_message = "Extension non supportée ou erreur de téléchargement"
|
||||||
|
config.pending_download = None
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.error(f"config.pending_download est None pour {games[config.current_game][0]}")
|
||||||
|
elif is_input_matched(event, "cancel"):
|
||||||
|
config.menu_state = "platform"
|
||||||
|
config.current_game = 0
|
||||||
|
config.scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug("Retour à platform")
|
||||||
|
|
||||||
# Download progress
|
elif config.menu_state == "history":
|
||||||
|
history = config.history
|
||||||
|
if is_input_matched(event, "up"):
|
||||||
|
if config.current_history_item > 0:
|
||||||
|
config.current_history_item -= 1
|
||||||
|
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, "down"):
|
||||||
|
if config.current_history_item < len(history) - 1:
|
||||||
|
config.current_history_item += 1
|
||||||
|
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, "page_up"):
|
||||||
|
config.current_history_item = max(0, config.current_history_item - config.visible_history_items)
|
||||||
|
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 dans l'historique")
|
||||||
|
elif is_input_matched(event, "page_down"):
|
||||||
|
config.current_history_item = min(len(history) - 1, config.current_history_item + config.visible_history_items)
|
||||||
|
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 suivante dans l'historique")
|
||||||
|
elif is_input_matched(event, "progress"):
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "confirm_clear_history"
|
||||||
|
config.confirm_clear_selection = 0 # 0 pour "Non", 1 pour "Oui"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug("Passage à confirm_clear_history depuis history")
|
||||||
|
elif is_input_matched(event, "confirm"):
|
||||||
|
if history:
|
||||||
|
entry = history[config.current_history_item]
|
||||||
|
platform = entry["platform"]
|
||||||
|
game_name = entry["game_name"]
|
||||||
|
# Rechercher l'URL dans config.games
|
||||||
|
for game in config.games:
|
||||||
|
if game[0] == game_name and config.platforms[config.current_platform] == platform:
|
||||||
|
config.pending_download = check_extension_before_download(game_name, platform, game[1])
|
||||||
|
if config.pending_download:
|
||||||
|
url, platform, game_name, is_zip_non_supported = config.pending_download
|
||||||
|
if is_zip_non_supported:
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "extension_warning"
|
||||||
|
config.extension_confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Extension non supportée pour retéléchargement, passage à extension_warning pour {game_name}")
|
||||||
|
else:
|
||||||
|
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||||
|
config.download_tasks[task] = (task, url, game_name, platform)
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "download_progress"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retéléchargement: {game_name} pour {platform} depuis {url}")
|
||||||
|
config.pending_download = None
|
||||||
|
action = "redownload"
|
||||||
|
else:
|
||||||
|
config.menu_state = "error"
|
||||||
|
config.error_message = "Extension non supportée ou erreur de retéléchargement"
|
||||||
|
config.pending_download = None
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.error(f"config.pending_download est None pour {game_name}")
|
||||||
|
break
|
||||||
|
elif is_input_matched(event, "cancel"):
|
||||||
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.current_history_item = 0
|
||||||
|
config.history_scroll_offset = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retour à {config.menu_state} depuis history")
|
||||||
|
# Ajouter un nouvel état "confirm_clear_history" après l'état "confirm_exit"
|
||||||
|
elif config.menu_state == "confirm_clear_history":
|
||||||
|
logger.debug(f"État confirm_clear_history, confirm_clear_selection={config.confirm_clear_selection}, événement={event.type}, valeur={getattr(event, 'value', None)}")
|
||||||
|
if is_input_matched(event, "confirm"):
|
||||||
|
logger.debug(f"Action confirm détectée dans confirm_clear_history")
|
||||||
|
if config.confirm_clear_selection == 1: # Oui
|
||||||
|
clear_history()
|
||||||
|
config.history = []
|
||||||
|
config.current_history_item = 0
|
||||||
|
config.history_scroll_offset = 0
|
||||||
|
config.menu_state = "history"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.info("Historique vidé après confirmation")
|
||||||
|
else: # Non
|
||||||
|
config.menu_state = "history"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug("Annulation du vidage de l'historique, retour à history")
|
||||||
|
elif is_input_matched(event, "left"):
|
||||||
|
logger.debug(f"Action left détectée dans confirm_clear_history")
|
||||||
|
config.confirm_clear_selection = 1 # Sélectionner "Non"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Changement sélection confirm_clear_history: {config.confirm_clear_selection}")
|
||||||
|
elif is_input_matched(event, "right"):
|
||||||
|
logger.debug(f"Action right détectée dans confirm_clear_history")
|
||||||
|
config.confirm_clear_selection = 0 # Sélectionner "Oui"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Changement sélection confirm_clear_history: {config.confirm_clear_selection}")
|
||||||
|
elif is_input_matched(event, "cancel"):
|
||||||
|
logger.debug(f"Action cancel détectée dans confirm_clear_history")
|
||||||
|
config.menu_state = "history"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug("Annulation du vidage de l'historique, retour à history")
|
||||||
|
# Progression téléchargement
|
||||||
elif config.menu_state == "download_progress":
|
elif config.menu_state == "download_progress":
|
||||||
if is_input_matched(event, "cancel"):
|
if is_input_matched(event, "cancel"):
|
||||||
if config.download_tasks:
|
for task in config.download_tasks:
|
||||||
task = list(config.download_tasks.keys())[0]
|
task.cancel()
|
||||||
config.download_tasks[task][0].cancel()
|
config.download_tasks.clear()
|
||||||
url = config.download_tasks[task][1]
|
config.download_progress.clear()
|
||||||
game_name = config.download_tasks[task][2]
|
|
||||||
if url in config.download_progress:
|
|
||||||
del config.download_progress[url]
|
|
||||||
del config.download_tasks[task]
|
|
||||||
config.download_result_message = f"Téléchargement annulé : {game_name}"
|
|
||||||
config.download_result_error = True
|
|
||||||
config.download_result_start_time = pygame.time.get_ticks()
|
|
||||||
config.menu_state = "download_result"
|
|
||||||
elif is_input_matched(event, "progress"):
|
|
||||||
config.menu_state = "game"
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Retour à game depuis download_progress")
|
|
||||||
|
|
||||||
# Confirmation de sortie
|
|
||||||
elif config.menu_state == "confirm_exit":
|
|
||||||
if is_input_matched(event, "left"):
|
|
||||||
config.confirm_selection = 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Sélection Oui")
|
|
||||||
elif is_input_matched(event, "right"):
|
|
||||||
config.confirm_selection = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Sélection Non")
|
|
||||||
elif is_input_matched(event, "confirm"):
|
|
||||||
if config.confirm_selection == 1:
|
|
||||||
logger.debug("Retour de 'quit' pour fermer l'application")
|
|
||||||
return "quit"
|
|
||||||
else:
|
|
||||||
config.menu_state = "platform"
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Retour à platform depuis confirm_exit")
|
|
||||||
elif is_input_matched(event, "cancel"):
|
|
||||||
config.menu_state = "platform"
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Annulation confirm_exit")
|
|
||||||
|
|
||||||
# Avertissement d'extension
|
|
||||||
elif config.menu_state == "extension_warning":
|
|
||||||
if is_input_matched(event, "left"):
|
|
||||||
config.extension_confirm_selection = 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Sélection Oui (extension_warning)")
|
|
||||||
elif is_input_matched(event, "right"):
|
|
||||||
config.extension_confirm_selection = 0
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Sélection Non (extension_warning)")
|
|
||||||
elif is_input_matched(event, "confirm"):
|
|
||||||
if config.extension_confirm_selection == 1:
|
|
||||||
if config.pending_download:
|
|
||||||
url, platform, game_name, is_zip_non_supported = config.pending_download
|
|
||||||
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported=is_zip_non_supported))
|
|
||||||
config.download_tasks[task] = (task, url, game_name, platform)
|
|
||||||
config.menu_state = "download_progress"
|
|
||||||
config.pending_download = None
|
|
||||||
config.needs_redraw = True
|
|
||||||
else:
|
|
||||||
config.menu_state = "game"
|
|
||||||
config.needs_redraw = True
|
|
||||||
else:
|
|
||||||
config.menu_state = "game"
|
|
||||||
config.pending_download = None
|
|
||||||
config.needs_redraw = True
|
|
||||||
logger.debug("Téléchargement annulé (extension_warning)")
|
|
||||||
elif is_input_matched(event, "cancel"):
|
|
||||||
config.menu_state = "game"
|
|
||||||
config.pending_download = None
|
config.pending_download = None
|
||||||
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Annulation extension_warning")
|
logger.debug(f"Téléchargement annulé, retour à {config.menu_state}")
|
||||||
|
elif is_input_matched(event, "progress"):
|
||||||
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retour à {config.menu_state} depuis download_progress")
|
||||||
|
|
||||||
# Résultat téléchargement
|
# Résultat téléchargement
|
||||||
elif config.menu_state == "download_result":
|
elif config.menu_state == "download_result":
|
||||||
if is_input_matched(event, "confirm"):
|
if is_input_matched(event, "confirm"):
|
||||||
config.menu_state = "game"
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.popup_timer = 0
|
||||||
|
config.pending_download = None
|
||||||
config.needs_redraw = True
|
config.needs_redraw = True
|
||||||
logger.debug("Retour à game depuis download_result")
|
logger.debug(f"Retour à {config.menu_state} depuis download_result")
|
||||||
|
|
||||||
# Enregistrer la touche pour la répétition
|
# Confirmation quitter
|
||||||
if config.repeat_action in ["up", "down", "page_up", "page_down", "left", "right"]:
|
elif config.menu_state == "confirm_exit":
|
||||||
if event.type == pygame.KEYDOWN:
|
if is_input_matched(event, "confirm"):
|
||||||
config.repeat_key = event.key
|
if config.confirm_selection == 1:
|
||||||
elif event.type == pygame.JOYBUTTONDOWN:
|
return "quit"
|
||||||
config.repeat_key = event.button
|
else:
|
||||||
elif event.type == pygame.JOYAXISMOTION:
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
config.repeat_key = (event.axis, 1 if event.value > 0 else -1)
|
config.needs_redraw = True
|
||||||
elif event.type == pygame.JOYHATMOTION:
|
logger.debug(f"Retour à {config.menu_state} depuis confirm_exit")
|
||||||
config.repeat_key = event.value
|
elif is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||||
|
config.confirm_selection = 1 - config.confirm_selection
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Changement sélection confirm_exit: {config.confirm_selection}")
|
||||||
|
|
||||||
|
# Avertissement extension
|
||||||
|
elif config.menu_state == "extension_warning":
|
||||||
|
if is_input_matched(event, "confirm"):
|
||||||
|
if config.extension_confirm_selection == 1:
|
||||||
|
if config.pending_download and len(config.pending_download) == 4:
|
||||||
|
url, platform, game_name, is_zip_non_supported = config.pending_download
|
||||||
|
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
|
||||||
|
config.download_tasks[task] = (task, url, game_name, platform)
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "download_progress"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Téléchargement confirmé après avertissement: {game_name} pour {platform} depuis {url}")
|
||||||
|
config.pending_download = None
|
||||||
|
action = "download"
|
||||||
|
else:
|
||||||
|
config.menu_state = "error"
|
||||||
|
config.error_message = "Données de téléchargement invalides"
|
||||||
|
config.pending_download = None
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.error("config.pending_download invalide")
|
||||||
|
else:
|
||||||
|
config.pending_download = None
|
||||||
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retour à {config.menu_state} depuis extension_warning")
|
||||||
|
elif is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||||
|
config.extension_confirm_selection = 1 - config.extension_confirm_selection
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Changement sélection extension_warning: {config.extension_confirm_selection}")
|
||||||
|
elif is_input_matched(event, "cancel"):
|
||||||
|
config.pending_download = None
|
||||||
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retour à {config.menu_state} depuis extension_warning")
|
||||||
|
|
||||||
|
# Menu pause
|
||||||
|
elif config.menu_state == "pause_menu":
|
||||||
|
if is_input_matched(event, "up"):
|
||||||
|
config.selected_option = max(0, config.selected_option - 1)
|
||||||
|
config.repeat_action = "up"
|
||||||
|
config.repeat_start_time = current_time + REPEAT_DELAY
|
||||||
config.repeat_last_action = current_time
|
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, "down"):
|
||||||
|
config.selected_option = min(3, config.selected_option + 1)
|
||||||
|
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, "confirm"):
|
||||||
|
if config.selected_option == 0: # Controls
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "controls_help"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Passage à controls_help depuis pause_menu")
|
||||||
|
elif config.selected_option == 1: # Remap controls
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "remap_controls"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Passage à remap_controls depuis pause_menu")
|
||||||
|
elif config.selected_option == 2: # History
|
||||||
|
config.history = load_history()
|
||||||
|
config.current_history_item = 0
|
||||||
|
config.history_scroll_offset = 0
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "history"
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Passage à history depuis pause_menu")
|
||||||
|
elif config.selected_option == 3: # Quit
|
||||||
|
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.menu_state = "confirm_exit"
|
||||||
|
config.confirm_selection = 0
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Passage à confirm_exit depuis pause_menu")
|
||||||
|
elif is_input_matched(event, "cancel"):
|
||||||
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
|
config.needs_redraw = True
|
||||||
|
logger.debug(f"Retour à {config.menu_state} depuis pause_menu")
|
||||||
|
|
||||||
elif event.type in (pygame.KEYUP, pygame.JOYBUTTONUP):
|
# Aide contrôles
|
||||||
if config.menu_state in ("game", "platform") and is_input_matched(event, config.repeat_action):
|
elif config.menu_state == "controls_help":
|
||||||
config.repeat_action = None
|
if is_input_matched(event, "cancel"):
|
||||||
config.repeat_key = None
|
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||||
config.repeat_start_time = 0
|
config.needs_redraw = True
|
||||||
config.needs_redraw = True
|
logger.debug(f"Retour à {config.menu_state} depuis controls_help")
|
||||||
|
|
||||||
# Gestion de la répétition automatique
|
# Remap controls
|
||||||
if config.menu_state in ("game", "platform") and config.repeat_action:
|
elif config.menu_state == "remap_controls":
|
||||||
if current_time >= config.repeat_start_time:
|
if is_input_matched(event, "cancel"):
|
||||||
if config.repeat_action in ["up", "down", "left", "right"] and current_time - config.repeat_last_action < REPEAT_ACTION_DEBOUNCE:
|
config.menu_state = "pause_menu"
|
||||||
return action
|
config.needs_redraw = True
|
||||||
|
logger.debug("Retour à pause_menu depuis remap_controls")
|
||||||
|
|
||||||
last_repeat_time = config.repeat_start_time - REPEAT_INTERVAL
|
# Gestion de la répétition automatique (relâchement)
|
||||||
|
if event.type in (pygame.KEYUP, pygame.JOYBUTTONUP, pygame.JOYAXISMOTION, pygame.JOYHATMOTION):
|
||||||
|
if event.type == pygame.JOYAXISMOTION and abs(event.value) > 0.5:
|
||||||
|
return action
|
||||||
|
if event.type == pygame.JOYHATMOTION and event.value != (0, 0):
|
||||||
|
return action
|
||||||
|
config.repeat_action = None
|
||||||
|
config.repeat_key = None
|
||||||
|
config.repeat_start_time = 0
|
||||||
|
logger.debug("Répétition arrêtée")
|
||||||
|
|
||||||
|
return action
|
||||||
|
|
||||||
|
async def handle_repeat_actions():
|
||||||
|
"""Gère la répétition automatique des actions."""
|
||||||
|
current_time = pygame.time.get_ticks()
|
||||||
|
if config.repeat_action and config.repeat_key and current_time > config.repeat_start_time:
|
||||||
|
if current_time - config.repeat_last_action > REPEAT_ACTION_DEBOUNCE:
|
||||||
|
logger.debug(f"Répétition action: {config.repeat_action}")
|
||||||
|
event_dict = {
|
||||||
|
"type": pygame.KEYDOWN if isinstance(config.repeat_key, int) and config.repeat_key < 1000 else pygame.JOYBUTTONDOWN if isinstance(config.repeat_key, int) else pygame.JOYAXISMOTION if isinstance(config.repeat_key, tuple) and len(config.repeat_key) == 2 else pygame.JOYHATMOTION,
|
||||||
|
"key": config.repeat_key if isinstance(config.repeat_key, int) and config.repeat_key < 1000 else None,
|
||||||
|
"button": config.repeat_key if isinstance(config.repeat_key, int) and config.repeat_key >= 1000 else None,
|
||||||
|
"axis": config.repeat_key[0] if isinstance(config.repeat_key, tuple) and len(config.repeat_key) == 2 else None,
|
||||||
|
"value": config.repeat_key[1] if isinstance(config.repeat_key, tuple) and len(config.repeat_key) == 2 else config.repeat_key if isinstance(config.repeat_key, tuple) else None
|
||||||
|
}
|
||||||
|
handle_controls(event_dict, None, None, None)
|
||||||
config.repeat_last_action = current_time
|
config.repeat_last_action = current_time
|
||||||
if config.menu_state == "game":
|
config.repeat_start_time = current_time + REPEAT_INTERVAL
|
||||||
if config.repeat_action == "down":
|
|
||||||
config.current_game = min(config.current_game + 1, len(config.filtered_games) - 1)
|
|
||||||
if config.current_game >= config.scroll_offset + config.visible_games:
|
|
||||||
config.scroll_offset += 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.repeat_action == "up":
|
|
||||||
config.current_game = max(config.current_game - 1, 0)
|
|
||||||
if config.current_game < config.scroll_offset:
|
|
||||||
config.scroll_offset -= 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.repeat_action == "page_down":
|
|
||||||
config.current_game = min(config.current_game + config.visible_games, len(config.filtered_games) - 1)
|
|
||||||
config.scroll_offset = min(config.scroll_offset + config.visible_games, len(config.filtered_games) - config.visible_games)
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.repeat_action == "page_up":
|
|
||||||
config.current_game = max(config.current_game - config.visible_games, 0)
|
|
||||||
config.scroll_offset = max(config.scroll_offset - config.visible_games, 0)
|
|
||||||
config.needs_redraw = True
|
|
||||||
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
|
|
||||||
if config.repeat_action == "down":
|
|
||||||
if current_grid_index + 3 <= max_index:
|
|
||||||
config.selected_platform += 3
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.repeat_action == "up":
|
|
||||||
if current_grid_index - 3 >= 0:
|
|
||||||
config.selected_platform -= 3
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.repeat_action == "left":
|
|
||||||
if current_grid_index % 3 != 0:
|
|
||||||
config.selected_platform -= 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.current_page > 0:
|
|
||||||
config.current_page -= 1
|
|
||||||
config.selected_platform = config.current_page * 9 + row * 3 + 2
|
|
||||||
if config.selected_platform >= len(config.platforms):
|
|
||||||
config.selected_platform = len(config.platforms) - 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif config.repeat_action == "right":
|
|
||||||
if current_grid_index % 3 != 2 and current_grid_index < max_index:
|
|
||||||
config.selected_platform += 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
elif (config.current_page + 1) * 9 < len(config.platforms):
|
|
||||||
config.current_page += 1
|
|
||||||
config.selected_platform = config.current_page * 9 + row * 3
|
|
||||||
if config.selected_platform >= len(config.platforms):
|
|
||||||
config.selected_platform = len(config.platforms) - 1
|
|
||||||
config.needs_redraw = True
|
|
||||||
config.repeat_start_time = last_repeat_time + REPEAT_INTERVAL
|
|
||||||
if config.repeat_start_time < current_time:
|
|
||||||
config.repeat_start_time = current_time + REPEAT_INTERVAL
|
|
||||||
|
|
||||||
return action
|
|
||||||
282
display.py
282
display.py
@@ -3,6 +3,7 @@ import config
|
|||||||
import math
|
import math
|
||||||
from utils import truncate_text_end, wrap_text, load_system_image, load_games
|
from utils import truncate_text_end, wrap_text, load_system_image, load_games
|
||||||
import logging
|
import logging
|
||||||
|
from history import load_history # Ajout de l'import
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -223,18 +224,18 @@ def draw_game_list(screen):
|
|||||||
|
|
||||||
line_height = config.font.get_height() + 10
|
line_height = config.font.get_height() + 10
|
||||||
margin_top_bottom = 20
|
margin_top_bottom = 20
|
||||||
extra_margin_top = 5 # Marge supplémentaire pour éviter le chevauchement avec le titre
|
extra_margin_top = 5
|
||||||
extra_margin_bottom = 40 # Marge supplémentaire en bas pour éloigner du texte des contrôles
|
extra_margin_bottom = 40
|
||||||
title_height = max(config.title_font.get_height(), config.search_font.get_height(), config.small_font.get_height()) + 20 # Hauteur du titre avec padding réduit
|
title_height = max(config.title_font.get_height(), config.search_font.get_height(), config.small_font.get_height()) + 20
|
||||||
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
|
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
|
||||||
games_per_page = available_height // line_height
|
games_per_page = available_height // line_height
|
||||||
|
config.visible_games = games_per_page # Mettre à jour config.visible_games
|
||||||
max_text_width = max([config.font.size(truncate_text_end(game[0] if isinstance(game, (list, tuple)) else game, config.font, config.screen_width - 80))[0] for game in games], default=300)
|
max_text_width = max([config.font.size(truncate_text_end(game[0] if isinstance(game, (list, tuple)) else game, config.font, config.screen_width - 80))[0] for game in games], default=300)
|
||||||
rect_width = max_text_width + 40
|
rect_width = max_text_width + 40
|
||||||
rect_height = games_per_page * line_height + 2 * margin_top_bottom
|
rect_height = games_per_page * line_height + 2 * margin_top_bottom
|
||||||
rect_x = (config.screen_width - rect_width) // 2
|
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
|
rect_y = title_height + extra_margin_top + (config.screen_height - title_height - extra_margin_top - extra_margin_bottom - rect_height) // 2
|
||||||
|
|
||||||
# Limiter scroll_offset pour éviter l'espace vide
|
|
||||||
config.scroll_offset = max(0, min(config.scroll_offset, max(0, len(games) - games_per_page)))
|
config.scroll_offset = max(0, min(config.scroll_offset, max(0, len(games) - games_per_page)))
|
||||||
if config.current_game < config.scroll_offset:
|
if config.current_game < config.scroll_offset:
|
||||||
config.scroll_offset = config.current_game
|
config.scroll_offset = config.current_game
|
||||||
@@ -243,7 +244,6 @@ def draw_game_list(screen):
|
|||||||
|
|
||||||
screen.blit(OVERLAY, (0, 0))
|
screen.blit(OVERLAY, (0, 0))
|
||||||
|
|
||||||
# Afficher le titre ou le texte de recherche/filtre
|
|
||||||
if config.search_mode:
|
if config.search_mode:
|
||||||
search_text = f"Filtrer : {config.search_query}_"
|
search_text = f"Filtrer : {config.search_query}_"
|
||||||
title_surface = config.search_font.render(search_text, True, (255, 255, 255))
|
title_surface = config.search_font.render(search_text, True, (255, 255, 255))
|
||||||
@@ -272,7 +272,6 @@ def draw_game_list(screen):
|
|||||||
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
||||||
screen.blit(title_surface, title_rect)
|
screen.blit(title_surface, title_rect)
|
||||||
|
|
||||||
# Afficher le rectangle de fond et la liste des jeux
|
|
||||||
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
|
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)
|
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
|
||||||
|
|
||||||
@@ -288,7 +287,126 @@ def draw_game_list(screen):
|
|||||||
draw_scrollbar(screen)
|
draw_scrollbar(screen)
|
||||||
if config.search_mode and config.is_non_pc:
|
if config.search_mode and config.is_non_pc:
|
||||||
draw_virtual_keyboard(screen)
|
draw_virtual_keyboard(screen)
|
||||||
|
|
||||||
|
def draw_history_list(screen):
|
||||||
|
"""Affiche l'historique des téléchargements sous forme de tableau avec système, nom du jeu et état."""
|
||||||
|
logger.debug("Début de draw_history_list")
|
||||||
|
|
||||||
|
history = config.history if hasattr(config, 'history') else load_history()
|
||||||
|
history_count = len(history)
|
||||||
|
|
||||||
|
if not history:
|
||||||
|
logger.debug("Aucun historique disponible")
|
||||||
|
message = "Aucun téléchargement dans l'historique"
|
||||||
|
lines = wrap_text(message, config.font, config.screen_width - 80)
|
||||||
|
line_height = config.font.get_height() + 5
|
||||||
|
text_height = len(lines) * line_height
|
||||||
|
margin_top_bottom = 20
|
||||||
|
rect_height = text_height + 2 * margin_top_bottom
|
||||||
|
max_text_width = max([config.font.size(line)[0] for line in lines], default=300)
|
||||||
|
rect_width = max_text_width + 40
|
||||||
|
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
|
||||||
|
margin_top_bottom = 20
|
||||||
|
extra_margin_top = 5
|
||||||
|
extra_margin_bottom = 40
|
||||||
|
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
|
||||||
|
config.visible_history_items = items_per_page # Mettre à jour config.visible_history_items
|
||||||
|
|
||||||
|
# Calculer la largeur des colonnes
|
||||||
|
col_platform_width = config.screen_width // 4 # ~25% pour le système
|
||||||
|
col_game_width = config.screen_width // 2 # ~50% pour le nom du jeu
|
||||||
|
col_status_width = config.screen_width // 4 # ~25% pour l'état
|
||||||
|
max_text_width = col_platform_width + col_game_width + col_status_width
|
||||||
|
rect_width = max_text_width + 40
|
||||||
|
rect_height = items_per_page * line_height + 2 * margin_top_bottom
|
||||||
|
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, (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)
|
||||||
|
|
||||||
|
# En-têtes du tableau
|
||||||
|
headers = ["Système", "Nom du jeu", "État"]
|
||||||
|
header_y = rect_y + margin_top_bottom - line_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)
|
||||||
|
|
||||||
|
# Lignes du tableau
|
||||||
|
for i in range(config.history_scroll_offset, min(config.history_scroll_offset + items_per_page, len(history))):
|
||||||
|
entry = history[i]
|
||||||
|
platform = entry["platform"]
|
||||||
|
game_name = entry["game_name"]
|
||||||
|
status = entry["status"]
|
||||||
|
color = (0, 150, 255) if i == config.current_history_item else (255, 255, 255)
|
||||||
|
platform_text = truncate_text_end(platform, config.small_font, col_platform_width - 10)
|
||||||
|
game_text = truncate_text_end(game_name, config.small_font, col_game_width - 10)
|
||||||
|
status_text = truncate_text_end(status, config.small_font, col_status_width - 10)
|
||||||
|
|
||||||
|
y_pos = rect_y + margin_top_bottom + (i - config.history_scroll_offset + 1) * 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}")
|
||||||
|
|
||||||
|
draw_history_scrollbar(screen)
|
||||||
|
|
||||||
|
def draw_history_scrollbar(screen):
|
||||||
|
"""Affiche la barre de défilement pour l'historique."""
|
||||||
|
if len(config.history) <= config.visible_history_items:
|
||||||
|
return
|
||||||
|
|
||||||
|
game_area_height = config.screen_height - 150
|
||||||
|
scrollbar_height = game_area_height * (config.visible_history_items / len(config.history))
|
||||||
|
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.history_scroll_offset / max(1, len(config.history) - config.visible_history_items))
|
||||||
|
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
|
||||||
|
|
||||||
def draw_virtual_keyboard(screen):
|
def draw_virtual_keyboard(screen):
|
||||||
"""Affiche un clavier virtuel pour la saisie dans search_mode, centré verticalement."""
|
"""Affiche un clavier virtuel pour la saisie dans search_mode, centré verticalement."""
|
||||||
keyboard_layout = [
|
keyboard_layout = [
|
||||||
@@ -352,10 +470,10 @@ def draw_progress_screen(screen):
|
|||||||
text_height = len(title_lines) * line_height
|
text_height = len(title_lines) * line_height
|
||||||
margin_top_bottom = 20
|
margin_top_bottom = 20
|
||||||
bar_height = int(config.screen_height * 0.0278) # ~30px pour 1080p
|
bar_height = int(config.screen_height * 0.0278) # ~30px pour 1080p
|
||||||
percent_height = line_height # Hauteur pour le texte de progression
|
percent_height = line_height
|
||||||
rect_height = text_height + bar_height + percent_height + 3 * margin_top_bottom
|
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)
|
max_text_width = max([config.font.size(line)[0] for line in title_lines], default=300)
|
||||||
bar_width = max_text_width # Ajuster la barre à la largeur du texte
|
bar_width = max_text_width
|
||||||
rect_width = max_text_width + 40
|
rect_width = max_text_width + 40
|
||||||
rect_x = (config.screen_width - rect_width) // 2
|
rect_x = (config.screen_width - rect_width) // 2
|
||||||
rect_y = (config.screen_height - rect_height) // 2
|
rect_y = (config.screen_height - rect_height) // 2
|
||||||
@@ -389,7 +507,7 @@ def draw_progress_screen(screen):
|
|||||||
percent_rect = percent_render.get_rect(center=(config.screen_width // 2, text_y + i * line_height + line_height // 2))
|
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)
|
screen.blit(percent_render, percent_rect)
|
||||||
logger.debug(f"Texte de progression affiché : texte={line}, position={percent_rect}, taille={percent_render.get_size()}")
|
logger.debug(f"Texte de progression affiché : texte={line}, position={percent_rect}, taille={percent_render.get_size()}")
|
||||||
|
|
||||||
def draw_scrollbar(screen):
|
def draw_scrollbar(screen):
|
||||||
"""Affiche la barre de défilement à droite de l’écran."""
|
"""Affiche la barre de défilement à droite de l’écran."""
|
||||||
if len(config.filtered_games) <= config.visible_games:
|
if len(config.filtered_games) <= config.visible_games:
|
||||||
@@ -400,11 +518,11 @@ def draw_scrollbar(screen):
|
|||||||
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.scroll_offset / max(1, len(config.filtered_games) - config.visible_games))
|
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.scroll_offset / max(1, len(config.filtered_games) - config.visible_games))
|
||||||
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
|
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
|
||||||
|
|
||||||
def draw_confirm_dialog(screen):
|
def draw_clear_history_dialog(screen):
|
||||||
"""Affiche la boîte de dialogue de confirmation pour quitter."""
|
"""Affiche la boîte de dialogue de confirmation pour vider l'historique."""
|
||||||
screen.blit(OVERLAY, (0, 0))
|
screen.blit(OVERLAY, (0, 0))
|
||||||
|
|
||||||
message = "Voulez-vous vraiment quitter ?"
|
message = "Vider l'historique ?"
|
||||||
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
|
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
|
||||||
line_height = config.font.get_height() + 5
|
line_height = config.font.get_height() + 5
|
||||||
text_height = len(wrapped_message) * line_height
|
text_height = len(wrapped_message) * line_height
|
||||||
@@ -424,14 +542,14 @@ def draw_confirm_dialog(screen):
|
|||||||
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
|
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)
|
screen.blit(text, text_rect)
|
||||||
|
|
||||||
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_selection == 1 else (255, 255, 255))
|
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_selection == 0 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))
|
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))
|
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(yes_text, yes_rect)
|
||||||
screen.blit(no_text, no_rect)
|
screen.blit(no_text, no_rect)
|
||||||
|
|
||||||
def draw_popup_message(screen, message, is_error):
|
def draw_popup_message(screen, message, is_error):
|
||||||
"""Affiche une popup avec un message de résultat."""
|
"""Affiche une popup avec un message de résultat."""
|
||||||
screen.blit(OVERLAY, (0, 0))
|
screen.blit(OVERLAY, (0, 0))
|
||||||
@@ -526,12 +644,12 @@ def draw_extension_warning(screen):
|
|||||||
def draw_controls(screen, menu_state):
|
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."""
|
"""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')
|
start_button = get_control_display('start', 'START')
|
||||||
control_text = f"Menu {menu_state} - {start_button} : Options - Controls"
|
control_text = f"Menu {menu_state} - {start_button} : Options - History - Controls"
|
||||||
max_width = config.screen_width - 40
|
max_width = config.screen_width - 40
|
||||||
wrapped_controls = wrap_text(control_text, config.small_font, max_width)
|
wrapped_controls = wrap_text(control_text, config.small_font, max_width)
|
||||||
line_height = config.small_font.get_height() + 5
|
line_height = config.small_font.get_height() + 5
|
||||||
rect_height = len(wrapped_controls) * line_height + 20
|
rect_height = len(wrapped_controls) * line_height + 20
|
||||||
rect_y = config.screen_height - rect_height - 40 # Marge inférieure de 40px
|
rect_y = config.screen_height - rect_height - 5
|
||||||
|
|
||||||
for i, line in enumerate(wrapped_controls):
|
for i, line in enumerate(wrapped_controls):
|
||||||
text_surface = config.small_font.render(line, True, (255, 255, 255))
|
text_surface = config.small_font.render(line, True, (255, 255, 255))
|
||||||
@@ -561,12 +679,13 @@ def draw_validation_transition(screen, platform_index):
|
|||||||
pygame.time.wait(10)
|
pygame.time.wait(10)
|
||||||
|
|
||||||
def draw_pause_menu(screen, selected_option):
|
def draw_pause_menu(screen, selected_option):
|
||||||
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Quitter."""
|
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Historique, Quitter."""
|
||||||
screen.blit(OVERLAY, (0, 0))
|
screen.blit(OVERLAY, (0, 0))
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
"Controls",
|
"Controls",
|
||||||
"Remap controls",
|
"Remap controls",
|
||||||
|
"History",
|
||||||
"Quit"
|
"Quit"
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -629,7 +748,6 @@ def draw_controls_help(screen, previous_state):
|
|||||||
common_controls["space"]()
|
common_controls["space"]()
|
||||||
] if config.search_mode and config.is_non_pc else []),
|
] if config.search_mode and config.is_non_pc else []),
|
||||||
*( [
|
*( [
|
||||||
f"Saisir texte : Filtrer" if config.search_mode else
|
|
||||||
f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}",
|
f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}",
|
||||||
f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}",
|
f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}",
|
||||||
common_controls["filter"]("Filtrer")
|
common_controls["filter"]("Filtrer")
|
||||||
@@ -650,6 +768,14 @@ def draw_controls_help(screen, previous_state):
|
|||||||
],
|
],
|
||||||
"extension_warning": [
|
"extension_warning": [
|
||||||
common_controls["confirm"]("Confirmer")
|
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"]()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,4 +807,120 @@ def draw_controls_help(screen, previous_state):
|
|||||||
for i, line in enumerate(wrapped_controls):
|
for i, line in enumerate(wrapped_controls):
|
||||||
text = config.font.render(line, True, (255, 255, 255))
|
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))
|
text_rect = text.get_rect(center=(config.screen_width // 2, popup_y + 40 + i * line_height))
|
||||||
screen.blit(text, text_rect)
|
screen.blit(text, text_rect)
|
||||||
|
|
||||||
|
def draw_history(screen):
|
||||||
|
"""Affiche la liste de l'historique des téléchargements."""
|
||||||
|
if not config.history:
|
||||||
|
text = config.font.render("Aucun téléchargement dans l'historique", True, (255, 255, 255))
|
||||||
|
screen.blit(text, (config.screen_width // 2 - text.get_width() // 2, config.screen_height // 2))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Calculer le nombre d'éléments visibles
|
||||||
|
item_height = config.small_font.get_height() + 10
|
||||||
|
config.visible_history_items = (config.screen_height - 200) // item_height
|
||||||
|
max_scroll = max(0, len(config.history) - config.visible_history_items)
|
||||||
|
config.history_scroll_offset = max(0, min(config.history_scroll_offset, max_scroll))
|
||||||
|
|
||||||
|
# Cadre semi-transparent
|
||||||
|
panel_width = config.screen_width - 100
|
||||||
|
panel_height = config.visible_history_items * item_height + 20
|
||||||
|
panel_x = (config.screen_width - panel_width) // 2
|
||||||
|
panel_y = (config.screen_height - panel_height) // 2
|
||||||
|
panel_surface = pygame.Surface((panel_width, panel_height), pygame.SRCALPHA)
|
||||||
|
panel_surface.fill((0, 0, 0, 128))
|
||||||
|
screen.blit(panel_surface, (panel_x, panel_y))
|
||||||
|
pygame.draw.rect(screen, (255, 255, 255), (panel_x, panel_y, panel_width, panel_height), 2)
|
||||||
|
|
||||||
|
# Afficher les colonnes
|
||||||
|
headers = ["Système", "Nom du jeu", "État"]
|
||||||
|
col_widths = [panel_width // 4, panel_width // 2, panel_width // 4]
|
||||||
|
header_y = panel_y + 10
|
||||||
|
for i, header in enumerate(headers):
|
||||||
|
text = config.small_font.render(header, True, (255, 255, 255))
|
||||||
|
screen.blit(text, (panel_x + sum(col_widths[:i]) + 10, header_y))
|
||||||
|
|
||||||
|
# Afficher les entrées
|
||||||
|
start_index = config.history_scroll_offset
|
||||||
|
end_index = min(start_index + config.visible_history_items, len(config.history))
|
||||||
|
for i, entry in enumerate(config.history[start_index:end_index]):
|
||||||
|
y = panel_y + 40 + i * item_height
|
||||||
|
color = (255, 255, 0) if i + start_index == config.current_history_item else (255, 255, 255)
|
||||||
|
system = config.platform_names.get(entry["platform"], entry["platform"])
|
||||||
|
system_text = truncate_text_end(system, config.small_font, col_widths[0] - 20)
|
||||||
|
game_text = truncate_text_end(entry["game_name"], config.small_font, col_widths[1] - 20)
|
||||||
|
status_text = entry["status"]
|
||||||
|
texts = [system_text, game_text, status_text]
|
||||||
|
for j, text in enumerate(texts):
|
||||||
|
rendered = config.small_font.render(text, True, color)
|
||||||
|
screen.blit(rendered, (panel_x + sum(col_widths[:j]) + 10, y))
|
||||||
|
if i + start_index == config.current_history_item:
|
||||||
|
pygame.draw.rect(screen, (255, 255, 0), (panel_x + 5, y - 5, panel_width - 10, item_height), 1)
|
||||||
|
|
||||||
|
# Barre de défilement
|
||||||
|
if len(config.history) > config.visible_history_items:
|
||||||
|
draw_scrollbar(screen, config.history_scroll_offset, len(config.history), config.visible_history_items, panel_x + panel_width - 10, panel_y, panel_height)
|
||||||
|
|
||||||
|
def draw_confirm_dialog(screen):
|
||||||
|
"""Affiche la boîte de dialogue de confirmation pour quitter."""
|
||||||
|
screen.blit(OVERLAY, (0, 0))
|
||||||
|
|
||||||
|
message = "Voulez-vous vraiment quitter ?"
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
13
history.py
13
history.py
@@ -1,7 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from config import HISTORY_FILE_PATH
|
import config
|
||||||
|
from config import HISTORY_PATH
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ DEFAULT_HISTORY_PATH = "/userdata/saves/ports/rgsx/history.json"
|
|||||||
|
|
||||||
def init_history():
|
def init_history():
|
||||||
"""Initialise le fichier history.json s'il n'existe pas."""
|
"""Initialise le fichier history.json s'il n'existe pas."""
|
||||||
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH)
|
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||||
if not os.path.exists(history_path):
|
if not os.path.exists(history_path):
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(history_path), exist_ok=True)
|
os.makedirs(os.path.dirname(history_path), exist_ok=True)
|
||||||
@@ -24,7 +25,7 @@ def init_history():
|
|||||||
|
|
||||||
def load_history():
|
def load_history():
|
||||||
"""Charge l'historique depuis history.json."""
|
"""Charge l'historique depuis history.json."""
|
||||||
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH)
|
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||||
try:
|
try:
|
||||||
with open(history_path, "r") as f:
|
with open(history_path, "r") as f:
|
||||||
history = json.load(f)
|
history = json.load(f)
|
||||||
@@ -40,7 +41,7 @@ def load_history():
|
|||||||
|
|
||||||
def save_history(history):
|
def save_history(history):
|
||||||
"""Sauvegarde l'historique dans history.json."""
|
"""Sauvegarde l'historique dans history.json."""
|
||||||
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH)
|
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||||
try:
|
try:
|
||||||
with open(history_path, "w") as f:
|
with open(history_path, "w") as f:
|
||||||
json.dump(history, f, indent=2)
|
json.dump(history, f, indent=2)
|
||||||
@@ -48,7 +49,7 @@ def save_history(history):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Erreur lors de l'écriture de {history_path} : {e}")
|
logger.error(f"Erreur lors de l'écriture de {history_path} : {e}")
|
||||||
|
|
||||||
def add_download_to_history(platform, game_name, status):
|
def add_to_history(platform, game_name, status):
|
||||||
"""Ajoute une entrée à l'historique."""
|
"""Ajoute une entrée à l'historique."""
|
||||||
history = load_history()
|
history = load_history()
|
||||||
history.append({
|
history.append({
|
||||||
@@ -61,7 +62,7 @@ def add_download_to_history(platform, game_name, status):
|
|||||||
|
|
||||||
def clear_history():
|
def clear_history():
|
||||||
"""Vide l'historique."""
|
"""Vide l'historique."""
|
||||||
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH)
|
history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
|
||||||
try:
|
try:
|
||||||
with open(history_path, "w") as f:
|
with open(history_path, "w") as f:
|
||||||
json.dump([], f)
|
json.dump([], f)
|
||||||
|
|||||||
39
network.py
39
network.py
@@ -6,10 +6,12 @@ import threading
|
|||||||
import pygame
|
import pygame
|
||||||
import zipfile
|
import zipfile
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
from urllib.parse import urljoin, unquote
|
from urllib.parse import urljoin, unquote
|
||||||
import asyncio
|
import asyncio
|
||||||
import config
|
import config
|
||||||
from utils import sanitize_filename
|
from utils import sanitize_filename
|
||||||
|
from history import add_to_history, load_history
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -330,12 +332,12 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
|||||||
if not success:
|
if not success:
|
||||||
raise Exception(f"Échec de l'extraction de l'archive: {msg}")
|
raise Exception(f"Échec de l'extraction de l'archive: {msg}")
|
||||||
result[0] = True
|
result[0] = True
|
||||||
result[1] = f"Téléchargé et extrait : {game_name}"
|
result[1] = f"Downloaded / extracted : {game_name}"
|
||||||
else:
|
else:
|
||||||
os.chmod(dest_path, 0o644)
|
os.chmod(dest_path, 0o644)
|
||||||
logger.debug(f"Téléchargement terminé: {dest_path}")
|
logger.debug(f"Téléchargement terminé: {dest_path}")
|
||||||
result[0] = True
|
result[0] = True
|
||||||
result[1] = f"Téléchargé : {game_name}"
|
result[1] = f"Download_OK : {game_name}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Erreur téléchargement {url}: {str(e)}")
|
logger.error(f"Erreur téléchargement {url}: {str(e)}")
|
||||||
if url in config.download_progress:
|
if url in config.download_progress:
|
||||||
@@ -348,7 +350,15 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
|||||||
finally:
|
finally:
|
||||||
logger.debug(f"Thread téléchargement terminé pour {url}")
|
logger.debug(f"Thread téléchargement terminé pour {url}")
|
||||||
with lock:
|
with lock:
|
||||||
|
config.download_result_message = result[1]
|
||||||
|
config.download_result_error = not result[0]
|
||||||
|
config.download_result_start_time = pygame.time.get_ticks()
|
||||||
|
config.menu_state = "download_result"
|
||||||
config.needs_redraw = True # Forcer le redraw
|
config.needs_redraw = True # Forcer le redraw
|
||||||
|
# Enregistrement dans l'historique
|
||||||
|
add_to_history(platform, game_name, "Download_OK" if result[0] else "Erreur")
|
||||||
|
config.history = load_history() # Recharger l'historique
|
||||||
|
logger.debug(f"Enregistrement dans l'historique: platform={platform}, game_name={game_name}, status={'Download_OK' if result[0] else 'Erreur'}")
|
||||||
|
|
||||||
thread = threading.Thread(target=download_thread)
|
thread = threading.Thread(target=download_thread)
|
||||||
logger.debug(f"Démarrage thread pour {url}")
|
logger.debug(f"Démarrage thread pour {url}")
|
||||||
@@ -359,37 +369,30 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
|||||||
thread.join()
|
thread.join()
|
||||||
logger.debug(f"Thread rejoint pour {url}")
|
logger.debug(f"Thread rejoint pour {url}")
|
||||||
|
|
||||||
with threading.Lock():
|
|
||||||
config.download_result_message = result[1]
|
|
||||||
config.download_result_error = not result[0]
|
|
||||||
config.download_result_start_time = pygame.time.get_ticks()
|
|
||||||
config.menu_state = "download_result"
|
|
||||||
config.needs_redraw = True # Forcer le redraw
|
|
||||||
logger.debug(f"Transition vers download_result, message={result[1]}, erreur={not result[0]}")
|
|
||||||
return result[0], result[1]
|
return result[0], result[1]
|
||||||
|
|
||||||
def check_extension_before_download(url, platform, game_name):
|
def check_extension_before_download(game_name, platform, url):
|
||||||
"""Vérifie l'extension avant de lancer le téléchargement."""
|
"""Vérifie l'extension avant de lancer le téléchargement et retourne un tuple de 4 éléments."""
|
||||||
try:
|
try:
|
||||||
sanitized_name = sanitize_filename(game_name)
|
sanitized_name = sanitize_filename(game_name)
|
||||||
extensions_data = load_extensions_json()
|
extensions_data = load_extensions_json()
|
||||||
if not extensions_data:
|
if not extensions_data:
|
||||||
logger.error(f"Fichier {JSON_EXTENSIONS} vide ou introuvable")
|
logger.error(f"Fichier {JSON_EXTENSIONS} vide ou introuvable")
|
||||||
return False, "Fichier de configuration des extensions introuvable", False
|
return None
|
||||||
|
|
||||||
is_supported = is_extension_supported(sanitized_name, platform, extensions_data)
|
is_supported = is_extension_supported(sanitized_name, platform, extensions_data)
|
||||||
extension = os.path.splitext(sanitized_name)[1].lower()
|
extension = os.path.splitext(sanitized_name)[1].lower()
|
||||||
is_archive = extension in (".zip", ".rar")
|
is_archive = extension in (".zip", ".rar")
|
||||||
|
|
||||||
if is_supported:
|
if is_supported:
|
||||||
logger.debug(f"L'extension de {sanitized_name} est supportée pour {platform}")
|
logger.debug(f"L'extension de {sanitized_name} est supportée pour {platform}")
|
||||||
return True, "", False
|
return (url, platform, game_name, False)
|
||||||
else:
|
else:
|
||||||
if is_archive:
|
if is_archive:
|
||||||
logger.debug(f"Fichier {extension.upper()} détecté pour {sanitized_name}, extraction automatique prévue")
|
logger.debug(f"Fichier {extension.upper()} détecté pour {sanitized_name}, extraction automatique prévue")
|
||||||
return False, f"Fichiers {extension.upper()} non supportés par cette plateforme, extraction automatique après le téléchargement.", True
|
return (url, platform, game_name, True)
|
||||||
logger.debug(f"L'extension de {sanitized_name} n'est pas supportée pour {platform}")
|
logger.debug(f"L'extension de {sanitized_name} n'est pas supportée pour {platform}")
|
||||||
return False, f"L'extension de {sanitized_name} n'est pas supportée pour {platform}", False
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Erreur vérification extension {url}: {str(e)}")
|
logger.error(f"Erreur vérification extension {url}: {str(e)}")
|
||||||
return False, str(e), False
|
return None
|
||||||
Reference in New Issue
Block a user