mirror of
https://github.com/RetroGameSets/RGSX.git
synced 2026-03-19 16:26:00 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56c87ab05f | ||
|
|
b12d645fbf | ||
|
|
04e68adef0 | ||
|
|
52f2b960c2 | ||
|
|
1ea604840e | ||
|
|
802696e78f |
@@ -13,7 +13,7 @@ except Exception:
|
||||
pygame = None # type: ignore
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "2.3.2.7"
|
||||
app_version = "2.3.2.8"
|
||||
|
||||
|
||||
def get_application_root():
|
||||
|
||||
@@ -517,6 +517,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
max_row = len(keyboard_layout) - 1
|
||||
max_col = len(keyboard_layout[row]) - 1
|
||||
if is_input_matched(event, "up"):
|
||||
if row == 0: # if you are in the first row and press UP jump to last row
|
||||
row = max_row + (1 if col <= 5 else 0)
|
||||
if row > 0:
|
||||
config.selected_key = (row - 1, min(col, len(keyboard_layout[row - 1]) - 1))
|
||||
config.repeat_action = "up"
|
||||
@@ -525,6 +527,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "down"):
|
||||
if (col <= 5 and row == max_row) or (col > 5 and row == max_row-1): # if you are in the last row and press DOWN jump to first row
|
||||
row = -1
|
||||
if row < max_row:
|
||||
config.selected_key = (row + 1, min(col, len(keyboard_layout[row + 1]) - 1))
|
||||
config.repeat_action = "down"
|
||||
@@ -533,6 +537,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "left"):
|
||||
if col == 0: # if you are in the first col and press LEFT jump to last col
|
||||
col = max_col + 1
|
||||
if col > 0:
|
||||
config.selected_key = (row, col - 1)
|
||||
config.repeat_action = "left"
|
||||
@@ -541,6 +547,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
|
||||
config.needs_redraw = True
|
||||
elif is_input_matched(event, "right"):
|
||||
if col == max_col: # if you are in the last col and press RIGHT jump to first col
|
||||
col = -1
|
||||
if col < max_col:
|
||||
config.selected_key = (row, col + 1)
|
||||
config.repeat_action = "right"
|
||||
@@ -1983,69 +1991,89 @@ def handle_controls(event, sources, joystick, screen):
|
||||
if filter_dict:
|
||||
config.game_filter_obj.load_from_dict(filter_dict)
|
||||
|
||||
# Construire la liste des options (comme dans draw_filter_advanced)
|
||||
options = []
|
||||
options.append(('header', 'region_title'))
|
||||
for region in GameFilters.REGIONS:
|
||||
options.append(('region', region))
|
||||
options.append(('separator', ''))
|
||||
options.append(('header', 'other_options'))
|
||||
options.append(('toggle', 'hide_non_release'))
|
||||
options.append(('toggle', 'one_rom_per_game'))
|
||||
options.append(('button_inline', 'priority_config'))
|
||||
# Construire la liste linéaire des éléments sélectionnables (pour simplifier l'indexation)
|
||||
# Régions individuelles
|
||||
num_regions = len(GameFilters.REGIONS)
|
||||
# Options toggle/button
|
||||
num_other_options = 3 # hide_non_release, one_rom_per_game, priority_config
|
||||
# Boutons en bas
|
||||
num_buttons = 3 # apply, reset, back
|
||||
|
||||
# Boutons séparés (3 boutons au total)
|
||||
buttons = [
|
||||
('button', 'apply'),
|
||||
('button', 'reset'),
|
||||
('button', 'back')
|
||||
]
|
||||
|
||||
# Total d'éléments sélectionnables
|
||||
total_items = len(options) + len(buttons)
|
||||
total_items = num_regions + num_other_options + num_buttons
|
||||
|
||||
if is_input_matched(event, "up"):
|
||||
# Chercher l'option sélectionnable précédente
|
||||
config.selected_filter_option = (config.selected_filter_option - 1) % total_items
|
||||
while config.selected_filter_option < len(options) and options[config.selected_filter_option][0] in ['header', 'separator']:
|
||||
# Navigation verticale dans la grille ou entre sections
|
||||
if config.selected_filter_option < num_regions:
|
||||
# Dans la grille des régions (3 colonnes)
|
||||
if config.selected_filter_option >= 3:
|
||||
# Monter d'une ligne
|
||||
config.selected_filter_option -= 3
|
||||
else:
|
||||
# Déjà en haut, aller aux boutons
|
||||
config.selected_filter_option = total_items - 2 # Bouton du milieu (reset)
|
||||
else:
|
||||
# Dans les options ou boutons, monter normalement
|
||||
config.selected_filter_option = (config.selected_filter_option - 1) % total_items
|
||||
|
||||
config.needs_redraw = True
|
||||
update_key_state("up", True, event.type, event.key if event.type == pygame.KEYDOWN else
|
||||
event.button if event.type == pygame.JOYBUTTONDOWN else
|
||||
(event.axis, event.value) if event.type == pygame.JOYAXISMOTION else
|
||||
event.value)
|
||||
|
||||
elif is_input_matched(event, "down"):
|
||||
# Chercher l'option sélectionnable suivante
|
||||
config.selected_filter_option = (config.selected_filter_option + 1) % total_items
|
||||
while config.selected_filter_option < len(options) and options[config.selected_filter_option][0] in ['header', 'separator']:
|
||||
# Navigation verticale
|
||||
if config.selected_filter_option < num_regions:
|
||||
# Dans la grille des régions
|
||||
if config.selected_filter_option + 3 < num_regions:
|
||||
# Descendre d'une ligne
|
||||
config.selected_filter_option += 3
|
||||
else:
|
||||
# Aller aux autres options
|
||||
config.selected_filter_option = num_regions
|
||||
else:
|
||||
# Dans les options ou boutons, descendre normalement
|
||||
config.selected_filter_option = (config.selected_filter_option + 1) % total_items
|
||||
|
||||
config.needs_redraw = True
|
||||
update_key_state("down", True, event.type, event.key if event.type == pygame.KEYDOWN else
|
||||
event.button if event.type == pygame.JOYBUTTONDOWN else
|
||||
(event.axis, event.value) if event.type == pygame.JOYAXISMOTION else
|
||||
event.value)
|
||||
elif is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||
# Navigation gauche/droite uniquement pour les boutons en bas
|
||||
if config.selected_filter_option >= len(options):
|
||||
button_index = config.selected_filter_option - len(options)
|
||||
if is_input_matched(event, "left"):
|
||||
button_index = (button_index - 1) % len(buttons)
|
||||
else:
|
||||
button_index = (button_index + 1) % len(buttons)
|
||||
config.selected_filter_option = len(options) + button_index
|
||||
|
||||
elif is_input_matched(event, "left"):
|
||||
# Navigation horizontale
|
||||
if config.selected_filter_option < num_regions:
|
||||
# Dans la grille des régions
|
||||
if config.selected_filter_option % 3 > 0:
|
||||
config.selected_filter_option -= 1
|
||||
config.needs_redraw = True
|
||||
elif config.selected_filter_option >= num_regions + num_other_options:
|
||||
# Dans les boutons en bas
|
||||
button_idx = config.selected_filter_option - (num_regions + num_other_options)
|
||||
button_idx = (button_idx - 1) % num_buttons
|
||||
config.selected_filter_option = num_regions + num_other_options + button_idx
|
||||
config.needs_redraw = True
|
||||
|
||||
elif is_input_matched(event, "right"):
|
||||
# Navigation horizontale
|
||||
if config.selected_filter_option < num_regions:
|
||||
# Dans la grille des régions
|
||||
if config.selected_filter_option % 3 < 2 and config.selected_filter_option + 1 < num_regions:
|
||||
config.selected_filter_option += 1
|
||||
config.needs_redraw = True
|
||||
elif config.selected_filter_option >= num_regions + num_other_options:
|
||||
# Dans les boutons en bas
|
||||
button_idx = config.selected_filter_option - (num_regions + num_other_options)
|
||||
button_idx = (button_idx + 1) % num_buttons
|
||||
config.selected_filter_option = num_regions + num_other_options + button_idx
|
||||
config.needs_redraw = True
|
||||
|
||||
elif is_input_matched(event, "confirm"):
|
||||
# Déterminer si c'est une option ou un bouton
|
||||
if config.selected_filter_option < len(options):
|
||||
option_type, *option_data = options[config.selected_filter_option]
|
||||
else:
|
||||
# C'est un bouton
|
||||
button_index = config.selected_filter_option - len(options)
|
||||
option_type, *option_data = buttons[button_index]
|
||||
|
||||
if option_type == 'region':
|
||||
# Basculer filtre région: include ↔ exclude (include par défaut)
|
||||
region = option_data[0]
|
||||
# Déterminer quel élément a été sélectionné
|
||||
if config.selected_filter_option < num_regions:
|
||||
# C'est une région
|
||||
region = GameFilters.REGIONS[config.selected_filter_option]
|
||||
current_state = config.game_filter_obj.region_filters.get(region, 'include')
|
||||
if current_state == 'include':
|
||||
config.game_filter_obj.region_filters[region] = 'exclude'
|
||||
@@ -2054,28 +2082,31 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Filtre région {region} modifié: {config.game_filter_obj.region_filters[region]}")
|
||||
|
||||
elif option_type == 'toggle':
|
||||
toggle_name = option_data[0]
|
||||
if toggle_name == 'hide_non_release':
|
||||
elif config.selected_filter_option < num_regions + num_other_options:
|
||||
# C'est une autre option
|
||||
option_idx = config.selected_filter_option - num_regions
|
||||
if option_idx == 0:
|
||||
# hide_non_release
|
||||
config.game_filter_obj.hide_non_release = not config.game_filter_obj.hide_non_release
|
||||
elif toggle_name == 'one_rom_per_game':
|
||||
config.needs_redraw = True
|
||||
logger.debug("Toggle hide_non_release modifié")
|
||||
elif option_idx == 1:
|
||||
# one_rom_per_game
|
||||
config.game_filter_obj.one_rom_per_game = not config.game_filter_obj.one_rom_per_game
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Toggle {toggle_name} modifié")
|
||||
|
||||
elif option_type == 'button_inline':
|
||||
button_name = option_data[0]
|
||||
if button_name == 'priority_config':
|
||||
# Ouvrir le menu de configuration de priorité
|
||||
config.needs_redraw = True
|
||||
logger.debug("Toggle one_rom_per_game modifié")
|
||||
elif option_idx == 2:
|
||||
# priority_config
|
||||
config.menu_state = "filter_priority_config"
|
||||
config.selected_priority_index = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug("Ouverture configuration priorité régions")
|
||||
|
||||
elif option_type == 'button':
|
||||
button_name = option_data[0]
|
||||
if button_name == 'apply':
|
||||
# Appliquer les filtres
|
||||
else:
|
||||
# C'est un bouton
|
||||
button_idx = config.selected_filter_option - (num_regions + num_other_options)
|
||||
if button_idx == 0:
|
||||
# Apply
|
||||
save_game_filters(config.game_filter_obj.to_dict())
|
||||
|
||||
# Appliquer aux jeux actuels
|
||||
@@ -2092,8 +2123,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
logger.debug("Filtres appliqués")
|
||||
|
||||
elif button_name == 'reset':
|
||||
# Réinitialiser les filtres
|
||||
elif button_idx == 1:
|
||||
# Reset
|
||||
config.game_filter_obj.reset()
|
||||
save_game_filters(config.game_filter_obj.to_dict())
|
||||
config.filtered_games = config.games
|
||||
@@ -2101,8 +2132,8 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.needs_redraw = True
|
||||
logger.debug("Filtres réinitialisés")
|
||||
|
||||
elif button_name == 'back':
|
||||
# Retour sans appliquer
|
||||
elif button_idx == 2:
|
||||
# Back
|
||||
config.menu_state = "game"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Retour sans appliquer les filtres")
|
||||
@@ -2593,4 +2624,4 @@ def get_emergency_controls():
|
||||
# manette basique
|
||||
"confirm_joy": {"type": "button", "button": 0},
|
||||
"cancel_joy": {"type": "button", "button": 1},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,18 @@ def draw_stylized_button(screen, text, x, y, width, height, selected=False):
|
||||
pygame.draw.rect(glow_surface, THEME_COLORS["fond_lignes"] + (50,), (5, 5, width, height), border_radius=12)
|
||||
screen.blit(glow_surface, (x - 5, y - 5))
|
||||
screen.blit(button_surface, (x, y))
|
||||
|
||||
# Vérifier si le texte dépasse la largeur disponible
|
||||
text_surface = config.font.render(text, True, THEME_COLORS["text"])
|
||||
available_width = width - 20 # Marge de 10px de chaque côté
|
||||
|
||||
if text_surface.get_width() > available_width:
|
||||
# Tronquer le texte avec "..."
|
||||
truncated_text = text
|
||||
while text_surface.get_width() > available_width and len(truncated_text) > 0:
|
||||
truncated_text = truncated_text[:-1]
|
||||
text_surface = config.font.render(truncated_text + "...", True, THEME_COLORS["text"])
|
||||
|
||||
text_rect = text_surface.get_rect(center=(x + width // 2, y + height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
@@ -1682,11 +1693,23 @@ def draw_language_menu(screen):
|
||||
vpad = max(8, min(14, int(title_surface.get_height() * 0.4)))
|
||||
title_bg_rect = title_rect.inflate(hpad, vpad)
|
||||
|
||||
# Dimensions responsives des boutons
|
||||
# Largeur bornée entre 260 et 380px (~40% de la largeur écran)
|
||||
button_width = max(260, min(380, int(config.screen_width * 0.4)))
|
||||
# Hauteur réduite et responsive (env. 5.5% de la hauteur écran), bornée 28..56
|
||||
button_height = max(28, min(56, int(config.screen_height * 0.055)))
|
||||
# Calculer hauteur dynamique basée sur la taille de police
|
||||
sample_text = config.font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
|
||||
# Calculer largeur maximale nécessaire pour les noms de langues
|
||||
max_text_width = 0
|
||||
for lang_code in available_languages:
|
||||
lang_name = get_language_name(lang_code)
|
||||
text_surface = config.font.render(lang_name, True, THEME_COLORS["text"])
|
||||
if text_surface.get_width() > max_text_width:
|
||||
max_text_width = text_surface.get_width()
|
||||
|
||||
# Largeur bornée entre valeur calculée et limites raisonnables
|
||||
button_width = max(260, min(500, max_text_width + 60))
|
||||
# Hauteur réduite et responsive (env. 5.5% de la hauteur écran), bornée mais aussi fonction de la police
|
||||
# Augmenter le padding pour grandes polices
|
||||
button_height = max(28, min(70, max(int(config.screen_height * 0.055), font_height + 20)))
|
||||
# Espacement vertical proportionnel et borné
|
||||
button_spacing = max(8, int(button_height * 0.35))
|
||||
|
||||
@@ -1737,8 +1760,17 @@ def draw_language_menu(screen):
|
||||
pygame.draw.rect(screen, button_color, (button_x, button_y, button_width, button_height), border_radius=10)
|
||||
pygame.draw.rect(screen, THEME_COLORS["border"], (button_x, button_y, button_width, button_height), 2, border_radius=10)
|
||||
|
||||
# Texte du bouton
|
||||
# Texte avec gestion du dépassement
|
||||
text_surface = config.font.render(lang_name, True, THEME_COLORS["text"])
|
||||
available_width = button_width - 20 # Marge de 10px de chaque côté
|
||||
|
||||
if text_surface.get_width() > available_width:
|
||||
# Tronquer le texte avec "..."
|
||||
truncated_text = lang_name
|
||||
while text_surface.get_width() > available_width and len(truncated_text) > 0:
|
||||
truncated_text = truncated_text[:-1]
|
||||
text_surface = config.font.render(truncated_text + "...", True, THEME_COLORS["text"])
|
||||
|
||||
text_rect = text_surface.get_rect(center=(button_x + button_width // 2, button_y + button_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
@@ -1864,8 +1896,20 @@ def draw_pause_menu(screen, selected_option):
|
||||
_("menu_support"), # 6 -> support
|
||||
_("menu_quit") # 7 -> quit
|
||||
]
|
||||
menu_width = int(config.screen_width * 0.6)
|
||||
button_height = int(config.screen_height * 0.048)
|
||||
# Calculer hauteur dynamique basée sur la taille de police
|
||||
sample_text = config.font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
button_height = max(int(config.screen_height * 0.048), font_height + 20)
|
||||
|
||||
# Calculer largeur maximale nécessaire pour le texte
|
||||
max_text_width = 0
|
||||
for option in options:
|
||||
text_surface = config.font.render(option, True, THEME_COLORS["text"])
|
||||
if text_surface.get_width() > max_text_width:
|
||||
max_text_width = text_surface.get_width()
|
||||
|
||||
# Largeur du menu basée sur le texte le plus long + marges
|
||||
menu_width = min(int(config.screen_width * 0.8), max(int(config.screen_width * 0.5), max_text_width + 80))
|
||||
margin_top_bottom = 24
|
||||
menu_height = len(options) * (button_height + 12) + 2 * margin_top_bottom
|
||||
menu_x = (config.screen_width - menu_width) // 2
|
||||
@@ -1910,8 +1954,23 @@ def draw_pause_menu(screen, selected_option):
|
||||
def _draw_submenu_generic(screen, title, options, selected_index):
|
||||
"""Helper générique pour dessiner un sous-menu hiérarchique."""
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
menu_width = int(config.screen_width * 0.72)
|
||||
button_height = int(config.screen_height * 0.045)
|
||||
|
||||
# Calculer hauteur dynamique basée sur la taille de police
|
||||
sample_text = config.font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
button_height = max(int(config.screen_height * 0.045), font_height + 18)
|
||||
|
||||
# Calculer largeur maximale nécessaire pour le texte (titre + options)
|
||||
max_text_width = 0
|
||||
title_surface = config.font.render(title, True, THEME_COLORS["text"])
|
||||
max_text_width = title_surface.get_width()
|
||||
for option in options:
|
||||
text_surface = config.font.render(option, True, THEME_COLORS["text"])
|
||||
if text_surface.get_width() > max_text_width:
|
||||
max_text_width = text_surface.get_width()
|
||||
|
||||
# Largeur du menu basée sur le texte le plus long + marges
|
||||
menu_width = min(int(config.screen_width * 0.85), max(int(config.screen_width * 0.55), max_text_width + 80))
|
||||
margin_top_bottom = 26
|
||||
menu_height = (len(options)+1) * (button_height + 10) + 2 * margin_top_bottom # +1 pour le titre
|
||||
menu_x = (config.screen_width - menu_width) // 2
|
||||
@@ -2623,7 +2682,10 @@ def draw_confirm_dialog(screen):
|
||||
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
|
||||
line_height = config.font.get_height() + 5
|
||||
text_height = len(wrapped_message) * line_height
|
||||
button_height = int(config.screen_height * 0.0463)
|
||||
# Adapter hauteur bouton en fonction de la taille de police
|
||||
sample_text = config.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.font.size(line)[0] for line in wrapped_message], default=300)
|
||||
@@ -2656,7 +2718,10 @@ def draw_reload_games_data_dialog(screen):
|
||||
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
|
||||
button_height = int(config.screen_height * 0.0463)
|
||||
# Adapter hauteur bouton en fonction de la taille de police
|
||||
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)
|
||||
@@ -3376,11 +3441,24 @@ def draw_filter_menu_choice(screen):
|
||||
_("filter_advanced")
|
||||
]
|
||||
|
||||
# Calculer hauteur dynamique basée sur la taille de police
|
||||
sample_text = config.font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
button_height = max(60, font_height + 30)
|
||||
|
||||
# Calculer largeur maximale nécessaire pour le texte
|
||||
max_text_width = 0
|
||||
for option in options:
|
||||
text_surface = config.font.render(option, True, THEME_COLORS["text"])
|
||||
if text_surface.get_width() > max_text_width:
|
||||
max_text_width = text_surface.get_width()
|
||||
|
||||
# Largeur du bouton basée sur le texte le plus long + marges
|
||||
button_width = max(400, max_text_width + 80)
|
||||
|
||||
# Calculer positions
|
||||
menu_y = 150
|
||||
button_height = 60
|
||||
button_spacing = 20
|
||||
button_width = 600
|
||||
|
||||
for i, option in enumerate(options):
|
||||
y = menu_y + i * (button_height + button_spacing)
|
||||
@@ -3398,8 +3476,17 @@ def draw_filter_menu_choice(screen):
|
||||
pygame.draw.rect(screen, color, (x, y, button_width, button_height), border_radius=12)
|
||||
pygame.draw.rect(screen, border_color, (x, y, button_width, button_height), 3, border_radius=12)
|
||||
|
||||
# Texte
|
||||
# Texte avec gestion du dépassement
|
||||
text_surface = config.font.render(option, True, THEME_COLORS["text"])
|
||||
available_width = button_width - 40 # Marge de 20px de chaque côté
|
||||
|
||||
if text_surface.get_width() > available_width:
|
||||
# Tronquer le texte avec "..."
|
||||
truncated_text = option
|
||||
while text_surface.get_width() > available_width and len(truncated_text) > 0:
|
||||
truncated_text = truncated_text[:-1]
|
||||
text_surface = config.font.render(truncated_text + "...", True, THEME_COLORS["text"])
|
||||
|
||||
text_rect = text_surface.get_rect(center=(config.screen_width // 2, y + button_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
@@ -3410,12 +3497,6 @@ def draw_filter_advanced(screen):
|
||||
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
# Titre
|
||||
title = _("filter_advanced_title")
|
||||
title_surface = config.title_font.render(title, True, THEME_COLORS["text"])
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, 40))
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
# Initialiser le filtre si nécessaire
|
||||
if not hasattr(config, 'game_filter_obj'):
|
||||
config.game_filter_obj = GameFilters()
|
||||
@@ -3425,18 +3506,15 @@ def draw_filter_advanced(screen):
|
||||
if filter_dict:
|
||||
config.game_filter_obj.load_from_dict(filter_dict)
|
||||
|
||||
# Zones d'affichage
|
||||
start_y = 100
|
||||
line_height = 50
|
||||
current_y = start_y
|
||||
|
||||
# Liste des options
|
||||
# Liste des options (sans les régions pour l'instant)
|
||||
options = []
|
||||
|
||||
# Section Régions
|
||||
# Section Régions (titre seulement)
|
||||
region_title = _("filter_region_title")
|
||||
options.append(('header', region_title))
|
||||
|
||||
# On va afficher les régions en grille 3x3, donc on ajoute des placeholders
|
||||
regions_list = []
|
||||
for region in GameFilters.REGIONS:
|
||||
region_key = f"filter_region_{region.lower()}"
|
||||
region_label = _(region_key)
|
||||
@@ -3449,7 +3527,10 @@ def draw_filter_advanced(screen):
|
||||
status = f"[V] {_('filter_region_include')}"
|
||||
color = THEME_COLORS["green"]
|
||||
|
||||
options.append(('region', region, f"{region_label}: {status}", color))
|
||||
regions_list.append(('region', region, f"{region_label}: {status}", color))
|
||||
|
||||
# Ajouter les régions comme une seule entrée "grid" dans options
|
||||
options.append(('region_grid', regions_list))
|
||||
|
||||
# Section Autres options
|
||||
options.append(('separator', ''))
|
||||
@@ -3477,31 +3558,152 @@ def draw_filter_advanced(screen):
|
||||
if not hasattr(config, 'selected_filter_option'):
|
||||
config.selected_filter_option = 0
|
||||
|
||||
# S'assurer que l'index est valide (options + 3 boutons)
|
||||
total_items = len(options) + len(buttons)
|
||||
# Calculer le nombre total d'items sélectionnables (régions individuelles + autres options + boutons)
|
||||
total_items = len(regions_list) + len([opt for opt in options if opt[0] in ['toggle', 'button_inline']]) + len(buttons)
|
||||
if config.selected_filter_option >= total_items:
|
||||
config.selected_filter_option = total_items - 1
|
||||
|
||||
for i, option in enumerate(options):
|
||||
# Calculer d'abord la hauteur totale nécessaire
|
||||
# Adapter la hauteur en fonction de la taille de police
|
||||
sample_text = config.font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
line_height = max(50, font_height + 30)
|
||||
item_height = max(45, font_height + 20)
|
||||
item_spacing_y = 10
|
||||
items_per_row = 3
|
||||
|
||||
# Titre
|
||||
title_height = 60
|
||||
|
||||
# Hauteur du header régions
|
||||
header_height = line_height
|
||||
|
||||
# Hauteur de la grille de régions
|
||||
num_rows = (len(regions_list) + items_per_row - 1) // items_per_row
|
||||
grid_height = num_rows * (item_height + item_spacing_y)
|
||||
|
||||
# Hauteur du séparateur
|
||||
separator_height = 10
|
||||
|
||||
# Hauteur du header autres options
|
||||
header2_height = line_height
|
||||
|
||||
# Hauteur des autres options (3 options)
|
||||
num_other_options = len([opt for opt in options if opt[0] in ['toggle', 'button_inline']])
|
||||
other_options_height = num_other_options * (item_height + 10)
|
||||
|
||||
# Hauteur des boutons
|
||||
# Adapter en fonction de la taille de police
|
||||
sample_text = config.font.render("Sample", True, THEME_COLORS["text"])
|
||||
font_height = sample_text.get_height()
|
||||
button_height = max(50, font_height + 20)
|
||||
buttons_top_margin = 30
|
||||
|
||||
# Hauteur totale du contenu
|
||||
total_content_height = (title_height + header_height + grid_height + separator_height +
|
||||
header2_height + other_options_height + buttons_top_margin + button_height)
|
||||
|
||||
# Calculer position de départ pour centrer verticalement
|
||||
control_bar_estimated_height = 80
|
||||
available_height = config.screen_height - control_bar_estimated_height
|
||||
start_y = (available_height - total_content_height) // 2
|
||||
if start_y < 20:
|
||||
start_y = 20 # Marge minimale du haut
|
||||
|
||||
current_y = start_y
|
||||
|
||||
# Titre
|
||||
title = _("filter_advanced_title")
|
||||
title_surface = config.title_font.render(title, True, THEME_COLORS["text"])
|
||||
title_rect = title_surface.get_rect(center=(config.screen_width // 2, current_y + 20))
|
||||
screen.blit(title_surface, title_rect)
|
||||
current_y += title_height
|
||||
|
||||
region_index_start = 0 # Les régions commencent à l'index 0
|
||||
|
||||
for option in options:
|
||||
option_type = option[0]
|
||||
|
||||
if option_type == 'header':
|
||||
# En-tête de section
|
||||
text_surface = config.font.render(option[1], True, THEME_COLORS["title_text"])
|
||||
screen.blit(text_surface, (100, current_y))
|
||||
text_rect = text_surface.get_rect(center=(config.screen_width // 2, current_y + 20))
|
||||
screen.blit(text_surface, text_rect)
|
||||
current_y += line_height
|
||||
|
||||
elif option_type == 'separator':
|
||||
current_y += 10
|
||||
current_y += separator_height
|
||||
|
||||
elif option_type in ['region', 'toggle', 'button_inline']:
|
||||
# Option sélectionnable
|
||||
x = 120
|
||||
width = config.screen_width - 240
|
||||
height = 45
|
||||
elif option_type == 'region_grid':
|
||||
# Afficher les régions en grille 3 par ligne
|
||||
regions_data = option[1]
|
||||
item_spacing_x = 20
|
||||
|
||||
# Calculer la largeur maximale nécessaire pour les boutons de régions
|
||||
max_region_width = 0
|
||||
for region_data in regions_data:
|
||||
text = region_data[2]
|
||||
text_surface = config.font.render(text, True, THEME_COLORS["text"])
|
||||
text_width = text_surface.get_width() + 30 # Padding de 30px
|
||||
if text_width > max_region_width:
|
||||
max_region_width = text_width
|
||||
|
||||
# Largeur minimale de 200px
|
||||
item_width = max(max_region_width, 200)
|
||||
|
||||
# Calculer la largeur totale de la grille
|
||||
total_grid_width = items_per_row * item_width + (items_per_row - 1) * item_spacing_x
|
||||
grid_start_x = (config.screen_width - total_grid_width) // 2
|
||||
|
||||
for idx, region_data in enumerate(regions_data):
|
||||
row = idx // items_per_row
|
||||
col = idx % items_per_row
|
||||
|
||||
x = grid_start_x + col * (item_width + item_spacing_x)
|
||||
y = current_y + row * (item_height + item_spacing_y)
|
||||
|
||||
# Index global de cette région
|
||||
global_idx = region_index_start + idx
|
||||
|
||||
# Couleur selon sélection
|
||||
if global_idx == config.selected_filter_option:
|
||||
bg_color = THEME_COLORS["button_selected"]
|
||||
border_color = THEME_COLORS["border_selected"]
|
||||
else:
|
||||
bg_color = THEME_COLORS["button_idle"]
|
||||
border_color = THEME_COLORS["border"]
|
||||
|
||||
# Dessiner fond
|
||||
pygame.draw.rect(screen, bg_color, (x, y, item_width, item_height), border_radius=8)
|
||||
pygame.draw.rect(screen, border_color, (x, y, item_width, item_height), 2, border_radius=8)
|
||||
|
||||
# Texte centré
|
||||
text = region_data[2]
|
||||
text_color = region_data[3]
|
||||
|
||||
text_surface = config.font.render(text, True, text_color)
|
||||
text_rect = text_surface.get_rect(center=(x + item_width // 2, y + item_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# Calculer la hauteur occupée par la grille
|
||||
current_y += num_rows * (item_height + item_spacing_y) + 10
|
||||
|
||||
elif option_type in ['toggle', 'button_inline']:
|
||||
# Option sélectionnable - largeur adaptée au texte
|
||||
text = option[2]
|
||||
text_surface = config.font.render(text, True, THEME_COLORS["text"])
|
||||
text_width = text_surface.get_width()
|
||||
|
||||
# Largeur avec padding
|
||||
width = text_width + 40
|
||||
x = (config.screen_width - width) // 2 # Centrer
|
||||
height = item_height
|
||||
|
||||
# Index global de cette option (après les régions)
|
||||
global_idx = len(regions_list) + len([opt for opt in options[:options.index(option)] if opt[0] in ['toggle', 'button_inline']])
|
||||
|
||||
# Couleur selon sélection
|
||||
if i == config.selected_filter_option:
|
||||
if global_idx == config.selected_filter_option:
|
||||
bg_color = THEME_COLORS["button_selected"]
|
||||
border_color = THEME_COLORS["border_selected"]
|
||||
else:
|
||||
@@ -3512,33 +3714,35 @@ def draw_filter_advanced(screen):
|
||||
pygame.draw.rect(screen, bg_color, (x, current_y, width, height), border_radius=8)
|
||||
pygame.draw.rect(screen, border_color, (x, current_y, width, height), 2, border_radius=8)
|
||||
|
||||
# Texte
|
||||
if option_type == 'region':
|
||||
text = option[2]
|
||||
text_color = option[3]
|
||||
else:
|
||||
text = option[2]
|
||||
text_color = THEME_COLORS["text"]
|
||||
|
||||
text_surface = config.font.render(text, True, text_color)
|
||||
text_rect = text_surface.get_rect(left=x + 20, centery=current_y + height // 2)
|
||||
# Texte centré
|
||||
text_color = THEME_COLORS["text"]
|
||||
text_rect = text_surface.get_rect(center=(x + width // 2, current_y + height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
current_y += height + 10
|
||||
|
||||
# Afficher les 3 boutons côte à côte en bas
|
||||
# Calculer la position pour éviter la barre de contrôles (hauteur estimée ~60-80px)
|
||||
control_bar_estimated_height = 80
|
||||
button_width = 200
|
||||
button_height = 50
|
||||
current_y += buttons_top_margin
|
||||
button_y = current_y
|
||||
button_spacing = 20
|
||||
total_buttons_width = button_width * 3 + button_spacing * 2
|
||||
button_start_x = (config.screen_width - total_buttons_width) // 2
|
||||
button_y = config.screen_height - control_bar_estimated_height - button_height - 20
|
||||
|
||||
# Calculer la largeur de chaque bouton en fonction du texte
|
||||
button_widths = []
|
||||
for button_id, button_text in buttons:
|
||||
text_surface = config.font.render(button_text, True, THEME_COLORS["text"])
|
||||
button_widths.append(text_surface.get_width() + 40) # Padding de 40px
|
||||
|
||||
# Largeur totale des boutons
|
||||
total_buttons_width = sum(button_widths) + button_spacing * (len(buttons) - 1)
|
||||
button_start_x = (config.screen_width - total_buttons_width) // 2
|
||||
|
||||
# Calculer l'index de début des boutons (après toutes les régions et autres options)
|
||||
button_index_start = len(regions_list) + num_other_options
|
||||
|
||||
current_button_x = button_start_x
|
||||
for i, (button_id, button_text) in enumerate(buttons):
|
||||
button_index = len(options) + i
|
||||
button_x = button_start_x + i * (button_width + button_spacing)
|
||||
button_index = button_index_start + i
|
||||
button_width = button_widths[i]
|
||||
|
||||
# Couleur selon sélection
|
||||
if button_index == config.selected_filter_option:
|
||||
@@ -3549,19 +3753,21 @@ def draw_filter_advanced(screen):
|
||||
border_color = THEME_COLORS["border"]
|
||||
|
||||
# Dessiner bouton
|
||||
pygame.draw.rect(screen, bg_color, (button_x, button_y, button_width, button_height), border_radius=8)
|
||||
pygame.draw.rect(screen, border_color, (button_x, button_y, button_width, button_height), 2, border_radius=8)
|
||||
pygame.draw.rect(screen, bg_color, (current_button_x, button_y, button_width, button_height), border_radius=8)
|
||||
pygame.draw.rect(screen, border_color, (current_button_x, button_y, button_width, button_height), 2, border_radius=8)
|
||||
|
||||
# Texte centré
|
||||
text_surface = config.font.render(button_text, True, THEME_COLORS["text"])
|
||||
text_rect = text_surface.get_rect(center=(button_x + button_width // 2, button_y + button_height // 2))
|
||||
text_rect = text_surface.get_rect(center=(current_button_x + button_width // 2, button_y + button_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
current_button_x += button_width + button_spacing
|
||||
|
||||
# Info filtre actif (au-dessus des boutons)
|
||||
if config.game_filter_obj.is_active():
|
||||
info_text = _("filter_active")
|
||||
info_surface = config.small_font.render(info_text, True, THEME_COLORS["green"])
|
||||
info_rect = info_surface.get_rect(center=(config.screen_width // 2, button_y - 30))
|
||||
info_rect = info_surface.get_rect(center=(config.screen_width // 2, button_y - 20))
|
||||
screen.blit(info_surface, info_rect)
|
||||
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@
|
||||
"web_restart_error": "Fehler beim Neustart: {0}",
|
||||
"web_support": "Support",
|
||||
"web_support_title": "📦 Support-Datei erstellt",
|
||||
"web_support_message": "Support-Datei erfolgreich erstellt!\\n\\n📁 Inhalt:\\n• Steuerungskonfiguration\\n• Download-Verlauf\\n• RGSX-Einstellungen\\n• Anwendungsprotokolle\\n• Webserver-Protokolle\\n\\n💬 Um Hilfe zu erhalten:\\n1. Trete dem RGSX Discord bei\\n2. Beschreibe dein Problem\\n3. Teile diese ZIP-Datei\\n\\nDownload startet...",
|
||||
"web_support_message": "Support-Datei erfolgreich erstellt!\n\n📁 Inhalt:\n• Steuerungskonfiguration\n• Download-Verlauf\n• RGSX-Einstellungen\n• Anwendungsprotokolle\n• Webserver-Protokolle\n\n💬 Um Hilfe zu erhalten:\n1. Trete dem RGSX Discord bei\n2. Beschreibe dein Problem\n3. Teile diese ZIP-Datei\n\nDownload startet...",
|
||||
"web_support_generating": "Support-Datei wird generiert...",
|
||||
"web_support_download": "Support-Datei herunterladen",
|
||||
"web_support_error": "Fehler beim Erstellen der Support-Datei: {0}",
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
"web_restart_error": "Restart error: {0}",
|
||||
"web_support": "Support",
|
||||
"web_support_title": "📦 Support File Generated",
|
||||
"web_support_message": "Support file created successfully!\\n\\n📁 Contents:\\n• Controls configuration\\n• Download history\\n• RGSX settings\\n• Application logs\\n• Web server logs\\n\\n💬 To get help:\\n1. Join RGSX Discord\\n2. Describe your issue\\n3. Share this ZIP file\\n\\nDownload will start...",
|
||||
"web_support_message": "Support file created successfully!\n\n📁 Contents:\n• Controls configuration\n• Download history\n• RGSX settings\n• Application logs\n• Web server logs\n\n💬 To get help:\n1. Join RGSX Discord\n2. Describe your issue\n3. Share this ZIP file\n\nDownload will start...",
|
||||
"web_support_generating": "Generating support file...",
|
||||
"web_support_download": "Download support file",
|
||||
"web_support_error": "Error generating support file: {0}",
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
"web_restart_error": "Error al reiniciar: {0}",
|
||||
"web_support": "Soporte",
|
||||
"web_support_title": "📦 Archivo de soporte generado",
|
||||
"web_support_message": "¡Archivo de soporte creado con éxito!\\n\\n📁 Contenido:\\n• Configuración de controles\\n• Historial de descargas\\n• Configuración RGSX\\n• Registros de la aplicación\\n• Registros del servidor web\\n\\n💬 Para obtener ayuda:\\n1. Únete al Discord de RGSX\\n2. Describe tu problema\\n3. Comparte este archivo ZIP\\n\\nLa descarga comenzará...",
|
||||
"web_support_message": "¡Archivo de soporte creado con éxito!\n\n📁 Contenido:\n• Configuración de controles\n• Historial de descargas\n• Configuración RGSX\n• Registros de la aplicación\n• Registros del servidor web\n\n💬 Para obtener ayuda:\n1. Únete al Discord de RGSX\n2. Describe tu problema\n3. Comparte este archivo ZIP\n\nLa descarga comenzará...",
|
||||
"web_support_generating": "Generando archivo de soporte...",
|
||||
"web_support_download": "Descargar archivo de soporte",
|
||||
"web_support_error": "Error al generar el archivo de soporte: {0}",
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
"web_restart_error": "Erreur lors du redémarrage : {0}",
|
||||
"web_support": "Support",
|
||||
"web_support_title": "📦 Fichier de support généré",
|
||||
"web_support_message": "Le fichier de support a été créé avec succès !\\n\\n📁 Contenu :\\n• Configuration des contrôles\\n• Historique des téléchargements\\n• Paramètres RGSX\\n• Logs de l'application\\n• Logs du serveur web\\n\\n💬 Pour obtenir de l'aide :\\n1. Rejoignez le Discord RGSX\\n2. Décrivez votre problème\\n3. Partagez ce fichier ZIP\\n\\nLe téléchargement va démarrer...",
|
||||
"web_support_message": "Le fichier de support a été créé avec succès !\n\n📁 Contenu :\n• Configuration des contrôles\n• Historique des téléchargements\n• Paramètres RGSX\n• Logs de l'application\n• Logs du serveur web\n\n💬 Pour obtenir de l'aide :\n1. Rejoignez le Discord RGSX\n2. Décrivez votre problème\n3. Partagez ce fichier ZIP\n\nLe téléchargement va démarrer...",
|
||||
"web_support_generating": "Génération du fichier de support...",
|
||||
"web_support_download": "Télécharger le fichier de support",
|
||||
"web_support_error": "Erreur lors de la génération du fichier de support : {0}",
|
||||
|
||||
@@ -338,7 +338,7 @@
|
||||
"web_restart_error": "Errore durante il riavvio: {0}",
|
||||
"web_support": "Supporto",
|
||||
"web_support_title": "📦 File di supporto generato",
|
||||
"web_support_message": "File di supporto creato con successo!\\n\\n📁 Contenuto:\\n• Configurazione controlli\\n• Cronologia download\\n• Impostazioni RGSX\\n• Log dell'applicazione\\n• Log del server web\\n\\n💬 Per ottenere aiuto:\\n1. Unisciti al Discord RGSX\\n2. Descrivi il tuo problema\\n3. Condividi questo file ZIP\\n\\nIl download inizierà...",
|
||||
"web_support_message": "File di supporto creato con successo!\n\n📁 Contenuto:\n• Configurazione controlli\n• Cronologia download\n• Impostazioni RGSX\n• Log dell'applicazione\n• Log del server web\n\n💬 Per ottenere aiuto:\n1. Unisciti al Discord RGSX\n2. Descrivi il tuo problema\n3. Condividi questo file ZIP\n\nIl download inizierà...",
|
||||
"web_support_generating": "Generazione file di supporto...",
|
||||
"web_support_download": "Scarica file di supporto",
|
||||
"web_support_error": "Errore nella generazione del file di supporto: {0}",
|
||||
|
||||
@@ -340,7 +340,7 @@
|
||||
"web_restart_error": "Erro ao reiniciar: {0}",
|
||||
"web_support": "Suporte",
|
||||
"web_support_title": "📦 Arquivo de suporte gerado",
|
||||
"web_support_message": "Arquivo de suporte criado com sucesso!\\n\\n📁 Conteúdo:\\n• Configuração de controles\\n• Histórico de downloads\\n• Configurações RGSX\\n• Logs da aplicação\\n• Logs do servidor web\\n\\n💬 Para obter ajuda:\\n1. Entre no Discord RGSX\\n2. Descreva seu problema\\n3. Compartilhe este arquivo ZIP\\n\\nO download vai começar...",
|
||||
"web_support_message": "Arquivo de suporte criado com sucesso!\n\n📁 Conteúdo:\n• Configuração de controles\n• Histórico de downloads\n• Configurações RGSX\n• Logs da aplicação\n• Logs do servidor web\n\n💬 Para obter ajuda:\n1. Entre no Discord RGSX\n2. Descreva seu problema\n3. Compartilhe este arquivo ZIP\n\nO download vai começar...",
|
||||
"web_support_generating": "Gerando arquivo de suporte...",
|
||||
"web_support_download": "Baixar arquivo de suporte",
|
||||
"web_support_error": "Erro ao gerar arquivo de suporte: {0}",
|
||||
|
||||
@@ -246,11 +246,11 @@ def get_translation(key, default=None):
|
||||
return key
|
||||
|
||||
# Fonction pour normaliser les tailles de fichier
|
||||
def normalize_size(size_str):
|
||||
def normalize_size(size_str, lang='en'):
|
||||
"""
|
||||
Normalise une taille de fichier dans différents formats (Ko, KiB, Mo, MiB, Go, GiB)
|
||||
en un format uniforme (Mo ou Go).
|
||||
Exemples: "150 Mo" -> "150 Mo", "1.5 Go" -> "1.5 Go", "500 Ko" -> "0.5 Mo", "2 GiB" -> "2.15 Go"
|
||||
en un format uniforme selon la langue (MB/GB pour anglais, Mo/Go pour français).
|
||||
Exemples: "150 Mo" -> "150 MB" (en), "1.5 Go" -> "1.5 GB" (en), "500 Ko" -> "0.5 MB"
|
||||
"""
|
||||
if not size_str:
|
||||
return None
|
||||
@@ -282,16 +282,24 @@ def normalize_size(size_str):
|
||||
elif unit in ['gio', 'gib']:
|
||||
value = value * 1024 # GiB en Mo
|
||||
|
||||
# Afficher en Go si > 1024 Mo, sinon en Mo
|
||||
if value >= 1024:
|
||||
return f"{value / 1024:.2f} Go".rstrip('0').rstrip('.')
|
||||
# Déterminer les unités selon la langue
|
||||
if lang == 'fr':
|
||||
mb_unit = 'Mo'
|
||||
gb_unit = 'Go'
|
||||
else:
|
||||
# Arrondir à 1 décimale pour Mo
|
||||
mb_unit = 'MB'
|
||||
gb_unit = 'GB'
|
||||
|
||||
# Afficher en GB/Go si > 1024 Mo, sinon en MB/Mo
|
||||
if value >= 1024:
|
||||
return f"{value / 1024:.2f} {gb_unit}".replace('.00 ', ' ').rstrip('0').rstrip('.')
|
||||
else:
|
||||
# Arrondir à 1 décimale pour MB/Mo
|
||||
rounded = round(value, 1)
|
||||
if rounded == int(rounded):
|
||||
return f"{int(rounded)} Mo"
|
||||
return f"{int(rounded)} {mb_unit}"
|
||||
else:
|
||||
return f"{rounded} Mo".rstrip('0').rstrip('.')
|
||||
return f"{rounded} {mb_unit}".rstrip('0').rstrip('.')
|
||||
except (ValueError, TypeError):
|
||||
return size_str # Retourner original si conversion échoue
|
||||
|
||||
@@ -472,6 +480,20 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
"""Répond avec un 404 générique."""
|
||||
self._set_headers('text/plain; charset=utf-8', status=404)
|
||||
self.wfile.write(b'Not found')
|
||||
|
||||
def _get_language_from_cookies(self):
|
||||
"""Récupère la langue depuis les cookies ou retourne 'en' par défaut"""
|
||||
cookie_header = self.headers.get('Cookie', '')
|
||||
if cookie_header:
|
||||
# Parser les cookies
|
||||
cookies = {}
|
||||
for cookie in cookie_header.split(';'):
|
||||
cookie = cookie.strip()
|
||||
if '=' in cookie:
|
||||
key, value = cookie.split('=', 1)
|
||||
cookies[key] = value
|
||||
return cookies.get('language', 'en')
|
||||
return 'en'
|
||||
|
||||
def _asset_version(self, relative_path: str) -> str:
|
||||
"""Retourne un identifiant de version basé sur la date de modification du fichier statique."""
|
||||
@@ -681,7 +703,7 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
'game_name': game_name,
|
||||
'platform': platform_name,
|
||||
'url': game[1] if len(game) > 1 and isinstance(game, (list, tuple)) else None,
|
||||
'size': normalize_size(game[2] if len(game) > 2 and isinstance(game, (list, tuple)) else None)
|
||||
'size': normalize_size(game[2] if len(game) > 2 and isinstance(game, (list, tuple)) else None, self._get_language_from_cookies())
|
||||
})
|
||||
except Exception as e:
|
||||
logger.debug(f"Erreur lors de la recherche dans {platform_name}: {e}")
|
||||
@@ -722,12 +744,15 @@ class RGSXHandler(BaseHTTPRequestHandler):
|
||||
platform_name = path.split('/api/games/')[-1]
|
||||
platform_name = urllib.parse.unquote(platform_name)
|
||||
|
||||
# Récupérer la langue depuis les cookies ou utiliser 'en' par défaut
|
||||
lang = self._get_language_from_cookies()
|
||||
|
||||
games, _, games_last_modified = get_cached_games(platform_name)
|
||||
games_formatted = [
|
||||
{
|
||||
'name': g[0],
|
||||
'url': g[1] if len(g) > 1 else None,
|
||||
'size': normalize_size(g[2] if len(g) > 2 else None)
|
||||
'size': normalize_size(g[2] if len(g) > 2 else None, lang)
|
||||
}
|
||||
for g in games
|
||||
]
|
||||
|
||||
@@ -473,3 +473,70 @@ header p { opacity: 0.9; font-size: 1.1em; }
|
||||
padding: 3px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Modal Support */
|
||||
.support-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.support-modal-content {
|
||||
background: #2c2c2c;
|
||||
color: #ffffff;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.support-modal h2 {
|
||||
margin: 0 0 20px 0;
|
||||
color: #4CAF50;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.support-modal-message {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 25px;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.support-modal button {
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.support-modal button:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
@@ -109,6 +109,53 @@
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// Modal pour afficher les messages support avec formatage
|
||||
function showSupportModal(title, message) {
|
||||
// Remplacer les \n littéraux par de vrais retours à la ligne
|
||||
message = message.replace(/\\n/g, '\n');
|
||||
|
||||
// Créer la modal
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'support-modal';
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.className = 'support-modal-content';
|
||||
|
||||
// Titre
|
||||
const titleElement = document.createElement('h2');
|
||||
titleElement.textContent = title;
|
||||
|
||||
// Message avec retours à la ligne préservés
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.className = 'support-modal-message';
|
||||
messageElement.textContent = message;
|
||||
|
||||
// Bouton OK
|
||||
const okButton = document.createElement('button');
|
||||
okButton.textContent = 'OK';
|
||||
okButton.onclick = () => {
|
||||
modal.style.animation = 'fadeOut 0.2s ease-in';
|
||||
setTimeout(() => modal.remove(), 200);
|
||||
};
|
||||
|
||||
// Assembler la modal
|
||||
modalContent.appendChild(titleElement);
|
||||
modalContent.appendChild(messageElement);
|
||||
modalContent.appendChild(okButton);
|
||||
modal.appendChild(modalContent);
|
||||
|
||||
// Ajouter au DOM
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Fermer en cliquant sur le fond
|
||||
modal.onclick = (e) => {
|
||||
if (e.target === modal) {
|
||||
modal.style.animation = 'fadeOut 0.2s ease-in';
|
||||
setTimeout(() => modal.remove(), 200);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Charger les traductions au démarrage
|
||||
async function loadTranslations() {
|
||||
try {
|
||||
@@ -1036,13 +1083,24 @@
|
||||
const getSizeInMo = (sizeElem) => {
|
||||
if (!sizeElem) return 0;
|
||||
const text = sizeElem.textContent;
|
||||
// Les tailles sont maintenant normalisées: "100 Mo" ou "2.5 Go"
|
||||
const match = text.match(/([0-9.]+)\\s*(Mo|Go)/i);
|
||||
// Support des formats: "100 Mo", "2.5 Go" (français) et "100 MB", "2.5 GB" (anglais)
|
||||
// Plus Ko/KB, o/B, To/TB
|
||||
const match = text.match(/([0-9.]+)\s*(o|B|Ko|KB|Mo|MB|Go|GB|To|TB)/i);
|
||||
if (!match) return 0;
|
||||
let size = parseFloat(match[1]);
|
||||
// Convertir Go en Mo pour comparaison
|
||||
if (match[2].toUpperCase() === 'GO') {
|
||||
size *= 1024;
|
||||
const unit = match[2].toUpperCase();
|
||||
|
||||
// Convertir tout en Mo
|
||||
if (unit === 'O' || unit === 'B') {
|
||||
size /= (1024 * 1024); // octets/bytes vers Mo
|
||||
} else if (unit === 'KO' || unit === 'KB') {
|
||||
size /= 1024; // Ko vers Mo
|
||||
} else if (unit === 'MO' || unit === 'MB') {
|
||||
// Déjà en Mo
|
||||
} else if (unit === 'GO' || unit === 'GB') {
|
||||
size *= 1024; // Go vers Mo
|
||||
} else if (unit === 'TO' || unit === 'TB') {
|
||||
size *= 1024 * 1024; // To vers Mo
|
||||
}
|
||||
return size;
|
||||
};
|
||||
@@ -2075,7 +2133,7 @@
|
||||
hide_non_release: document.getElementById('hide-non-release')?.checked || savedHideNonRelease,
|
||||
one_rom_per_game: document.getElementById('one-rom-per-game')?.checked || savedOneRomPerGame,
|
||||
regex_mode: document.getElementById('regex-mode')?.checked || savedRegexMode,
|
||||
region_priority: regionPriority
|
||||
region_priority: regionPriorityOrder
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2175,7 +2233,7 @@
|
||||
}
|
||||
|
||||
// Générer un fichier ZIP de support
|
||||
async function generateSupportZip() {
|
||||
async function generateSupportZip(event) {
|
||||
try {
|
||||
// Afficher un message de chargement
|
||||
const loadingMsg = t('web_support_generating');
|
||||
@@ -2218,8 +2276,8 @@
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
|
||||
// Afficher le message d'instructions
|
||||
alert(t('web_support_title') + '\\n\\n' + t('web_support_message'));
|
||||
// Afficher le message d'instructions dans une modal
|
||||
showSupportModal(t('web_support_title'), t('web_support_message'));
|
||||
|
||||
// Restaurer le bouton
|
||||
if (originalButton) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2.3.2.7"
|
||||
"version": "2.3.2.8"
|
||||
}
|
||||
Reference in New Issue
Block a user