From 0ade44babd4cff2d1ea4afb55f994237c415f81d Mon Sep 17 00:00:00 2001 From: skymike03 Date: Tue, 12 Aug 2025 00:44:16 +0200 Subject: [PATCH] v1.9.8.7 - Move accessibility settings loading and saving on accessibility.py - Updated controls to replace old "progress" status button with "clear_history" - Refactored control mappings and descriptions to align with the new naming conventions. - Updated language files - Improved the controls help menu layout for better readability and organization. --- ports/RGSX/__main__.py | 29 +++- ports/RGSX/accessibility.py | 28 +++- ports/RGSX/config.py | 2 +- ports/RGSX/controls.py | 131 +++++----------- ports/RGSX/controls_mapper.py | 89 ++++++----- ports/RGSX/display.py | 288 +++++++++++++++++++--------------- ports/RGSX/es_input_parser.py | 6 +- ports/RGSX/languages/de.json | 5 +- ports/RGSX/languages/en.json | 5 +- ports/RGSX/languages/es.json | 5 +- ports/RGSX/languages/fr.json | 5 +- ports/RGSX/utils.py | 24 +-- 12 files changed, 323 insertions(+), 294 deletions(-) diff --git a/ports/RGSX/__main__.py b/ports/RGSX/__main__.py index 90b6792..fd0649b 100644 --- a/ports/RGSX/__main__.py +++ b/ports/RGSX/__main__.py @@ -1,13 +1,14 @@ import os os.environ["SDL_FBDEV"] = "/dev/fb0" import pygame # type: ignore -# type: ignore[reportAttributeAccessIssue] import asyncio import platform import logging import requests import queue import datetime +import config + from display import ( init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_virtual_keyboard, draw_popup_result_download, @@ -19,14 +20,14 @@ from display import ( from language import handle_language_menu_events, _ from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates from controls import handle_controls, validate_menu_state, process_key_repeats, get_emergency_controls -from controls_mapper import load_controls_config, save_controls_config, map_controls, draw_controls_mapping, get_actions +from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, get_actions from utils import ( detect_non_pc, load_sources, check_extension_before_download, extract_zip_data, - play_random_music, load_accessibility_settings, load_music_config + play_random_music, load_music_config ) from history import load_history, save_history -import config from config import OTA_data_ZIP +from accessibility import load_accessibility_settings # Configuration du logging try: @@ -267,7 +268,15 @@ async def main(): if config.menu_state == "confirm_clear_history": action = handle_controls(event, sources, joystick, screen) - config.needs_redraw = True + if action == "confirm": + config.history.clear() + save_history(config.history) + config.menu_state = "history" + config.needs_redraw = True + logger.debug("Historique effacé") + elif action == "cancel": + config.menu_state = "history" + config.needs_redraw = True continue if config.menu_state == "confirm_cancel_download": @@ -314,7 +323,7 @@ async def main(): logger.debug("Téléchargement annulé, retour à l'état précédent") continue - if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "history"]: + if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_result", "history"]: action = handle_controls(event, sources, joystick, screen) config.needs_redraw = True if action == "quit": @@ -456,7 +465,13 @@ async def main(): config.needs_redraw = True logger.debug(f"Retéléchargement terminé pour {game_name}, succès={success}, message={message}") break - + elif action in ("clear_history", "delete_history") and config.menu_state == "history": + # Ouvrir le dialogue de confirmation + config.previous_menu_state = config.menu_state + config.menu_state = "confirm_clear_history" + config.confirm_selection = 0 + config.needs_redraw = True + continue diff --git a/ports/RGSX/accessibility.py b/ports/RGSX/accessibility.py index 2b7f9e1..fb919a1 100644 --- a/ports/RGSX/accessibility.py +++ b/ports/RGSX/accessibility.py @@ -1,8 +1,34 @@ import pygame #type:ignore import config -from utils import save_accessibility_settings +import os +import json +from logging import getLogger from language import _ +logger = getLogger(__name__) + +def load_accessibility_settings(): + """Charge les paramètres d'accessibilité depuis accessibility.json.""" + accessibility_path = os.path.join(config.SAVE_FOLDER, "accessibility.json") + try: + if os.path.exists(accessibility_path): + with open(accessibility_path, 'r', encoding='utf-8') as f: + return json.load(f) + except Exception as e: + logger.error(f"Erreur lors du chargement de accessibility.json: {str(e)}") + return {"font_scale": 1.0} + +def save_accessibility_settings(settings): + """Sauvegarde les paramètres d'accessibilité dans accessibility.json.""" + accessibility_path = os.path.join(config.SAVE_FOLDER, "accessibility.json") + try: + os.makedirs(config.SAVE_FOLDER, exist_ok=True) + with open(accessibility_path, 'w', encoding='utf-8') as f: + json.dump(settings, f, indent=2) + logger.debug(f"Paramètres d'accessibilité sauvegardés: {settings}") + except Exception as e: + logger.error(f"Erreur lors de la sauvegarde de accessibility.json: {str(e)}") + def draw_accessibility_menu(screen): """Affiche le menu d'accessibilité avec curseur pour la taille de police.""" from display import OVERLAY, THEME_COLORS, draw_stylized_button diff --git a/ports/RGSX/config.py b/ports/RGSX/config.py index 86ca272..ee56ef8 100644 --- a/ports/RGSX/config.py +++ b/ports/RGSX/config.py @@ -4,7 +4,7 @@ import sys import logging # Version actuelle de l'application -app_version = "1.9.8.6" +app_version = "1.9.8.7" def get_application_root(): """Détermine le dossier de l'application de manière portable.""" diff --git a/ports/RGSX/controls.py b/ports/RGSX/controls.py index 2340e10..ea7d93e 100644 --- a/ports/RGSX/controls.py +++ b/ports/RGSX/controls.py @@ -26,7 +26,7 @@ key_states = {} # Dictionnaire pour suivre l'état des touches # Liste des états valides VALID_STATES = [ - "platform", "game", "download_progress", "download_result", "confirm_exit", + "platform", "game", "download_result", "confirm_exit", "extension_warning", "pause_menu", "controls_help", "history", "controls_mapping", "redownload_game_cache", "restart_popup", "error", "loading", "confirm_clear_history", "language_select" @@ -52,7 +52,7 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH): "up": {"type": "key", "key": pygame.K_UP}, "down": {"type": "key", "key": pygame.K_DOWN}, "start": {"type": "key", "key": pygame.K_p}, - "progress": {"type": "key", "key": pygame.K_x}, + "clear_history": {"type": "key", "key": pygame.K_x}, "history": {"type": "key", "key": pygame.K_h}, "page_up": {"type": "key", "key": pygame.K_PAGEUP}, "page_down": {"type": "key", "key": pygame.K_PAGEDOWN}, @@ -62,17 +62,27 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH): } try: - with open(path, "r") as f: - config_data = json.load(f) - # Vérifier et compléter les actions manquantes - for action, default_mapping in default_config.items(): - if action not in config_data: - logger.warning(f"Action {action} manquante dans {path}, utilisation de la valeur par défaut") - config_data[action] = default_mapping - return config_data - except (FileNotFoundError, json.JSONDecodeError) as e: - logger.error(f"Erreur lors de la lecture de {path} : {e}, utilisation de la configuration par défaut") - return default_config + if os.path.exists(path): + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + if not isinstance(data, dict): + data = {} + else: + data = {} + changed = False + for k, v in default_config.items(): + if k not in data: + data[k] = v + changed = True + if changed: + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + logging.getLogger(__name__).debug(f"controls.json complété avec les actions manquantes: {path}") + return data + except Exception as e: + logging.getLogger(__name__).error(f"Erreur load_controls_config: {e}") + return default_config.copy() # Fonction pour vérifier si un événement correspond à une action def is_input_matched(event, action_name): @@ -254,11 +264,6 @@ def handle_controls(event, sources, joystick, screen): config.repeat_start_time = 0 config.repeat_last_action = current_time config.needs_redraw = True - elif is_input_matched(event, "progress"): - if config.download_tasks: - config.menu_state = "download_progress" - config.needs_redraw = True - logger.debug("Retour à download_progress depuis platform") elif is_input_matched(event, "history"): config.menu_state = "history" config.needs_redraw = True @@ -356,20 +361,20 @@ def handle_controls(event, sources, joystick, screen): config.scroll_offset = 0 config.needs_redraw = True logger.debug("Sortie du mode recherche") - elif is_input_matched(event, "filter") or is_input_matched(event, "confirm"): + elif is_input_matched(event, "filter"): config.search_mode = False config.filter_active = bool(config.search_query) config.needs_redraw = True logger.debug(f"Validation du filtre avec manette: query={config.search_query}, filter_active={config.filter_active}") elif config.search_mode and not config.is_non_pc: # Gestion de la recherche sur PC (clavier et manette) - if is_input_matched(event, "filter"): + if is_input_matched(event, "confirm"): config.search_mode = False config.filter_active = True config.current_game = 0 config.scroll_offset = 0 config.needs_redraw = True - logger.debug(f"Validation du filtre avec bouton filter sur PC: query={config.search_query}") + logger.debug(f"Validation du filtre avec bouton entree sur PC: query={config.search_query}") elif is_input_matched(event, "cancel"): config.search_mode = False config.search_query = "" @@ -397,31 +402,7 @@ def handle_controls(event, sources, joystick, screen): 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)}") - # Gestion de la validation - elif is_input_matched(event, "confirm"): - config.search_mode = False - config.filter_active = True # Conserver le filtre actif - config.current_game = 0 - config.scroll_offset = 0 - config.needs_redraw = True - logger.debug(f"Validation de la recherche: query={config.search_query}, jeux filtrés={len(config.filtered_games)}") - # Gestion de l'annulation - elif is_input_matched(event, "cancel"): - config.search_mode = False - config.search_query = "" - config.filtered_games = config.games - config.current_game = 0 - config.scroll_offset = 0 - config.needs_redraw = True - logger.debug("Sortie du mode recherche") - # Gestion de la validation avec le bouton filter - elif is_input_matched(event, "filter"): - config.search_mode = False - config.filter_active = True - config.current_game = 0 - config.scroll_offset = 0 - config.needs_redraw = True - logger.debug(f"Validation du filtre avec bouton filter: query={config.search_query}, jeux filtrés={len(config.filtered_games)}") + else: if is_input_matched(event, "up"): @@ -462,13 +443,7 @@ def handle_controls(event, sources, joystick, screen): config.scroll_offset = 0 config.selected_key = (0, 0) config.needs_redraw = True - logger.debug("Entrée en mode recherche") - elif is_input_matched(event, "progress"): - if config.download_tasks: - config.previous_menu_state = config.menu_state - config.menu_state = "download_progress" - config.needs_redraw = True - logger.debug(f"Retour à download_progress depuis {config.previous_menu_state}") + logger.debug("Entrée en mode recherche") elif is_input_matched(event, "history"): config.menu_state = "history" config.needs_redraw = True @@ -683,7 +658,9 @@ def handle_controls(event, sources, joystick, screen): config.repeat_last_action = current_time config.needs_redraw = True #logger.debug("Page suivante dans l'historique") - elif is_input_matched(event, "progress"): + elif (is_input_matched(event, "clear_history") + or is_input_matched(event, "delete_history") + or 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" @@ -782,12 +759,12 @@ def handle_controls(event, sources, joystick, screen): elif is_input_matched(event, "cancel"): config.menu_state = "history" config.needs_redraw = True - - # Confirmation vider l'historique" + + # Confirmation vider l'historique 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") + # 0 = Non, 1 = Oui if config.confirm_clear_selection == 1: # Oui clear_history() config.history = [] @@ -799,38 +776,13 @@ def handle_controls(event, sources, joystick, screen): 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" + elif is_input_matched(event, "left") or is_input_matched(event, "right"): + config.confirm_clear_selection = 1 - config.confirm_clear_selection 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": - if is_input_matched(event, "cancel"): - for task in config.download_tasks: - task.cancel() - config.download_tasks.clear() - config.download_progress.clear() - config.pending_download = None - config.menu_state = validate_menu_state(config.previous_menu_state) - config.needs_redraw = True - 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 elif config.menu_state == "download_result": @@ -958,7 +910,6 @@ def handle_controls(event, sources, joystick, screen): if config.redownload_confirm_selection == 1: # Oui logger.debug("Début du redownload des jeux") config.download_tasks.clear() - config.download_progress.clear() config.pending_download = None if os.path.exists(config.APP_FOLDER + "/sources.json"): try: @@ -1151,7 +1102,11 @@ def get_emergency_controls(): "left": {"type": "key", "key": pygame.K_LEFT}, "right": {"type": "key", "key": pygame.K_RIGHT}, "start": {"type": "key", "key": pygame.K_p}, - # Ajouter aussi les contrôles manette de base si disponible - "confirm_joy": {"type": "button", "button": 0}, # A/Croix - "cancel_joy": {"type": "button", "button": 1}, # B/Rond + "history": {"type": "key", "key": pygame.K_h}, + "clear_history": {"type": "key", "key": pygame.K_x}, + "page_up": {"type": "key", "key": pygame.K_PAGEUP}, + "page_down": {"type": "key", "key": pygame.K_PAGEDOWN}, + # manette basique + "confirm_joy": {"type": "button", "button": 0}, + "cancel_joy": {"type": "button", "button": 1}, } \ No newline at end of file diff --git a/ports/RGSX/controls_mapper.py b/ports/RGSX/controls_mapper.py index 7ba45d5..710457f 100644 --- a/ports/RGSX/controls_mapper.py +++ b/ports/RGSX/controls_mapper.py @@ -28,7 +28,7 @@ ACTION_DEFS = [ {"name": "page_up"}, {"name": "page_down"}, {"name": "history"}, - {"name": "delete_history"}, + {"name": "clear_history"}, {"name": "delete"}, {"name": "space"}, ] @@ -60,14 +60,14 @@ SDL_TO_PYGAME_KEY = { # Noms lisibles pour les touches clavier KEY_NAMES = { - pygame.K_RETURN: "Entrée", + pygame.K_RETURN: "Enter", pygame.K_ESCAPE: "Échap", pygame.K_SPACE: "Espace", - pygame.K_UP: "Flèche Haut", - pygame.K_DOWN: "Flèche Bas", - pygame.K_LEFT: "Flèche Gauche", - pygame.K_RIGHT: "Flèche Droite", - pygame.K_BACKSPACE: "Retour Arrière", + pygame.K_UP: "↑", + pygame.K_DOWN: "↓", + pygame.K_LEFT: "←", + pygame.K_RIGHT: "→", + pygame.K_BACKSPACE: "Backspace", pygame.K_TAB: "Tab", pygame.K_LALT: "Alt", pygame.K_RALT: "AltGR", @@ -116,23 +116,23 @@ KEY_NAMES = { pygame.K_7: "7", pygame.K_8: "8", pygame.K_9: "9", - pygame.K_KP0: "Pavé 0", - pygame.K_KP1: "Pavé 1", - pygame.K_KP2: "Pavé 2", - pygame.K_KP3: "Pavé 3", - pygame.K_KP4: "Pavé 4", - pygame.K_KP5: "Pavé 5", - pygame.K_KP6: "Pavé 6", - pygame.K_KP7: "Pavé 7", - pygame.K_KP8: "Pavé 8", - pygame.K_KP9: "Pavé 9", - pygame.K_KP_PERIOD: "Pavé .", - pygame.K_KP_DIVIDE: "Pavé /", - pygame.K_KP_MULTIPLY: "Pavé *", - pygame.K_KP_MINUS: "Pavé -", - pygame.K_KP_PLUS: "Pavé +", - pygame.K_KP_ENTER: "Pavé Entrée", - pygame.K_KP_EQUALS: "Pavé =", + pygame.K_KP0: "Num 0", + pygame.K_KP1: "Num 1", + pygame.K_KP2: "Num 2", + pygame.K_KP3: "Num 3", + pygame.K_KP4: "Num 4", + pygame.K_KP5: "Num 5", + pygame.K_KP6: "Num 6", + pygame.K_KP7: "Num 7", + pygame.K_KP8: "Num 8", + pygame.K_KP9: "Num 9", + pygame.K_KP_PERIOD: "Num .", + pygame.K_KP_DIVIDE: "Num /", + pygame.K_KP_MULTIPLY: "Num *", + pygame.K_KP_MINUS: "Num -", + pygame.K_KP_PLUS: "Num +", + pygame.K_KP_ENTER: "Num Enter", + pygame.K_KP_EQUALS: "Num =", pygame.K_F1: "F1", pygame.K_F2: "F2", pygame.K_F3: "F3", @@ -152,9 +152,9 @@ KEY_NAMES = { pygame.K_DELETE: "Suppr", pygame.K_HOME: "Début", pygame.K_END: "Fin", - pygame.K_PAGEUP: "Page Haut", - pygame.K_PAGEDOWN: "Page Bas", - pygame.K_PRINT: "Impr Écran", + pygame.K_PAGEUP: "Page+", + pygame.K_PAGEDOWN: "Page-", + pygame.K_PRINT: "PrintScreen", pygame.K_SYSREQ: "SysReq", pygame.K_BREAK: "Pause", pygame.K_PAUSE: "Pause", @@ -286,17 +286,36 @@ HOLD_DURATION = 1000 JOYHAT_DEBOUNCE = 200 # Délai anti-rebond pour JOYHATMOTION (ms) -def load_controls_config(): +def load_controls_config(path=CONTROLS_CONFIG_PATH): """Charge la configuration des contrôles depuis controls.json""" try: - if os.path.exists(CONTROLS_CONFIG_PATH): - with open(CONTROLS_CONFIG_PATH, "r") as f: - config_data = json.load(f) - logger.debug(f"Configuration des contrôles chargée : {config_data}") - return config_data + if os.path.exists(path): + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + if not isinstance(data, dict): + data = {} else: - logger.debug("Aucun fichier controls.json trouvé") - return {} + data = {} + changed = False + + # Normaliser les alias vers l’action canonique "clear_history" + # Votre controls.json a "delete_history": mappez-le vers "clear_history" + if "delete_history" in data and "clear_history" not in data: + data["clear_history"] = data["delete_history"] + changed = True + # Ancien alias éventuel + if "progress" in data and "clear_history" not in data: + data["clear_history"] = data["progress"] + changed = True + + # Compléter avec des valeurs par défaut si nécessaire (facultatif selon votre implémentation) + # ...existing code de complétion si présent... + + if changed: + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + return data except Exception as e: logger.error(f"Erreur lors du chargement de controls.json : {e}") return {} diff --git a/ports/RGSX/display.py b/ports/RGSX/display.py index f917335..c2f08bb 100644 --- a/ports/RGSX/display.py +++ b/ports/RGSX/display.py @@ -248,14 +248,14 @@ def get_control_display(action, default): if control_type == 'key': key_code = control_config.get('key') key_names = { - pygame.K_RETURN: "Entrée", + pygame.K_RETURN: "Enter", pygame.K_ESCAPE: "Échap", pygame.K_SPACE: "Espace", - pygame.K_UP: "Flèche Haut", - pygame.K_DOWN: "Flèche Bas", - pygame.K_LEFT: "Flèche Gauche", - pygame.K_RIGHT: "Flèche Droite", - pygame.K_BACKSPACE: "Retour Arrière", + pygame.K_UP: "↑", + pygame.K_DOWN: "↓", + pygame.K_LEFT: "←", + pygame.K_RIGHT: "→", + pygame.K_BACKSPACE: "Backspace", pygame.K_TAB: "Tab", pygame.K_LALT: "Alt", pygame.K_RALT: "AltGR", @@ -304,23 +304,23 @@ def get_control_display(action, default): pygame.K_7: "7", pygame.K_8: "8", pygame.K_9: "9", - pygame.K_KP0: "Pavé 0", - pygame.K_KP1: "Pavé 1", - pygame.K_KP2: "Pavé 2", - pygame.K_KP3: "Pavé 3", - pygame.K_KP4: "Pavé 4", - pygame.K_KP5: "Pavé 5", - pygame.K_KP6: "Pavé 6", - pygame.K_KP7: "Pavé 7", - pygame.K_KP8: "Pavé 8", - pygame.K_KP9: "Pavé 9", - pygame.K_KP_PERIOD: "Pavé .", - pygame.K_KP_DIVIDE: "Pavé /", - pygame.K_KP_MULTIPLY: "Pavé *", - pygame.K_KP_MINUS: "Pavé -", - pygame.K_KP_PLUS: "Pavé +", - pygame.K_KP_ENTER: "Pavé Entrée", - pygame.K_KP_EQUALS: "Pavé =", + pygame.K_KP0: "Num 0", + pygame.K_KP1: "Num 1", + pygame.K_KP2: "Num 2", + pygame.K_KP3: "Num 3", + pygame.K_KP4: "Num 4", + pygame.K_KP5: "Num 5", + pygame.K_KP6: "Num 6", + pygame.K_KP7: "Num 7", + pygame.K_KP8: "Num 8", + pygame.K_KP9: "Num 9", + pygame.K_KP_PERIOD: "Num .", + pygame.K_KP_DIVIDE: "Num /", + pygame.K_KP_MULTIPLY: "Num *", + pygame.K_KP_MINUS: "Num -", + pygame.K_KP_PLUS: "Num +", + pygame.K_KP_ENTER: "Num Enter", + pygame.K_KP_EQUALS: "Num =", pygame.K_F1: "F1", pygame.K_F2: "F2", pygame.K_F3: "F3", @@ -340,9 +340,9 @@ def get_control_display(action, default): pygame.K_DELETE: "Suppr", pygame.K_HOME: "Début", pygame.K_END: "Fin", - pygame.K_PAGEUP: "Page Haut", - pygame.K_PAGEDOWN: "Page Bas", - pygame.K_PRINT: "Impr Écran", + pygame.K_PAGEUP: "Page+", + pygame.K_PAGEDOWN: "Page-", + pygame.K_PRINT: "Printscreen", pygame.K_SYSREQ: "SysReq", pygame.K_BREAK: "Pause", pygame.K_PAUSE: "Pause", @@ -1249,121 +1249,161 @@ def draw_pause_menu(screen, selected_option): # Menu aide contrôles def draw_controls_help(screen, previous_state): - """Affiche la liste des contrôles avec un style moderne.""" - start_text = _("controls_action_start") - history_text = _("controls_action_history") - delete_text = _("controls_action_delete") - space_text = _("controls_action_space") - nav_text = _("controls_navigation") - pages_text = _("controls_pages") - confirm_select_text = _("controls_confirm_select") - cancel_back_text = _("controls_cancel_back") - clear_history_text = _("controls_action_delete_history") - filter_search_text = _("controls_filter_search") - + """Affiche la liste des contrôles (aide) avec mise en page adaptative.""" + # Contenu des catégories control_categories = { _("controls_category_navigation"): [ - f"{get_control_display('up', '↑')} {get_control_display('down', '↓')} {get_control_display('left', '←')} {get_control_display('right', '→')} : {nav_text}", - f"{get_control_display('page_up', 'LB')} {get_control_display('page_down', 'RB')} : {pages_text}" + f"{get_control_display('up', '↑')} {get_control_display('down', '↓')} {get_control_display('left', '←')} {get_control_display('right', '→')} : {_('controls_navigation')}", + f"{get_control_display('page_up', 'LB')} {get_control_display('page_down', 'RB')} : {_('controls_pages')}", ], _("controls_category_main_actions"): [ - f"{get_control_display('confirm', 'A')} : {confirm_select_text}", - f"{get_control_display('cancel', 'B')} : {cancel_back_text}", - f"{get_control_display('start', 'Start')} : {start_text}" + f"{get_control_display('confirm', 'A')} : {_('controls_confirm_select')}", + f"{get_control_display('cancel', 'B')} : {_('controls_cancel_back')}", + f"{get_control_display('start', 'Start')} : {_('controls_action_start')}", ], _("controls_category_downloads"): [ - f"{get_control_display('history', 'Y')} : {history_text}", - f"{get_control_display('progress', 'X')} : {clear_history_text}" + f"{get_control_display('history', 'Y')} : {_('controls_action_history')}", + f"{get_control_display('clear_history', 'X')} : {_('controls_action_clear_history')}", + ], + _("controls_category_search"): [ + f"{get_control_display('filter', 'Select')} : {_('controls_filter_search')}", + f"{get_control_display('delete', 'Suppr')} : {_('controls_action_delete')}", + f"{get_control_display('space', 'Espace')} : {_('controls_action_space')}", ], - _("controls_category_search"): [ - f"{get_control_display('filter', 'Select')} : {filter_search_text}", - f"{get_control_display('delete', 'Suppr')} : {delete_text}", - f"{get_control_display('space', 'Espace')} : {space_text}" - ] - } - - state_controls = { - "error": control_categories, - "platform": control_categories, - "game": control_categories, - "download_progress": control_categories, - "download_result": control_categories, - "confirm_exit": control_categories, - "extension_warning": control_categories, - "history": control_categories } - control_columns = state_controls.get(previous_state, {}) - if not control_columns: - + # États autorisés (même logique qu'avant) + allowed_states = { + "error", "platform", "game", "download_result", "confirm_exit", + "extension_warning", "history", "clear_history" + } + if previous_state not in allowed_states: return screen.blit(OVERLAY, (0, 0)) - # Organisation en 2x2 - categories = list(control_columns.keys()) - col1 = [categories[0], categories[2]] # Navigation, Historique/Téléchargements - col2 = [categories[1], categories[3]] # Actions principales, Recherche / Filtre - - # Calculer la largeur nécessaire - max_text_width = 0 - for category, controls in control_columns.items(): - for control in controls: - text_width = config.small_font.size(control)[0] - max_text_width = max(max_text_width, text_width) - - col_width = max_text_width + 40 - popup_width = col_width * 2 + 100 # Plus d'espace entre colonnes - popup_height = 320 - popup_x = (config.screen_width - popup_width) // 2 - popup_y = (config.screen_height - popup_height) // 2 + # Paramètres d'affichage + font = config.small_font + title_font = config.title_font + section_font = config.font + line_spacing = max(4, font.get_height() // 6) + section_spacing = font.get_height() // 2 + title_spacing = font.get_height() + padding = 24 + inter_col_spacing = 48 + max_panel_width = int(config.screen_width * 0.9) + max_panel_height = int(config.screen_height * 0.9) - # Fond principal - pygame.draw.rect(screen, THEME_COLORS["button_idle"], (popup_x, popup_y, popup_width, popup_height), border_radius=12) - pygame.draw.rect(screen, THEME_COLORS["border"], (popup_x, popup_y, popup_width, popup_height), 2, border_radius=12) + # Découpage en 2 colonnes (équilibré) + categories_list = list(control_categories.items()) + mid = len(categories_list) // 2 + col1_categories = categories_list[:mid] + col2_categories = categories_list[mid:] + + # Largeur cible par colonne (avant wrapping) + target_col_width = (max_panel_width - 2 * padding - inter_col_spacing) // 2 + + def wrap_lines_for_column(cat_pairs): + wrapped = [] # liste de (is_section_title, surface) + max_width = 0 + total_height = 0 + for section_title, lines in cat_pairs: + # Titre section + sec_surf = section_font.render(section_title, True, THEME_COLORS["fond_lignes"]) + wrapped.append((True, sec_surf)) + total_height += sec_surf.get_height() + line_spacing + + for raw_line in lines: + # Wrap par mots + words = raw_line.split() + cur = "" + for word in words: + test = (cur + " " + word).strip() + if font.size(test)[0] <= target_col_width: + cur = test + else: + if cur: + line_surf = font.render(cur, True, THEME_COLORS["text"]) + wrapped.append((False, line_surf)) + total_height += line_surf.get_height() + line_spacing + max_width = max(max_width, line_surf.get_width()) + cur = word + if cur: + line_surf = font.render(cur, True, THEME_COLORS["text"]) + wrapped.append((False, line_surf)) + total_height += line_surf.get_height() + line_spacing + max_width = max(max_width, line_surf.get_width()) + + total_height += section_spacing # espace après section + max_width = max(max_width, sec_surf.get_width()) + + if wrapped and not wrapped[-1][0]: + total_height -= section_spacing # retirer excédent final + return wrapped, max_width, total_height + + col1_wrapped, col1_w, col1_h = wrap_lines_for_column(col1_categories) + col2_wrapped, col2_w, col2_h = wrap_lines_for_column(col2_categories) + + col_widths_sum = col1_w + col2_w + inter_col_spacing + content_width = min(max_panel_width - 2 * padding, max(col_widths_sum, col1_w + col2_w + inter_col_spacing)) + panel_width = content_width + 2 * padding + + title_surf = title_font.render(_("controls_help_title"), True, THEME_COLORS["text"]) + title_height = title_surf.get_height() + + content_height = max(col1_h, col2_h) + panel_height = title_height + title_spacing + content_height + 2 * padding + if panel_height > max_panel_height: + panel_height = max_panel_height + enable_clip = True + else: + enable_clip = False + + panel_x = (config.screen_width - panel_width) // 2 + panel_y = (config.screen_height - panel_height) // 2 + + # Fond panel + pygame.draw.rect(screen, THEME_COLORS["button_idle"], (panel_x, panel_y, panel_width, panel_height), border_radius=16) + pygame.draw.rect(screen, THEME_COLORS["border"], (panel_x, panel_y, panel_width, panel_height), 2, border_radius=16) # Titre - title_text = _("controls_help_title") - title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"]) - title_rect = title_surface.get_rect(center=(config.screen_width // 2, popup_y + 25)) - screen.blit(title_surface, title_rect) + title_rect = title_surf.get_rect(center=(panel_x + panel_width // 2, panel_y + padding + title_height // 2)) + screen.blit(title_surf, title_rect) + + # Zones de colonnes + col_top = panel_y + padding + title_height + title_spacing + col1_x = panel_x + padding + col2_x = panel_x + panel_width - padding - col2_w + + # Clip si nécessaire + prev_clip = None + if enable_clip: + prev_clip = screen.get_clip() + clip_rect = pygame.Rect(panel_x + padding, col_top, panel_width - 2 * padding, panel_height - (col_top - panel_y) - padding) + screen.set_clip(clip_rect) + + # Dessin colonne 1 + y1 = col_top + last_section = False + for is_section, surf in col1_wrapped: + if is_section: + y1 += 0 + if y1 + surf.get_height() > panel_y + panel_height - padding: + break + screen.blit(surf, (col1_x, y1)) + y1 += surf.get_height() + (section_spacing if is_section else line_spacing) + + # Dessin colonne 2 + y2 = col_top + for is_section, surf in col2_wrapped: + if y2 + surf.get_height() > panel_y + panel_height - padding: + break + screen.blit(surf, (col2_x, y2)) + y2 += surf.get_height() + (section_spacing if is_section else line_spacing) + + if enable_clip and prev_clip is not None: + screen.set_clip(prev_clip) - # Affichage en colonnes - start_y = popup_y + 60 - - # Colonne 1 - current_y = start_y - for category in col1: - controls = control_columns[category] - # Titre - cat_surface = config.font.render(category, True, THEME_COLORS["fond_lignes"]) - cat_rect = cat_surface.get_rect(x=popup_x + 20, y=current_y) - screen.blit(cat_surface, cat_rect) - current_y += 30 # Plus d'espace après titre - # Contrôles - for control in controls: - ctrl_surface = config.small_font.render(f"• {control}", True, THEME_COLORS["text"]) - ctrl_rect = ctrl_surface.get_rect(x=popup_x + 30, y=current_y) - screen.blit(ctrl_surface, ctrl_rect) - current_y += 20 - current_y += 20 # Plus d'espace entre sections - - # Colonne 2 - current_y = start_y - for category in col2: - controls = control_columns[category] - # Titre - cat_surface = config.font.render(category, True, THEME_COLORS["fond_lignes"]) - cat_rect = cat_surface.get_rect(x=popup_x + col_width + 40, y=current_y) # Plus d'espace entre colonnes - screen.blit(cat_surface, cat_rect) - current_y += 30 # Plus d'espace après titre - # Contrôles - for control in controls: - ctrl_surface = config.small_font.render(f"• {control}", True, THEME_COLORS["text"]) - ctrl_rect = ctrl_surface.get_rect(x=popup_x + col_width + 50, y=current_y) # Plus d'espace entre colonnes - screen.blit(ctrl_surface, ctrl_rect) - current_y += 20 - current_y += 20 # Plus d'espace entre sections # Menu Quitter Appli def draw_confirm_dialog(screen): diff --git a/ports/RGSX/es_input_parser.py b/ports/RGSX/es_input_parser.py index b7994a8..d34d5ef 100644 --- a/ports/RGSX/es_input_parser.py +++ b/ports/RGSX/es_input_parser.py @@ -28,7 +28,7 @@ def parse_es_input_config(): "right": "right", "pageup": "page_up", "pagedown": "page_down", - "y": "progress", + "y": "clear_history", "x": "history", "select": "filter", "leftshoulder": "delete", @@ -129,7 +129,7 @@ def parse_es_input_config(): controls_config["start"] = {"type": "key", "key": pygame.K_p} controls_config["filter"] = {"type": "key", "key": pygame.K_f} controls_config["history"] = {"type": "key", "key": pygame.K_h} - controls_config["progress"] = {"type": "key", "key": pygame.K_x} + controls_config["clear_history"] = {"type": "key", "key": pygame.K_x} controls_config["page_up"] = {"type": "key", "key": pygame.K_PAGEUP} controls_config["page_down"] = {"type": "key", "key": pygame.K_PAGEDOWN} @@ -143,7 +143,7 @@ def parse_es_input_config(): "right": {"type": "key", "key": pygame.K_RIGHT}, "page_up": {"type": "key", "key": pygame.K_PAGEUP}, "page_down": {"type": "key", "key": pygame.K_PAGEDOWN}, - "progress": {"type": "key", "key": pygame.K_x}, + "clear_history": {"type": "key", "key": pygame.K_x}, "history": {"type": "key", "key": pygame.K_h}, "filter": {"type": "key", "key": pygame.K_f}, "delete": {"type": "key", "key": pygame.K_DELETE}, diff --git a/ports/RGSX/languages/de.json b/ports/RGSX/languages/de.json index 096d873..f4b5fcb 100644 --- a/ports/RGSX/languages/de.json +++ b/ports/RGSX/languages/de.json @@ -104,7 +104,7 @@ "controls_action_right": "Rechts", "controls_action_page_up": "Vorherige Seite", "controls_action_page_down": "Nächste Seite", - "controls_action_delete_history": "Verlauf leeren", + "controls_action_clear_history": "Verlauf leeren", "controls_action_history": "Verlauf", "controls_action_filter": "Filtern", "controls_action_delete": "Löschen", @@ -119,7 +119,7 @@ "controls_desc_right": "Nach rechts navigieren", "controls_desc_page_up": "Vorherige Seite/Schnelles Scrollen nach oben (z.B.: BildAuf, LB)", "controls_desc_page_down": "Nächste Seite/Schnelles Scrollen nach unten (z.B.: BildAb, RB)", - "controls_desc_delete_history": "Verlauf löschen (z.B.: X)", + "controls_desc_clear_history": "Verlauf löschen (z.B.: X)", "controls_desc_history": "Verlauf öffnen (z.B.: H, Y)", "controls_desc_filter": "Filter öffnen (z.B.: F, Select)", "controls_desc_delete": "Zeichen löschen (z.B.: LT, Entf)", @@ -131,7 +131,6 @@ "action_quit": "Beenden", "action_select": "Auswählen", "action_history": "Verlauf", - "action_progress": "Fortschritt", "action_download": "Herunterladen", "action_filter": "Filtern", "action_cancel": "Abbrechen", diff --git a/ports/RGSX/languages/en.json b/ports/RGSX/languages/en.json index 2cc979d..b09bd7b 100644 --- a/ports/RGSX/languages/en.json +++ b/ports/RGSX/languages/en.json @@ -95,7 +95,7 @@ "controls_action_right": "Right", "controls_action_page_up": "Previous Page", "controls_action_page_down": "Next Page", - "controls_action_delete_history": "Clear History", + "controls_action_clear_history": "Clear History", "controls_action_history": "History", "controls_action_filter": "Filter", "controls_action_delete": "Delete", @@ -110,7 +110,7 @@ "controls_desc_right": "Navigate right", "controls_desc_page_up": "Previous page/Fast scroll up (e.g. PageUp, LB)", "controls_desc_page_down": "Next page/Fast scroll down (e.g. PageDown, RB)", - "controls_desc_delete_history": "Clear History (e.g. X)", + "controls_desc_clear_history": "Clear History (e.g. X)", "controls_desc_history": "Open history (e.g. H, Y)", "controls_desc_filter": "Open filter (e.g. F, Select)", "controls_desc_delete": "Delete character (e.g. LT, Delete)", @@ -122,7 +122,6 @@ "action_quit": "Quit", "action_select": "Select", "action_history": "History", - "action_progress": "Progress", "action_download": "Download", "action_filter": "Filter", "action_cancel": "Cancel", diff --git a/ports/RGSX/languages/es.json b/ports/RGSX/languages/es.json index a422b14..ddc8370 100644 --- a/ports/RGSX/languages/es.json +++ b/ports/RGSX/languages/es.json @@ -106,7 +106,7 @@ "controls_action_right": "Derecha", "controls_action_page_up": "Página anterior", "controls_action_page_down": "Página siguiente", - "controls_action_delete_history": "Vaciar Historial", + "controls_action_clear_history": "Vaciar Historial", "controls_action_history": "Historial", "controls_action_filter": "Filtrar", "controls_action_delete": "Eliminar", @@ -121,7 +121,7 @@ "controls_desc_right": "Navegar a derecha", "controls_desc_page_up": "Página anterior/Desplazamiento rápido arriba (ej: RePág, LB)", "controls_desc_page_down": "Página siguiente/Desplazamiento rápido abajo (ej: AvPág, RB)", - "controls_desc_delete_history": "Borrar Historial (ej: X)", + "controls_desc_clear_history": "Borrar Historial (ej: X)", "controls_desc_history": "Abrir historial (ej: H, Y)", "controls_desc_filter": "Abrir filtro (ej: F, Select)", "controls_desc_delete": "Eliminar carácter (ej: LT, Supr)", @@ -133,7 +133,6 @@ "action_quit": "Salir", "action_select": "Seleccionar", "action_history": "Historial", - "action_progress": "Progreso", "action_download": "Descargar", "action_filter": "Filtrar", "action_cancel": "Cancelar", diff --git a/ports/RGSX/languages/fr.json b/ports/RGSX/languages/fr.json index b0d4b3c..d245e2b 100644 --- a/ports/RGSX/languages/fr.json +++ b/ports/RGSX/languages/fr.json @@ -101,7 +101,7 @@ "controls_action_right": "Droite", "controls_action_page_up": "Page Précédente", "controls_action_page_down": "Page Suivante", - "controls_action_delete_history": "Vider Historique", + "controls_action_clear_history": "Vider Historique", "controls_action_history": "Historique", "controls_action_filter": "Filtrer", "controls_action_delete": "Supprimer", @@ -117,7 +117,7 @@ "controls_desc_page_up": "Défilement Rapide - (ex: PageUp, LB)", "controls_desc_page_down": "Défilement Rapide + (ex: PageDown, RB)", "controls_desc_history": "Ouvrir l'historique (ex: H, Y)", - "controls_desc_delete_history": "Effacer Historique (ex: X)", + "controls_desc_clear_history": "Effacer Historique (ex: X)", "controls_desc_filter": "Mode Filtre : Ouvrir/Valider (ex: F, Select)", "controls_desc_delete": "Mode Filtre : Supprimer caractère (ex: LT, Suppr)", "controls_desc_space": "Mode Filtre : Ajouter espace (ex: RT, Espace)", @@ -128,7 +128,6 @@ "action_quit": "Quitter", "action_select": "Sélectionner", "action_history": "Historique", - "action_progress": "Progression", "action_download": "Télécharger", "action_filter": "Filtrer", "action_cancel": "Annuler", diff --git a/ports/RGSX/utils.py b/ports/RGSX/utils.py index 02ce0d9..ad5175e 100644 --- a/ports/RGSX/utils.py +++ b/ports/RGSX/utils.py @@ -12,28 +12,6 @@ import zipfile import time import random from config import JSON_EXTENSIONS, SAVE_FOLDER - -def load_accessibility_settings(): - """Charge les paramètres d'accessibilité depuis accessibility.json.""" - accessibility_path = os.path.join(SAVE_FOLDER, "accessibility.json") - try: - if os.path.exists(accessibility_path): - with open(accessibility_path, 'r', encoding='utf-8') as f: - return json.load(f) - except Exception as e: - logger.error(f"Erreur lors du chargement de accessibility.json: {str(e)}") - return {"font_scale": 1.0} - -def save_accessibility_settings(settings): - """Sauvegarde les paramètres d'accessibilité dans accessibility.json.""" - accessibility_path = os.path.join(SAVE_FOLDER, "accessibility.json") - try: - os.makedirs(SAVE_FOLDER, exist_ok=True) - with open(accessibility_path, 'w', encoding='utf-8') as f: - json.dump(settings, f, indent=2) - logger.debug(f"Paramètres d'accessibilité sauvegardés: {settings}") - except Exception as e: - logger.error(f"Erreur lors de la sauvegarde de accessibility.json: {str(e)}") from history import save_history from language import _ # Import de la fonction de traduction from datetime import datetime @@ -59,7 +37,7 @@ def detect_non_pc(): except (subprocess.SubprocessError, FileNotFoundError): logger.debug(f"batocera-es-swissknife non disponible, utilisation de platform.machine(): {arch}") - is_non_pc = arch not in ["x86_64", "amd64"] + is_non_pc = arch not in ["x86_64", "amd64", "AMD64"] logger.debug(f"Système détecté: {platform.system()}, architecture: {arch}, is_non_pc={is_non_pc}") return is_non_pc