mirror of
https://github.com/RetroGameSets/RGSX.git
synced 2026-03-20 16:55:39 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b437f31854 |
@@ -97,6 +97,7 @@ _run_windows_gamelist_update()
|
||||
|
||||
try:
|
||||
config.update_checked = False
|
||||
config.gamelist_update_prompted = False # Flag pour ne pas redemander la mise à jour plusieurs fois
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -677,6 +678,8 @@ async def main():
|
||||
"pause_menu",
|
||||
"pause_controls_menu",
|
||||
"pause_display_menu",
|
||||
"pause_display_layout_menu",
|
||||
"pause_display_font_menu",
|
||||
"pause_games_menu",
|
||||
"pause_settings_menu",
|
||||
"pause_api_keys_status",
|
||||
@@ -690,11 +693,11 @@ async def main():
|
||||
"history_game_options",
|
||||
"history_show_folder",
|
||||
"history_scraper_info",
|
||||
"scraper", # Ajout du scraper pour gérer les contrôles
|
||||
"scraper",
|
||||
"history_error_details",
|
||||
"history_confirm_delete",
|
||||
"history_extract_archive",
|
||||
"text_file_viewer", # Visualiseur de fichiers texte
|
||||
"text_file_viewer",
|
||||
# Menus filtrage avancé
|
||||
"filter_menu_choice",
|
||||
"filter_advanced",
|
||||
@@ -733,6 +736,11 @@ async def main():
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "gamelist_update_prompt":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
continue
|
||||
|
||||
if config.menu_state == "extension_warning":
|
||||
logger.debug(f"[EXTENSION_WARNING] Processing extension_warning, previous_menu_state={config.previous_menu_state}, pending_download={bool(config.pending_download)}")
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
@@ -1085,6 +1093,12 @@ async def main():
|
||||
elif config.menu_state == "pause_display_menu":
|
||||
from display import draw_pause_display_menu
|
||||
draw_pause_display_menu(screen, getattr(config, 'pause_display_selection', 0))
|
||||
elif config.menu_state == "pause_display_layout_menu":
|
||||
from display import draw_pause_display_layout_menu
|
||||
draw_pause_display_layout_menu(screen, getattr(config, 'pause_display_layout_selection', 0))
|
||||
elif config.menu_state == "pause_display_font_menu":
|
||||
from display import draw_pause_display_font_menu
|
||||
draw_pause_display_font_menu(screen, getattr(config, 'pause_display_font_selection', 0))
|
||||
elif config.menu_state == "pause_games_menu":
|
||||
from display import draw_pause_games_menu
|
||||
draw_pause_games_menu(screen, getattr(config, 'pause_games_selection', 0))
|
||||
@@ -1141,6 +1155,9 @@ async def main():
|
||||
draw_cancel_download_dialog(screen)
|
||||
elif config.menu_state == "reload_games_data":
|
||||
draw_reload_games_data_dialog(screen)
|
||||
elif config.menu_state == "gamelist_update_prompt":
|
||||
from display import draw_gamelist_update_prompt
|
||||
draw_gamelist_update_prompt(screen)
|
||||
elif config.menu_state == "restart_popup":
|
||||
draw_popup(screen)
|
||||
elif config.menu_state == "accessibility_menu":
|
||||
@@ -1379,11 +1396,47 @@ async def main():
|
||||
elif loading_step == "load_sources":
|
||||
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
|
||||
sources = load_sources()
|
||||
config.menu_state = "platform"
|
||||
config.loading_progress = 100.0
|
||||
config.current_loading_system = ""
|
||||
|
||||
# Vérifier si une mise à jour de la liste des jeux est nécessaire (seulement si pas déjà demandé)
|
||||
if not config.gamelist_update_prompted:
|
||||
from rgsx_settings import get_last_gamelist_update
|
||||
from config import GAMELIST_UPDATE_DAYS
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
last_update = get_last_gamelist_update()
|
||||
should_prompt_update = False
|
||||
|
||||
if last_update is None:
|
||||
# Première utilisation, proposer la mise à jour
|
||||
logger.info("Première utilisation détectée, proposition de mise à jour de la liste des jeux")
|
||||
should_prompt_update = True
|
||||
else:
|
||||
try:
|
||||
last_update_date = datetime.strptime(last_update, "%Y-%m-%d")
|
||||
days_since_update = (datetime.now() - last_update_date).days
|
||||
logger.info(f"Dernière mise à jour de la liste des jeux: {last_update} ({days_since_update} jours)")
|
||||
|
||||
if days_since_update >= GAMELIST_UPDATE_DAYS:
|
||||
logger.info(f"Mise à jour de la liste des jeux recommandée (>{GAMELIST_UPDATE_DAYS} jours)")
|
||||
should_prompt_update = True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la vérification de la date de mise à jour: {e}")
|
||||
|
||||
if should_prompt_update:
|
||||
config.menu_state = "gamelist_update_prompt"
|
||||
config.gamelist_update_selection = 1 # 0=Non, 1=Oui (par défaut)
|
||||
config.gamelist_update_prompted = True # Marquer comme déjà demandé
|
||||
logger.debug("Affichage du prompt de mise à jour de la liste des jeux")
|
||||
else:
|
||||
config.menu_state = "platform"
|
||||
logger.debug(f"Fin chargement, passage à platform, progress={config.loading_progress}")
|
||||
else:
|
||||
config.menu_state = "platform"
|
||||
logger.debug(f"Prompt déjà affiché, passage à platform, progress={config.loading_progress}")
|
||||
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Fin chargement, passage à platform, progress={config.loading_progress}")
|
||||
|
||||
# Gestion de l'état de transition
|
||||
if config.transition_state == "to_game":
|
||||
|
||||
@@ -13,7 +13,10 @@ except Exception:
|
||||
pygame = None # type: ignore
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "2.4.0.2"
|
||||
app_version = "2.4.1.0"
|
||||
|
||||
# Nombre de jours avant de proposer la mise à jour de la liste des jeux
|
||||
GAMELIST_UPDATE_DAYS = 7
|
||||
|
||||
|
||||
def get_application_root():
|
||||
|
||||
@@ -45,9 +45,12 @@ VALID_STATES = [
|
||||
"extension_warning", "pause_menu", "controls_help", "history", "controls_mapping",
|
||||
"reload_games_data", "restart_popup", "error", "loading", "confirm_clear_history",
|
||||
"language_select", "filter_platforms", "display_menu", "confirm_cancel_download",
|
||||
"gamelist_update_prompt",
|
||||
# Nouveaux sous-menus hiérarchiques (refonte pause menu)
|
||||
"pause_controls_menu", # sous-menu Controls (aide, remap)
|
||||
"pause_display_menu", # sous-menu Display (layout, font size, unsupported, unknown ext, filter)
|
||||
"pause_display_layout_menu",# sous-menu Display > Layout (disposition avec visualisation)
|
||||
"pause_display_font_menu", # sous-menu Display > Font (taille police + footer)
|
||||
"pause_games_menu", # sous-menu Games (source mode, update/redownload cache)
|
||||
"pause_settings_menu", # sous-menu Settings (music on/off, symlink toggle, api keys status)
|
||||
"pause_api_keys_status", # sous-menu API Keys (affichage statut des clés)
|
||||
@@ -493,7 +496,12 @@ def handle_controls(event, sources, joystick, screen):
|
||||
|
||||
config.current_game = 0
|
||||
config.scroll_offset = 0
|
||||
draw_validation_transition(screen, config.current_platform)
|
||||
|
||||
# Désactiver l'animation de transition en mode performance (light mode)
|
||||
from rgsx_settings import get_light_mode
|
||||
if not get_light_mode():
|
||||
draw_validation_transition(screen, config.current_platform)
|
||||
|
||||
config.menu_state = "game"
|
||||
config.needs_redraw = True
|
||||
#logger.debug(f"Plateforme sélectionnée: {config.platforms[config.current_platform]}, {len(config.games)} jeux chargés")
|
||||
@@ -1755,8 +1763,11 @@ def handle_controls(event, sources, joystick, screen):
|
||||
# Sous-menu Display
|
||||
elif config.menu_state == "pause_display_menu":
|
||||
sel = getattr(config, 'pause_display_selection', 0)
|
||||
# layout, font size, footer font size, font family, monitor, light, allow unknown extensions, back (8)
|
||||
total = 8
|
||||
# layout, font submenu, family, [monitor if multi], light, unknown, back
|
||||
from rgsx_settings import get_available_monitors
|
||||
monitors = get_available_monitors()
|
||||
show_monitor = len(monitors) > 1
|
||||
total = 7 if show_monitor else 6 # dynamic total based on monitor count
|
||||
if is_input_matched(event, "up"):
|
||||
config.pause_display_selection = (sel - 1) % total
|
||||
config.needs_redraw = True
|
||||
@@ -1764,61 +1775,24 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.pause_display_selection = (sel + 1) % total
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm"):
|
||||
# 0 layout cycle
|
||||
if sel == 0 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
|
||||
# 0 layout submenu - open submenu on confirm
|
||||
if sel == 0 and is_input_matched(event, "confirm"):
|
||||
config.menu_state = "pause_display_layout_menu"
|
||||
# Trouver l'index actuel pour la sélection
|
||||
layouts = [(3,3),(3,4),(4,3),(4,4)]
|
||||
try:
|
||||
idx = layouts.index((config.GRID_COLS, config.GRID_ROWS))
|
||||
except ValueError:
|
||||
idx = 0
|
||||
idx = (idx + 1) % len(layouts) if is_input_matched(event, "right") else (idx - 1) % len(layouts)
|
||||
new_cols, new_rows = layouts[idx]
|
||||
try:
|
||||
set_display_grid(new_cols, new_rows)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur set_display_grid: {e}")
|
||||
config.GRID_COLS = new_cols
|
||||
config.GRID_ROWS = new_rows
|
||||
# Afficher un popup indiquant que le changement sera effectif après redémarrage
|
||||
try:
|
||||
config.popup_message = _("popup_layout_changed_restart_required") if _ else "Layout changed. Restart required to apply."
|
||||
config.popup_timer = 3000
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur popup layout: {e}")
|
||||
config.pause_display_layout_selection = idx
|
||||
config.needs_redraw = True
|
||||
# 1 font size
|
||||
elif sel == 1 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
|
||||
opts = getattr(config, 'font_scale_options', [0.75,1.0,1.25,1.5,1.75])
|
||||
idx = getattr(config, 'current_font_scale_index', 1)
|
||||
idx = max(0, idx-1) if is_input_matched(event, "left") else min(len(opts)-1, idx+1)
|
||||
if idx != getattr(config, 'current_font_scale_index', 1):
|
||||
config.current_font_scale_index = idx
|
||||
scale = opts[idx]
|
||||
config.accessibility_settings["font_scale"] = scale
|
||||
try:
|
||||
save_accessibility_settings(config.accessibility_settings)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur sauvegarde accessibilité: {e}")
|
||||
try:
|
||||
config.init_font()
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur init polices: {e}")
|
||||
config.needs_redraw = True
|
||||
# 2 footer font size
|
||||
elif sel == 2 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
|
||||
from accessibility import update_footer_font_scale
|
||||
footer_opts = getattr(config, 'footer_font_scale_options', [0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0])
|
||||
idx = getattr(config, 'current_footer_font_scale_index', 3)
|
||||
idx = max(0, idx-1) if is_input_matched(event, "left") else min(len(footer_opts)-1, idx+1)
|
||||
if idx != getattr(config, 'current_footer_font_scale_index', 3):
|
||||
config.current_footer_font_scale_index = idx
|
||||
try:
|
||||
update_footer_font_scale()
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur update footer font scale: {e}")
|
||||
config.needs_redraw = True
|
||||
# 3 font family cycle
|
||||
elif sel == 3 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
# 1 font size submenu - open submenu on confirm
|
||||
elif sel == 1 and is_input_matched(event, "confirm"):
|
||||
config.menu_state = "pause_display_font_menu"
|
||||
config.pause_display_font_selection = 0
|
||||
config.needs_redraw = True
|
||||
# 2 font family cycle
|
||||
elif sel == 2 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
try:
|
||||
families = getattr(config, 'FONT_FAMILIES', ["pixel"]) or ["pixel"]
|
||||
current = get_font_family()
|
||||
@@ -1851,26 +1825,20 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur changement font family: {e}")
|
||||
# 4 monitor selection
|
||||
elif sel == 4 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
|
||||
# 3 monitor selection (only if multiple monitors)
|
||||
elif sel == 3 and show_monitor and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
try:
|
||||
from rgsx_settings import get_display_monitor, set_display_monitor, get_available_monitors
|
||||
monitors = get_available_monitors()
|
||||
num_monitors = len(monitors)
|
||||
if num_monitors > 1:
|
||||
current = get_display_monitor()
|
||||
new_monitor = (current - 1) % num_monitors if is_input_matched(event, "left") else (current + 1) % num_monitors
|
||||
set_display_monitor(new_monitor)
|
||||
config.popup_message = _("display_monitor_restart_required") if _ else "Restart required to apply monitor change"
|
||||
config.popup_timer = 3000
|
||||
else:
|
||||
config.popup_message = _("display_monitor_single_only") if _ else "Only one monitor detected"
|
||||
config.popup_timer = 2000
|
||||
from rgsx_settings import get_display_monitor, set_display_monitor
|
||||
current = get_display_monitor()
|
||||
new_monitor = (current - 1) % len(monitors) if is_input_matched(event, "left") else (current + 1) % len(monitors)
|
||||
set_display_monitor(new_monitor)
|
||||
config.popup_message = _("display_monitor_restart_required") if _ else "Restart required to apply monitor change"
|
||||
config.popup_timer = 3000
|
||||
config.needs_redraw = True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur changement moniteur: {e}")
|
||||
# 5 light mode toggle
|
||||
elif sel == 5 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
# light mode toggle (index 4 if show_monitor, else 3)
|
||||
elif ((sel == 4 and show_monitor) or (sel == 3 and not show_monitor)) and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
try:
|
||||
from rgsx_settings import get_light_mode, set_light_mode
|
||||
current = get_light_mode()
|
||||
@@ -1880,8 +1848,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur toggle light mode: {e}")
|
||||
# 6 allow unknown extensions
|
||||
elif sel == 6 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
# allow unknown extensions (index 5 if show_monitor, else 4)
|
||||
elif ((sel == 5 and show_monitor) or (sel == 4 and not show_monitor)) and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")):
|
||||
try:
|
||||
current = get_allow_unknown_extensions()
|
||||
new_val = set_allow_unknown_extensions(not current)
|
||||
@@ -1890,8 +1858,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur toggle allow_unknown_extensions: {e}")
|
||||
# 7 back
|
||||
elif sel == 7 and is_input_matched(event, "confirm"):
|
||||
# back (index 6 if show_monitor, else 5)
|
||||
elif ((sel == 6 and show_monitor) or (sel == 5 and not show_monitor)) and is_input_matched(event, "confirm"):
|
||||
config.menu_state = "pause_menu"
|
||||
config.last_state_change_time = pygame.time.get_ticks()
|
||||
config.needs_redraw = True
|
||||
@@ -1900,6 +1868,95 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.last_state_change_time = pygame.time.get_ticks()
|
||||
config.needs_redraw = True
|
||||
|
||||
# Sous-menu Display > Layout (disposition avec visualisation)
|
||||
elif config.menu_state == "pause_display_layout_menu":
|
||||
sel = getattr(config, 'pause_display_layout_selection', 0)
|
||||
total = 5 # 3x3, 3x4, 4x3, 4x4, back
|
||||
if is_input_matched(event, "up"):
|
||||
config.pause_display_layout_selection = (sel - 1) % total
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "down"):
|
||||
config.pause_display_layout_selection = (sel + 1) % total
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "confirm"):
|
||||
if sel < 4: # Une des dispositions
|
||||
layouts = [(3,3),(3,4),(4,3),(4,4)]
|
||||
new_cols, new_rows = layouts[sel]
|
||||
try:
|
||||
set_display_grid(new_cols, new_rows)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur set_display_grid: {e}")
|
||||
config.GRID_COLS = new_cols
|
||||
config.GRID_ROWS = new_rows
|
||||
# Afficher un popup indiquant que le changement sera effectif après redémarrage
|
||||
try:
|
||||
config.popup_message = _("popup_layout_changed_restart").format(new_cols, new_rows) if _ else f"Layout changed to {new_cols}x{new_rows}. Restart required to apply."
|
||||
config.popup_timer = 3000
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur popup layout: {e}")
|
||||
config.menu_state = "pause_display_menu"
|
||||
config.needs_redraw = True
|
||||
elif sel == 4: # Back
|
||||
config.menu_state = "pause_display_menu"
|
||||
config.last_state_change_time = pygame.time.get_ticks()
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "cancel") or is_input_matched(event, "start"):
|
||||
config.menu_state = "pause_display_menu"
|
||||
config.last_state_change_time = pygame.time.get_ticks()
|
||||
config.needs_redraw = True
|
||||
|
||||
# Sous-menu Display > Font (tailles de police)
|
||||
elif config.menu_state == "pause_display_font_menu":
|
||||
sel = getattr(config, 'pause_display_font_selection', 0)
|
||||
total = 3 # font size, footer font size, back
|
||||
if is_input_matched(event, "up"):
|
||||
config.pause_display_font_selection = (sel - 1) % total
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "down"):
|
||||
config.pause_display_font_selection = (sel + 1) % total
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm"):
|
||||
# 0 font size
|
||||
if sel == 0 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
|
||||
opts = getattr(config, 'font_scale_options', [0.75,1.0,1.25,1.5,1.75])
|
||||
idx = getattr(config, 'current_font_scale_index', 1)
|
||||
idx = max(0, idx-1) if is_input_matched(event, "left") else min(len(opts)-1, idx+1)
|
||||
if idx != getattr(config, 'current_font_scale_index', 1):
|
||||
config.current_font_scale_index = idx
|
||||
scale = opts[idx]
|
||||
config.accessibility_settings["font_scale"] = scale
|
||||
try:
|
||||
save_accessibility_settings(config.accessibility_settings)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur sauvegarde accessibilité: {e}")
|
||||
try:
|
||||
config.init_font()
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur init polices: {e}")
|
||||
config.needs_redraw = True
|
||||
# 1 footer font size
|
||||
elif sel == 1 and (is_input_matched(event, "left") or is_input_matched(event, "right")):
|
||||
from accessibility import update_footer_font_scale
|
||||
footer_opts = getattr(config, 'footer_font_scale_options', [0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0])
|
||||
idx = getattr(config, 'current_footer_font_scale_index', 3)
|
||||
idx = max(0, idx-1) if is_input_matched(event, "left") else min(len(footer_opts)-1, idx+1)
|
||||
if idx != getattr(config, 'current_footer_font_scale_index', 3):
|
||||
config.current_footer_font_scale_index = idx
|
||||
try:
|
||||
update_footer_font_scale()
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur update footer font scale: {e}")
|
||||
config.needs_redraw = True
|
||||
# 2 back
|
||||
elif sel == 2 and is_input_matched(event, "confirm"):
|
||||
config.menu_state = "pause_display_menu"
|
||||
config.last_state_change_time = pygame.time.get_ticks()
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "cancel") or is_input_matched(event, "start"):
|
||||
config.menu_state = "pause_display_menu"
|
||||
config.last_state_change_time = pygame.time.get_ticks()
|
||||
config.needs_redraw = True
|
||||
|
||||
# Sous-menu Games
|
||||
elif config.menu_state == "pause_games_menu":
|
||||
sel = getattr(config, 'pause_games_selection', 0)
|
||||
@@ -2209,6 +2266,55 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
logger.debug("Retour à pause_menu depuis controls_mapping")
|
||||
|
||||
# Prompt de mise à jour automatique de la liste des jeux
|
||||
elif config.menu_state == "gamelist_update_prompt":
|
||||
if is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||
config.gamelist_update_selection = 1 - config.gamelist_update_selection
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "confirm"):
|
||||
if config.gamelist_update_selection == 1: # Oui
|
||||
logger.info("Utilisateur a accepté la mise à jour de la liste des jeux")
|
||||
# Lancer le téléchargement
|
||||
config.download_tasks.clear()
|
||||
config.pending_download = None
|
||||
if os.path.exists(config.SOURCES_FILE):
|
||||
try:
|
||||
if os.path.exists(config.SOURCES_FILE):
|
||||
os.remove(config.SOURCES_FILE)
|
||||
if os.path.exists(os.path.join(config.SAVE_FOLDER, "sources.json")):
|
||||
os.remove(os.path.join(config.SAVE_FOLDER, "sources.json"))
|
||||
if os.path.exists(config.GAMES_FOLDER):
|
||||
shutil.rmtree(config.GAMES_FOLDER)
|
||||
if os.path.exists(config.IMAGES_FOLDER):
|
||||
shutil.rmtree(config.IMAGES_FOLDER)
|
||||
# Mettre à jour la date
|
||||
from rgsx_settings import set_last_gamelist_update
|
||||
set_last_gamelist_update()
|
||||
config.menu_state = "restart_popup"
|
||||
config.popup_message = _("popup_gamelist_updating") if _ else "Updating game list... Restarting..."
|
||||
config.popup_timer = 2000
|
||||
config.needs_redraw = True
|
||||
restart_application(2000)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la mise à jour: {e}")
|
||||
config.menu_state = "loading"
|
||||
config.needs_redraw = True
|
||||
else:
|
||||
# Pas de cache existant, juste mettre à jour la date et continuer
|
||||
from rgsx_settings import set_last_gamelist_update
|
||||
set_last_gamelist_update()
|
||||
config.menu_state = "loading"
|
||||
config.needs_redraw = True
|
||||
else: # Non
|
||||
logger.info("Utilisateur a refusé la mise à jour de la liste des jeux")
|
||||
# Ne pas mettre à jour la date pour redemander plus tard
|
||||
config.menu_state = "platform"
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "cancel"):
|
||||
logger.info("Utilisateur a annulé le prompt de mise à jour")
|
||||
config.menu_state = "platform"
|
||||
config.needs_redraw = True
|
||||
|
||||
# Redownload game cache
|
||||
elif config.menu_state == "reload_games_data":
|
||||
if is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||
@@ -2234,6 +2340,9 @@ def handle_controls(event, sources, joystick, screen):
|
||||
if os.path.exists(config.IMAGES_FOLDER):
|
||||
shutil.rmtree(config.IMAGES_FOLDER)
|
||||
logger.debug("Dossier images supprimé avec succès")
|
||||
# Mettre à jour la date de dernière mise à jour
|
||||
from rgsx_settings import set_last_gamelist_update
|
||||
set_last_gamelist_update()
|
||||
config.menu_state = "restart_popup"
|
||||
config.popup_message = _("popup_redownload_success")
|
||||
config.popup_timer = 2000 # bref message
|
||||
|
||||
@@ -799,6 +799,10 @@ def draw_platform_grid(screen):
|
||||
"""Affiche la grille des plateformes avec un style moderne et fluide."""
|
||||
global platform_images_cache
|
||||
|
||||
# Vérifier si le mode performance est activé
|
||||
from rgsx_settings import get_light_mode
|
||||
light_mode = get_light_mode()
|
||||
|
||||
if not config.platforms or config.selected_platform >= len(config.platforms):
|
||||
platform_name = _("platform_no_platform")
|
||||
logger.warning("Aucune plateforme ou selected_platform hors limites")
|
||||
@@ -827,35 +831,45 @@ def draw_platform_grid(screen):
|
||||
|
||||
# Effet de pulsation subtil pour le titre - calculé une seule fois par frame
|
||||
current_time = pygame.time.get_ticks()
|
||||
pulse_factor = 0.08 * (1 + math.sin(current_time / 400))
|
||||
|
||||
# Ombre portée pour le titre
|
||||
shadow_surf = pygame.Surface((title_rect_inflated.width + 12, title_rect_inflated.height + 12), pygame.SRCALPHA)
|
||||
pygame.draw.rect(shadow_surf, (0, 0, 0, 140), (6, 6, title_rect_inflated.width, title_rect_inflated.height), border_radius=16)
|
||||
screen.blit(shadow_surf, (title_rect_inflated.left - 6, title_rect_inflated.top - 6))
|
||||
if not light_mode:
|
||||
# Mode normal : effets visuels complets
|
||||
pulse_factor = 0.08 * (1 + math.sin(current_time / 400))
|
||||
|
||||
# Ombre portée pour le titre
|
||||
shadow_surf = pygame.Surface((title_rect_inflated.width + 12, title_rect_inflated.height + 12), pygame.SRCALPHA)
|
||||
pygame.draw.rect(shadow_surf, (0, 0, 0, 140), (6, 6, title_rect_inflated.width, title_rect_inflated.height), border_radius=16)
|
||||
screen.blit(shadow_surf, (title_rect_inflated.left - 6, title_rect_inflated.top - 6))
|
||||
|
||||
# Glow multicouche pour le titre
|
||||
for i in range(2):
|
||||
glow_size = title_rect_inflated.inflate(15 + i * 8, 15 + i * 8)
|
||||
title_glow = pygame.Surface((glow_size.width, glow_size.height), pygame.SRCALPHA)
|
||||
alpha = int((30 + 20 * pulse_factor) * (1 - i / 2))
|
||||
pygame.draw.rect(title_glow, (*THEME_COLORS["neon"][:3], alpha),
|
||||
title_glow.get_rect(), border_radius=16 + i * 2)
|
||||
screen.blit(title_glow, (title_rect_inflated.left - 8 - i * 4, title_rect_inflated.top - 8 - i * 4))
|
||||
|
||||
# Fond du titre avec dégradé
|
||||
title_bg = pygame.Surface((title_rect_inflated.width, title_rect_inflated.height), pygame.SRCALPHA)
|
||||
for i in range(title_rect_inflated.height):
|
||||
ratio = i / title_rect_inflated.height
|
||||
alpha = int(THEME_COLORS["button_idle"][3] * (1 + ratio * 0.1))
|
||||
pygame.draw.line(title_bg, (*THEME_COLORS["button_idle"][:3], alpha),
|
||||
(0, i), (title_rect_inflated.width, i))
|
||||
screen.blit(title_bg, title_rect_inflated.topleft)
|
||||
|
||||
# Reflet en haut du titre
|
||||
highlight = pygame.Surface((title_rect_inflated.width - 8, title_rect_inflated.height // 3), pygame.SRCALPHA)
|
||||
highlight.fill((255, 255, 255, 25))
|
||||
screen.blit(highlight, (title_rect_inflated.left + 4, title_rect_inflated.top + 4))
|
||||
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=14)
|
||||
else:
|
||||
# Mode performance : rendu simplifié
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_idle"], title_rect_inflated, border_radius=14)
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=14)
|
||||
|
||||
# Glow multicouche pour le titre
|
||||
for i in range(2):
|
||||
glow_size = title_rect_inflated.inflate(15 + i * 8, 15 + i * 8)
|
||||
title_glow = pygame.Surface((glow_size.width, glow_size.height), pygame.SRCALPHA)
|
||||
alpha = int((30 + 20 * pulse_factor) * (1 - i / 2))
|
||||
pygame.draw.rect(title_glow, (*THEME_COLORS["neon"][:3], alpha),
|
||||
title_glow.get_rect(), border_radius=16 + i * 2)
|
||||
screen.blit(title_glow, (title_rect_inflated.left - 8 - i * 4, title_rect_inflated.top - 8 - i * 4)) # Fond du titre avec dégradé
|
||||
title_bg = pygame.Surface((title_rect_inflated.width, title_rect_inflated.height), pygame.SRCALPHA)
|
||||
for i in range(title_rect_inflated.height):
|
||||
ratio = i / title_rect_inflated.height
|
||||
alpha = int(THEME_COLORS["button_idle"][3] * (1 + ratio * 0.1))
|
||||
pygame.draw.line(title_bg, (*THEME_COLORS["button_idle"][:3], alpha),
|
||||
(0, i), (title_rect_inflated.width, i))
|
||||
screen.blit(title_bg, title_rect_inflated.topleft)
|
||||
|
||||
# Reflet en haut du titre
|
||||
highlight = pygame.Surface((title_rect_inflated.width - 8, title_rect_inflated.height // 3), pygame.SRCALPHA)
|
||||
highlight.fill((255, 255, 255, 25))
|
||||
screen.blit(highlight, (title_rect_inflated.left + 4, title_rect_inflated.top + 4))
|
||||
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], title_rect_inflated, 2, border_radius=14)
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
# Configuration de la grille - calculée une seule fois
|
||||
@@ -920,8 +934,12 @@ def draw_platform_grid(screen):
|
||||
screen.blit(page_indicator, page_rect)
|
||||
|
||||
# Calculer une seule fois la pulsation pour les éléments sélectionnés (réduite)
|
||||
pulse = 0.05 * math.sin(current_time / 300) # Réduit de 0.1 à 0.05
|
||||
glow_intensity = 40 + int(30 * math.sin(current_time / 300))
|
||||
if not light_mode:
|
||||
pulse = 0.05 * math.sin(current_time / 300) # Réduit de 0.1 à 0.05
|
||||
glow_intensity = 40 + int(30 * math.sin(current_time / 300))
|
||||
else:
|
||||
pulse = 0
|
||||
glow_intensity = 0
|
||||
|
||||
# Pré-calcul des images pour optimiser le rendu
|
||||
start_idx = config.current_page * systems_per_page
|
||||
@@ -936,8 +954,14 @@ def draw_platform_grid(screen):
|
||||
|
||||
# Animation fluide pour l'item sélectionné (réduite pour éviter chevauchement)
|
||||
is_selected = idx == config.selected_platform
|
||||
scale_base = 1.15 if is_selected else 1.0 # Réduit de 1.5 à 1.15
|
||||
scale = scale_base + pulse if is_selected else scale_base
|
||||
if light_mode:
|
||||
# Mode performance : pas d'animation, taille fixe
|
||||
scale_base = 1.0
|
||||
scale = 1.0
|
||||
else:
|
||||
# Mode normal : animation réduite
|
||||
scale_base = 1.15 if is_selected else 1.0 # Réduit de 1.5 à 1.15
|
||||
scale = scale_base + pulse if is_selected else scale_base
|
||||
|
||||
# Récupération robuste du dict via nom
|
||||
display_name = visible_platforms[idx]
|
||||
@@ -1006,6 +1030,7 @@ def draw_platform_grid(screen):
|
||||
|
||||
image_rect = scaled_image.get_rect(center=(x, y))
|
||||
|
||||
|
||||
# Effet visuel moderne similaire au titre pour toutes les images
|
||||
border_radius = 12
|
||||
padding = 12
|
||||
@@ -1018,57 +1043,80 @@ def draw_platform_grid(screen):
|
||||
container_left = x - rect_width // 2
|
||||
container_top = y - rect_height // 2
|
||||
|
||||
# Ombre portée
|
||||
shadow_surf = pygame.Surface((rect_width + 12, rect_height + 12), pygame.SRCALPHA)
|
||||
pygame.draw.rect(shadow_surf, (0, 0, 0, 160), (6, 6, rect_width, rect_height), border_radius=border_radius + 4)
|
||||
screen.blit(shadow_surf, (container_left - 6, container_top - 6))
|
||||
|
||||
# Effet de glow multicouche pour l'item sélectionné
|
||||
if is_selected:
|
||||
neon_color = THEME_COLORS["neon"]
|
||||
if not light_mode:
|
||||
# Mode normal : effets visuels complets
|
||||
# Ombre portée
|
||||
shadow_surf = pygame.Surface((rect_width + 12, rect_height + 12), pygame.SRCALPHA)
|
||||
pygame.draw.rect(shadow_surf, (0, 0, 0, 160), (6, 6, rect_width, rect_height), border_radius=border_radius + 4)
|
||||
screen.blit(shadow_surf, (container_left - 6, container_top - 6))
|
||||
|
||||
# Glow multicouche (2 couches pour effet profondeur)
|
||||
for i in range(2):
|
||||
glow_size = (rect_width + 15 + i * 8, rect_height + 15 + i * 8)
|
||||
glow_surf = pygame.Surface(glow_size, pygame.SRCALPHA)
|
||||
alpha = int((glow_intensity + 40) * (1 - i / 2))
|
||||
pygame.draw.rect(glow_surf, neon_color + (alpha,), glow_surf.get_rect(), border_radius=border_radius + i * 2)
|
||||
screen.blit(glow_surf, (container_left - 8 - i * 4, container_top - 8 - i * 4))
|
||||
# Effet de glow multicouche pour l'item sélectionné
|
||||
if is_selected:
|
||||
neon_color = THEME_COLORS["neon"]
|
||||
|
||||
# Glow multicouche (2 couches pour effet profondeur)
|
||||
for i in range(2):
|
||||
glow_size = (rect_width + 15 + i * 8, rect_height + 15 + i * 8)
|
||||
glow_surf = pygame.Surface(glow_size, pygame.SRCALPHA)
|
||||
alpha = int((glow_intensity + 40) * (1 - i / 2))
|
||||
pygame.draw.rect(glow_surf, neon_color + (alpha,), glow_surf.get_rect(), border_radius=border_radius + i * 2)
|
||||
screen.blit(glow_surf, (container_left - 8 - i * 4, container_top - 8 - i * 4))
|
||||
|
||||
# Fond avec dégradé vertical (similaire au titre)
|
||||
bg_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA)
|
||||
base_color = THEME_COLORS["button_idle"] if is_selected else THEME_COLORS["fond_image"]
|
||||
|
||||
for i in range(rect_height):
|
||||
ratio = i / rect_height
|
||||
# Dégradé du haut (plus clair) vers le bas (plus foncé)
|
||||
alpha = int(base_color[3] * (1 + ratio * 0.15)) if len(base_color) > 3 else int(200 * (1 + ratio * 0.15))
|
||||
color = (*base_color[:3], min(255, alpha))
|
||||
pygame.draw.line(bg_surface, color, (0, i), (rect_width, i))
|
||||
|
||||
screen.blit(bg_surface, (container_left, container_top))
|
||||
|
||||
# Reflet en haut (highlight pour effet glossy)
|
||||
highlight_height = rect_height // 3
|
||||
highlight = pygame.Surface((rect_width - 8, highlight_height), pygame.SRCALPHA)
|
||||
highlight.fill((255, 255, 255, 35 if is_selected else 20))
|
||||
screen.blit(highlight, (container_left + 4, container_top + 4))
|
||||
else:
|
||||
# Mode performance : fond simple sans effets
|
||||
bg_color = THEME_COLORS["button_idle"] if is_selected else THEME_COLORS["fond_image"]
|
||||
pygame.draw.rect(screen, bg_color, (container_left, container_top, rect_width, rect_height), border_radius=border_radius)
|
||||
|
||||
# Fond avec dégradé vertical (similaire au titre)
|
||||
bg_surface = pygame.Surface((rect_width, rect_height), pygame.SRCALPHA)
|
||||
base_color = THEME_COLORS["button_idle"] if is_selected else THEME_COLORS["fond_image"]
|
||||
# Bordure
|
||||
if light_mode and is_selected:
|
||||
# Mode performance : bordure épaisse et très visible pour l'item sélectionné
|
||||
border_color = THEME_COLORS["neon"] # Couleur verte bien visible
|
||||
border_width = 4 # Bordure plus épaisse
|
||||
elif not light_mode and is_selected:
|
||||
# Mode normal : bordure neon
|
||||
border_color = THEME_COLORS["neon"]
|
||||
border_width = 2
|
||||
else:
|
||||
# Non sélectionné : bordure standard
|
||||
border_color = THEME_COLORS["border"]
|
||||
border_width = 2
|
||||
|
||||
for i in range(rect_height):
|
||||
ratio = i / rect_height
|
||||
# Dégradé du haut (plus clair) vers le bas (plus foncé)
|
||||
alpha = int(base_color[3] * (1 + ratio * 0.15)) if len(base_color) > 3 else int(200 * (1 + ratio * 0.15))
|
||||
color = (*base_color[:3], min(255, alpha))
|
||||
pygame.draw.line(bg_surface, color, (0, i), (rect_width, i))
|
||||
|
||||
screen.blit(bg_surface, (container_left, container_top))
|
||||
|
||||
# Reflet en haut (highlight pour effet glossy)
|
||||
highlight_height = rect_height // 3
|
||||
highlight = pygame.Surface((rect_width - 8, highlight_height), pygame.SRCALPHA)
|
||||
highlight.fill((255, 255, 255, 35 if is_selected else 20))
|
||||
screen.blit(highlight, (container_left + 4, container_top + 4))
|
||||
|
||||
# Bordure avec effet 3D
|
||||
border_color = THEME_COLORS["neon"] if is_selected else THEME_COLORS["border"]
|
||||
border_rect = pygame.Rect(container_left, container_top, rect_width, rect_height)
|
||||
pygame.draw.rect(screen, border_color, border_rect, 2, border_radius=border_radius)
|
||||
pygame.draw.rect(screen, border_color, border_rect, border_width, border_radius=border_radius)
|
||||
|
||||
# Centrer l'image dans le container (l'image peut être plus petite que le container)
|
||||
centered_image_rect = scaled_image.get_rect(center=(x, y))
|
||||
|
||||
# Affichage de l'image avec un léger effet de transparence pour les items non sélectionnés
|
||||
if not is_selected:
|
||||
temp_image = scaled_image.copy()
|
||||
temp_image.set_alpha(220)
|
||||
screen.blit(temp_image, centered_image_rect)
|
||||
else:
|
||||
# Affichage de l'image
|
||||
if light_mode:
|
||||
# Mode performance : pas d'effet de transparence
|
||||
screen.blit(scaled_image, centered_image_rect)
|
||||
else:
|
||||
# Mode normal : effet de transparence pour les items non sélectionnés
|
||||
if not is_selected:
|
||||
temp_image = scaled_image.copy()
|
||||
temp_image.set_alpha(220)
|
||||
screen.blit(temp_image, centered_image_rect)
|
||||
else:
|
||||
screen.blit(scaled_image, centered_image_rect)
|
||||
|
||||
# Nettoyer le cache périodiquement (garder seulement les images utilisées récemment)
|
||||
if len(platform_images_cache) > 50: # Limite arbitraire pour éviter une croissance excessive
|
||||
@@ -2405,14 +2453,8 @@ def draw_pause_controls_menu(screen, selected_index):
|
||||
draw_menu_instruction(screen, text, last_button_bottom)
|
||||
|
||||
def draw_pause_display_menu(screen, selected_index):
|
||||
# Layout label
|
||||
layouts = [(3,3),(3,4),(4,3),(4,4)]
|
||||
try:
|
||||
idx = layouts.index((config.GRID_COLS, config.GRID_ROWS))
|
||||
except ValueError:
|
||||
idx = 0
|
||||
layout_value = f"{layouts[idx][0]}x{layouts[idx][1]}"
|
||||
layout_txt = f"{_('submenu_display_layout') if _ else 'Layout'}: < {layout_value} >"
|
||||
# Layout label - now opens a submenu
|
||||
layout_txt = f"{_('submenu_display_layout') if _ else 'Layout'} >"
|
||||
# Font size
|
||||
opts = getattr(config, 'font_scale_options', [0.75, 1.0, 1.25, 1.5, 1.75])
|
||||
cur_idx = getattr(config, 'current_font_scale_index', 1)
|
||||
@@ -2433,16 +2475,16 @@ def draw_pause_display_menu(screen, selected_index):
|
||||
fam_label = family_map.get(current_family, current_family)
|
||||
font_family_txt = f"{_('submenu_display_font_family') if _ else 'Font'}: < {fam_label} >"
|
||||
|
||||
# Monitor selection
|
||||
# Monitor selection - only show if multiple monitors
|
||||
current_monitor = get_display_monitor()
|
||||
monitors = get_available_monitors()
|
||||
num_monitors = len(monitors)
|
||||
if num_monitors > 1:
|
||||
show_monitor_option = num_monitors > 1
|
||||
|
||||
if show_monitor_option:
|
||||
monitor_info = monitors[current_monitor] if current_monitor < num_monitors else monitors[0]
|
||||
monitor_value = f"{monitor_info['name']} ({monitor_info['resolution']})"
|
||||
else:
|
||||
monitor_value = _('display_monitor_single') if _ else "Single monitor"
|
||||
monitor_txt = f"{_('display_monitor') if _ else 'Monitor'}: < {monitor_value} >"
|
||||
monitor_txt = f"{_('display_monitor') if _ else 'Monitor'}: < {monitor_value} >"
|
||||
|
||||
# Allow unknown extensions
|
||||
allow_unknown = get_allow_unknown_extensions()
|
||||
@@ -2459,19 +2501,26 @@ def draw_pause_display_menu(screen, selected_index):
|
||||
|
||||
back_txt = _("menu_back") if _ else "Back"
|
||||
|
||||
# Build options list - same for all platforms
|
||||
# layout, font, footer, family, monitor, light, unknown, back (8)
|
||||
options = [layout_txt, font_txt, footer_font_txt, font_family_txt, monitor_txt, light_txt, unknown_txt, back_txt]
|
||||
# Build options list - conditional monitor option
|
||||
# layout, font submenu, family, [monitor if multi], light, unknown, back
|
||||
font_submenu_txt = f"{_('submenu_display_font_size') if _ else 'Font Size'} >"
|
||||
options = [layout_txt, font_submenu_txt, font_family_txt]
|
||||
instruction_keys = [
|
||||
"instruction_display_layout",
|
||||
"instruction_display_font_size",
|
||||
"instruction_display_footer_font_size",
|
||||
"instruction_display_font_family",
|
||||
"instruction_display_monitor",
|
||||
]
|
||||
|
||||
if show_monitor_option:
|
||||
options.append(monitor_txt)
|
||||
instruction_keys.append("instruction_display_monitor")
|
||||
|
||||
options.extend([light_txt, unknown_txt, back_txt])
|
||||
instruction_keys.extend([
|
||||
"instruction_display_light_mode",
|
||||
"instruction_display_unknown_ext",
|
||||
"instruction_generic_back",
|
||||
]
|
||||
])
|
||||
|
||||
_draw_submenu_generic(screen, _("menu_display"), options, selected_index)
|
||||
key = instruction_keys[selected_index] if 0 <= selected_index < len(instruction_keys) else None
|
||||
@@ -2487,6 +2536,155 @@ def draw_pause_display_menu(screen, selected_index):
|
||||
last_button_bottom = start_y + (len(options)-1) * (button_height + 10) + button_height
|
||||
draw_menu_instruction(screen, _(key), last_button_bottom)
|
||||
|
||||
def draw_pause_display_layout_menu(screen, selected_index):
|
||||
"""Sous-menu pour la disposition avec visualisation schématique des grilles."""
|
||||
layouts = [(3,3),(3,4),(4,3),(4,4)]
|
||||
layout_labels = ["3x3", "3x4", "4x3", "4x4"]
|
||||
|
||||
# Trouver le layout actuel
|
||||
try:
|
||||
current_idx = layouts.index((config.GRID_COLS, config.GRID_ROWS))
|
||||
except ValueError:
|
||||
current_idx = 0
|
||||
|
||||
# Créer les options avec indicateur du layout actuel
|
||||
options = []
|
||||
for i, label in enumerate(layout_labels):
|
||||
if i == current_idx:
|
||||
options.append(f"{label} [CURRENT]" if not _ else f"{label} [{_('status_current') if _ else 'ACTUEL'}]")
|
||||
else:
|
||||
options.append(label)
|
||||
options.append(_("menu_back") if _ else "Back")
|
||||
|
||||
# Dessiner le menu de base
|
||||
title = _("submenu_display_layout") if _ else "Layout"
|
||||
|
||||
# Calculer les dimensions
|
||||
button_height = int(config.screen_height * 0.045)
|
||||
menu_width = int(config.screen_width * 0.72)
|
||||
margin_top_bottom = 26
|
||||
|
||||
# Calculer la hauteur nécessaire pour les boutons
|
||||
menu_height = (len(options)+1) * (button_height + 10) + 2 * margin_top_bottom
|
||||
menu_x = (config.screen_width - menu_width) // 2
|
||||
menu_y = (config.screen_height - menu_height) // 2
|
||||
|
||||
# Fond du menu
|
||||
menu_rect = pygame.Rect(menu_x, menu_y, menu_width, menu_height)
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_idle"], menu_rect, border_radius=14)
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], menu_rect, 3, border_radius=14)
|
||||
|
||||
# Titre
|
||||
title_surface = config.font.render(title, True, THEME_COLORS["text"])
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, menu_y + margin_top_bottom//2 + title_surface.get_height()//2))
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
# Position de départ pour le contenu
|
||||
content_start_y = title_rect.bottom + 20
|
||||
|
||||
# Division en deux colonnes : gauche pour la grille, droite pour les options
|
||||
left_column_x = menu_x + 20
|
||||
left_column_width = int(menu_width * 0.4)
|
||||
right_column_x = left_column_x + left_column_width + 20
|
||||
right_column_width = menu_width - left_column_width - 60
|
||||
|
||||
# COLONNE GAUCHE : Dessiner uniquement la grille sélectionnée
|
||||
if selected_index < len(layouts):
|
||||
cols, rows = layouts[selected_index]
|
||||
|
||||
# Calculer la taille des cellules pour le schéma
|
||||
cell_size = min(60, (left_column_width - 20) // max(cols, rows))
|
||||
grid_width = cols * cell_size
|
||||
grid_height = rows * cell_size
|
||||
|
||||
# Centrer la grille verticalement dans l'espace disponible
|
||||
available_height = (len(options) * (button_height + 10)) - 10
|
||||
grid_x = left_column_x + (left_column_width - grid_width) // 2
|
||||
grid_y = content_start_y + (available_height - grid_height) // 2
|
||||
|
||||
# Dessiner le schéma de la grille sélectionnée
|
||||
for row in range(rows):
|
||||
for col in range(cols):
|
||||
cell_rect = pygame.Rect(
|
||||
grid_x + col * cell_size,
|
||||
grid_y + row * cell_size,
|
||||
cell_size - 3,
|
||||
cell_size - 3
|
||||
)
|
||||
# Couleur selon si c'est aussi le layout actuel
|
||||
if selected_index == current_idx:
|
||||
# Sélectionné ET actuel : vert brillant
|
||||
pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], cell_rect)
|
||||
pygame.draw.rect(screen, THEME_COLORS["text"], cell_rect, 2)
|
||||
else:
|
||||
# Seulement sélectionné : bleu clair
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_selected"], cell_rect)
|
||||
pygame.draw.rect(screen, THEME_COLORS["text"], cell_rect, 2)
|
||||
|
||||
# COLONNE DROITE : Dessiner les boutons d'options
|
||||
for i, option in enumerate(options):
|
||||
button_x = right_column_x
|
||||
button_y = content_start_y + i * (button_height + 10)
|
||||
button_width = right_column_width
|
||||
|
||||
button_rect = pygame.Rect(button_x, button_y, button_width, button_height)
|
||||
|
||||
if i == selected_index:
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_selected"], button_rect, border_radius=8)
|
||||
else:
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_idle"], button_rect, border_radius=8)
|
||||
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], button_rect, 2, border_radius=8)
|
||||
|
||||
text_surface = config.font.render(option, True, THEME_COLORS["text"])
|
||||
text_rect = text_surface.get_rect(center=button_rect.center)
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# Instruction en bas
|
||||
last_button_bottom = content_start_y + (len(options)-1) * (button_height + 10) + button_height
|
||||
if selected_index < len(layouts):
|
||||
instruction = _("instruction_display_layout") if _ else "Left/Right: Navigate • Confirm: Select"
|
||||
else:
|
||||
instruction = _("instruction_generic_back") if _ else "Confirm: Go back"
|
||||
draw_menu_instruction(screen, instruction, last_button_bottom)
|
||||
|
||||
def draw_pause_display_font_menu(screen, selected_index):
|
||||
"""Sous-menu pour les tailles de police."""
|
||||
# Font size
|
||||
opts = getattr(config, 'font_scale_options', [0.75, 1.0, 1.25, 1.5, 1.75])
|
||||
cur_idx = getattr(config, 'current_font_scale_index', 1)
|
||||
font_value = f"{opts[cur_idx]}x"
|
||||
font_txt = f"{_('submenu_display_font_size') if _ else 'Font Size'}: < {font_value} >"
|
||||
|
||||
# Footer font size
|
||||
footer_opts = getattr(config, 'footer_font_scale_options', [0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0])
|
||||
footer_cur_idx = getattr(config, 'current_footer_font_scale_index', 3)
|
||||
footer_font_value = f"{footer_opts[footer_cur_idx]}x"
|
||||
footer_font_txt = f"{_('accessibility_footer_font_size').split(':')[0] if _ else 'Footer Font Size'}: < {footer_font_value} >"
|
||||
|
||||
back_txt = _("menu_back") if _ else "Back"
|
||||
|
||||
options = [font_txt, footer_font_txt, back_txt]
|
||||
instruction_keys = [
|
||||
"instruction_display_font_size",
|
||||
"instruction_display_footer_font_size",
|
||||
"instruction_generic_back",
|
||||
]
|
||||
|
||||
_draw_submenu_generic(screen, _("submenu_display_font_size") if _ else "Font Size", options, selected_index)
|
||||
key = instruction_keys[selected_index] if 0 <= selected_index < len(instruction_keys) else None
|
||||
if key:
|
||||
button_height = int(config.screen_height * 0.045)
|
||||
menu_width = int(config.screen_width * 0.72)
|
||||
margin_top_bottom = 26
|
||||
menu_height = (len(options)+1) * (button_height + 10) + 2 * margin_top_bottom
|
||||
menu_y = (config.screen_height - menu_height) // 2
|
||||
title_surface = config.font.render("X", True, THEME_COLORS["text"])
|
||||
title_rect_height = title_surface.get_height()
|
||||
start_y = menu_y + margin_top_bottom//2 + title_rect_height + 10 + 10
|
||||
last_button_bottom = start_y + (len(options)-1) * (button_height + 10) + button_height
|
||||
draw_menu_instruction(screen, _(key), last_button_bottom)
|
||||
|
||||
def draw_pause_games_menu(screen, selected_index):
|
||||
mode = get_sources_mode()
|
||||
source_label = _("games_source_rgsx") if mode == "rgsx" else _("games_source_custom")
|
||||
@@ -3082,6 +3280,54 @@ def draw_reload_games_data_dialog(screen):
|
||||
draw_stylized_button(screen, _("button_no"), no_x, buttons_y, button_width, button_height, selected=config.redownload_confirm_selection == 0)
|
||||
|
||||
|
||||
def draw_gamelist_update_prompt(screen):
|
||||
"""Affiche la boîte de dialogue pour proposer la mise à jour de la liste des jeux."""
|
||||
global OVERLAY
|
||||
if OVERLAY is None or OVERLAY.get_size() != (config.screen_width, config.screen_height):
|
||||
OVERLAY = pygame.Surface((config.screen_width, config.screen_height), pygame.SRCALPHA)
|
||||
OVERLAY.fill((0, 0, 0, 150))
|
||||
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
from config import GAMELIST_UPDATE_DAYS
|
||||
from rgsx_settings import get_last_gamelist_update
|
||||
|
||||
last_update = get_last_gamelist_update()
|
||||
if last_update:
|
||||
message = _("gamelist_update_prompt_with_date").format(GAMELIST_UPDATE_DAYS, last_update) if _ else f"Game list hasn't been updated for more than {GAMELIST_UPDATE_DAYS} days (last update: {last_update}). Download the latest version?"
|
||||
else:
|
||||
message = _("gamelist_update_prompt_first_time") if _ else "Would you like to download the latest game list?"
|
||||
|
||||
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 80)
|
||||
line_height = config.small_font.get_height() + 5
|
||||
text_height = len(wrapped_message) * line_height
|
||||
|
||||
sample_text = config.small_font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
button_height = max(int(config.screen_height * 0.0463), font_height + 15)
|
||||
margin_top_bottom = 20
|
||||
rect_height = text_height + button_height + 2 * margin_top_bottom
|
||||
max_text_width = max([config.small_font.size(line)[0] for line in wrapped_message], default=300)
|
||||
rect_width = max_text_width + 80
|
||||
rect_x = (config.screen_width - rect_width) // 2
|
||||
rect_y = (config.screen_height - rect_height) // 2
|
||||
|
||||
pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12)
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12)
|
||||
|
||||
for i, line in enumerate(wrapped_message):
|
||||
text = config.small_font.render(line, True, THEME_COLORS["text"])
|
||||
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)
|
||||
|
||||
button_width = min(160, (rect_width - 60) // 2)
|
||||
yes_x = rect_x + rect_width // 2 - button_width - 10
|
||||
no_x = rect_x + rect_width // 2 + 10
|
||||
buttons_y = rect_y + text_height + margin_top_bottom
|
||||
draw_stylized_button(screen, _("button_yes"), yes_x, buttons_y, button_width, button_height, selected=config.gamelist_update_selection == 1)
|
||||
draw_stylized_button(screen, _("button_no"), no_x, buttons_y, button_width, button_height, selected=config.gamelist_update_selection == 0)
|
||||
|
||||
|
||||
def draw_support_dialog(screen):
|
||||
"""Affiche la boîte de dialogue du fichier de support généré."""
|
||||
global OVERLAY
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"confirm_exit_with_downloads": "Achtung: {0} Download(s) laufen. Trotzdem beenden?",
|
||||
"confirm_clear_history": "Verlauf löschen?",
|
||||
"confirm_redownload_cache": "Spieleliste aktualisieren?",
|
||||
"gamelist_update_prompt_with_date": "Die Spieleliste wurde seit mehr als {0} Tagen nicht aktualisiert (letzte Aktualisierung: {1}). Die neueste Version herunterladen?",
|
||||
"gamelist_update_prompt_first_time": "Möchten Sie die neueste Spieleliste herunterladen?",
|
||||
"popup_redownload_success": "Cache gelöscht, bitte die Anwendung neu starten",
|
||||
"popup_no_cache": "Kein Cache gefunden.\nBitte starte die Anwendung neu, um die Spiele zu laden.",
|
||||
"popup_countdown": "Diese Nachricht schließt in {0} Sekunde{1}",
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"confirm_exit_with_downloads": "Attention: {0} download(s) in progress. Quit anyway?",
|
||||
"confirm_clear_history": "Clear history?",
|
||||
"confirm_redownload_cache": "Update games list?",
|
||||
"gamelist_update_prompt_with_date": "Game list hasn't been updated for more than {0} days (last update: {1}). Download the latest version?",
|
||||
"gamelist_update_prompt_first_time": "Would you like to download the latest game list?",
|
||||
"popup_redownload_success": "Cache cleared, please restart the application",
|
||||
"popup_no_cache": "No cache found.\nPlease restart the application to load games.",
|
||||
"popup_countdown": "This message will close in {0} second{1}",
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
"confirm_exit": "¿Salir de la aplicación?",
|
||||
"confirm_exit_with_downloads": "Atención: {0} descarga(s) en curso. ¿Salir de todas formas?",
|
||||
"confirm_clear_history": "¿Vaciar el historial?",
|
||||
"confirm_redownload_cache": "¿Actualizar la lista de juegos?",
|
||||
"popup_redownload_success": "Caché borrada, por favor reinicia la aplicación",
|
||||
"confirm_redownload_cache": "¿Actualizar la lista de juegos?", "gamelist_update_prompt_with_date": "La lista de juegos no se ha actualizado durante más de {0} días (última actualización: {1}). ¿Descargar la última versión?",
|
||||
"gamelist_update_prompt_first_time": "¿Desea descargar la última lista de juegos?", "popup_redownload_success": "Caché borrada, por favor reinicia la aplicación",
|
||||
"popup_no_cache": "No se encontró caché.\nPor favor, reinicia la aplicación para cargar los juegos.",
|
||||
"popup_countdown": "Este mensaje se cerrará en {0} segundo{1}",
|
||||
"language_select_title": "Selección de idioma",
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"confirm_exit_with_downloads": "Attention : {0} téléchargement(s) en cours. Quitter quand même ?",
|
||||
"confirm_clear_history": "Vider l'historique ?",
|
||||
"confirm_redownload_cache": "Mettre à jour la liste des jeux ?",
|
||||
"gamelist_update_prompt_with_date": "La liste des jeux n'a pas été mise à jour depuis plus de {0} jours (dernière mise à jour : {1}). Télécharger la dernière version ?",
|
||||
"gamelist_update_prompt_first_time": "Souhaitez-vous télécharger la dernière liste des jeux ?",
|
||||
"popup_redownload_success": "Le cache a été effacé, merci de relancer l'application",
|
||||
"popup_no_cache": "Aucun cache trouvé.\nVeuillez redémarrer l'application pour charger les jeux.",
|
||||
"popup_countdown": "Ce message se fermera dans {0} seconde{1}",
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
"confirm_exit": "Uscire dall'applicazione?",
|
||||
"confirm_exit_with_downloads": "Attenzione: {0} download in corso. Uscire comunque?",
|
||||
"confirm_clear_history": "Cancellare la cronologia?",
|
||||
"confirm_redownload_cache": "Aggiornare l'elenco dei giochi?",
|
||||
"popup_redownload_success": "Cache pulita, riavvia l'applicazione",
|
||||
"confirm_redownload_cache": "Aggiornare l'elenco dei giochi?", "gamelist_update_prompt_with_date": "L'elenco dei giochi non è stato aggiornato da più di {0} giorni (ultimo aggiornamento: {1}). Scaricare l'ultima versione?",
|
||||
"gamelist_update_prompt_first_time": "Vuoi scaricare l'ultimo elenco dei giochi?", "popup_redownload_success": "Cache pulita, riavvia l'applicazione",
|
||||
"popup_no_cache": "Nessuna cache trovata.\nRiavvia l'applicazione per caricare i giochi.",
|
||||
"popup_countdown": "Questo messaggio si chiuderà tra {0} secondo{1}",
|
||||
"language_select_title": "Selezione lingua",
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"confirm_exit_with_downloads": "Atenção: {0} download(s) em andamento. Sair mesmo assim?",
|
||||
"confirm_clear_history": "Limpar histórico?",
|
||||
"confirm_redownload_cache": "Atualizar lista de jogos?",
|
||||
"gamelist_update_prompt_with_date": "A lista de jogos não foi atualizada há mais de {0} dias (última atualização: {1}). Baixar a versão mais recente?",
|
||||
"gamelist_update_prompt_first_time": "Gostaria de baixar a última lista de jogos?",
|
||||
"popup_redownload_success": "Cache limpo, reinicie a aplicação",
|
||||
"popup_no_cache": "Nenhum cache encontrado.\nReinicie a aplicação para carregar os jogos.",
|
||||
"popup_countdown": "Esta mensagem fechará em {0} segundo{1}",
|
||||
|
||||
@@ -76,7 +76,8 @@ def load_rgsx_settings():
|
||||
"show_unsupported_platforms": False,
|
||||
"allow_unknown_extensions": False,
|
||||
"roms_folder": "",
|
||||
"web_service_at_boot": False
|
||||
"web_service_at_boot": False,
|
||||
"last_gamelist_update": None
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -110,6 +111,27 @@ def save_rgsx_settings(settings):
|
||||
print(f"Erreur lors de la sauvegarde de rgsx_settings.json: {str(e)}")
|
||||
|
||||
|
||||
def get_last_gamelist_update(settings=None):
|
||||
"""Récupère la date de dernière mise à jour de la liste des jeux."""
|
||||
if settings is None:
|
||||
settings = load_rgsx_settings()
|
||||
return settings.get("last_gamelist_update", None)
|
||||
|
||||
|
||||
def set_last_gamelist_update(date_string=None):
|
||||
"""Définit la date de dernière mise à jour de la liste des jeux.
|
||||
Si date_string est None, utilise la date actuelle.
|
||||
"""
|
||||
from datetime import datetime
|
||||
settings = load_rgsx_settings()
|
||||
if date_string is None:
|
||||
date_string = datetime.now().strftime("%Y-%m-%d")
|
||||
settings["last_gamelist_update"] = date_string
|
||||
save_rgsx_settings(settings)
|
||||
logger.info(f"Date de dernière mise à jour de la liste des jeux: {date_string}")
|
||||
return date_string
|
||||
|
||||
|
||||
|
||||
def load_symlink_settings():
|
||||
"""Load symlink settings from rgsx_settings.json."""
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2.4.0.2"
|
||||
"version": "2.4.1.0"
|
||||
}
|
||||
Reference in New Issue
Block a user