Compare commits

...

1 Commits

Author SHA1 Message Date
skymike03
50c9b9caad v2.5.0.1 (2026.01.27)
- add Nintendo/Xbox Layout in Menu>Controls>Controls Help to invert displayed buttons
2026-01-29 18:58:56 +01:00
12 changed files with 241 additions and 50 deletions

View File

@@ -147,6 +147,14 @@ initialize_language()
config.sources_mode = get_sources_mode()
config.custom_sources_url = get_custom_sources_url()
logger.debug(f"Mode sources initial: {config.sources_mode}, URL custom: {config.custom_sources_url}")
# Charger l'option nintendo_layout depuis les settings
try:
from rgsx_settings import get_nintendo_layout
config.nintendo_layout = get_nintendo_layout()
logger.debug(f"nintendo_layout initial: {config.nintendo_layout}")
except Exception:
# fallback: si l'import ou la lecture échoue, conserver la valeur par défaut dans config
logger.debug("Impossible de charger nintendo_layout depuis rgsx_settings")
# Détection du système grace a une commande windows / linux (on oublie is non-pc c'est juste pour connaitre le materiel et le systeme d'exploitation)
def detect_system_info():

View File

@@ -1,3 +1,4 @@
import os
import logging
import platform
@@ -13,7 +14,7 @@ except Exception:
pygame = None # type: ignore
# Version actuelle de l'application
app_version = "2.5.0.0"
app_version = "2.5.0.1"
# Nombre de jours avant de proposer la mise à jour de la liste des jeux
GAMELIST_UPDATE_DAYS = 7
@@ -378,6 +379,7 @@ sources_mode = "rgsx" # Mode des sources de jeux (rgsx/custom)
custom_sources_url = {OTA_data_ZIP} # URL personnalisée si mode custom
selected_language_index = 0 # Index de la langue sélectionnée dans la liste
# Recherche et filtres
filtered_games = [] # Liste des jeux filtrés par recherche ou filtre
search_mode = False # Indicateur si le mode recherche est actif

View File

@@ -1739,18 +1739,26 @@ def handle_controls(event, sources, joystick, screen):
# Sous-menu Controls
elif config.menu_state == "pause_controls_menu":
# Ajout de l'option inversion ABXY
options = [
{"key": "help", "title": _("controls_help_title"), "desc": _("instruction_controls_help")},
{"key": "remap", "title": _("menu_remap_controls"), "desc": _("instruction_controls_remap")},
{"key": "back", "title": _("menu_back"), "desc": _("instruction_generic_back")},
]
sel = getattr(config, 'pause_controls_selection', 0)
total = len(options)
if is_input_matched(event, "up"):
config.pause_controls_selection = (sel - 1) % 3
config.pause_controls_selection = (sel - 1) % total
config.needs_redraw = True
elif is_input_matched(event, "down"):
config.pause_controls_selection = (sel + 1) % 3
config.pause_controls_selection = (sel + 1) % total
config.needs_redraw = True
elif is_input_matched(event, "confirm"):
if sel == 0: # Aide
selected = options[sel]["key"]
if selected == "help":
config.previous_menu_state = "pause_controls_menu"
config.menu_state = "controls_help"
elif sel == 1: # Remap
elif selected == "remap":
if os.path.exists(config.CONTROLS_CONFIG_PATH):
try:
os.remove(config.CONTROLS_CONFIG_PATH)
@@ -1758,6 +1766,7 @@ def handle_controls(event, sources, joystick, screen):
logger.error(f"Erreur suppression controls_config: {e}")
config.previous_menu_state = "pause_controls_menu"
config.menu_state = "controls_mapping"
# invert_abxy moved to controls_help submenu (interactive)
else: # Back
config.menu_state = "pause_menu"
config.last_state_change_time = pygame.time.get_ticks()
@@ -1767,6 +1776,31 @@ def handle_controls(event, sources, joystick, screen):
config.last_state_change_time = pygame.time.get_ticks()
config.needs_redraw = True
# Menu Aide Contrôles (affichage interactif du style manette)
elif config.menu_state == "controls_help":
# Left/Right change controller style immediately
if is_input_matched(event, "left") or is_input_matched(event, "right"):
try:
from rgsx_settings import set_nintendo_layout, get_nintendo_layout
# Toggle style
new_val = set_nintendo_layout(not get_nintendo_layout())
config.nintendo_layout = new_val
# Clear icon cache so help screen updates immediately
try:
from display import clear_help_icon_cache
clear_help_icon_cache()
except Exception:
pass
config.popup_message = (_("menu_nintendo_layout_on") if new_val else _("menu_nintendo_layout_off"))
config.popup_timer = 1200
config.needs_redraw = True
except Exception as e:
logger.error(f"Erreur toggle nintendo_layout from controls_help: {e}")
elif is_input_matched(event, "confirm") or is_input_matched(event, "cancel") or is_input_matched(event, "start"):
# Return to previous menu
config.menu_state = validate_menu_state(config.previous_menu_state)
config.needs_redraw = True
# Sous-menu Display
elif config.menu_state == "pause_display_menu":
sel = getattr(config, 'pause_display_selection', 0)

View File

@@ -303,22 +303,43 @@ def _images_base_dir() -> str:
def _action_icon_filename(action_name: str) -> Optional[str]:
# Map actions to icon filenames present in assets/images
mapping = {
"up": "dpad_up.svg",
"down": "dpad_down.svg",
"left": "dpad_left.svg",
"right": "dpad_right.svg",
"confirm": "buttons_south.svg", # A (south)
"cancel": "buttons_east.svg", # B (east)
"clear_history": "buttons_west.svg", # X (west)
"history": "buttons_north.svg", # Y (north)
"start": "button_start.svg",
"filter": "button_select.svg",
"delete": "button_l.svg", # LB
"space": "button_r.svg", # RB
"page_up": "button_lt.svg",
"page_down": "button_rt.svg",
}
# Option d'inversion ABXY (A/B <-> X/Y) via config.nintendo_layout
is_nintendo = getattr(config, 'nintendo_layout', False)
if is_nintendo:
mapping = {
"up": "dpad_up.svg",
"down": "dpad_down.svg",
"left": "dpad_left.svg",
"right": "dpad_right.svg",
"confirm": "buttons_east.svg",
"cancel": "buttons_south.svg",
"clear_history": "buttons_west.svg",
"history": "buttons_north.svg",
"start": "button_start.svg",
"filter": "button_select.svg",
"delete": "button_l.svg",
"space": "button_r.svg",
"page_up": "button_lt.svg",
"page_down": "button_rt.svg",
}
else:
mapping = {
"up": "dpad_up.svg",
"down": "dpad_down.svg",
"left": "dpad_left.svg",
"right": "dpad_right.svg",
"confirm": "buttons_south.svg",
"cancel": "buttons_east.svg",
"clear_history": "buttons_north.svg",
"history": "buttons_west.svg",
"start": "button_start.svg",
"filter": "button_select.svg",
"delete": "button_l.svg",
"space": "button_r.svg",
"page_up": "button_lt.svg",
"page_down": "button_rt.svg",
}
return mapping.get(action_name)
def _load_svg_icon_surface(svg_path: str, size: int) -> Optional[pygame.Surface]:

View File

@@ -26,6 +26,15 @@ OVERLAY = None # Initialisé dans init_display()
# --- Helpers: SVG icons for controls (local cache, optional cairosvg) ---
_HELP_ICON_CACHE = {}
def clear_help_icon_cache():
"""Vide le cache des surfaces d'icônes d'aide pour forcer leur rechargement.
Appeler cette fonction après un changement de mapping d'icônes (ex: inversion ABXY).
"""
try:
_HELP_ICON_CACHE.clear()
logger.debug("Help icon cache cleared")
except Exception:
pass
def _images_base_dir():
try:
base_dir = os.path.join(os.path.dirname(__file__), "assets", "images")
@@ -34,22 +43,41 @@ def _images_base_dir():
return base_dir
def _action_icon_filename(action_name: str):
mapping = {
"up": "dpad_up.svg",
"down": "dpad_down.svg",
"left": "dpad_left.svg",
"right": "dpad_right.svg",
"confirm": "buttons_south.svg",
"cancel": "buttons_east.svg",
"clear_history": "buttons_west.svg",
"history": "buttons_north.svg",
"start": "button_start.svg",
"filter": "button_select.svg",
"delete": "button_l.svg",
"space": "button_r.svg",
"page_up": "button_lt.svg",
"page_down": "button_rt.svg",
}
is_nintendo = getattr(config, 'nintendo_layout', False)
if is_nintendo:
mapping = {
"up": "dpad_up.svg",
"down": "dpad_down.svg",
"left": "dpad_left.svg",
"right": "dpad_right.svg",
"confirm": "buttons_east.svg",
"cancel": "buttons_south.svg",
"clear_history": "buttons_west.svg",
"history": "buttons_north.svg",
"start": "button_start.svg",
"filter": "button_select.svg",
"delete": "button_l.svg",
"space": "button_r.svg",
"page_up": "button_lt.svg",
"page_down": "button_rt.svg",
}
else:
mapping = {
"up": "dpad_up.svg",
"down": "dpad_down.svg",
"left": "dpad_left.svg",
"right": "dpad_right.svg",
"confirm": "buttons_south.svg",
"cancel": "buttons_east.svg",
"clear_history": "buttons_north.svg",
"history": "buttons_west.svg",
"start": "button_start.svg",
"filter": "button_select.svg",
"delete": "button_l.svg",
"space": "button_r.svg",
"page_up": "button_lt.svg",
"page_down": "button_rt.svg",
}
return mapping.get(action_name)
def _load_svg_icon_surface(svg_path: str, size: int):
@@ -2569,22 +2597,20 @@ def _draw_submenu_generic(screen, title, options, selected_index, instruction_te
draw_menu_instruction(screen, instruction_text)
def draw_pause_controls_menu(screen, selected_index):
# Synchronisé avec controls.py : help, remap, back
options = [
_("menu_controls"), # aide contrôles (réutilisée)
_("menu_remap_controls"), # remap
_("menu_back") if _ else "Back"
_( "controls_help_title"),
_( "menu_remap_controls"),
_( "menu_back") if _ else "Back"
]
# Instructions contextuelles
instruction_keys = [
"instruction_controls_help", # pour menu_controls (afficher l'aide)
"instruction_controls_remap", # remap
"instruction_generic_back", # retour
"instruction_controls_help",
"instruction_controls_remap",
"instruction_generic_back",
]
key = instruction_keys[selected_index] if 0 <= selected_index < len(instruction_keys) else None
instruction_text = _(key) if key else None
# Dessiner le menu avec l'instruction
_draw_submenu_generic(screen, _("menu_controls") if _ else "Controls", options, selected_index, instruction_text)
_draw_submenu_generic(screen, _( "menu_controls") if _ else "Controls", options, selected_index, instruction_text)
def draw_pause_display_menu(screen, selected_index):
# Layout label - now opens a submenu
@@ -3290,7 +3316,9 @@ def draw_controls_help(screen, previous_state):
title_height = title_surf.get_height()
content_height = max(col1_h, col2_h)
panel_height = title_height + title_spacing + content_height + 2 * padding
# Réserver un espace supplémentaire en bas pour éviter que le cadre ne coupe les icônes/boutons
extra_bottom_space = max(20, int(font.get_height() * 1.5))
panel_height = title_height + title_spacing + content_height + 2 * padding + extra_bottom_space
if panel_height > max_panel_height:
panel_height = max_panel_height
enable_clip = True
@@ -3342,6 +3370,53 @@ def draw_controls_help(screen, previous_state):
if enable_clip and prev_clip is not None:
screen.set_clip(prev_clip)
# Footer: controller style selector display
try:
style_is_inverted = getattr(config, 'nintendo_layout', False)
style_label = _('controller_style_label') if _ else 'Controller Style :'
# When inverted flag is True we show Nintendo style (A/B swapped vs Xbox)
style_name = _('controller_style_nintendo') if style_is_inverted else _('controller_style_xbox')
# Render footer with left/right helper icons and the current controller style label
style_label = style_label
style_name = style_name
icon_size = max(18, font.get_height())
left_icon = get_help_icon_surface('left', icon_size)
right_icon = get_help_icon_surface('right', icon_size)
label_surf = font.render(f"{style_label} {style_name}", True, THEME_COLORS['text'])
# Compose horizontal footer surface: [left_icon] label [right_icon]
parts_width = 0
parts_height = 0
if left_icon:
parts_width += left_icon.get_width() + 8
parts_height = max(parts_height, left_icon.get_height())
parts_width += label_surf.get_width()
parts_height = max(parts_height, label_surf.get_height())
if right_icon:
parts_width += 8 + right_icon.get_width()
parts_height = max(parts_height, right_icon.get_height())
footer_surf = pygame.Surface((max(1, parts_width), max(1, parts_height)), pygame.SRCALPHA)
x = 0
if left_icon:
footer_surf.blit(left_icon, (x, (parts_height - left_icon.get_height()) // 2))
x += left_icon.get_width() + 8
footer_surf.blit(label_surf, (x, (parts_height - label_surf.get_height()) // 2))
x += label_surf.get_width()
if right_icon:
x += 8
footer_surf.blit(right_icon, (x, (parts_height - right_icon.get_height()) // 2))
# Place footer inside the panel, just above the bottom padding so it stays visible
try:
footer_y = panel_y + panel_height - padding - (footer_surf.get_height() // 2) - 4
except Exception:
footer_y = panel_y + panel_height - padding - 8
footer_rect = footer_surf.get_rect(center=(config.screen_width // 2, int(footer_y)))
screen.blit(footer_surf, footer_rect)
except Exception:
pass
# Menu Quitter Appli
def draw_confirm_dialog(screen):

View File

@@ -62,6 +62,12 @@
"language_changed": "Sprache geändert zu {0}",
"menu_controls": "Steuerung",
"menu_remap_controls": "Steuerung neu zuordnen",
"menu_nintendo_layout_on": "Nintendo-Controller-Layout",
"menu_nintendo_layout_off": "Xbox-Controller-Layout",
"instruction_nintendo_layout": "Invertiert die angezeigten Steuerungen, um das Layout anzupassen",
"controller_style_label": "Controller-Stil :",
"controller_style_nintendo": "Nintendo",
"controller_style_xbox": "Xbox",
"menu_history": "Verlauf",
"menu_language": "Sprache",
"menu_accessibility": "Barrierefreiheit",

View File

@@ -62,6 +62,9 @@
"language_changed": "Language changed to {0}",
"menu_controls": "Controls",
"menu_remap_controls": "Remap controls",
"menu_nintendo_layout_on": "Nintendo Controller layout",
"menu_nintendo_layout_off": "Xbox Controller layout",
"instruction_nintendo_layout": "Inverts the displayed controls to match layout",
"menu_history": "History",
"menu_language": "Language",
"menu_accessibility": "Accessibility",
@@ -205,6 +208,9 @@
"instruction_quit_app": "Exit the RGSX application",
"instruction_quit_restart": "Restart the RGSX application",
"instruction_controls_help": "Show full controller & keyboard reference",
"controller_style_label": "Controller Style :",
"controller_style_nintendo": "Nintendo",
"controller_style_xbox": "Xbox",
"instruction_controls_remap": "Change button / key bindings",
"instruction_generic_back": "Return to the previous menu",
"instruction_display_layout": "Cycle grid dimensions (columns × rows)",

View File

@@ -60,6 +60,12 @@
"language_changed": "Idioma cambiado a {0}",
"menu_controls": "Controles",
"menu_remap_controls": "Remapear controles",
"menu_nintendo_layout_on": "Diseño de controlador Nintendo",
"menu_nintendo_layout_off": "Diseño de controlador Xbox",
"instruction_nintendo_layout": "Invierte los controles mostrados para coincidir con el diseño",
"controller_style_label": "Estilo de controlador :",
"controller_style_nintendo": "Nintendo",
"controller_style_xbox": "Xbox",
"menu_history": "Historial",
"menu_language": "Idioma",
"menu_accessibility": "Accesibilidad",

View File

@@ -205,6 +205,12 @@
"instruction_quit_app": "Quitter l'application RGSX",
"instruction_quit_restart": "Redémarrer l'application RGSX",
"instruction_controls_help": "Afficher la référence complète manette & clavier",
"menu_nintendo_layout_on": "Disposition manette Nintendo",
"menu_nintendo_layout_off": "Disposition manette Xbox",
"instruction_nintendo_layout": "Inverse l'affichage des contrôles pour correspondre au layout",
"controller_style_label": "Style manette :",
"controller_style_nintendo": "Nintendo",
"controller_style_xbox": "Xbox",
"instruction_controls_remap": "Modifier l'association boutons / touches",
"instruction_generic_back": "Revenir au menu précédent",
"instruction_display_layout": "Changer les dimensions de la grille",

View File

@@ -60,6 +60,12 @@
"language_changed": "Lingua cambiata in {0}",
"menu_controls": "Controlli",
"menu_remap_controls": "Rimappa controlli",
"menu_nintendo_layout_on": "Layout controller Nintendo",
"menu_nintendo_layout_off": "Layout controller Xbox",
"instruction_nintendo_layout": "Inverti i controlli visualizzati per corrispondere al layout",
"controller_style_label": "Stile controller :",
"controller_style_nintendo": "Nintendo",
"controller_style_xbox": "Xbox",
"menu_history": "Cronologia",
"menu_language": "Lingua",
"menu_accessibility": "Accessibilità",

View File

@@ -62,6 +62,12 @@
"language_changed": "Idioma alterado para {0}",
"menu_controls": "Controles",
"menu_remap_controls": "Remapear controles",
"menu_nintendo_layout_on": "Layout do controle Nintendo",
"menu_nintendo_layout_off": "Layout do controle Xbox",
"instruction_nintendo_layout": "Inverte os controles exibidos para corresponder ao layout",
"controller_style_label": "Estilo do controle :",
"controller_style_nintendo": "Nintendo",
"controller_style_xbox": "Xbox",
"menu_history": "Histórico",
"menu_language": "Idioma",
"menu_accessibility": "Acessibilidade",
@@ -465,6 +471,4 @@
"folder_create_error": "Erro ao criar: {0}",
"controls_action_select_char": "Adicionar",
"folder_browser_browse": "Explorar"
}
}
}

View File

@@ -75,6 +75,7 @@ def load_rgsx_settings():
},
"show_unsupported_platforms": False,
"allow_unknown_extensions": False,
"nintendo_layout": False,
"roms_folder": "",
"web_service_at_boot": False,
"last_gamelist_update": None,
@@ -298,6 +299,22 @@ def set_allow_unknown_extensions(enabled: bool) -> bool:
save_rgsx_settings(settings)
return settings["allow_unknown_extensions"]
# ----------------------- Invert ABXY layout ----------------------- #
def get_nintendo_layout(settings=None) -> bool:
"""Retourne True si l'inversion ABXY (icônes) est activée."""
if settings is None:
settings = load_rgsx_settings()
return bool(settings.get("nintendo_layout", False))
def set_nintendo_layout(enabled: bool) -> bool:
"""Active/désactive l'inversion ABXY (icônes) et sauvegarde."""
settings = load_rgsx_settings()
settings["nintendo_layout"] = bool(enabled)
save_rgsx_settings(settings)
return settings["nintendo_layout"]
# ----------------------- Hide premium systems toggle ----------------------- #
def get_hide_premium_systems(settings=None) -> bool: