diff --git a/README.md b/README.md index 655e7bb..0a7b627 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ The application supports multiple sources like myrient and 1fichier. These sourc - Downloads require no authentication or account for most sources. - Systems marked `(1fichier)` in the name will only be accessible if you provide your 1fichier API key (see below). - **Download history** : View and re-download previous files. +- **Multi-select downloads** : Mark multiple games in the game list with the key mapped to Clear History (default X) to enqueue several downloads in one batch. Press Confirm to start batch. - **Control customization** : Remap keyboard or controller keys to your preference with automatic button name detection from EmulationStation (beta). - **Font size adjustment** : If you find the text too small/too large, you can change it in the menu. - **Search mode** : Filter games by name for quick navigation with virtual keyboard on controller. @@ -96,6 +97,7 @@ INFO: for retrobat on first launch, the application will download Python in the - Select a platform, then a game. - Press the confirm key (default, **Enter** or **A** button) to start the download. +- Optional: Press the key mapped to Clear History (default **X**) on several games to toggle multi-selection ([X] marker). Then press Confirm to launch a sequential batch download. - Follow the progress in the `HISTORY` menu. --- @@ -193,7 +195,7 @@ git checkout -b feature/your-feature-name ## ⚠️ Known issues / To implement -- Multiple download management +- (None currently listed) --- diff --git a/README_FR.md b/README_FR.md index ed045ab..f54894a 100644 --- a/README_FR.md +++ b/README_FR.md @@ -14,6 +14,7 @@ L'application prend en charge plusieurs sources comme myrient, 1fichier. Ces sou - Les téléchargements ne nécessitent aucune authentification ni compte pour la plupart. - Les systèmes notés `(1fichier)` dans le nom ne seront accessibles que si vous renseignez votre clé API 1fichier (voir plus bas). - **Historique des téléchargements** : Consultez et retéléchargez les anciens fichiers. +- **Téléchargements multi-sélection** : Marquez plusieurs jeux dans la liste avec la touche associée à Vider Historique (par défaut X) pour préparer un lot. Appuyez ensuite sur Confirmer pour lancer les téléchargements en séquence. - **Personnalisation des contrôles** : Remappez les touches du clavier ou de la manette à votre convenance avec détection automatique des noms de boutons depuis EmulationStation(beta). - **Changement de taille de police** : Si vous trouvez les écritures trop petites/trop grosses, vous pouvez le changer dans le menu. - **Mode recherche** : Filtrez les jeux par nom pour une navigation rapide avec clavier virtuel sur manette. @@ -94,6 +95,7 @@ INFO : pour retrobat au premier lancement, l'application téléchargera Python d - Sélectionnez une plateforme, puis un jeu. - Appuyez sur la touche configurée confirm (par défaut, **Entrée** ou bouton **A**) pour lancer le téléchargement. +- Option : appuyez sur la touche Vider Historique (par défaut **X**) sur plusieurs jeux pour activer/désactiver leur sélection (marqueur [X]). Puis validez pour lancer un lot de téléchargements. - Suivez la progression dans le menu `HISTORIQUE`. --- @@ -187,7 +189,7 @@ git checkout -b feature/nom-de-votre-fonctionnalité ## ⚠️ Problèmes connus / À implémenter -- Gestion des téléchargements multiples +- (Aucun listé actuellement) --- diff --git a/ports/RGSX/__main__.py b/ports/RGSX/__main__.py index 1f86551..729771d 100644 --- a/ports/RGSX/__main__.py +++ b/ports/RGSX/__main__.py @@ -11,7 +11,7 @@ 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, + draw_progress_screen, draw_controls, draw_virtual_keyboard, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_cancel_download_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup, draw_gradient, @@ -329,7 +329,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_result", "history"]: + if config.menu_state in ["platform", "game", "error", "confirm_exit", "history"]: action = handle_controls(event, sources, joystick, screen) config.needs_redraw = True if action == "quit": @@ -442,14 +442,14 @@ async def main(): config.previous_menu_state = config.menu_state logger.debug(f"Previous menu state défini: {config.previous_menu_state}") success, message = download_from_1fichier(url, platform, game_name, is_zip_non_supported) + # Ancien popup download_result supprimé : retour direct à l'historique config.download_result_message = message config.download_result_error = not success - config.download_result_start_time = pygame.time.get_ticks() - config.menu_state = "download_result" config.download_progress.clear() config.pending_download = None + config.menu_state = "history" config.needs_redraw = True - logger.debug(f"Retéléchargement 1fichier terminé pour {game_name}, succès={success}, message={message}") + logger.debug(f"Retéléchargement 1fichier terminé pour {game_name}, succès={success}, message={message}, retour direct history") else: is_supported, message, is_zip_non_supported = check_extension_before_download(url, platform, game_name) if not is_supported: @@ -464,12 +464,11 @@ async def main(): success, message = download_rom(url, platform, game_name, is_zip_non_supported) config.download_result_message = message config.download_result_error = not success - config.download_result_start_time = pygame.time.get_ticks() - config.menu_state = "download_result" config.download_progress.clear() config.pending_download = None + config.menu_state = "history" config.needs_redraw = True - logger.debug(f"Retéléchargement terminé pour {game_name}, succès={success}, message={message}") + logger.debug(f"Retéléchargement terminé pour {game_name}, succès={success}, message={message}, retour direct history") break elif action in ("clear_history", "delete_history") and config.menu_state == "history": # Ouvrir le dialogue de confirmation @@ -500,10 +499,9 @@ async def main(): break config.download_result_message = message config.download_result_error = not success - config.download_result_start_time = pygame.time.get_ticks() - config.menu_state = "download_result" config.download_progress.clear() config.pending_download = None + config.menu_state = "history" config.needs_redraw = True del config.download_tasks[task_id] except Exception as e: @@ -521,10 +519,9 @@ async def main(): break config.download_result_message = message config.download_result_error = True - config.download_result_start_time = pygame.time.get_ticks() - config.menu_state = "download_result" config.download_progress.clear() config.pending_download = None + config.menu_state = "history" config.needs_redraw = True del config.download_tasks[task_id] else: @@ -567,13 +564,7 @@ async def main(): config.needs_redraw = True del config.download_tasks[task_id] - # Gestion de la fin du popup download_result - if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000: - config.menu_state = "history" # Rester dans l'historique après le popup - config.download_progress.clear() - config.pending_download = None - config.needs_redraw = True - logger.debug(f"Fin popup download_result, retour à history") + # Popup download_result supprimé : plus de temporisation de 3s # Affichage if config.needs_redraw: @@ -588,7 +579,9 @@ async def main(): elif config.menu_state == "error": draw_error_screen(screen) elif config.menu_state == "update_result": - draw_popup_result_download(screen, config.update_result_message, config.update_result_error) + # Ancien popup supprimé : afficher directement l'historique + config.menu_state = "history" + draw_history_list(screen) elif config.menu_state == "platform": draw_platform_grid(screen) elif config.menu_state == "game": @@ -600,8 +593,7 @@ async def main(): draw_virtual_keyboard(screen) elif config.menu_state == "download_progress": draw_progress_screen(screen) - elif config.menu_state == "download_result": - draw_popup_result_download(screen, config.download_result_message, config.download_result_error) + # État download_result supprimé elif config.menu_state == "confirm_exit": draw_confirm_dialog(screen) elif config.menu_state == "extension_warning": diff --git a/ports/RGSX/config.py b/ports/RGSX/config.py index b01d262..560834e 100644 --- a/ports/RGSX/config.py +++ b/ports/RGSX/config.py @@ -5,7 +5,7 @@ import platform from rgsx_settings import load_rgsx_settings, save_rgsx_settings, migrate_old_settings # Version actuelle de l'application -app_version = "1.9.9.0" +app_version = "1.9.9.1" def get_operating_system(): """Renvoie le nom du système d'exploitation.""" @@ -178,6 +178,10 @@ popup_timer = 0 # Temps restant pour le popup en millisecondes (0 = inactif) last_frame_time = pygame.time.get_ticks() current_music_name = None music_popup_start_time = 0 +selected_games = set() # Indices des jeux sélectionnés pour un téléchargement multiple (menu game) +batch_download_indices = [] # File d'attente des indices de jeux à traiter en lot +batch_in_progress = False # Indique qu'un lot est en cours +batch_pending_game = None # Données du jeu en attente de confirmation d'extension GRID_COLS = 3 # Number of columns in the platform grid diff --git a/ports/RGSX/controls.py b/ports/RGSX/controls.py index 84e1423..0f68afa 100644 --- a/ports/RGSX/controls.py +++ b/ports/RGSX/controls.py @@ -20,13 +20,16 @@ from language import _ # Import de la fonction de traduction logger = logging.getLogger(__name__) +# Extensions d'archives pour lesquelles on ignore l'avertissement d'extension non supportée +ARCHIVE_EXTENSIONS = {'.zip', '.7z', '.rar', '.tar', '.gz', '.xz', '.bz2'} + # Variables globales pour la répétition key_states = {} # Dictionnaire pour suivre l'état des touches # Liste des états valides VALID_STATES = [ - "platform", "game", "download_result", "confirm_exit", + "platform", "game", "confirm_exit", "extension_warning", "pause_menu", "controls_help", "history", "controls_mapping", "redownload_game_cache", "restart_popup", "error", "loading", "confirm_clear_history", "language_select" @@ -462,6 +465,16 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = "history" config.needs_redraw = True logger.debug("Ouverture history depuis game") + # Bascule de sélection multiple avec la touche clear_history (réutilisée) + elif is_input_matched(event, "clear_history"): + if games: + idx = config.current_game + if idx in config.selected_games: + config.selected_games.remove(idx) + else: + config.selected_games.add(idx) + config.needs_redraw = True + logger.debug(f"Multi-select toggle index={idx}, now selected={len(config.selected_games)}") elif is_input_matched(event, "cancel"): config.menu_state = "platform" config.current_game = 0 @@ -473,9 +486,68 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = "redownload_game_cache" config.needs_redraw = True logger.debug("Passage à redownload_game_cache depuis game") - # Sélectionner un jeu, événement confirm + # Télécharger les jeux sélectionnés (multi) ou le jeu courant elif is_input_matched(event, "confirm"): - if games: + # Batch multi-sélection + if games and config.selected_games: + config.batch_download_indices = sorted({i for i in config.selected_games if 0 <= i < len(games)}) + config.selected_games.clear() + config.batch_in_progress = True + config.batch_pending_game = None + + def process_next_batch_item(): + # si un jeu attend encore confirmation, ne pas avancer + if config.batch_pending_game: + return False + while config.batch_download_indices: + idx = config.batch_download_indices.pop(0) + g = games[idx] + url = g[1] + game_name = g[0] + platform = config.platforms[config.current_platform]["name"] if isinstance(config.platforms[config.current_platform], dict) else config.platforms[config.current_platform] + logger.debug(f"Batch step: {game_name} idx={idx} restants={len(config.batch_download_indices)}") + config.pending_download = check_extension_before_download(url, platform, game_name) + if not config.pending_download: + continue # passe au suivant + is_supported = is_extension_supported( + sanitize_filename(game_name), + platform, + load_extensions_json() + ) + ext = os.path.splitext(url)[1].lower() + if not is_supported and ext not in ARCHIVE_EXTENSIONS: + # Stocker comme pending sans dupliquer l'entrée + config.batch_pending_game = (url, platform, game_name, config.pending_download[3]) + config.previous_menu_state = config.menu_state + config.menu_state = "extension_warning" + config.extension_confirm_selection = 0 + config.needs_redraw = True + return False + # Téléchargement direct + config.history.append(add_to_history(platform, game_name, "downloading", url, 0, "Téléchargement en cours")) + config.current_history_item = len(config.history) -1 + task_id = str(pygame.time.get_ticks()) + if is_1fichier_url(url): + config.API_KEY_1FICHIER = load_api_key_1fichier() + if not config.API_KEY_1FICHIER: + config.history[-1]["status"] = "Erreur" + config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente" + save_history(config.history) + continue + task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id)) + else: + task = asyncio.create_task(download_rom(url, platform, game_name, config.pending_download[3], task_id)) + config.download_tasks[task_id] = (task, url, game_name, platform) + # passer à l'élément suivant (boucle while) + return True # fin lot + + process_next_batch_item() + # Aller à l'historique si pas d'avertissement en attente + if config.menu_state == "game" and not config.batch_pending_game: + config.menu_state = "history" + config.needs_redraw = True + action = "download" + elif games: url = games[config.current_game][1] game_name = games[config.current_game][0] platform = config.platforms[config.current_platform]["name"] if isinstance(config.platforms[config.current_platform], dict) else config.platforms[config.current_platform] @@ -516,7 +588,8 @@ def handle_controls(event, sources, joystick, screen): platform, load_extensions_json() ) - if not is_supported: + ext = os.path.splitext(url)[1].lower() + if not is_supported and ext not in ARCHIVE_EXTENSIONS: config.previous_menu_state = config.menu_state config.menu_state = "extension_warning" config.extension_confirm_selection = 0 @@ -548,7 +621,8 @@ def handle_controls(event, sources, joystick, screen): platform, load_extensions_json() ) - if not is_supported: + ext = os.path.splitext(url)[1].lower() + if not is_supported and ext not in ARCHIVE_EXTENSIONS: config.previous_menu_state = config.menu_state config.menu_state = "extension_warning" config.extension_confirm_selection = 0 @@ -616,6 +690,55 @@ def handle_controls(event, sources, joystick, screen): logger.debug(f"Téléchargement confirmé après avertissement: {game_name} pour {platform} depuis {url}, task_id={task_id}") config.pending_download = None action = "download" + # Reprendre batch si présent + # Reprise batch si un jeu était en attente + if config.batch_pending_game: + config.batch_pending_game = None + if config.batch_in_progress: + config.menu_state = "game" + # Relancer la progression du lot + # Appeler la fonction locale si disponible (ré-implémentation légère ici) + try: + games = config.filtered_games if config.filter_active or config.search_mode else config.games + while config.batch_download_indices and not config.batch_pending_game: + idx = config.batch_download_indices.pop(0) + if idx < 0 or idx >= len(games): + continue + g = games[idx] + url = g[1]; game_name = g[0] + platform = config.platforms[config.current_platform]["name"] if isinstance(config.platforms[config.current_platform], dict) else config.platforms[config.current_platform] + config.pending_download = check_extension_before_download(url, platform, game_name) + if not config.pending_download: + continue + is_supported = is_extension_supported(sanitize_filename(game_name), platform, load_extensions_json()) + ext = os.path.splitext(url)[1].lower() + if not is_supported and ext not in ARCHIVE_EXTENSIONS: + config.batch_pending_game = (url, platform, game_name, config.pending_download[3]) + config.previous_menu_state = config.menu_state + config.menu_state = "extension_warning" + config.extension_confirm_selection = 0 + config.needs_redraw = True + break + config.history.append(add_to_history(platform, game_name, "downloading", url, 0, "Téléchargement en cours")) + config.current_history_item = len(config.history) -1 + task_id = str(pygame.time.get_ticks()) + if is_1fichier_url(url): + config.API_KEY_1FICHIER = load_api_key_1fichier() + if not config.API_KEY_1FICHIER: + config.history[-1]["status"] = "Erreur" + config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente" + save_history(config.history) + continue + task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id)) + else: + task = asyncio.create_task(download_rom(url, platform, game_name, config.pending_download[3], task_id)) + config.download_tasks[task_id] = (task, url, game_name, platform) + if not config.batch_download_indices and not config.batch_pending_game: + # Batch terminé + config.batch_in_progress = False + config.menu_state = "history" + except Exception as e: + logger.error(f"Erreur reprise batch après warning: {e}") else: config.menu_state = "error" config.error_message = _("error_invalid_download_data") @@ -628,6 +751,52 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = validate_menu_state(config.previous_menu_state) config.needs_redraw = True logger.debug(f"Retour à {config.menu_state} depuis extension_warning") + if config.batch_pending_game: + # Annulation de ce jeu -> on le saute + config.batch_pending_game = None + if config.batch_in_progress: + config.menu_state = "game" + # Reprise similaire à ci-dessus + try: + games = config.filtered_games if config.filter_active or config.search_mode else config.games + while config.batch_download_indices and not config.batch_pending_game: + idx = config.batch_download_indices.pop(0) + if idx < 0 or idx >= len(games): + continue + g = games[idx] + url = g[1]; game_name = g[0] + platform = config.platforms[config.current_platform]["name"] if isinstance(config.platforms[config.current_platform], dict) else config.platforms[config.current_platform] + config.pending_download = check_extension_before_download(url, platform, game_name) + if not config.pending_download: + continue + is_supported = is_extension_supported(sanitize_filename(game_name), platform, load_extensions_json()) + ext = os.path.splitext(url)[1].lower() + if not is_supported and ext not in ARCHIVE_EXTENSIONS: + config.batch_pending_game = (url, platform, game_name, config.pending_download[3]) + config.previous_menu_state = config.menu_state + config.menu_state = "extension_warning" + config.extension_confirm_selection = 0 + config.needs_redraw = True + break + config.history.append(add_to_history(platform, game_name, "downloading", url, 0, "Téléchargement en cours")) + config.current_history_item = len(config.history) -1 + task_id = str(pygame.time.get_ticks()) + if is_1fichier_url(url): + config.API_KEY_1FICHIER = load_api_key_1fichier() + if not config.API_KEY_1FICHIER: + config.history[-1]["status"] = "Erreur" + config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente" + save_history(config.history) + continue + task = asyncio.create_task(download_from_1fichier(url, platform, game_name, config.pending_download[3], task_id)) + else: + task = asyncio.create_task(download_rom(url, platform, game_name, config.pending_download[3], task_id)) + config.download_tasks[task_id] = (task, url, game_name, platform) + if not config.batch_download_indices and not config.batch_pending_game: + config.batch_in_progress = False + config.menu_state = "history" + except Exception as e: + logger.error(f"Erreur reprise batch annulation warning: {e}") 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 @@ -636,6 +805,10 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = validate_menu_state(config.previous_menu_state) config.needs_redraw = True logger.debug(f"Retour à {config.menu_state} depuis extension_warning") + if config.batch_pending_game: + config.batch_pending_game = None + if config.batch_in_progress: + config.menu_state = "game" #Historique elif config.menu_state == "history": @@ -690,7 +863,7 @@ def handle_controls(event, sources, joystick, screen): config.pending_download = check_extension_before_download(game[1], platform, game_name) if config.pending_download: url, platform, game_name, is_zip_non_supported = config.pending_download - if is_zip_non_supported: + if is_zip_non_supported and os.path.splitext(url)[1].lower() not in ARCHIVE_EXTENSIONS: config.previous_menu_state = config.menu_state config.menu_state = "extension_warning" config.extension_confirm_selection = 0 @@ -798,14 +971,7 @@ def handle_controls(event, sources, joystick, screen): config.needs_redraw = True logger.debug("Annulation du vidage de l'historique, retour à history") - # Résultat téléchargement - elif config.menu_state == "download_result": - if is_input_matched(event, "confirm"): - config.menu_state = validate_menu_state(config.previous_menu_state) - config.popup_timer = 0 - config.pending_download = None - config.needs_redraw = True - logger.debug(f"Retour à {config.menu_state} depuis download_result") + # État download_result supprimé # Confirmation quitter elif config.menu_state == "confirm_exit": diff --git a/ports/RGSX/controls_mapper.py b/ports/RGSX/controls_mapper.py index 710457f..c87a7ed 100644 --- a/ports/RGSX/controls_mapper.py +++ b/ports/RGSX/controls_mapper.py @@ -308,8 +308,6 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH): 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) diff --git a/ports/RGSX/display.py b/ports/RGSX/display.py index 231a4bf..e2fc78c 100644 --- a/ports/RGSX/display.py +++ b/ports/RGSX/display.py @@ -624,8 +624,12 @@ def draw_game_list(screen): for i in range(config.scroll_offset, min(config.scroll_offset + items_per_page, len(games))): game_name = games[i][0] if isinstance(games[i], (list, tuple)) else games[i] - color = THEME_COLORS["fond_lignes"] if i == config.current_game else THEME_COLORS["text"] - game_text = truncate_text_middle(game_name, config.small_font, rect_width - 40, is_filename=False) + is_marked = i in getattr(config, 'selected_games', set()) + # Couleur verte si jeu sous curseur OU marqué en multi-sélection + color = THEME_COLORS["fond_lignes"] if (i == config.current_game or is_marked) else THEME_COLORS["text"] + # Préfixe ASCII pour distinguer les jeux marqués (éviter collision glyphes) + prefix = "[X] " if is_marked else " " + game_text = truncate_text_middle(prefix + game_name, config.small_font, rect_width - 40, is_filename=False) text_surface = config.small_font.render(game_text, True, color) text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + (i - config.scroll_offset) * line_height + line_height // 2)) if i == config.current_game: @@ -715,6 +719,13 @@ def draw_history_list(screen): extra_margin_bottom = 80 title_height = config.title_font.get_height() + 20 + # Sécuriser current_history_item pour éviter IndexError + if history: + if config.current_history_item < 0 or config.current_history_item >= len(history): + config.current_history_item = max(0, min(len(history) - 1, config.current_history_item)) + else: + config.current_history_item = 0 + speed = 0.0 if history and history[config.current_history_item].get("status") in ["Téléchargement", "downloading"]: speed = history[config.current_history_item].get("speed", 0.0) @@ -1019,37 +1030,7 @@ def draw_progress_screen(screen): progress_width = int(bar_width * (min(100, max(0, progress_percent)) / 100)) -# Écran popup résultat téléchargement -def draw_popup_result_download(screen, message, is_error): - """Affiche une popup flottante centrée avec un message de résultat et un compte à rebours.""" - if message is None: - message = _("download_canceled") - logger.debug(f"Message popup : {message}, is_error={is_error}") - - screen.blit(OVERLAY, (0, 0)) - - popup_width = int(config.screen_width * 0.8) - line_height = config.small_font.get_height() + 10 - wrapped_message = wrap_text(message, config.small_font, popup_width - 40) - text_height = len(wrapped_message) * line_height - margin_top_bottom = 20 - popup_height = text_height + 2 * margin_top_bottom + line_height - popup_x = (config.screen_width - popup_width) // 2 - popup_y = (config.screen_height - popup_height) // 2 - - pygame.draw.rect(screen, THEME_COLORS["button_idle"], (popup_x, popup_y, popup_width, popup_height), border_radius=12) - pygame.draw.rect(screen, THEME_COLORS["border"], (popup_x, popup_y, popup_width, popup_height), 2, border_radius=12) - - for i, line in enumerate(wrapped_message): - text_surface = config.small_font.render(line, True, THEME_COLORS["error_text"] if is_error else THEME_COLORS["text"]) - text_rect = text_surface.get_rect(center=(config.screen_width // 2, popup_y + margin_top_bottom + i * line_height + line_height // 2)) - screen.blit(text_surface, text_rect) - - remaining_time = 3 - countdown_text = _("popup_countdown").format(remaining_time, 's' if remaining_time != 1 else '') - countdown_surface = config.small_font.render(countdown_text, True, THEME_COLORS["text"]) - countdown_rect = countdown_surface.get_rect(center=(config.screen_width // 2, popup_y + margin_top_bottom + len(wrapped_message) * line_height + line_height // 2)) - screen.blit(countdown_surface, countdown_rect) +## Ancienne fonction draw_popup_result_download supprimée (popup de fin de téléchargement retiré) # Écran avertissement extension non supportée téléchargement def draw_extension_warning(screen): @@ -1282,7 +1263,7 @@ def draw_controls_help(screen, previous_state): # États autorisés (même logique qu'avant) allowed_states = { - "error", "platform", "game", "download_result", "confirm_exit", + "error", "platform", "game", "confirm_exit", "extension_warning", "history", "clear_history" } if previous_state not in allowed_states: diff --git a/ports/RGSX/languages/de.json b/ports/RGSX/languages/de.json index d1035bf..91fc726 100644 --- a/ports/RGSX/languages/de.json +++ b/ports/RGSX/languages/de.json @@ -103,7 +103,7 @@ "controls_action_right": "Rechts", "controls_action_page_up": "Vorherige Seite", "controls_action_page_down": "Nächste Seite", - "controls_action_clear_history": "Verlauf leeren", + "controls_action_clear_history": "Mehrfachauswahl / Verlauf leeren", "controls_action_history": "Verlauf", "controls_action_filter": "Filtern", "controls_action_delete": "Löschen", @@ -118,7 +118,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_clear_history": "Verlauf löschen (z.B.: X)", + "controls_desc_clear_history": "Mehrfachauswahl (Spieleliste) / Verlauf löschen (Verlaufsmenü) (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)", @@ -173,7 +173,7 @@ "controls_confirm_select": "Bestätigen/Auswählen", "controls_cancel_back": "Abbrechen/Zurück", "controls_history": "Verlauf", - "controls_clear_history": "Verlauf löschen", + "controls_clear_history": "Mehrfachauswahl / Verlauf", "controls_filter_search": "Filtern/Suchen", "network_download_failed": "Download nach {0} Versuchen fehlgeschlagen", "network_api_error": "Fehler bei der API-Anfrage, der Schlüssel könnte falsch sein: {0}", diff --git a/ports/RGSX/languages/en.json b/ports/RGSX/languages/en.json index 0632de6..308e7ad 100644 --- a/ports/RGSX/languages/en.json +++ b/ports/RGSX/languages/en.json @@ -103,7 +103,7 @@ "controls_action_right": "Right", "controls_action_page_up": "Previous Page", "controls_action_page_down": "Next Page", - "controls_action_clear_history": "Clear History", + "controls_action_clear_history": "Multi-select / Clear History", "controls_action_history": "History", "controls_action_filter": "Filter", "controls_action_delete": "Delete", @@ -118,7 +118,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_clear_history": "Clear History (e.g. X)", + "controls_desc_clear_history": "Multi-select (game list) / Clear history (history menu) (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)", @@ -184,7 +184,7 @@ "controls_confirm_select": "Confirm/Select", "controls_cancel_back": "Cancel/Back", "controls_history": "History", - "controls_clear_history": "Clear History", + "controls_clear_history": "Multi-select / History", "controls_filter_search": "Filter/Search", "menu_symlink_option": "Symlink Option", diff --git a/ports/RGSX/languages/es.json b/ports/RGSX/languages/es.json index b75ff6c..3ba3742 100644 --- a/ports/RGSX/languages/es.json +++ b/ports/RGSX/languages/es.json @@ -104,7 +104,7 @@ "controls_action_right": "Derecha", "controls_action_page_up": "Página anterior", "controls_action_page_down": "Página siguiente", - "controls_action_clear_history": "Vaciar Historial", + "controls_action_clear_history": "Multi-selección / Vaciar historial", "controls_action_history": "Historial", "controls_action_filter": "Filtrar", "controls_action_delete": "Eliminar", @@ -119,7 +119,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_clear_history": "Borrar Historial (ej: X)", + "controls_desc_clear_history": "Multi-selección (lista juegos) / Vaciar historial (menú 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)", @@ -174,7 +174,7 @@ "controls_confirm_select": "Confirmar/Seleccionar", "controls_cancel_back": "Cancelar/Volver", "controls_history": "Historial", - "controls_clear_history": "Vaciar historial", + "controls_clear_history": "Multi-selección / Historial", "controls_filter_search": "Filtrar/Buscar", "network_download_failed": "Error en la descarga tras {0} intentos", "network_api_error": "Error en la solicitud de API, la clave puede ser incorrecta: {0}", diff --git a/ports/RGSX/languages/fr.json b/ports/RGSX/languages/fr.json index 4c66e13..96064ff 100644 --- a/ports/RGSX/languages/fr.json +++ b/ports/RGSX/languages/fr.json @@ -100,7 +100,7 @@ "controls_action_right": "Droite", "controls_action_page_up": "Page Précédente", "controls_action_page_down": "Page Suivante", - "controls_action_clear_history": "Vider Historique", + "controls_action_clear_history": "Multi-sélection / Vider Historique", "controls_action_history": "Historique", "controls_action_filter": "Filtrer", "controls_action_delete": "Supprimer", @@ -116,7 +116,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_clear_history": "Effacer Historique (ex: X)", + "controls_desc_clear_history": "Multi-sélection (liste jeux) / Vider historique (menu 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)", @@ -170,7 +170,7 @@ "controls_confirm_select": "Confirmer/Sélectionner", "controls_cancel_back": "Annuler/Retour", "controls_history": "Historique", - "controls_clear_history": "Effacer historique", + "controls_clear_history": "Multi-sélection / Historique", "controls_filter_search": "Filtrer/Rechercher", "network_download_failed": "Échec du téléchargement après {0} tentatives", "network_api_error": "Erreur lors de la requête API, la clé est peut-être incorrecte: {0}",