Qu’est-ce qu’un thème ?
Ce qu’un thème change, et ce qu’il ne change pas.
Un thème Recalbox habille l’interface : la liste de vos systèmes au démarrage, celle de vos jeux, les menus, l’écran de veille.
Ce qu’un thème décide
- où les éléments se placent à l’écran, et de quelle taille ;
- quelles images s’affichent : fonds, logos, cadres, illustrations ;
- les couleurs et les polices, y compris celles des menus ;
- ce qui s’affiche selon le contexte : la définition de l’écran, le type de machine, le jeu survolé.
Ce qu’un thème ne décide pas
- le contenu des menus : Recalbox les construit lui-même. Un thème n’en règle que l’habillage ;
- les jeux, leurs informations, les émulateurs ;
- le comportement des boutons.
Le principe, en une phrase
Recalbox lit des fichiers XML qui déclarent des composants — une image, un texte, une liste — chacun avec sa position, sa taille et son apparence.
<image name="fond">
<pos>0 0</pos>
<size>1 1</size>
<path>./data/fond.jpg</path>
</image>
Le studio écrit ces fichiers pour vous. Cette documentation explique la syntaxe complète : elle doit vous permettre d’écrire un thème entièrement à la main si vous le souhaitez.
Le thème minimal qui fonctionne
Les quatre vues, dans un seul fichier.
Un thème s’emboîte toujours de la même façon, et c’est le plan de toute cette documentation :
un thème contient des vues — un écran chacune ; une vue contient des composants — une image, un texte, une liste ; un composant porte des propriétés — sa position, sa taille, sa couleur.
Voici un thème complet et fonctionnel : les quatre vues y sont. Créez un dossier mon-theme/ dans /recalbox/share/themes/, mettez-y ce theme.xml, il apparaîtra dans la liste des thèmes.
<?xml version="1.0" encoding="UTF-8"?>
<theme name="Mon Thème" version="1.0" author="Moi"
recalbox="10.0" compatibility="hdmi,crt" resolutions="hd,fhd">
<!-- ── 1. LA LISTE DES SYSTÈMES ────────────────────────────── -->
<view name="system">
<box name="fond" extra="true">
<pos>0 0</pos><size>1 1</size>
<color>101820</color><zIndex>1</zIndex>
</box>
<carousel name="systemcarousel">
<type>horizontal</type>
<pos>0 0.35</pos><size>1 0.3</size>
<logoSize>0.2 0.12</logoSize>
<maxLogoCount>5</maxLogoCount>
</carousel>
<image name="logo"/>
<text name="systemInfo">
<pos>0.5 0.75</pos><origin>0.5 0.5</origin>
<fontSize>0.03</fontSize><color>8A92A6</color>
<alignment>center</alignment>
<backgroundColor>00000000</backgroundColor>
</text>
</view>
<!-- ── 2. LA LISTE DES JEUX ────────────────────────────────── -->
<view name="detailed">
<box name="fond" extra="true">
<pos>0 0</pos><size>1 1</size>
<color>101820</color><zIndex>1</zIndex>
</box>
<textlist name="gamelist">
<pos>0.05 0.15</pos><size>0.42 0.75</size>
<primaryColor>C6CBD8</primaryColor>
<secondaryColor>8A92A6</secondaryColor>
<selectedColor>101820</selectedColor>
<selectorColor>4FE3C1</selectorColor>
<fontSize>0.035</fontSize>
</textlist>
<image name="md_image">
<pos>0.74 0.4</pos><origin>0.5 0.5</origin>
<maxSize>0.4 0.45</maxSize>
</image>
<text name="md_description">
<pos>0.54 0.68</pos><size>0.4 0.22</size>
<fontSize>0.024</fontSize><color>A0A8BA</color>
</text>
</view>
<!-- ── 3. L’HABILLAGE DES MENUS ────────────────────────────── -->
<view name="menu">
<menuBackground>
<color>101820F0</color>
</menuBackground>
<menuText>
<fontSize>0.038</fontSize>
<color>C6CBD8</color>
<selectedColor>101820</selectedColor>
<selectorColor>4FE3C1</selectorColor>
</menuText>
</view>
<!-- ── 4. L’ÉCRAN DE VEILLE ────────────────────────────────── -->
<view name="gameclip">
<extras>
<text name="titre">
<pos>0.06 0.86</pos>
<text>${game.name}</text>
<fontSize>0.05</fontSize><color>FFFFFF</color>
</text>
</extras>
</view>
</theme>
Ce qu’il faut y remarquer
extra="true"sur le fond : sans lui, il ne s’afficherait pas. C’est la règle à retenir, expliquée dans La règle la plus importante ;systemcarousel,logo,gamelist,md_imagesont des noms réservés : c’est ce nom-là — pas le type de la balise — qui les relie au moteur. Il y en a une trentaine, et chaque vue a les siens : la liste complète est dans Les vues et leurs composants ;${system}et${game.name}sont des variables : Recalbox les remplace au moment d’afficher, par le nom du système en cours ou du jeu survolé. C’est ce qui rend un thème vivant sans rien écrire de particulier — voir Se servir des variables ;- la vue
menune se compose pas : on n’y pose rien, on habille ce que Recalbox construit ; - la vue
gameclip, c’est l’écran de veille — Recalbox y enchaîne des extraits de jeux, et le thème n’y ajoute guère que des informations sur le jeu montré.
Tout tient dans un seul fichier ici, pour que ce soit lisible d’un coup d’œil. Dès que le thème grossit, on le découpe — voir Découper son thème.
Faire son premier thème
La marche à suivre — avec le studio, ou à la main.
Le résultat est le même : un dossier de thème. Deux chemins pour y arriver.
Avec le studio
- Partez d’un modèle. La page blanche est le plus mauvais départ. Un modèle arrive avec ses composants déjà en place : vous les remplacez.
- Choisissez vos définitions d’écran — HD 16/9, CRT 4/3, écran vertical (TATE). Ce qui tient sur un écran large ne tient pas sur un téléviseur cathodique. Commencez par une seule, ajoutez les autres ensuite.
- Posez vos composants : faites-les glisser depuis la colonne de gauche, réglez-les dans celle de droite.
- Changez de système dans Options de la vue et regardez : c’est l’erreur la plus fréquente, un thème réglé sur une seule machine et qui se casse ailleurs. Les logos n’ont pas tous la même forme, les noms pas tous la même longueur.
- Exportez, copiez le dossier sur votre machine, essayez — voir Essayer son thème.
À la main
- Partez d’un thème existant. Ouvrez-en un, lisez-le : c’est la façon la plus rapide de comprendre ce qui se fait. Le thème officiel de Recalbox est un bon départ.
- Créez le dossier et son
theme.xml— le seul fichier obligatoire. Voir Les dossiers et les fichiers. - Annoncez ce que vous visez dans la balise
<theme>:compatibilitypour les types d’écran,resolutionspour les définitions. Ce sont ces deux attributs qui allument les pictogrammes du gestionnaire de thèmes, et les annoncer faux, c’est promettre ce que le thème ne tient pas. Voir L’en-tête. - Écrivez vos vues, une par écran, et découpez en plusieurs fichiers dès que ça grossit — voir Le thème minimal qui fonctionne, puis Découper son thème.
- Servez les autres écrans avec des conditions plutôt qu’en recopiant tout :
<include if="crt">. - Copiez le dossier sur votre machine et essayez — et lisez
themes.logau premier composant invisible.
Dans les deux cas, la même erreur guette : ne vérifier que sur SON écran et SON système.
Les dossiers et les fichiers
Une seule règle, et beaucoup de liberté.
Un thème est un dossier placé dans /recalbox/share/themes/.
La seule règle
Ce dossier doit contenir un fichier
theme.xml.
C’est tout. theme.xml est le point d’entrée : Recalbox le cherche à la racine du dossier, et s’il n’y est pas, le thème n’existe pas.
Rien d’autre n’est imposé. Ni nom de sous-dossier, ni découpage, ni organisation. Vous pouvez écrire un thème entier dans ce seul fichier.
Découper, parce que c’est plus pratique
Un thème sérieux fait vite des milliers de lignes. On le découpe donc en plusieurs fichiers XML, rangés comme on veut, que theme.xml inclut :
mon-theme/
theme.xml ← le seul nom imposé
variables.xml
views/
system.xml
detailed.xml
menu.xml
data/
fonts/
images/
Ces noms-là sont les vôtres : appelez-les comme vous voulez. L’usage, chez les thèmes publiés comme à l’export du studio, est de les écrire en anglais — views/, data/, fonts/ — parce que c’est la langue des balises qu’ils contiennent.
Le mécanisme d’inclusion est expliqué dans Découper son thème.
Les chemins
Un chemin écrit dans un fichier est relatif à ce fichier. Dès qu’on découpe en sous-dossiers, cela devient une source d’erreurs.
Passez par ${root}, qui désigne toujours la racine du thème :
<path>${root}/data/images/fond.jpg</path> <!-- ✅ marche depuis n’importe quel fichier -->
<path>../data/images/fond.jpg</path> <!-- fragile : dépend d’où est écrite la ligne --> L’en-tête : la balise <theme>
Ce qui identifie votre thème — et qui ne va QUE dans theme.xml.
<theme> est la racine de tout fichier XML d’un thème. Mais ses attributs d’identité n’ont de sens que dans theme.xml : c’est ce fichier-là que lit le gestionnaire de thèmes pour savoir de quoi il s’agit.
À retenir Dans
theme.xml, renseignez-les tous : sans eux, votre thème s’affiche sans nom, sans version, et le gestionnaire ne sait pas sur quels écrans il fonctionne. Dans les autres fichiers, écrivez<theme>nu : les répéter n’apporte rien et peut semer la confusion.
Les attributs
| Attribut | Rôle | Exemple | Si absent |
|---|---|---|---|
name | Nom affiché dans la liste des thèmes | name="Mon Thème" | le nom du dossier |
version | Version du thème | version="1.2" | non affichée |
author | L’auteur | author="Benoît" | non affiché |
recalbox | Version minimale de Recalbox exigée | recalbox="10.0" | toutes versions |
compatibility | Types d’écran gérés : hdmi, crt, jamma, tate | compatibility="hdmi,crt" | hdmi |
resolutions | Définitions gérées : qvga, vga, hd, fhd | resolutions="hd,fhd" | fhd,hd |
compatibility et resolutions sont exactement les pictogrammes affichés par le gestionnaire de thèmes : HDMI / CRT / JAMMA / TATE d’un côté, SD 240p / SD+ 480p / HD 720p / FULLHD 1080p de l’autre. Les annoncer faux, c’est promettre ce que le thème ne tient pas.
⚠️ Sans eux, votre thème ne peut pas être publié dans le gestionnaire de thèmes : un nom, une version et un auteur sont exigés. Voir Partager son thème.
Les deux cas
Dans theme.xml — la balise complète :
<theme name="Mon Thème" version="1.2" author="Benoît"
recalbox="10.0" compatibility="hdmi,crt" resolutions="hd,fhd">
…
</theme>
Dans tous les autres fichiers du thème — la balise nue :
<theme>
<view name="system"> … </view>
</theme> Découper son thème : <include>
Un thème sérieux ne tient pas dans un seul fichier.
<include> charge un autre fichier à cet endroit précis, comme si son contenu était recopié là.
<include>${root}/views/system.xml</include>
<include path="${root}/views/detailed.xml" />
Les deux écritures fonctionnent : le chemin en contenu de la balise, ou en attribut path.
L’ordre compte
Recalbox lit de haut en bas, et deux composants de même nom se remplacent : le dernier gagne.
C’est tout le mécanisme des surcouches — et c’est ainsi que sont bâties les options du thème : chaque choix est un fichier chargé par-dessus, qui ne redéfinit que ce qui change. Voir Ce qu’est une option.
<include>${root}/views/base.xml</include> <!-- pose le fond en bleu -->
<include>${root}/options/rouge.xml</include> <!-- le repasse en rouge -->
Inclure sous condition
Un <include> accepte if=, comme un composant : le fichier n’est chargé que si la condition est vraie.
<include if="crt">${root}/views/system-crt.xml</include>
C’est ainsi qu’on sert une mise en page différente selon l’écran, sans dupliquer tout le reste. La liste des conditions est dans Afficher sous condition.
Les composants libres : extra="true" et <extras>
Pourquoi un composant que vous ajoutez ne s’affiche pas — et les deux façons de le déclarer.
Deux familles de composants
Dans une vue, Recalbox distingue :
- les éléments qu’il construit lui-même — le carrousel, la liste de jeux, la barre d’aide, la jaquette du jeu… Ils ont un nom réservé, et le thème ne fait que les régler ;
- les composants que vous ajoutez — un texte, une image, un bloc de couleur, une vidéo. Ce sont les composants libres.
Un composant libre doit être déclaré comme tel
Un composant libre posé directement dans la vue n’est pas dessiné : la machine ne construit que les éléments marqués extra. Deux écritures possibles, et elles font exactement la même chose :
<image name="monLogo" extra="true">…</image>
<extras>
<image name="monLogo">…</image>
<text name="maMention">…</text>
</extras>
<extras> est un conteneur : chacun de ses enfants reçoit extra="true", d’un coup. C’est plus court et plus lisible dès qu’il y a plusieurs composants — c’est ce que le studio écrit.
Ce qu’il faut retenir
<extras>n’est pas un composant : il ne se dessine pas, il ne se positionne pas.- Il ne se met pas dans lui-même : un
<extras>dans un<extras>n’a aucun effet supplémentaire. - Il fonctionne dans la vue Systèmes, la Liste de jeux et l’Écran de veille. ⚠️ Pas dans le Menu : le moteur ne va y chercher aucun composant libre. Un
<extras>écrit pour la vue Menu est lu sans erreur… et ne dessine jamais rien. - Les éléments à nom réservé restent en dehors : ils existent déjà, on ne fait que les régler.
Dans le studio, vous n’avez rien à faire : les composants que vous ajoutez sont automatiquement écrits dans
<extras>, et les éléments réservés en dehors.
Vos propres variables : <variables>
Ce qui s’écrit dans le fichier : la balise, les règles de nom, la portée.
Cette page décrit ce qui s’écrit dans le fichier. Pour les créer sans écrire une ligne de XML, voyez Vos variables sur mesure, dans « Les données dynamiques ».
C’est le mécanisme le plus utile d’un thème un peu sérieux, et pourtant le moins connu.
<variables>
<variable name="CouleurPrincipale" value="2E447C" />
<variable name="PoliceTitre" value="${root}/data/fonts/Exo2.otf" />
<variable name="Alpha50" value="80" />
</variables>
Ensuite, partout dans le thème :
<box name="fond">
<color>${CouleurPrincipale}</color>
</box>
<text name="titre">
<fontPath>${PoliceTitre}</fontPath>
<color>${CouleurPrincipale}${Alpha50}</color>
</text>
Changez la valeur en un seul endroit, tout le thème suit. C’est ce qui rend les options de couleur possibles : un fichier de surcouche redéfinit la variable, et rien d’autre.
Les règles
nameetvaluesont obligatoires tous les deux ; sans l’un des deux, la ligne est ignorée et signalée dans le journal ;- un
namevide est refusé ; - une variable peut en contenir une autre, y compris une variable de Recalbox :
<variable name="CheminLogo" value="${root}/data/logos/${system.name}.svg" />
- le bloc
<variables>accepte une condition, et chaque<variable>aussi :
<variables if="crt">
<variable name="TailleTitre" value="0.09" />
</variables>
- on peut avoir plusieurs blocs
<variables>, dans n’importe quel fichier ; - une variable redéfinie plus loin écrase la précédente — comme les composants.
⚠️ Le bloc se déclare tout en haut
<variables>vaut pour tout ce qui est lu APRÈS lui.
Recalbox remplace chaque ${nom} à mesure qu’il lit les fichiers. Une variable déclarée au sommet du thème vaut donc partout ; déclarée au milieu, elle ne vaut que pour la suite.
D’où la règle, valable pour tous les thèmes : le bloc des variables en premier, avant les vues, avant tout le reste.
Une variable redéfinie plus loin remplace la précédente pour la suite de la lecture — c’est ce qui permet à un choix d’option de recolorer tout un thème, à condition d’être chargé avant les vues. Voir Déclarer une option.
Où les déclarer
Dans theme.xml, avant les <include> qui s’en servent : le moteur lit les variables d’un fichier avant d’en traiter les inclusions.
L’usage, repris du thème officiel, est de leur donner leur propre fichier — variables.xml — inclus en tout premier :
<include>${root}/variables.xml</include> Les trois façons d’écrire une propriété
Balise, balise à valeur, ou attribut — et pourquoi ça change tout.
Une même propriété peut s’écrire de trois manières. Elles sont équivalentes… sauf sur un point décisif.
1. En balise enfant
<image name="fond">
<path>./data/fond.jpg</path>
</image>
La forme historique. La plus lisible dès qu’un composant a plusieurs propriétés.
2. En balise enfant à attribut value
<image name="fond">
<path value="./data/fond.jpg" />
</image>
Strictement équivalente à la première.
3. En attribut du composant
<image name="fond" path="./data/fond.jpg" pos="0 0" size="1 1" />
Tout tient sur une ligne. Très pratique pour les composants simples.
La différence qui compte
Seules les formes 1 et 2 acceptent une condition sur UNE propriété.
<image name="fond">
<path if="crt">./data/fond-crt.jpg</path>
<path if="!crt">./data/fond-hd.jpg</path>
</image>
Impossible en forme 3 : if= sur la balise mère conditionnerait le composant entier, pas une de ses propriétés.
Elles se mélangent librement
C’est le point important : vous n’avez pas à choisir une façon et vous y tenir. Le lecteur de Recalbox les accepte toutes les trois, y compris dans le même composant.
<image name="fond" pos="0 0" size="1 1" zIndex="1">
<path if="crt">${root}/images/fond-crt.jpg</path>
<path if="!crt">${root}/images/fond-hd.jpg</path>
<color value="FFFFFFC0" />
</image>
Trois écritures dans le même composant : les valeurs simples en attributs sur la première ligne, celle qui a des variantes en balises conditionnelles, et une dernière en balise à value. C’est parfaitement valide, et c’est même ce qu’on écrit en pratique.
➡️ Règle simple : une seule valeur → attribut ; des variantes → balise enfant.
Ratio, pourcentage ou pixels
Les trois écritures acceptées pour une position ou une taille.
Plusieurs propriétés s’écrivent avec deux nombres séparés d’une espace : x y. C’est le cas de pos, size, maxSize, origin, rotationOrigin, logoSize et reflection.
Chaque nombre accepte trois écritures, et on peut les mélanger dans la même paire.
| Écriture | Exemple | Ce que ça veut dire |
|---|---|---|
| Ratio (par défaut) | 0.5 0.25 | une proportion de l’écran, de 0 à 1 |
| Pourcentage | 50% 25% | la même chose, écrit autrement |
| Pixels | 960p 270p | des pixels réels, suffixe p |
<pos>0.5 0.5</pos> <!-- le centre -->
<pos>50% 50%</pos> <!-- exactement pareil -->
<pos>960p 540p</pos> <!-- le centre… d’un écran 1920×1080 seulement -->
Ratio ou pourcentage : à préférer
x est une proportion de la largeur, y de la hauteur. Un composant en 0.5 0.5 est au centre d’un 1920×1080 comme d’un 640×480. C’est ce qui permet à un thème de tenir sur plusieurs écrans.
Les pixels : une position fixe
Un pixel est un pixel. 960p, c’est 960 pixels depuis le bord gauche, un point.
Cela ne s’adapte à rien : sur un écran qui fait moins de 960 pixels de large, le composant est hors de l’écran. C’est une position absolue, à l’opposé du ratio.
Les pixels ne se justifient donc que pour ce qui doit rester fixe quelle que soit la définition : l’épaisseur d’un filet, un décalage de quelques points. Pour tout le reste, prenez le ratio.
Le cas particulier de fontSize
fontSize ne prend qu’un seul nombre, et son unité dépend de sa valeur :
| Valeur | Ce que ça veut dire |
|---|---|
< 1 | une proportion du petit côté de l’écran — 0.05 = 5 % de la hauteur en 16:9 |
>= 1 | une taille de référence 240p, que Recalbox multiplie selon l’écran |
Au-dessus de 1, la valeur n’est pas des pixels. C’est une taille pensée pour un écran de 240 lignes, multipliée d’un cran à chaque palier de définition : ×1 jusqu’à 288 pixels de petit côté, ×2 jusqu’à 576, ×4 en 1080p. <fontSize>8</fontSize> fait donc 16 pixels sur un écran 480p et 32 pixels en 1080p. La même règle vaut partout — textes du menu compris.
<fontSize>0.045</fontSize> <!-- 4,5 % du petit côté : s’adapte partout, à préférer -->
<fontSize>8</fontSize> <!-- taille « 240p » : 16 px en 480p, 32 px en 1080p --> Position, taille, origine, rotation
Placer un composant exactement où on veut.
pos et origin vont toujours ensemble
C’est le point qu’il faut avoir compris avant tout le reste, parce que les deux ne parlent pas de la même chose :
pos— où, dans l’ÉCRAN, on pose le composant.origin— quel point DU COMPOSANT on pose à cet endroit.
L’un est une position dans l’écran, l’autre un point du composant. C’est la rencontre des deux qui décide de l’endroit où le composant apparaît.
pos — une position dans l’écran
<pos>0.1 0.2</pos>
Ces deux nombres se mesurent depuis le coin haut-gauche de l’écran : 0 0 est ce coin, 1 1 le coin bas-droit.
Le plus simple est de les lire comme des pourcentages — c’est exactement la même chose : 0.1 = 10 %, 0.2 = 20 %, 0.5 = 50 %. Donc pos 0.1 0.2, c’est 10 % de la largeur et 20 % de la hauteur.
pos ne dit rien du composant lui-même : c’est un simple point dans l’écran. Ce qui décide de quelle partie du composant vient s’y poser, c’est origin.
origin — quel point DU COMPOSANT
origin répond à l’autre moitié de la question : maintenant qu’on sait où dans l’écran, quelle partie du composant vient s’y poser ?
Ces neuf valeurs sont les plus courantes, mais n’importe quelle paire entre 0 et 1 fonctionne :
Par défaut, origin vaut 0 0 — le coin haut-gauche du composant. C’est pour cela qu’un composant sans origin s’étend vers la droite et vers le bas à partir de sa pos.
L’exemple qui fait tout comprendre
Prenons une image paysage dans un écran 16:9 :
<pos>0.5 0.5</pos>
<origin>1 1</origin>
pos 0.5 0.5, c’est le centre de l’écran. On pourrait croire que l’image y sera centrée. Elle ne l’est pas : origin 1 1 désigne le coin bas-droit de l’image, et c’est ce coin-là qu’on vient poser au centre.
L’image se retrouve donc entièrement en haut à gauche du centre.
Pour centrer vraiment
<pos>0.5 0.5</pos>
<origin>0.5 0.5</origin>
Cette fois c’est le centre de l’image qu’on pose au centre de l’écran.
Le tableau complet
origin | Le point du composant posé sur pos |
|---|---|
0 0 | coin haut-gauche — la valeur par défaut |
0.5 0 | milieu du bord haut |
1 0 | coin haut-droit |
0 0.5 | milieu du bord gauche |
0.5 0.5 | le centre |
1 0.5 | milieu du bord droit |
0 1 | coin bas-gauche |
0.5 1 | milieu du bord bas |
1 1 | coin bas-droit |
À quoi ça sert vraiment
Sans origin, tout composant est posé par son coin haut-gauche : pour centrer quelque chose, il faudrait calculer 0.5 − largeur/2, et recalculer à chaque fois que la largeur change.
Avec origin, on ne calcule plus rien :
| Ce qu’on veut | pos | origin |
|---|---|---|
| centré à l’écran | 0.5 0.5 | 0.5 0.5 |
| collé au bord droit | 1 … | 1 … |
| collé au bord bas | … 1 | … 1 |
| centré en bas | 0.5 1 | 0.5 1 |
C’est particulièrement utile pour un logo de système, dont la largeur change d’une machine à l’autre : avec origin, il reste aligné quoi qu’il arrive.
size — la taille imposée
<size>0.3 0.2</size>
Le composant fait exactement cette taille. Pour une image, cela veut dire qu’elle est déformée pour remplir la boîte.
N’en donner qu’une seule
Mettez 0 à l’autre : la dimension manquante se calcule pour conserver les proportions.
<size>0.3 0</size> <!-- 30 % de large, hauteur proportionnelle -->
⚠️ Ce n’est pas la même chose que keepratio ou maxSize, même si les deux conservent les proportions :
| Écriture | Ce qui est garanti |
|---|---|
size 0.3 0 | la largeur fait exactement 30 % ; la hauteur suit, quelle qu’elle soit — elle peut déborder |
maxSize 0.3 0.2 | l’image tient dans la boîte : la dimension la plus contraignante gagne, la largeur peut donc être réduite |
Autrement dit : size avec un zéro impose une contrainte, maxSize en impose deux.
maxSize — la taille maximale, sans déformer
<maxSize>0.3 0.2</maxSize>
L’image est agrandie ou réduite en conservant ses proportions pour tenir dans la boîte. Elle en occupe donc rarement toute la surface.
Pour un logo de système, c’est presque toujours
maxSizequ’il faut. Les logos n’ont pas la même forme d’une machine à l’autre : avecsize, certains seraient écrasés.
size accompagné de keepratio donne le même résultat que maxSize — et c’est souvent l’écriture qu’on préfère, parce qu’elle dit la taille voulue au lieu d’une limite :
<size>0.3 0.2</size>
<keepratio>true</keepratio> <!-- ⚠️ tout en minuscules -->
maxSize n’existe que sur image et video.
rotation et rotationOrigin
<rotation>90</rotation>
<rotationOrigin>0.5 0.5</rotationOrigin>
rotation est en degrés, sens horaire. rotationOrigin désigne le point autour duquel le composant pivote, dans ses propres proportions. Sans rotationOrigin, le pivot est le coin haut-gauche (0 0) — écrivez 0.5 0.5 pour tourner autour du centre.
La rotation s’applique aux images, aux blocs de couleur, aux vidéos et aux blocs Markdown. Un texte tourné se dessine aux petites inclinaisons mais disparaît à 90° ; un texte défilant tourné ne dessine que son fond, jamais son texte. Pour un titre vertical, passez par une image ou un bloc Markdown.
La profondeur (
zIndex) et l’extinction (disabled) ont leur propre page : Profondeur et visibilité.
Profondeur et visibilité
Ce qui passe devant quoi, et comment éteindre un composant.
Deux propriétés que tout composant accepte, et qui ne parlent ni de position ni de taille : laquelle passe devant l’autre, et laquelle ne s’affiche pas du tout.
zIndex — les calques
<zIndex>40</zIndex>
C’est un système de calques, exactement comme dans un logiciel de dessin : chaque composant est une feuille, et zIndex dit dans quel ordre elles sont empilées. Plus le nombre est grand, plus le composant est près de vous.
Les composants réservés ont leurs valeurs par défaut, et les composants libres reçoivent 10 si vous ne dites rien. Laissez de la place entre les vôtres — 10, 20, 30 — pour pouvoir en glisser un entre deux plus tard.
disabled — éteindre un composant
<disabled>true</disabled>
Le composant est lu mais pas affiché. Pratique pour masquer un composant réservé dont on ne veut pas, sans avoir à le redéfinir entièrement.
⚠️ Ni carousel ni textlist ne l’acceptent.
Les couleurs et les dégradés
RRGGBB, transparence, et les huit coins.
L’écriture
Une couleur s’écrit en hexadécimal, sans dièse :
<color>2E447C</color> <!-- opaque -->
<color>2E447C80</color> <!-- à moitié transparent -->
- 6 caractères :
RRGGBB, opaque ; - 8 caractères :
RRGGBBAA, les deux derniers donnent l’opacité —00invisible,80à moitié,FFopaque.
Les valeurs de transparence les plus utiles
Le code d’une couleur, on le connaît. Les deux caractères de l’opacité, beaucoup moins :
| Opacité | À écrire | Opacité | À écrire |
|---|---|---|---|
| 0 % — invisible | 00 | 60 % | 99 |
| 10 % | 1A | 70 % | B3 |
| 20 % | 33 | 75 % | BF |
| 25 % | 40 | 80 % | CC |
| 30 % | 4D | 90 % | E6 |
| 40 % | 66 | 95 % | F2 |
| 50 % | 80 | 100 % — opaque | FF |
Le calcul, si votre valeur n’y est pas : le pourcentage × 255, écrit en hexadécimal.
Les dégradés
box et image acceptent une couleur par bord ou par coin. Il suffit d’en donner deux pour obtenir un dégradé.
| Propriété | Effet |
|---|---|
colorTop + colorBottom | dégradé vertical |
colorLeft + colorRight | dégradé horizontal |
colorTopLeft, colorTopRight, colorBottomLeft, colorBottomRight | dégradé aux quatre coins |
<box name="ombre-du-haut">
<pos>0 0</pos>
<size>1 0.3</size>
<colorTop>00000080</colorTop>
<colorBottom>00000000</colorBottom>
</box>
Un dégradé du noir à moitié transparent vers le transparent complet : le classique voile sous lequel un titre reste lisible quelle que soit l’image derrière.
Teinter une image
Sur un composant image, color ne remplit pas : elle multiplie l’image. Une image blanche prend donc exactement la couleur donnée — c’est ainsi qu’on recolore une icône sans refaire le fichier.
<image name="etoile">
<path>${root}/data/arts/etoile-blanche.svg</path>
<color>FFC24B</color> <!-- l’étoile devient dorée -->
</image> Conditionner, traduire, régionaliser
Une valeur différente selon l’écran, la machine ou la langue.
Une condition sur une propriété
<text name="titre">
<fontSize if="crt">0.09</fontSize>
<fontSize if="!crt">0.05</fontSize>
<color>FFFFFF</color>
</text>
Un seul composant, deux tailles selon l’écran. La liste des conditions est dans la page Afficher sous condition.
Une condition sur le composant entier
<image name="filtre" if="crt">
<path>${root}/data/arts/scanlines.png</path>
</image>
Le composant n’existe que si la condition est vraie. La liste complète est dans Afficher sous condition, et Combiner plusieurs conditions explique et, ou et les parenthèses.
ifexists et ifnotexists
Ces deux-là ne testent pas la machine mais la présence d’un fichier :
<image name="jaquette">
<path ifexists="${game.media.imagepath}">${game.media.imagepath}</path>
<path ifnotexists="${game.media.imagepath}">${root}/images/pas-dimage.png</path>
</image>
C’est la façon propre de gérer les jeux sans jaquette — sans quoi la case reste vide.
Traduire un texte
Un suffixe de langue sur la propriété suffit :
<text name="bienvenue">
<text>Bienvenue</text>
<text.en>Welcome</text.en>
<text.es>Bienvenido</text.es>
</text>
Recalbox prend la variante qui correspond, et la version sans suffixe si aucune ne correspond.
Ce qu’un suffixe accepte
| Suffixe | Ce qu’il vise |
|---|---|
.fr .es .de | la langue de la machine, en minuscules |
.fr_FR | la langue et le pays |
.US .EU .JP | la région, en MAJUSCULES |
La région
C’est ce qui permet d’écrire « Genesis » aux États-Unis et « Mega Drive » en Europe. Même mécanisme que la langue, avec un suffixe en majuscules — c’est ce qu’emploie le thème officiel de Recalbox :
<box name="fond"
color.US="2E447C"
color.EU="7C2E44"
color.JP="447C2E" />
Les trois valeurs sont US, EU et JP. Une propriété sans suffixe vaut pour toutes les régions.
⚠️ Il y a toujours une région active. Sur une machine neuve c’est US : ce que vous écrivez sans suffixe est ce que verra la plupart des gens, et .EU ou .JP ne servent qu’à s’en écarter.
La région se change sur la machine, dans les propriétés de l’interface — et Recalbox relit alors le thème en entier, comme pour une option. Le studio propose le même choix, pour voir ce que verra chaque public.
Sur quoi ça marche : sur TOUT
Il n’existe aucune liste de propriétés traduisibles. Le suffixe est examiné sur chaque propriété avant même que le moteur sache de laquelle il s’agit — donc toutes l’acceptent :
<text name="titre" text.fr="Bienvenue" text.es="Bienvenido" />
<image name="logo" path.US="genesis.png" path.EU="megadrive.png" />
<text name="mention" fontSize.de="0.03" /> <!-- l’allemand est plus long -->
<box name="bandeau" color.JP="D62828" />
Les deux écritures le supportent — en attribut (size.fr="…") comme en sous-nœud (<size.fr>…</size.fr>) — et même le nom de la balise (<text.fr name="…">). Il vaut aussi pour le title et le help d’une option.
⚠️ La version localisée gagne définitivement. Dès qu’une variante a été appliquée, la version sans suffixe de la même propriété est refusée, y compris dans un fichier lu plus tard. Une surcouche ne peut donc pas « reprendre la main » sur une propriété déjà localisée : elle doit fournir sa propre variante localisée.
Charger un texte depuis un fichier
text, scrolltext et markdown acceptent path à la place de text : le contenu est alors lu dans le fichier. Pratique pour un long texte de présentation.
<markdown name="apropos">
<path>${root}/data/textes/apropos.md</path>
</markdown> Nommer et réutiliser
Le nom d’un composant, et comment en créer plusieurs d’un coup.
Deux choses à ne pas confondre
<image name="mon-fond">
imageest le type de composant : il décide de ce que le composant sait faire et des propriétés qu’il accepte. Il doit être choisi dans la liste des types existants — voir Les composants. Un type inventé est ignoré ;nameest votre étiquette. Elle est libre… sauf si vous reprenez un des noms réservés par Recalbox, auquel cas le composant est relié au moteur — voir Les vues et leurs composants.
Deux composants de même name dans la même vue n’en font qu’un : le second complète ou remplace le premier. C’est voulu — c’est le mécanisme des surcouches — mais c’est une source d’erreurs quand on nomme deux décors « fond » sans y penser.
Créer plusieurs composants d’un coup
<box name="bande1, bande2, bande3, bande4">
<size>0.01 1</size>
<color>FFFFFF20</color>
</box>
Quatre composants, mêmes propriétés. Il reste à leur donner leur position individuelle plus loin :
<box name="bande1"><pos>0.90 0</pos></box>
<box name="bande2"><pos>0.92 0</pos></box>
Modifier sans tout réécrire
Comme le dernier gagne, il suffit de redéclarer la seule propriété qui change :
<include>${root}/views/base.xml</include>
<view name="system">
<box name="fond"><color>7C2E44</color></box> <!-- le reste est conservé -->
</view> Tous les composants
Ce qui existe, et ce que chacun sait faire.
Un composant se déclare par son type — le nom de la balise. Un type qui n’est pas dans cette liste est ignoré par Recalbox et signalé dans le journal.
Afficher quelque chose
| Type | Ce qu’il fait |
|---|---|
text | un texte, sur une ou plusieurs lignes |
scrolltext | un texte qui défile quand il est trop long |
markdown | un texte mis en forme (gras, titres, listes) |
image | une image |
video | une vidéo |
box | un bloc de couleur, ou un dégradé |
datetime | une date |
rating | une note, en étoiles |
sound | un son (aucun affichage) |
Listes et navigation
| Type | Ce qu’il fait |
|---|---|
textlist | la liste des jeux |
carousel | le carrousel des systèmes |
helpsystem | la barre d’aide en bas de l’écran |
L’habillage des menus
Ces neuf-là ne se placent pas : ils règlent l’apparence des menus que Recalbox construit lui-même.
menuBackground · menuIcons · menuText · menuTextSmall · menuSection · menuSwitch · menuSlider · menuButton · menuSize
L’habillage du clavier virtuel
keyboard ne se place pas non plus : il donne ses couleurs et sa police au clavier que Recalbox ouvre pour une recherche ou une saisie. Il se règle dans Composants globaux.
Attention à la casse
Les noms sont sensibles à la casse. menuswitch ne fonctionne pas, il faut menuSwitch. Une seule propriété fait exception en s’écrivant tout en minuscules : keepratio.
text, scrolltext, markdown
Les trois façons d’afficher du texte.
text — le texte courant (18 propriétés)
<text name="titre">
<pos>0.06 0.08</pos>
<size>0.5 0.1</size>
<text>${system}</text>
<fontPath>${root}/data/fonts/Exo2.otf</fontPath>
<fontSize>0.05</fontSize>
<color>FFFFFF</color>
<alignment>left</alignment>
<forceUppercase>true</forceUppercase>
</text>
| Propriété | Type | Rôle |
|---|---|---|
pos size origin rotation rotationOrigin | paire | placement — voir Position, taille, origine |
text | texte | le contenu, variables comprises |
path | chemin | lit le contenu dans un fichier, au lieu de text |
fontPath | chemin | la police |
fontSize | nombre | < 1 = ratio de hauteur d’écran, >= 1 = pixels |
fontStyle | texte | normal, bold, italic, bolditalic |
color | couleur | la couleur du texte |
backgroundColor | couleur | un fond derrière le texte |
alignment | texte | voir ci-dessous |
forceUppercase | oui/non | tout en capitales |
lineSpacing | nombre | interligne, 1.2 par défaut |
multiline | oui/non | autoriser le retour à la ligne |
zIndex | nombre | profondeur |
disabled | oui/non | éteindre le composant |
alignment agit sur deux axes
La valeur combine l’horizontale et la verticale.
Neuf positions, treize façons de les écrire — quatre valeurs sont des synonymes :
| Position | À écrire | Synonyme |
|---|---|---|
| haut-gauche | topleft | |
| haut-centre | topcenter | top |
| haut-droite | topright | |
| centre-gauche | centerleft | left |
| centre | center | |
| centre-droite | centerright | right |
| bas-gauche | bottomleft | |
| bas-centre | bottomcenter | bottom |
| bas-droite | bottomright |
⚠️ Une valeur inconnue ne laisse pas l’alignement précédent : elle retombe sur centre-gauche, la valeur par défaut.
Le texte est aligné dans sa boîte size : sans size, l’alignement n’a rien pour s’exercer.
scrolltext — le texte qui défile (16 propriétés)
Mêmes propriétés que text, sans multiline et lineSpacing. Le texte défile horizontalement quand il dépasse de sa boîte.
<scrolltext name="titre-long">
<size>0.4 0.06</size>
<text>${game.name}</text>
<fontSize>0.04</fontSize>
</scrolltext>
À réserver aux valeurs dont on ne maîtrise pas la longueur — un nom de jeu, un développeur.
markdown — le texte mis en forme (15 propriétés)
<markdown name="synopsis">
<pos>0.06 0.5</pos>
<size>0.4 0.35</size>
<text>${game.synopsis}</text>
<fontSize>0.028</fontSize>
<color>C6CBD8</color>
</markdown>
Mêmes propriétés que text, sans fontStyle, backgroundColor ni multiline.
Il comprend une mise en forme simple dans le texte : **gras**, *italique*, # titre, listes à tirets. Utile pour un synopsis ou une page « à propos ».
⚠️
markdownne gère pas le défilement : un texte plus long que sa boîte est coupé.
image et video
Afficher une image ou une vidéo, et les teinter.
image (21 propriétés)
<image name="jaquette">
<pos>0.75 0.4</pos>
<origin>0.5 0.5</origin>
<maxSize>0.35 0.4</maxSize>
<path>${game.media.imagepath}</path>
<zIndex>30</zIndex>
</image>
| Propriété | Type | Rôle |
|---|---|---|
pos size origin rotation rotationOrigin | paire | placement |
maxSize | paire | taille maximale sans déformer — voir Position, taille |
keepratio | oui/non | conserver les proportions (⚠️ tout en minuscules) |
path | chemin | le fichier image |
tile | oui/non | répéter l’image en mosaïque au lieu de l’étirer |
color | couleur | teinte l’image (multiplication) |
colorTop colorBottom colorLeft colorRight | couleur | teinte en dégradé |
colorTopLeft colorTopRight colorBottomLeft colorBottomRight | couleur | teinte aux quatre coins |
reflection | paire | un reflet sous l’image : opacité de départ et de fin |
zIndex disabled | profondeur, extinction |
Formats
PNG, JPG et SVG. Le SVG est recommandé pour les logos : il reste net à toutes les tailles.
tile — la texture répétée
<image name="grille">
<size>1 1</size>
<path>${root}/data/arts/motif.png</path>
<tile>true</tile>
</image>
L’image garde sa taille d’origine et se répète pour couvrir la boîte.
reflection
<reflection>0.5 0.0</reflection>
Ajoute sous l’image un reflet retourné, de 50 % d’opacité en haut à 0 % en bas.
video (15 propriétés)
<video name="md_video">
<pos>0.75 0.4</pos>
<origin>0.5 0.5</origin>
<maxSize>0.35 0.3</maxSize>
<delay>1.5</delay>
<loops>0</loops>
</video>
Mêmes propriétés de placement que image, plus :
| Propriété | Type | Rôle |
|---|---|---|
delay | nombre | secondes avant que la vidéo démarre |
loops | nombre | nombre de lectures ; 0 = en boucle |
animations | texte | l’effet d’apparition |
link | texte | lier la lecture à un autre composant |
reflection | paire | reflet, comme sur image |
video n’accepte ni tile ni les couleurs de teinte.
Un
delayd’une à deux secondes évite que la vidéo se déclenche à chaque jeu simplement traversé en faisant défiler la liste.
box — le bloc de couleur
Fonds, voiles, bandes et dégradés.
Le composant le plus simple, et l’un des plus utiles : fonds, voiles, bandes, séparateurs. 16 propriétés.
<box name="fond" extra="true">
<pos>0 0</pos>
<size>1 1</size>
<color>101820</color>
<zIndex>1</zIndex>
</box>
| Propriété | Rôle |
|---|---|
pos size origin rotation rotationOrigin | placement |
color | couleur unie |
colorTop colorBottom | dégradé vertical |
colorLeft colorRight | dégradé horizontal |
colorTopLeft colorTopRight colorBottomLeft colorBottomRight | dégradé aux quatre coins |
zIndex disabled | profondeur, extinction |
Pas de path : une box n’affiche pas d’image. Pour un fond en image, prenez image.
Le voile de lisibilité
Le cas d’usage le plus fréquent : rendre un texte lisible sur une image quelconque.
<box name="voile-du-bas" extra="true">
<pos>0 0.7</pos>
<size>1 0.3</size>
<colorTop>00000000</colorTop>
<colorBottom>000000C0</colorBottom>
<zIndex>20</zIndex>
</box>
Du transparent vers le noir à 75 % : le bas de l’écran s’assombrit progressivement, et le texte posé par-dessus reste lisible quelle que soit l’illustration derrière.
Une bande fine
<box name="filet" extra="true">
<pos>0.06 0.18</pos>
<size>0.3 2p</size>
<color>FFFFFF40</color>
</box>
2p = deux pixels de haut, quelle que soit la définition : c’est l’un des rares cas où l’unité en pixels est le bon choix.
textlist — la liste des jeux
La liste où l’on choisit son jeu : ses couleurs, son surligneur, sa police.
⚠️ Cette liste ne se pose pas où l’on veut.
textlistn’existe que dans la vue Jeux, sous le nom réservégamelist. Ailleurs — ou sous un autre nom — elle n’est pas construite, et vous ne pouvez pas en ajouter une seconde.
Les propriétés (19)
<textlist name="gamelist">
<pos>0.05 0.2</pos>
<size>0.4 0.7</size>
<primaryColor>C6CBD8</primaryColor>
<secondaryColor>8A92A6</secondaryColor>
<selectedColor>101820</selectedColor>
<selectorColor>4FE3C1</selectorColor>
<selectorHeight>0.055</selectorHeight>
<fontSize>0.035</fontSize>
<horizontalMargin>0.01</horizontalMargin>
</textlist>
| Propriété | Rôle |
|---|---|
pos size origin | placement |
primaryColor | la couleur des jeux |
secondaryColor | la couleur des dossiers |
selectedColor | la couleur du texte de la ligne choisie |
selectorColor | la couleur du surligneur |
selectorImagePath | une image de surligneur, à la place de la couleur |
selectorImageTile | répéter cette image en mosaïque |
selectorHeight | la hauteur du surligneur |
selectorOffsetY | son décalage vertical |
fontPath fontSize | la police |
alignment | l’alignement des lignes |
horizontalMargin | la marge à gauche et à droite |
forceUppercase | tout en capitales |
lineSpacing | l’interligne — c’est lui qui espace les lignes |
scrollSound | le son joué au défilement |
zIndex | profondeur |
⚠️ Pas de disabled : une liste de jeux ne s’éteint pas.
primaryColor et secondaryColor sont la source de confusion la plus fréquente : le second n’est pas la couleur alternée d’une ligne sur deux, c’est celle des dossiers.
carousel — le carrousel des systèmes
La bande qui fait défiler les systèmes : sens, taille des logos, et le mode texte.
Le carrousel affiche les systèmes, logo par logo. Il n’existe que dans la vue Systèmes, sous le nom réservé systemcarousel, et le moteur n’en construit qu’un seul. Ailleurs, ou sous un autre nom, il n’est pas construit du tout.
Le sens de défilement
type accepte horizontal (la valeur par défaut), vertical et vertical_wheel — la roue. Il n’existe pas de roue horizontale : toute autre valeur retombe silencieusement sur horizontal.
Les propriétés (28)
<carousel name="systemcarousel">
<type>vertical</type>
<pos>0 0</pos>
<size>0.25 1</size>
<color>00000020</color>
<logoSize>0.12 0.075</logoSize>
<logoScale>1.5</logoScale>
<maxLogoCount>7</maxLogoCount>
<logoAlignment>center</logoAlignment>
<defaultTransition>instant</defaultTransition>
</carousel>
| Propriété | Rôle |
|---|---|
type | horizontal, vertical, vertical_wheel |
pos size origin | placement |
color | le fond du carrousel |
logoSize | la taille d’un logo (paire) |
logoScale | l’agrandissement du logo choisi |
logoRotation logoRotationOrigin | rotation des logos (roues) |
logoAlignment | l’alignement des logos dans leur case |
maxLogoCount | combien de logos visibles à la fois |
defaultTransition | fade ou instant ; toute autre valeur donne slide |
fontPath fontSize fontColor | la police du mode texte |
forceUppercase | le nom des systèmes tout en capitales |
textOnly | écrire les noms au lieu des logos |
primaryColor secondaryColor | la couleur des noms |
selectedColor | la couleur du nom choisi |
selectorColor selectorHeight | le surligneur du mode texte |
selectorOffsetX selectorOffsetY | son décalage |
textOffsetX | le décalage du texte |
lineSpacing horizontalMargin | interligne et marges du mode texte |
zIndex | profondeur |
⚠️ Pas de disabled : le carrousel ne s’éteint pas.
⚠️ maxLogoCount n’est pas un nombre de logos affichés. Il ne sert qu’à l’espacement et au centrage : le moteur en dessine davantage, qui débordent puis sont écrêtés. Et la valeur est arrondie à l’entier — une décimale (2.5) ne sert donc à rien.
Les logos, ou les noms
Dans la rubrique Liste des systèmes, « CE QU’ON VOIT DÉFILER » choisit entre :
- Les logos — le comportement habituel ;
- Les noms — le carrousel écrit le nom de chaque système.
⚠️ Ce n’est pas un quatrième sens de défilement : le mode texte se combine avec horizontal, vertical et roue. Un carrousel de noms peut donc défiler dans n’importe quel sens.
Les propriétés qui vont avec
En mode texte, la police, sa taille et sa couleur se règlent dans la rubrique Texte. S’ajoutent deux décalages propres au carrousel :
- Décalage horizontal du surligneur (
selectorOffsetX) ; - Décalage horizontal du texte (
textOffsetX).
Ce que ça écrit
<carousel name="systemcarousel" type="vertical">
<textOnly>true</textOnly>
<textOffsetX>0.02</textOffsetX>
</carousel>
Disponible à partir de Recalbox 10.1. Sur une machine plus ancienne,
textOnlyest ignoré et le carrousel affiche les logos.
rating — la note en étoiles
La note du jeu, dessinée en étoiles : les deux images qui la composent.
Les propriétés (9)
<rating name="md_rating">
<pos>0.06 0.72</pos>
<size>0.12 0.024</size>
<filledPath>${root}/data/arts/etoile-pleine.svg</filledPath>
<unfilledPath>${root}/data/arts/etoile-vide.svg</unfilledPath>
</rating>
| Propriété | Rôle |
|---|---|
pos size origin rotation rotationOrigin | placement |
filledPath | l’image de l’étoile pleine |
unfilledPath | l’image de l’étoile vide |
zIndex disabled | profondeur, extinction |
size désigne la totalité des cinq étoiles. Une largeur de cinq fois la hauteur donne des étoiles carrées.
Pas de couleur : pour changer la teinte, changez les images — ou fournissez des images blanches et teintez-les… ce que rating ne permet pas. Deux fichiers sont donc nécessaires.
datetime — une date
La date de sortie d’un jeu, ou celle de la dernière partie.
Un datetime affiche une date que Recalbox connaît, jamais un texte libre. Son nom dit quelle date — md_releasedate la sortie du jeu, md_lastplayed la dernière partie — et sa propriété display dit sous quelle forme l’écrire.
<datetime name="md_releasedate">
<pos>0.06 0.66</pos>
<fontSize>0.028</fontSize>
<color>C6CBD8</color>
<display>date</display>
</datetime>
display — la forme de la date
| À écrire | Ce qui s’affiche |
|---|---|
date | 1991/06/23 |
dateTime | 1991/06/23 14:05:30 |
year | 1991 |
time | 14:05:30 |
realTime | l’heure qu’il est — pas une date du jeu |
RelativeToNow | « il y a 3 jours » |
⚠️ La casse compte. datetime ne fonctionne pas, il faut dateTime ; relativeToNow non plus, il faut RelativeToNow. Une valeur inconnue laisse la forme précédente et part dans themes.log.
Les propriétés (12)
| Propriété | Rôle |
|---|---|
pos size origin | placement |
display | la forme de la date |
color backgroundColor | couleurs |
fontPath fontSize | police |
alignment forceUppercase | mise en forme |
zIndex disabled | profondeur, extinction |
⚠️ Un datetime sans color est invisible : c’est le seul composant qui remet la couleur à zéro au lieu de garder la précédente. Et de alignment, seule la composante horizontale est retenue — le vertical est toujours centré.
Un jeu sans date affiche une ligne vide : c’est la donnée qui manque, pas le thème.
sound — la musique du thème
Un morceau, ou un dossier de morceaux, joué pendant la navigation.
Un sound ne se dessine pas : il n’a ni position, ni taille, ni profondeur. Il porte un chemin, et rien d’autre.
Deux noms, deux comportements :
| Nom | Ce que la machine fait |
|---|---|
bgsound | joue ce morceau |
directory | tire au sort dans ce dossier |
⚠️ La machine ne le lit que dans la vue Systèmes. Posé ailleurs, il ne joue jamais. En revanche, un fichier de système peut le redéfinir : c’est ainsi qu’on donne une musique par système.
La musique de l’utilisateur, si elle existe, passe avant celle du thème.
helpsystem — la barre d’aide
Les 32 icônes de boutons, une par une.
La barre en bas de l’écran qui rappelle à quoi servent les boutons. 38 propriétés : six de mise en forme, et 32 icônes.
Recalbox choisit lui-même ce que la barre annonce, écran par écran, et traduit chaque libellé. Votre thème ne règle que la forme : le placement, la police, les couleurs, et l’image de chaque pictogramme.
<helpsystem name="help">
<pos>0.02 0.955</pos>
<fontPath>${root}/data/fonts/Exo2.otf</fontPath>
<fontSize>0.025</fontSize>
<textColor>C6CBD8</textColor>
<iconColor>FFFFFF</iconColor>
<iconA>${root}/data/arts/boutons/a.svg</iconA>
<iconB>${root}/data/arts/boutons/b.svg</iconB>
</helpsystem>
Mise en forme
| Propriété | Rôle |
|---|---|
pos size | placement |
textColor | la couleur des libellés |
iconColor | la teinte des icônes — fournissez-les en blanc |
fontPath fontSize | la police |
Les 32 icônes
Remplacer une icône est facultatif : Recalbox fournit les siennes. Vous ne redéfinissez que celles que vous voulez.
Directions — iconUpDown, iconLeftRight, iconUpDownLeftRight
Boutons — iconA, iconB, iconX, iconY
Gâchettes — iconL, iconR, iconL2, iconR2, iconL3, iconR3, iconLR, iconL2R2, iconL3R3
Système — iconStart, iconSelect, iconHotkey
Combinaisons avec Hotkey — iconHkA, iconHkB, iconHkX, iconHkY, iconHkL, iconHkR, iconHkLeftRight
Joysticks — iconJ1UpDown, iconJ1LeftRight, iconJ1UpDownLeftRight, iconJ2UpDown, iconJ2LeftRight, iconJ2UpDownLeftRight
Une combinaison, c’est deux pictogrammes
Quand l’aide porte sur une combinaison, Recalbox dessine iconHotkey, puis la touche.
[HK] [A] Lancer le jeu
iconHkA est donc l’image du bouton A employé en combinaison — pas un dessin de « HK + A ». Fournissez-y un simple bouton : le raccourci est déjà devant.
iconHotkeycompte double : il apparaît devant chaque combinaison de la barre. C’est l’icône à soigner en premier si votre thème en affiche.
Le nom est imposé : help
Recalbox va chercher le composant nommé help, et lui seul. <helpsystem name="barre"> existera dans votre fichier sans jamais rien piloter.
La barre se règle vue par vue
Recalbox relit le <helpsystem> de la vue à chaque changement d’écran, et repart de ses propres icônes. Ce qu’une vue ne déclare pas revient donc au défaut de Recalbox — jamais à ce qu’une autre vue avait posé.
Deux manières de faire :
- le même jeu partout — déclarez la barre dans une vue qui couvre tout :
<view name="system, detailed, menu">; - une barre différente par écran — un
<helpsystem>dans chaque vue, avec ses propres images.
Fournissez des icônes blanches et servez-vous de
iconColor: un seul jeu de fichiers suffit alors pour toutes les variantes de couleur de votre thème.
Proposer plusieurs jeux d’icônes
C’est ce que font les grands thèmes : un jeu SNES, un jeu Xbox, un jeu PlayStation… et l’utilisateur choisit dans Menu → Interface → Thème.
Chaque jeu est un fichier qui ne contient que le <helpsystem> et ses images :
<!-- ./options/icones-snes.xml -->
<theme>
<view name="system, detailed, menu">
<helpsystem name="help">
<iconA>${root}/data/icones/snes/a.svg</iconA>
<iconB>${root}/data/icones/snes/b.svg</iconB>
</helpsystem>
</view>
</theme>
Et vous les proposez ainsi, sans rien déclarer d’autre :
<include subset="iconset" name="1 - SNES">${root}/options/icones-snes.xml</include>
<include subset="iconset" name="2 - Xbox">${root}/options/icones-xbox.xml</include>
iconsetest un nom réservé : Recalbox affiche « SELECT THEME’S ICONSET », traduit dans la langue de la machine. Pas besoin de balise<subset>pour le nommer.
L’habillage des menus
Les neuf composants qui règlent les menus de Recalbox.
Recalbox construit ses menus lui-même : leur contenu ne vous appartient pas. Le thème n’en règle que l’apparence, à travers neuf composants qui ne se placent pas.
menuBackground (3)
<menuBackground>
<color>101820F0</color>
<path>${root}/data/arts/cadre-menu.png</path>
<fadePath>${root}/data/arts/voile.png</fadePath>
</menuBackground>
| Propriété | Rôle |
|---|---|
color | la couleur du cadre — elle teinte l’image si path est donné |
path | l’image du cadre |
fadePath | l’image du voile qui assombrit la vue derrière |
menuText (6) — les lignes du menu
| Propriété | Rôle |
|---|---|
fontPath fontSize | la police |
color | le texte des lignes |
selectedColor | le texte de la ligne choisie |
selectorColor | le surligneur |
separatorColor | les filets entre les lignes |
menuTextSmall (5) — le petit texte
fontPath, fontSize, color, selectedColor, selectorColor. Sert aux valeurs des propriétés et aux listes déroulantes.
menuSection (5) — les intertitres
fontPath, fontSize, color, selectedColor, alignment.
menuSize (1)
<menuSize><height>0.85</height></menuSize>
La hauteur maximale du cadre du menu, en proportion de l’écran. C’est la seule mesure qu’un thème impose aux menus.
menuSwitch (2) — les interrupteurs
pathOn, pathOff — les deux images d’une propriété oui/non.
menuSlider (1) — les jauges
path — l’image du curseur.
menuButton (2) — les boutons
path, filledPath — l’état normal et l’état pressé.
menuIcons (23) — les icônes des rubriques
Une icône par rubrique du menu :
iconSystem · iconUpdates · iconThemes · iconGames · iconUI · iconTate · iconControllers · iconSound · iconNetwork · iconScraper · iconBios · iconDownload · iconLicense · iconAdvanced · iconArcade · iconKodi · iconCardReader · iconRecalboxRGBDual · iconQuit · iconRestart · iconShutdown · iconFastShutdown · iconList
⚠️
iconListest au singulier, contrairement à la rubrique « Listes » qu’elle représente. Au pluriel, elle est ignorée.
L’habillage du clavier virtuel
Les couleurs et la police du clavier qui s’ouvre pour chercher un jeu ou saisir un texte.
Comme les menus, il ne se place pas : c’est le clavier qui décide de sa géométrie, le thème n’en choisit que les couleurs et la police.
Il s’ouvre par-dessus l’écran où l’on se trouve, quel qu’il soit — on le règle donc une seule fois, dans Composants globaux.
| Propriété | Ce qu’il peint |
|---|---|
keyColor | le fond de chaque touche au repos |
keySelectedColor | la touche sur laquelle on est |
keyTextColor | la lettre écrite sur la touche |
keyDisabledColor | la lettre d’un caractère que la saisie refuse |
keyModifierColor | Maj / Ctrl / Alt enfoncée pour une seule touche |
keyModifierLockedColor | Maj / Ctrl / Alt verrouillée |
keyTitleColor | le titre au-dessus du clavier |
keyEditTextColor | le texte en train d’être tapé |
fontPath | la police — la taille reste décidée par le clavier |
Trois claviers, et c’est l’utilisateur qui choisit
Le thème ne décide pas lequel s’affiche : c’est une propriété de la console. Et ils ne lisent pas tous vos couleurs.
- Roue d’arcade — seulement quatre : touche survolée, lettre, titre, texte saisi. Ni fond de touche, ni police.
- Clavier classique — les huit, plus la police.
- Clavier simplifié — les huit sauf les deux couleurs de Maj / Ctrl / Alt (armée et verrouillée) : ce clavier n’a pas ces touches.
Réglez donc en priorité les quatre que tous lisent : votre habillage tiendra quel que soit le clavier.
Ce que ça écrit
<view name="system, basic, detailed, menu, gameclip">
<keyboard name="keyboard">
<keyColor>1B1D22</keyColor>
<keySelectedColor>4FE3C1</keySelectedColor>
<keyTextColor>FFFFFF</keyTextColor>
<keyTitleColor>FFFFFF</keyTitleColor>
<keyEditTextColor>FFFFFF</keyEditTextColor>
</keyboard>
</view>
Le name doit valoir exactement keyboard, et il n’accepte ni pos ni size.
Disponible à partir de Recalbox 10.1. Sur une machine plus ancienne, le bloc est ignoré et le clavier garde ses couleurs d’usine.
⚠️ La règle la plus importante
Pourquoi votre composant n’apparaît pas.
C’est la cause du « j’ai posé mon image et je ne la vois pas ».
Une vue ne dessine que deux choses :
- ses composants réservés, qu’elle construit elle-même ;
- les composants marqués
extra="true".Un composant libre sans
extran’apparaît jamais.
<view name="system">
<image name="mon-decor" extra="true"> <!-- ✅ s’affiche -->
<path>${root}/data/arts/decor.png</path>
</image>
<image name="autre-decor"> <!-- ❌ invisible -->
<path>${root}/data/arts/decor.png</path>
</image>
</view>
L’autre écriture, équivalente, est de les regrouper :
<view name="system">
<extras>
<image name="mon-decor"> … </image>
<text name="ma-legende"> … </text>
</extras>
</view>
Seuls SIX types peuvent être posés librement
La fabrique des composants libres ne sait construire que ceux-ci :
✅ Utilisable en extra | ❌ Refusé |
|---|---|
image · box · video · text · scrolltext · markdown | textlist · carousel · datetime · rating · sound · helpsystem · keyboard · container · ninepatch · tous les menu* |
Un type refusé écrit dans le journal Extra type unknown: Rating et rien n’est dessiné.
➡️ rating, datetime, textlist et carousel ne s’utilisent que sous leur nom réservé, dans une vue qui les prévoit. Vous ne pouvez pas ajouter une deuxième note ou une deuxième liste.
➡️ ninepatch n’a de nom réservé nulle part : refusé en composant libre, et aucune vue ne le construit. Il est donc inutilisable en pratique, malgré sa présence dans le moteur.
➡️ container ne se pose pas non plus, mais il n’est pas inutile : le moteur l’applique par-dessus un autre composant. Dans la liste de jeux, md_description déclaré en <text> règle du même coup le cadre défilant qui l’entoure — pos, size et zIndex vont au cadre, le reste au texte. Vous ne l’écrivez jamais vous-même.
Trois autres limites
- une seule
<video>par vue — la deuxième est ignorée sans message ; - un extra reçoit un
zIndexde 10 par défaut ; - les extras sont triés par
zIndexavant affichage.
L’exception
helpsystem continue de fonctionner même écrit dans un bloc <extras> : la vue le retrouve par son nom. L’avertissement du journal est sans conséquence.
La vue system — les machines
Le carrousel, le logo, la ligne d’informations.
Le premier écran : la liste de vos machines.
Les composants réservés
| Nom | Type | Rôle |
|---|---|---|
systemcarousel | carousel | le carrousel — un seul, non renommable |
logo | image | le logo du système dans le carrousel |
systemInfo | text | la ligne « 510 jeux disponibles, 13 favoris » |
bgsound | sound | la musique de fond du thème |
directory | sound | le dossier de musiques du thème |
bgsoundetdirectorysont les seuls noms que<sound>accepte, et uniquement dans cette vue.
Deux pièges
systemInfo a un fond gris par défaut. Pour l’enlever :
<text name="systemInfo">
<backgroundColor>00000000</backgroundColor>
</text>
Le carrousel est unique. Un second <carousel> est lu sans erreur mais jamais dessiné.
Le logo par défaut
Quand le thème ne fournit pas de logo, Recalbox cherche le sien dans cet ordre :
<systeme>-<langue_PAYS>.svg → <systeme>-<langue>.svg
→ <systeme>-<region>.svg → <systeme>.svg
C’est ce qui permet d’avoir un logo « Genesis » aux États-Unis et « Mega Drive » en Europe sans rien écrire.
Le carrousel en détail
| Propriété | Défaut | Précision |
|---|---|---|
type | horizontal | horizontal, vertical, vertical_wheel ; toute autre valeur retombe sur horizontal |
logoSize | calculé | ratio de l’écran, pas du carrousel |
logoScale | 1.2 | agrandissement du logo choisi |
maxLogoCount | 3 | arrondi à l’entier — une décimale ne sert à rien |
color | transparent | le fond du carrousel |
L’espacement des logos se calcule ainsi :
espacement = (longueur − logoSize × maxLogoCount) / maxLogoCount + logoSize
où longueur est size.y en vertical, size.x en horizontal.
La vue detailed — les jeux
La liste, la fiche du jeu, et ses trente composants réservés.
La liste des jeux d’un système, avec la fiche du jeu survolé.
C’est la vue de toutes les listes de jeux : la vue
basicn’est jamais demandée par le moteur, et la vue d’arcade réutilisedetailed.
La liste et les médias
| Nom | Type | Rôle |
|---|---|---|
gamelist | textlist | la liste des jeux |
logo | image | le logo du système |
md_image | image | la jaquette |
default_image_path | image | l’image de remplacement quand le jeu n’a pas de jaquette |
md_video | video | la vidéo d’aperçu |
md_region1 … md_region4 | image | les quatre drapeaux de région du jeu |
Sur
md_image, lepathécrit dans le thème est ignoré : l’image vient du jeu. Sur lesmd_region*, seulespos,size,zIndexetpathsont lues.
Les informations du jeu
| Nom | Type |
|---|---|
md_description | text, markdown ou scrolltext — au choix |
md_folder_name | text |
md_rating | rating |
md_releasedate, md_lastplayed | datetime |
md_developer, md_publisher, md_genre, md_players, md_playcount, md_favorite | text |
Les libellés
md_lbl_rating, md_lbl_releasedate, md_lbl_developer, md_lbl_publisher, md_lbl_genre, md_lbl_players, md_lbl_lastplayed, md_lbl_playcount, md_lbl_favorite
⚠️ Leur texte est imposé. Recalbox écrit « Note : », « Sorti le : », « Développeur : »… traduits dans la langue de la machine, après application du thème. Un
text=dans le thème est écrasé. Vous réglez leur mise en forme, jamais leur contenu.De même, les valeurs
md_*acceptent toutes les propriétés sauftext: leur contenu vient du jeu.
Les couleurs de la liste
gamelist utilise cinq couleurs, dont trois ne sont pas thémables :
| Ligne | Couleur |
|---|---|
| un jeu | primaryColor |
| un dossier | secondaryColor |
| un jeu estompé | calculé : primaryColor avec l’opacité divisée par deux |
| un dossier estompé | calculé de même |
| le fond d’un en-tête de tri | imposé |
Composants présents mais inertes
template_flag, template_genre et template_players sont lus par le moteur mais jamais utilisés : les couleurs des décorations ne sont pas thémables. Ne perdez pas de temps dessus.
La vue gameclip — l’écran de veille
Ce qui s’affiche quand la machine ne fait rien.
L’écran de veille lance des extraits de jeux. Le thème l’habille autour.
Ce qui est thémable
Cette vue est construite presque entièrement à partir de vos extras : posez vos composants avec extra="true" et ils s’affichent.
Un seul composant réservé y est thémable : la vidéo. Les composants d’informations (md_rating, md_developer, md_genre…) existent dans cette vue mais sont désactivés côté thème : contrairement à detailed, vous ne pouvez pas les mettre en forme.
➡️ Pour afficher le nom ou le développeur du jeu dans l’écran de veille, posez vos propres text avec les variables :
<view name="gameclip">
<extras>
<text name="titre">
<pos>0.06 0.85</pos>
<text>${game.name}</text>
<fontSize>0.05</fontSize>
<color>FFFFFF</color>
</text>
<text name="editeur">
<pos>0.06 0.91</pos>
<text>${game.developer} · ${game.releasedate}</text>
<fontSize>0.03</fontSize>
</text>
</extras>
</view>
Les variables du jeu y fonctionnent
Le contexte de cette vue contient le système ET le jeu : toutes les ${game.*} sont résolues. C’est ce qui rend l’écran de veille intéressant à habiller.
La vue menu — les menus
Ce qu’un thème peut, et ne peut pas, changer aux menus.
Recalbox construit ses menus lui-même : leur contenu, leur ordre et leurs libellés ne vous appartiennent pas.
Ce que le thème fournit, ce sont des styles — et une seule mesure.
<view name="menu">
<menuBackground>
<color>101820F0</color>
<path>${root}/data/arts/cadre.png</path>
</menuBackground>
<menuText>
<fontPath>${root}/data/fonts/Exo2.otf</fontPath>
<fontSize>0.038</fontSize>
<color>C6CBD8</color>
<selectedColor>101820</selectedColor>
<selectorColor>4FE3C1</selectorColor>
<separatorColor>FFFFFF20</separatorColor>
</menuText>
<menuSize><height>0.85</height></menuSize>
</view>
Les neuf composants disponibles sont décrits dans L’habillage des menus.
Ce que vous ne pouvez pas faire
- ajouter, retirer ou renommer une ligne de menu ;
- changer l’ordre des rubriques ;
- poser un composant libre dans un menu : les extras ne sont pas lus dans cette vue.
Ce à quoi il faut penser
Les menus s’affichent par-dessus la vue en cours. Le voile (fadePath de menuBackground) assombrit ce qu’il y a derrière : sans lui, un menu à fond transparent devient illisible sur un thème clair.
Un calque présent sur toutes les vues
Un décor commun écrit une seule fois, au lieu d’être recopié dans chaque vue.
Le problème
Un voile cathodique, un logo de marque, un cadre : vous le voulez sur la liste des systèmes et sur la liste de jeux et sur le menu. Le recopier dans chaque vue marche… jusqu’au jour où vous en modifiez un seul. Les autres restent en arrière, et rien ne le signale.
La solution
Clic droit sur le calque (ou le ⋯ de l’inspecteur) → « Présent sur toutes les vues ».
Le calque quitte sa vue et rejoint les calques communs : il n’y en a plus qu’un seul, partagé. Le régler d’où que ce soit le règle partout.
Pour faire marche arrière : « Ne garder que sur cette vue ».
Ce que ça écrit dans le thème
Un seul bloc, dont le nom liste les vues :
<!-- global.xml -->
<view name="system, basic, detailed, menu, gameclip">
<extras>
<image name="voileCRT" extra="true">…</image>
</extras>
</view>
La machine découpe ce nom sur les virgules et pose l’élément dans chaque vue. Le fichier est chargé avant les vues : ce qui vaut partout est un socle, qu’une vue peut encore corriger.
À ne pas confondre
« Dupliquer vers une autre mise en page… » est autre chose : il copie le calque dans une autre mise en page de la même vue (par exemple « Vertical gauche » et « Horizontal »), et les deux copies sont ensuite indépendantes.
⚠️ Le nom de vue
menuest écrit parce que le thème le demande, mais le moteur n’y lit aucun composant libre : le calque ne se voit pas dans les menus.
À l’import, un thème qui écrit déjà un bloc couvrant toutes les vues retrouve son calque commun. Un bloc qui n’en couvre qu’une partie reste éclaté par vue : il ne vaut que là où le thème l’a mis.
Se servir des variables
Afficher le nom d’un jeu, choisir une image selon le système.
Une variable s’écrit ${…} et Recalbox la remplace au moment d’afficher.
<text name="titre">
<text>Bienvenue sur ${system}</text>
</text>
→ « Bienvenue sur Super Nintendo ».
Dans un chemin d’image : le plus utile
<image name="console" extra="true">
<path>${root}/data/arts/consoles/${system.name}.png</path>
</image>
Une seule ligne, et chaque système affiche sa propre image. Il suffit que les fichiers portent le nom interne du système : snes.png, megadrive.png…
Où chaque variable fonctionne
C’est la règle qui surprend le plus, et elle vient du moteur :
| Famille | Vues où elle est résolue |
|---|---|
${system…} | Systèmes et Liste de jeux — une liste appartient toujours à un système |
${game…} | Liste de jeux et Écran de veille — là où il y a un jeu survolé |
${recalbox…} ${settings…} ${hardware…} ${display…} | partout |
Une variable employée là où elle n’existe pas n’est pas remplacée : le texte brut s’affiche tel quel, ${game.name} compris. Le studio ne propose que celles qui fonctionnent sur la vue en cours.
Quand la ligne survolée n’est pas un jeu
Dans la liste de jeux, le curseur ne traverse pas que des jeux : il passe aussi sur les dossiers et sur les en-têtes de tri, ces intertitres que Recalbox ajoute dès que la liste est triée autrement que par ordre alphabétique. Les ${game…} répondent quand même, mais ils décrivent alors une ligne qui n’a pas de jeu :
| Ligne survolée | ${game.name} | ${game.releasedate} | ${game.file.name} |
|---|---|---|---|
| un jeu | son nom | sa date de sortie | le fichier de la rom |
| un dossier | le nom du dossier | INCONNU | le nom du dossier |
| un en-tête de tri | rien | INCONNU | rien |
Un composant qui affiche une donnée du jeu n’a donc plus rien à dire sur ces lignes — et il y reste pourtant affiché, par-dessus le nom du dossier que Recalbox écrit au même moment. Limitez-le aux lignes de jeu :
<text name="sortie" extra="true" showIf="game">
<text>Sortie : ${game.releasedate}</text>
</text>
Le studio s’en charge : dès qu’un composant emploie une donnée du jeu, sa visibilité passe sur « un jeu ». L’onglet Visibilité du composant permet de la rouvrir aux dossiers et aux en-têtes quand c’est voulu.
${root} — à ne jamais oublier
${root} désigne la racine du thème sélectionné. Sans lui, les chemins sont relatifs au fichier qui les écrit, et votre thème casse dès qu’on le range autrement.
<path>${root}/data/arts/fond.jpg</path> <!-- ✅ -->
<path>../data/arts/fond.jpg</path> <!-- fragile -->
Les anciennes variables $…
Vous croiserez dans de vieux thèmes des variables sans accolades :
| Ancienne | Ce qu’elle donne | Équivalent actuel |
|---|---|---|
$system | le nom court — « snes » | ${system.name} |
$theme | le dossier du thème | ${root} |
⚠️ $system et ${system} ne donnent pas la même chose : le premier rend « snes », le second « Super Nintendo ». Elles sont encore acceptées, mais dépréciées : n’écrivez plus que la forme à accolades.
Le tirage au sort
<path>${random.between(fond1.jpg,fond2.jpg,fond3.jpg)}</path>
<fontSize>${random.range(1,10)}</fontSize>
random.between choisit une valeur au hasard dans la liste, random.range un nombre entre deux bornes. Le tirage a lieu au chargement du thème, pas à chaque affichage.
Toutes les variables
La liste complète, et où chacune fonctionne.
Voici toutes les variables que Recalbox sait remplacer, relevées dans le moteur.
Une variable s’écrit ${…} et Recalbox la remplace au moment d’afficher.
Le système
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${system} | Nom du système | Le nom complet — ex. « Sega Megadrive » | Systèmes, Jeux |
${system.input.keyboard} | Clavier nécessaire ? | mandatory · recommended · optional · no | Systèmes, Jeux |
${system.input.mouse} | Souris nécessaire ? | mandatory · recommended · optional · no | Systèmes, Jeux |
${system.input.pad} | Manette nécessaire ? | mandatory · recommended · optional · no | Systèmes, Jeux |
${system.logo} | Logo du système | Le chemin du logo fourni par Recalbox | Systèmes, Jeux |
${system.manufacturer} | Fabricant | Ex. « Sega », « Nintendo ». Vide si inconnu | Systèmes, Jeux |
${system.name} | Nom court du système | — | Systèmes, Jeux |
${system.releasedate} | Année de sortie | Année et mois — ex. « 1988-10 » | Systèmes, Jeux |
${system.type} | Type de machine (nom technique) | arcade · console · handheld · computer · engine · port · fantasy · virtual · virtual-arcade | Systèmes, Jeux |
${system.type.name} | Type de machine | Le même, en clair : « Home Console », « handheld Console », « Arcade »… | Systèmes, Jeux |
Le jeu
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${game.developer} | Développeur | Ex. « Konami ». « UNKNOWN » si absent | Veille, Jeux |
${game.file.name} | Nom du fichier | Le nom du fichier, extension comprise | Veille, Jeux |
${game.file.path} | Chemin complet du fichier | Le chemin complet du fichier | Veille, Jeux |
${game.file.stem} | Nom du fichier (sans extension) | Le nom du fichier, sans extension | Veille, Jeux |
${game.genre.normalized} | Genre (nom technique) | Le genre normalisé, en anglais — « Platform », « Shoot’em Up », « Racing »… | Veille, Jeux |
${game.genre.raw} | Genre | Le genre tel qu’écrit dans la fiche du jeu | Veille, Jeux |
${game.isadult} | Réservé aux adultes ? | yes ou no | Veille, Jeux |
${game.isfavorite} | Est un favori ? | yes ou no (jamais true/false) | Veille, Jeux |
${game.ishidden} | Est masqué ? | yes ou no | Veille, Jeux |
${game.islastversion} | Est la dernière version ? | yes ou no | Veille, Jeux |
${game.isnotagame} | N’est pas un jeu ? | yes ou no | Veille, Jeux |
${game.ispreinstalled} | Est préinstallé ? | yes ou no | Jeux, Veille |
${game.license} | Licence | La licence, souvent vide | Veille, Jeux |
${game.name} | Nom du jeu | Le nom du jeu | Veille, Jeux |
${game.players} | Nombre de joueurs | « 1 », « 2 », « 1-4 », « 4+ »… | Veille, Jeux |
${game.players.max} | Joueurs — maximum | Un nombre — ex. « 4 » | Veille, Jeux |
${game.players.min} | Joueurs — minimum | Un nombre — ex. « 1 » | Veille, Jeux |
${game.publisher} | Éditeur | Ex. « Sega ». « UNKNOWN » si absent | Veille, Jeux |
${game.releasedate} | Date de sortie | Date ISO — ex. « 1991-06-23 ». « UNKNOWN » si absente | Veille, Jeux |
${game.synopsis} | Description | Le texte de présentation, souvent long | Veille, Jeux |
Note et statistiques
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${game.lastplayed} | Dernière partie | Date ISO, ou « NEVER » si jamais joué | Veille, Jeux |
${game.rating.10} | Note (sur 10) | Un entier de 0 à 10 | Veille, Jeux |
${game.rating.100} | Note (sur 100) | Un entier de 0 à 100 | Veille, Jeux |
${game.rating.5} | Note (sur 5) | Un entier de 0 à 5 — pas des étoiles | Veille, Jeux |
${game.timesplayed} | Nombre de parties | Un nombre de parties | Veille, Jeux |
${game.totalplayed} | Temps de jeu total | Une durée — ex. « 3h 12m ». « NONE » si zéro | Veille, Jeux |
Images et vidéo du jeu
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${game.media.boxpath} | Boîtier (box art) | — | Veille, Jeux |
${game.media.imagepath} | Jaquette / image | Le chemin de la jaquette. Vide si le jeu n’en a pas — voir ifexists | Veille, Jeux |
${game.media.thumbpath} | Vignette | Le chemin de la vignette | Veille, Jeux |
${game.media.videopath} | Vidéo | Le chemin de la vidéo | Veille, Jeux |
Support du jeu
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${game.support.index} | Index du support | Le numéro du disque — vide s’il n’y en a qu’un | Veille, Jeux |
${game.support.number} | Numéro du support | Le tout assemblé — ex. « 2A/3 » | Veille, Jeux |
${game.support.side} | Face du support | La face du support — A, B… | Veille, Jeux |
${game.support.total} | Nombre de supports | Le nombre de supports. « UNKNOWN » si inconnu | Veille, Jeux |
${game.support.type} | Type de support | Cartridge · CD/DVD · Harddisk · Files · Tape · Quick Disc · 3" Floppy · 3".5 Floppy · 5".25 Floppy · PCB · Unknown | Veille, Jeux |
Système du jeu
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${game.system} | Nom du système du jeu | Le nom complet du système du jeu | Veille, Jeux |
${game.system.input.keyboard} | Clavier exigé par le système du jeu | mandatory · recommended · optional · no | Veille, Jeux |
${game.system.input.mouse} | Souris exigée par le système du jeu | mandatory · recommended · optional · no | Veille, Jeux |
${game.system.input.pad} | Manette exigée par le système du jeu | mandatory · recommended · optional · no | Veille, Jeux |
${game.system.logo} | Logo du système du jeu | Le chemin de son logo | Veille, Jeux |
${game.system.manufacturer} | Fabricant du système du jeu | Son fabricant | Veille, Jeux |
${game.system.name} | Nom court du système du jeu | Son nom interne | Veille, Jeux |
${game.system.releasedate} | Année du système du jeu | Son année de sortie | Veille, Jeux |
${game.system.type} | Type du système du jeu (nom technique) | Comme ${system.type} : console · handheld · arcade… | Veille, Jeux |
${game.system.type.name} | Type du système du jeu | Le même, en clair | Veille, Jeux |
Émulateur
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${game.emulator.compatibility} | Compatibilité | unknown · low · average · good · high · perfect | Veille, Jeux |
${game.emulator.extensions} | Extensions gérées | Les extensions gérées — ex. « .bin .gen .md » | Veille, Jeux |
${game.emulator.hasnetplay} | Compatible jeu en ligne ? | yes ou no | Veille, Jeux |
${game.emulator.hassoftpatching} | Accepte les patchs ? | yes ou no | Veille, Jeux |
${game.emulator.islibretro} | Est un cœur Libretro ? | yes ou no | Veille, Jeux |
${game.emulator.name} | Nom de l’émulateur | Ex. « libretro picodrive » | Veille, Jeux |
${game.emulator.speed} | Vitesse | unknown · low · average · good · high · perfect | Veille, Jeux |
La machine et ses réglages
| À écrire | Ce que c’est | Ce qu’elle renvoie | Où |
|---|---|---|---|
${display.overscan} | Overscan ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${random.between(a,b,c)} | Une valeur au hasard parmi… | une des valeurs données | partout |
${random.range(1,10)} | Un nombre au hasard entre… | un entier entre les deux bornes | partout |
${display.resolution} | Résolution | fhd (1080p et plus) · hd (720p) · vga · qvga | Veille, Systèmes, Menu, Jeux |
${display.tate} | Écran vertical (TATE) ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${display.tateleft} | Vertical vers la gauche ? | yes ou no | Systèmes, Menu, Jeux, Veille |
${display.tateright} | Vertical vers la droite ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${hardware.board} | Modèle de machine | Le modèle — « RPi 5 », « PC x64 », « RG351P/M »… | Veille, Systèmes, Menu, Jeux |
${hardware.crt} | Écran CRT ? | yes ou no | Menu, Jeux, Veille, Systèmes |
${hardware.isanbernic} | Est-ce une Anbernic ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${hardware.isodroid} | Est-ce un Odroid ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${hardware.ispc} | Est-ce un PC ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${hardware.ispi} | Est-ce un Raspberry Pi ? | yes ou no | Veille, Systèmes, Menu, Jeux |
${hardware.jamma} | Borne Jamma ? | yes ou no | Systèmes, Menu, Jeux, Veille |
${recalbox.built} | Date de compilation | La date de compilation | Veille, Systèmes, Menu, Jeux |
${recalbox.version} | Version de Recalbox | Ex. « 10.0 » | Veille, Systèmes, Menu, Jeux |
${root} | Dossier du thème | La racine du thème sélectionné — à mettre devant tous vos chemins | Menu, Jeux, Veille, Systèmes |
${settings.language} | Langue | La langue seule — ex. « fr » | Veille, Systèmes, Menu, Jeux |
${settings.locale} | Langue et pays | Langue et pays — ex. « fr_FR » | Veille, Systèmes, Menu, Jeux |
${settings.region} | Région choisie | eu · us · jp | Veille, Systèmes, Menu, Jeux |
${settings.timezone} | Fuseau horaire | Ex. « Europe/Paris » | Veille, Systèmes, Menu, Jeux |
Vos variables sur mesure
Vos propres valeurs nommées — une couleur, une police — créées depuis le studio.
Les variables de la page précédente sont celles de Recalbox : ${system}, ${game.developer}… Elles sont fournies, vous ne faites que les employer.
Celles-ci sont les vôtres. Vous donnez un nom à une valeur — une couleur, une police, un chemin — vous employez ce nom partout, et le jour où vous changez la valeur, tout le thème suit. C’est ce qui rend les options de couleur possibles : un choix redéfinit la variable, et rien d’autre.
Les créer dans le studio
- ouvrez Variables dans la barre d’outils : le panneau s’ouvre à droite ;
- + Nouvelle variable, donnez-lui un nom, puis dites ce que c’est — une couleur (vous obtenez le nuancier, transparence comprise), une police (choisie parmi celles du thème), ou autre valeur (un texte, un chemin, ou une autre variable) ;
- dans un champ de couleur, le petit { } posé contre le champ propose les vôtres, et dans la liste des polices elles apparaissent en tête.
Le studio affiche sous le champ la valeur calculée : une variable qui en emploie une autre se relit d’un coup d’œil.
À la main, sans le studio
Elles s’écrivent dans un bloc <variables>. La balise, les règles de nom, la portée et les conditions sont expliquées sur Vos propres variables, dans « La structure d’un thème ».
Afficher sous condition
Les 26 conditions : la machine, l’écran, et le système affiché.
Une condition if= fait apparaître un composant seulement dans certains cas.
<image name="fond" if="crt">…</image>
Elles se combinent avec ! (pas), & (et), | (ou) — ou, si vous préférez les mots, not, and, or : if="crt and not tate". Voir Combiner plusieurs conditions.
⚠️ Un identifiant absent de cette liste est ignoré par Recalbox et signalé comme inconnu dans themes.log.
Partout — la machine et l’écran
| Condition | Vraie quand… |
|---|---|
hd fhd vga qvga | l’écran est de cette définition |
crt | l’écran est un tube cathodique |
overscan | l’image déborde (CRT sans Jamma) |
tate tateleft tateright | l’écran est vertical |
jamma bartop | la machine est une borne |
ispc ispi isodroid isanbernic | la machine est de ce modèle |
nomenu | les menus sont désactivés |
Seulement là où un système est affiché
Ces huit-là interrogent le système en cours. Elles ne valent donc que dans la vue Systèmes et dans la Liste de jeux — ailleurs, il n’y a pas de système, et la condition est toujours fausse.
| Condition | Vraie quand… |
|---|---|
console handheld computer arcade engine port fantasy | le système est de ce type |
virtual | c’est un système automatique (Favoris, Derniers joués, Tous les jeux…) |
favorite | c’est le système automatique Favoris |
lastplayed | c’est le système automatique Derniers joués |
⚠️
favoritene dit pas que le jeu survolé est un favori : elle parle du système. Il n’existe aucune condition sur le jeu ni sur un dossier — pour réagir au contenu d’un jeu, on emploie ses variables (${game.isfavorite}) ouifexists.
Combiner plusieurs conditions
ET, OU, « pas ça », et les groupes entre parenthèses.
Une étiquette par condition
Vous choisissez une condition dans la liste, elle devient une étiquette. Le petit bouton devant dit dans quel sens elle compte :
- si — seulement dans ce cas ;
- si pas — partout sauf dans ce cas.
Sur une étiquette déjà posée, ce même bouton inverse le sens ; le ✕ la retire.
Les groupes
Un groupe est une parenthèse. Dedans, vous dites s’il faut toutes les conditions, ou au moins une. Entre deux groupes, la même question se pose : ET ou OU.
Trois écritures pour les mêmes opérateurs
| Ce qu’on veut dire | Signe | En toutes lettres |
|---|---|---|
| et | & | AND |
| ou | | | OR |
| pas | ! | NOT |
Les deux écritures se mélangent, et la casse n’a aucune importance : if="crt AND !tate" vaut if="crt & !tate".
⚠️ && et || ne fonctionnent pas. Deux signes d’affilée sont une erreur de syntaxe, et une expression en erreur vaut faux : le composant disparaît sans que rien ne le dise à l’écran.
Exemple : « sur un écran cathodique, et en arcade ou en favoris » s’écrit
crt & (arcade | favorite)
⚠️ L’ordre des groupes compte
La machine lit de gauche à droite, sans priorité. a | b & c y vaut (a | b) & c, et non a | (b & c). C’est pour ça que le studio met systématiquement les parenthèses : ce que vous lisez est exactement ce que la machine comprendra.
À l’intérieur d’un même groupe, en revanche, l’ordre n’a aucune importance : « pas Full HD et arcade » dit la même chose que « arcade et pas Full HD ».
Une condition s’écrit à deux endroits
La même condition, au même endroit du composant ou sur une seule de ses propriétés, ne dit pas la même chose :
<image name="filtre" if="crt">…</image> <!-- ① le composant n’existe QUE sur un CRT -->
<text name="titre"> <!-- ② le composant existe toujours… -->
<fontSize if="crt">0.09</fontSize> <!-- …mais sa taille change sur un CRT -->
<fontSize if="!crt">0.05</fontSize>
</text>
① sur le composant : il apparaît, ou il n’existe pas du tout. ② sur une propriété : le composant est toujours là, une seule de ses valeurs change.
Dans le studio, c’est le même bouton et la même fenêtre dans les deux cas — ce que vous construisez ici sert là-bas à l’identique.
Les systèmes
Les noms internes attendus dans vos dossiers et vos fichiers.
Chaque système porte plusieurs noms, et il ne faut pas les confondre :
- Le système — son nom usuel, celui dont on parle. Il n’apparaît nulle part dans les fichiers ;
- les variables, remplies par le moteur au moment d’afficher.
Le dossier à créer dans votre thème porte le nom de ${system.name}. Quelques systèmes font exception : ils sont listés sous leur tableau.
Les machines (121)
| Le système | ${system.name} | ${system} | ${system.manufacturer} | ${system.type} |
|---|---|---|---|---|
| 240ptestsuite | 240ptestsuite | 240ptestsuite | virtual | virtual |
| Acorn BBC Micro | bbcmicro | Acorn BBC Micro | Acorn | computer |
| Amiga AGA | amiga1200 | Amiga AGA | Commodore | computer |
| Amiga CD32 | amigacd32 | Amiga CD32 | Commodore | console |
| Amiga CDTV | amigacdtv | Amiga CDTV | Commodore | console |
| Amiga ECS/OCS | amiga600 | Amiga ECS/OCS | Commodore | computer |
| Amstrad GX4000 | gx4000 | Amstrad GX4000 | Amstrad | console |
| AmstradCPC | amstradcpc | AmstradCPC | Amstrad | computer |
| Apple II | apple2 | Apple II | Apple | computer |
| Apple IIGS | apple2gs | Apple IIGS | Apple | computer |
| Apple Macintosh | macintosh | Apple Macintosh | Apple | computer |
| Arduboy | arduboy | Arduboy | fantasy | fantasy |
| Atari 2600 | atari2600 | Atari 2600 | Atari | console |
| Atari 5200 | atari5200 | Atari 5200 | Atari | console |
| Atari 7800 | atari7800 | Atari 7800 | Atari | console |
| Atari 8bits | atari800 | Atari 8bits | Atari | computer |
| Atari Jaguar | jaguar | Atari Jaguar | Atari | console |
| Atari ST | atarist | Atari ST | Atari | computer |
| Colecovision | colecovision | Colecovision | Coleco | console |
| Commodore 64 | c64 | Commodore 64 | Commodore | computer |
| Commodore VIC-20 | vic20 | Commodore VIC-20 | Commodore | computer |
| Daphne | daphne | Daphne | Daphne | arcade |
| DICE | dice | DICE | DICE | arcade |
| Dos (x86) | dos | Dos (x86) | IBM | computer |
| Dragon 32/64 | dragon | Dragon 32/64 | DragonData | computer |
| EasyRPG | easyrpg | EasyRPG | virtual | engine |
| Elektronika BK | bk | Elektronika BK | Elektronika | computer |
| Epoch Cassette Vision | cassettevision | Epoch Cassette Vision | Epoch | console |
| Exelvision EXL 100 | exl100 | Exelvision EXL 100 | Exelvision | computer |
| Fairchild Channel F | channelf | Fairchild Channel F | Fairchild | console |
| Family Computer Disk System | fds | Family Computer Disk System | Nintendo | console |
| FinalBurn Neo | fbneo | FinalBurn Neo | FBN | arcade |
| Game and Watch | gw | Game and Watch | Nintendo | handheld |
| Game Boy | gb | Game Boy | Nintendo | handheld |
| Game Boy Advance | gba | Game Boy Advance | Nintendo | handheld |
| Game Boy Color | gbc | Game Boy Color | Nintendo | handheld |
| GameCube | gamecube | GameCube | Nintendo | console |
| Infocom Z-Machine | zmachine | Infocom Z-Machine | Infocom | engine |
| LowRes NX | lowresnx | LowRes NX | virtual | fantasy |
| Lutro | lutro | Lutro | virtual | fantasy |
| Lynx | lynx | Lynx | Atari | handheld |
| Mame | mame | Mame | Mame | arcade |
| Mattel Intellivision | intellivision | Mattel Intellivision | Mattel | console |
| MegaDuck | megaduck | MegaDuck | Welback | handheld |
| MGT SAM Coupé | samcoupe | MGT SAM Coupé | MGT | computer |
| Moonlight | moonlight | Moonlight | NVidia | virtual |
| MSX1 | msx1 | MSX1 | Microsoft | computer |
| MSX2 | msx2 | MSX2 | Microsoft | computer |
| MSXturboR | msxturbor | MSXturboR | Microsoft | computer |
| NEC PC-88 | pc88 | NEC PC-88 | NEC | computer |
| NEC PC-98 | pc98 | NEC PC-98 | NEC | computer |
| NEC PC-FX | pcfx | NEC PC-FX | NEC | console |
| Neo-Geo AES | neogeo | Neo-Geo AES | SNK | console |
| Neo-Geo CD | neogeocd | Neo-Geo CD | SNK | console |
| Neo-Geo Pocket | ngp | Neo-Geo Pocket | SNK | handheld |
| Neo-Geo Pocket Color | ngpc | Neo-Geo Pocket Color | SNK | handheld |
| Nintendo 64 | n64 | Nintendo 64 | Nintendo | console |
| Nintendo 64DD | 64dd | Nintendo 64DD | Nintendo | console |
| Nintendo DS | nds | Nintendo DS | Nintendo | handheld |
| Nintendo Entertainment System | nes | Nintendo Entertainment System | Nintendo | console |
| Odyssey2 | o2em | Odyssey2 | Magnavox | console |
| OpenBOR | openbor | OpenBOR | Senile Team | engine |
| Oric/Atmos | oricatmos | Oric/Atmos | Tangerine | computer |
| Othello Multivision | multivision | Othello Multivision | Tsukuda | console |
| Palm | palm | Palm | Palm | handheld |
| Panasonic 3DO | 3do | Panasonic 3DO | Panasonic | console |
| PC Engine | pcengine | PC Engine | NEC | console |
| PC Engine CD | pcenginecd | PC Engine CD | NEC | console |
| Philips CD-I | cdi | Philips CD-I | Phillips | console |
| Philips P2000T | p2000t | Philips P2000T | Philips | computer |
| Philips VG 5000 | vg5000 | Philips VG 5000 | Philips | computer |
| PICO-8 | pico8 | PICO-8 | virtual | fantasy |
| Pocket Challenge v2 | pcv2 | Pocket Challenge v2 | Benesse | handheld |
| Pokémon Mini | pokemini | Pokémon Mini | Nintendo | handheld |
| Sammy Atomiswave | atomiswave | Sammy Atomiswave | Sammy | arcade |
| Satellaview | satellaview | Satellaview | Nintendo | console |
| Screenshots | imageviewer | Screenshots | virtual | virtual |
| ScummVM | scummvm | ScummVM | Ludvig Strigeus | engine |
| Sega 32X | sega32x | Sega 32X | Sega | console |
| Sega CD | segacd | Sega CD | Sega | console |
| Sega Dreamcast | dreamcast | Sega Dreamcast | Sega | console |
| Sega Game Gear | gamegear | Sega Game Gear | Sega | handheld |
| Sega Master System / Mark III | mastersystem | Sega Master System / Mark III | Sega | console |
| Sega Megadrive | megadrive | Sega Megadrive | Sega | console |
| Sega Model3 | model3 | Sega Model3 | Sega | arcade |
| Sega NAOMI | naomi | Sega NAOMI | Sega | arcade |
| Sega NAOMI 2 | naomi2 | Sega NAOMI 2 | Sega | arcade |
| Sega NAOMI GD-ROM System | naomigd | Sega NAOMI GD-ROM System | Sega | arcade |
| Sega Pico | pico | Sega Pico | Sega | console |
| Sega Saturn | saturn | Sega Saturn | Sega | console |
| Sega SG1000 | sg1000 | Sega SG1000 | Sega | console |
| Sharp X1 | x1 | Sharp X1 | Sharp | computer |
| Sharp X68000 | x68000 | Sharp X68000 | Sharp | computer |
| Solarus | solarus | Solarus | Solarus | engine |
| Sony Playstation 1 | psx | Sony Playstation 1 | Sony | console |
| Sony Playstation 2 | ps2 | Sony Playstation 2 | Sony | console |
| Sony Playstation Portable | psp | Sony Playstation Portable | Sony | handheld |
| Spectravideo | spectravideo | Spectravideo | Spectravideo | computer |
| ST-V | stv | ST-V | Sega | arcade |
| SuFami Turbo | sufami | SuFami Turbo | Bandai | console |
| Super Cassette Vision | scv | Super Cassette Vision | Epoch | console |
| Super Nintendo Entertainment System | snes | Super Nintendo Entertainment System | Nintendo | console |
| Supergrafx | supergrafx | Supergrafx | NEC | console |
| Texas Instrument TI-99/4A | ti994a | Texas Instrument TI-99/4A | Texas Instrument | computer |
| Thomson | thomson | Thomson | Thomson | computer |
| TIC-80 | tic80 | TIC-80 | port | fantasy |
| TRS-80 Color Computer | trs80coco | TRS-80 Color Computer | Tandy | computer |
| Uzebox | uzebox | Uzebox | port | console |
| Vectrex | vectrex | Vectrex | MB | console |
| Videopac+ G7400 | videopacplus | Videopac+ G7400 | Philips | console |
| Vircon32 | vircon32 | Vircon32 | virtual | console |
| Virtual Boy | virtualboy | Virtual Boy | Nintendo | console |
| Visual Pinball Standalone | vpinball | Visual Pinball Standalone | Randy Davis | engine |
| WASM-4 | wasm4 | WASM-4 | Bruno Garcia | fantasy |
| Watara Supervision | supervision | Watara Supervision | Watara | handheld |
| Wii | wii | Wii | Nintendo | console |
| WonderSwan | wswan | WonderSwan | Bandai | handheld |
| WonderSwan Color | wswanc | WonderSwan Color | Bandai | handheld |
| Xbox | xbox | Xbox | Microsoft | console |
| ZX81 | zx81 | ZX81 | Sinclair | computer |
| ZXSpectrum | zxspectrum | ZXSpectrum | Sinclair | computer |
Les dossiers qui ne portent pas le nom du système. Pour ceux-là, et pour eux seuls, le dossier de votre thème s’appelle autrement que ${system.name}.
| Le système | ${system.name} | Le dossier à créer |
|---|---|---|
| Dos (x86) | dos | pc |
| GameCube | gamecube | gc |
| Odyssey2 | o2em | odyssey2 |
| Oric/Atmos | oricatmos | oric |
| Thomson | thomson | to8 |
| WonderSwan | wswan | wonderswan |
| WonderSwan Color | wswanc | wonderswancolor |
Les systèmes virtuels (11)
Recalbox les fabrique lui-même, à partir de vos jeux : ils n’ont pas de fichier de système, mais ils ont bien un dossier de thème, et vous pouvez les habiller comme les autres.
| Le système | ${system.name} | ${system} | ${system.type} |
|---|---|---|---|
| Ports | ports | Ports | virtual |
| Favorites | favorites | Favorites | virtual |
| Last played | lastplayed | Last played | virtual |
| All games | allgames | All games | virtual |
| Multiplayer | multiplayer | Multiplayer | virtual |
| Arcade | arcade | Arcade | virtual-arcade |
| Lightgun | lightgun | Lightgun | virtual |
| Tate | tate | Tate | virtual |
| Dial | dial | Dial | virtual |
| Trackball | trackball | Trackball | virtual |
| Challenges | challenges | Challenges | virtual |
Les dossiers qui ne portent pas le nom du système. Pour ceux-là, et pour eux seuls, le dossier de votre thème s’appelle autrement que ${system.name}.
| Le système | ${system.name} | Le dossier à créer |
|---|---|---|
| Last played | lastplayed | auto-lastplayed |
| All games | allgames | auto-allgames |
| Multiplayer | multiplayer | auto-multiplayer |
| Lightgun | lightgun | auto-lightgun |
| Tate | tate | auto-tate |
| Dial | dial | auto-dial |
| Trackball | trackball | auto-trackball |
| Challenges | challenges | auto-challenges |
Arcade par constructeur (54) (${system.type} = virtual-arcade)
| Le système | ${system.name} | ${system} |
|---|---|---|
| Acclaim | arcade-manufacturer-acclaim | Acclaim |
| Atari | arcade-manufacturer-atari | Atari |
| Atlus | arcade-manufacturer-atlus | Atlus |
| Banpresto | arcade-manufacturer-banpresto | Banpresto |
| Capcom Cps1 | arcade-manufacturer-capcom-cps1 | Capcom Cps1 |
| Capcom Cps2 | arcade-manufacturer-capcom-cps2 | Capcom Cps2 |
| Capcom Cps3 | arcade-manufacturer-capcom-cps3 | Capcom Cps3 |
| Capcom | arcade-manufacturer-capcom | Capcom |
| Cave | arcade-manufacturer-cave | Cave |
| Data east | arcade-manufacturer-data east | Data east |
| Exidy | arcade-manufacturer-exidy | Exidy |
| Hng64 | arcade-manufacturer-hng64 | Hng64 |
| Igs | arcade-manufacturer-igs | Igs |
| Irem M72 | arcade-manufacturer-irem-m72 | Irem M72 |
| Irem M92 | arcade-manufacturer-irem-m92 | Irem M92 |
| Irem | arcade-manufacturer-irem | Irem |
| Itech | arcade-manufacturer-itech | Itech |
| Jaleco | arcade-manufacturer-jaleco | Jaleco |
| Kaneko | arcade-manufacturer-kaneko | Kaneko |
| Konami Gx | arcade-manufacturer-konami-gx | Konami Gx |
| Konami | arcade-manufacturer-konami | Konami |
| Midway | arcade-manufacturer-midway | Midway |
| Mitchell | arcade-manufacturer-mitchell | Mitchell |
| Namco Na | arcade-manufacturer-namco-na | Namco Na |
| Namco Nb | arcade-manufacturer-namco-nb | Namco Nb |
| Namco System1 | arcade-manufacturer-namco-system1 | Namco System1 |
| Namco System10 | arcade-manufacturer-namco-system10 | Namco System10 |
| Namco System11 | arcade-manufacturer-namco-system11 | Namco System11 |
| Namco System12 | arcade-manufacturer-namco-system12 | Namco System12 |
| Namco System18 | arcade-manufacturer-namco-system18 | Namco System18 |
| Namco System2 | arcade-manufacturer-namco-system2 | Namco System2 |
| Namco | arcade-manufacturer-namco | Namco |
| Neogeo | arcade-manufacturer-neogeo | Neogeo |
| Nichibutsu | arcade-manufacturer-nichibutsu | Nichibutsu |
| Nintendo | arcade-manufacturer-nintendo | Nintendo |
| Nmk | arcade-manufacturer-nmk | Nmk |
| Psikyo | arcade-manufacturer-psikyo | Psikyo |
| Raizing | arcade-manufacturer-raizing | Raizing |
| Sammy | arcade-manufacturer-sammy | Sammy |
| Sega Stv | arcade-manufacturer-sega-stv | Sega Stv |
| Sega System16 | arcade-manufacturer-sega-system16 | Sega System16 |
| Sega System18 | arcade-manufacturer-sega-system18 | Sega System18 |
| Sega System32 | arcade-manufacturer-sega-system32 | Sega System32 |
| Sega | arcade-manufacturer-sega | Sega |
| Seibu | arcade-manufacturer-seibu | Seibu |
| Seta | arcade-manufacturer-seta | Seta |
| Snk | arcade-manufacturer-snk | Snk |
| Taito F3 | arcade-manufacturer-taito-f3 | Taito F3 |
| Taito Gnet | arcade-manufacturer-taito-gnet | Taito Gnet |
| Taito | arcade-manufacturer-taito | Taito |
| Technos | arcade-manufacturer-technos | Technos |
| Tecmo | arcade-manufacturer-tecmo | Tecmo |
| Toaplan | arcade-manufacturer-toaplan | Toaplan |
| Visco | arcade-manufacturer-visco | Visco |
Par genre (56) (${system.type} = virtual)
| Le système | ${system.name} | ${system} |
|---|---|---|
| Action | genre-action | Action |
| Platform | genre-actionplatformer | Platform |
| Platform Shooter | genre-actionplatformshooter | Platform Shooter |
| First Person Shooter | genre-actionfirstpersonshooter | First Person Shooter |
| Shoot'em Up | genre-actionshootemup | Shoot'em Up |
| Shoot with Gun | genre-actionshootwithgun | Shoot with Gun |
| Fighting | genre-actionfighting | Fighting |
| Beat'em All | genre-actionbeatemup | Beat'em All |
| Infiltration | genre-actionstealth | Infiltration |
| Battle Royale | genre-actionbattleroyale | Battle Royale |
| Rythm & Music | genre-actionrythm | Rythm & Music |
| Adventure | genre-adventure | Adventure |
| Textual Adventure | genre-adventuretext | Textual Adventure |
| Graphical Adventure | genre-adventuregraphics | Graphical Adventure |
| Visual Novel | genre-adventurevisualnovels | Visual Novel |
| Interactive Movie | genre-adventureinteractivemovie | Interactive Movie |
| Real Time 3D Adventure | genre-adventurerealtime3d | Real Time 3D Adventure |
| Survival | genre-adventuresurvivalhorror | Survival |
| RPG | genre-rpg | RPG |
| Action RPG | genre-rpgaction | Action RPG |
| MMORPG | genre-rpgmmo | MMORPG |
| Dungeon Crawler | genre-rpgdungeoncrawler | Dungeon Crawler |
| Tactical RPG | genre-rpgtactical | Tactical RPG |
| JRPG | genre-rpgjapanese | JRPG |
| Party based RPG | genre-rpgfirstpersonpartybased | Party based RPG |
| Simulation | genre-simulation | Simulation |
| Build & Management | genre-simulationbuildandmanagement | Build & Management |
| Life Simulation | genre-simulationlife | Life Simulation |
| Fishing & Hunting | genre-simulationfishandhunt | Fishing & Hunting |
| Vehicle Simulation | genre-simulationvehicle | Vehicle Simulation |
| Science Fiction Simulation | genre-simulationscifi | Science Fiction Simulation |
| Strategy | genre-strategy | Strategy |
| eXplore, eXpand, eXploit & eXterminate | genre-strategy4x | eXplore, eXpand, eXploit & eXterminate |
| Artillery | genre-strategyartillery | Artillery |
| Auto-battler | genre-strategyautobattler | Auto-battler |
| Multiplayer Online Battle Arena | genre-strategymoba | Multiplayer Online Battle Arena |
| Real Time Strategy | genre-strategyrts | Real Time Strategy |
| Turn Based Strategy | genre-strategytbs | Turn Based Strategy |
| Tower Defense | genre-strategytowerdefense | Tower Defense |
| Wargame | genre-strategywargame | Wargame |
| Sports | genre-sports | Sports |
| Racing | genre-sportracing | Racing |
| Sport Simulation | genre-sportsimulation | Sport Simulation |
| Competition Sport | genre-sportcompetitive | Competition Sport |
| Fighting/Violent Sport | genre-sportfight | Fighting/Violent Sport |
| Pinball | genre-pinball | Pinball |
| Board game | genre-board | Board game |
| Casual game | genre-casual | Casual game |
| Digital Cards | genre-digitalcard | Digital Cards |
| Puzzle & Logic | genre-puzzleandlogic | Puzzle & Logic |
| Multiplayer Party Game | genre-party | Multiplayer Party Game |
| Trivia | genre-trivia | Trivia |
| Casino | genre-casino | Casino |
| Multi Game Compilation | genre-compilation | Multi Game Compilation |
| Demo from Demo Screne | genre-demoscene | Demo from Demo Screne |
| Educative | genre-educative | Educative |
Les polices
Celles de la machine, et les vôtres.
Une police vient de deux endroits, et cela change ce qu’il faut livrer.
Celles de la machine
<fontPath>:/ubuntu_condensed.ttf</fontPath>
Le :/ désigne les ressources d’EmulationStation. Rien à copier dans votre thème : le fichier est présent sur toutes les Recalbox.
Les vôtres
<fontPath>${root}/data/fonts/Exo2.otf</fontPath>
Le fichier doit être dans votre thème, et sera livré avec lui. TTF et OTF fonctionnent.
La taille
fontSize change d’unité selon sa valeur : en dessous de 1, c’est une proportion de la hauteur de l’écran ; à partir de 1, ce sont des pixels. Tout est expliqué dans Ratio, pourcentage ou pixels. Préférez le ratio : 0.045 donne la même proportion partout.
Les polices à pixels
f8bitfortressplus n’est nette qu’à des tailles multiples de 7 en pixels. Entre ces valeurs, elle bave. C’est vrai de toutes les polices dessinées pixel par pixel : si vous en ajoutez une, vérifiez à quelles tailles elle est propre.
fontStyle
normal · bold · italic · bolditalic
⚠️ Ne fonctionne que si la police contient ces graisses. Une police livrée en un seul fichier « Regular » ne deviendra pas grasse : il faut fournir le fichier « Bold » et le désigner par son propre fontPath.
Livrées avec Recalbox
Rien à copier dans votre thème.
| Police | À écrire |
|---|---|
| Ubuntu Condensed | :/ubuntu_condensed.ttf |
| DejaVu Sans Condensed | :/dejavusanscondensed.ttf |
| Ubuntu Mono | :/UbuntuMonoR.ttf |
| 8-bit Fortress Plus — police à pixels, nette par multiples de 7 | :/f8bitfortressplus.ttf |
Ce qu’est une option
Offrir des choix à l’utilisateur de votre thème.
Une option est une propriété que l’utilisateur trouvera dans les menus de sa machine : « Couleurs du thème : Bleu / Vert / Rouge », « Filtre rétro : CRT / scanlines / aucun ».
C’est ce qui distingue un thème riche d’un thème figé.
Ce qu’une option fait vraiment
Une option n’est pas un interrupteur « montrer ou cacher ». C’est un fichier chargé par-dessus le thème, qui redéfinit ce qu’il veut : une couleur, une police, une mise en page entière.
Sur les thèmes officiels, l’immense majorité des options ne changent que des couleurs — souvent en redéfinissant une simple <variable>. Les masquages sont rares.
Une option agit sur toutes les vues à la fois : c’est une propriété du thème, pas d’une vue.
Les options du thème officiel
recalbox-next-2025 en propose dix, ce qui donne une bonne idée de ce qui se fait :
| Option | Ce qu’elle change |
|---|---|
systemView | la mise en page de la liste des systèmes |
gameList | celle de la liste de jeux |
gameclipview | celle de l’écran de veille |
SysInfos | les informations des systèmes : complètes, minimales, masquées |
gameInfos | les informations des jeux |
colorTheme | 12 palettes de couleurs |
shader | le filtre rétro : CRT, scanlines, nid d’abeille, aucun |
shadow | l’ombrage |
bands | les bandes de couleur : fines, épaisses, aucune |
iconesetTheme | les icônes de la barre d’aide : 8 jeux |
Déclarer une option : <subset>
La syntaxe exacte, en deux temps.
Une option se construit en deux temps : on la déclare, puis on liste ses choix.
1. Déclarer le groupe
<subset subset="colorTheme"
title="THEME : Colors" title.fr="THÈME : Couleurs"
help="Choose the color set" help.fr="Choisissez la palette" />
| Attribut | Rôle |
|---|---|
subset | l’identifiant du groupe — c’est lui qui relie les choix entre eux |
title | le libellé lu par l’utilisateur dans le menu |
help | la phrase d’explication sous le libellé |
title et help acceptent un suffixe de langue : title.fr, title.es… La version sans suffixe sert de repli.
2. Lister les choix
Chaque choix est un <include> qui porte le même subset :
<include subset="colorTheme" name="Blue" name.fr="Bleu">${root}/options/couleurs/bleu.xml</include>
<include subset="colorTheme" name="Green" name.fr="Vert">${root}/options/couleurs/vert.xml</include>
<include subset="colorTheme" name="Red" name.fr="Rouge">${root}/options/couleurs/rouge.xml</include>
| Attribut | Rôle |
|---|---|
subset | à quel groupe ce choix appartient |
name | le libellé du choix dans la liste (traduisible : name.fr) |
Le choix « aucun »
Un <include> vide donne l’option « aucun » — utile pour laisser le thème dans son état d’origine :
<include subset="shader" name="None" name.fr="Aucun"></include>
Ne proposer un choix que sur certains écrans
<include subset="systemView" if="(hd | fhd) and !tate"
name="Vertical left" name.fr="Vertical gauche">${root}/_views/vertical.xml</include>
<include subset="systemView" if="crt | jamma"
name="Horizontal">${root}/_views/horizontal.xml</include>
L’utilisateur ne voit que les choix pertinents pour son matériel. C’est ainsi que le filtre rétro n’apparaît pas sur un écran cathodique, qui n’en a pas besoin.
Le contenu d’un fichier de choix
C’est un fichier de thème ordinaire, qui ne redéfinit que ce qui change :
<?xml version="1.0" encoding="UTF-8"?>
<theme>
<variables>
<variable name="CouleurPrincipale" value="7C2E44" />
</variables>
</theme>
Trois lignes utiles, et tout le thème passe au rouge — à condition d’avoir bâti le thème sur des variables plutôt que sur des couleurs écrites en dur.
⚠️ Les options se chargent AVANT les vues. Une variable vaut pour ce qui est lu après elle : c’est là que le choix doit passer pour que les vues en profitent.
L’ordre à écrire dans theme.xml :
<theme name="Mon Thème" …>
<include>${root}/variables.xml</include> <!-- 1. les valeurs par défaut -->
<include>${root}/options.xml</include> <!-- 2. le choix les redéfinit -->
<include>${root}/views/system.xml</include> <!-- 3. les vues, qui les emploient -->
</theme>
Ce qui se passe quand la personne change de choix
Recalbox relit le thème en entier — tous les fichiers, depuis theme.xml, avec le nouveau choix actif. C’est pour cela qu’un bref « Mise à jour du thème… » s’affiche à ce moment-là.
Une option qui ne change que des couleurs redéfinit uniquement des variables. C’est la façon la plus courte d’écrire un jeu de couleurs — et la principale raison de se servir de
<variables>.
L’ordre des choix
Le préfixe numérique — et pourquoi il ne s’affiche pas.
Recalbox ne garde pas l’ordre dans lequel vous écrivez vos <include>. Il trie les choix lui-même, en deux temps :
- si TOUS les choix ont un numéro en tête de leur
name, il trie par ce numéro ; - sinon, il trie par ordre alphabétique du
name.
C’est pour cela qu’on numérote.
La bonne écriture
<include subset="bands" name="1 - Thin" name.fr="1 - Fines">…</include>
<include subset="bands" name="2 - Thick" name.fr="2 - Épaisses">…</include>
<include subset="bands" name="3 - None" name.fr="3 - Aucune">…</include>
Le préfixe ne s’affiche pas. Recalbox le repère, s’en sert pour trier, puis le retire avant d’afficher le libellé. L’utilisateur lit « Fines », « Épaisses », « Aucune ».
Ce que le moteur accepte comme préfixe
Un nombre, puis une espace, un tiret ou un point, le tout dans les huit premiers caractères. Ces trois écritures fonctionnent :
1 - Fines
1. Fines
1 Fines
Le piège
Le tri numérique n’est employé que si tous les choix sont numérotés. Un seul oubli, et Recalbox retombe sur l’ordre alphabétique — vos choix se réordonnent tout seuls, sans message.
<include subset="bands" name="1 - Fines">…</include>
<include subset="bands" name="2 - Épaisses">…</include>
<include subset="bands" name="Aucune">…</include> <!-- ❌ casse le tri des trois -->
Le nom des FICHIERS, lui, est libre
À ne pas confondre : le numéro va dans l’attribut name, pas dans le nom du fichier. Recalbox ne regarde jamais comment votre fichier s’appelle.
options/
bandes/
fines.xml ← nommez-les comme vous voulez
epaisses.xml
aucune.xml
Ce qui compte : un dossier par option, et des noms qui disent ce qu’ils font.
Essayer son thème
Le copier sur la machine, et lire le journal en cas de souci.
Un thème ne se juge qu’allumé, sur un écran. L’aperçu du studio est fidèle, mais rien ne remplace la machine.
- Exporter le thème depuis le studio ;
- copier le dossier dans
/recalbox/share/themes/; - sur la machine : Menu → Propriétés de l’interface → Thème, et choisir le vôtre.
📄 Le journal : themes.log
C’est le premier endroit à regarder quand quelque chose ne s’affiche pas.
/recalbox/share/system/logs/themes.log
Il consigne, fichier et ligne à l’appui :
- une propriété inconnue — souvent une faute de casse (
keepRatioau lieu dekeepratio) ; - un composant dont le type n’existe pas ;
- un
Extra type unknown: …— vous avez posé enextraun type qui ne l’accepte pas ; - une variable sans
nameou sansvalue; - une paire mal écrite (
pos,size).
Un composant invisible sans rien dans le journal signifie presque toujours qu’il manque extra="true" : voir La règle la plus importante.
Partager son thème
Proposer son thème au gestionnaire de thèmes de Recalbox, depuis le studio.
Recalbox a un gestionnaire de thèmes : on y installe un thème sans rien copier à la main. Pour qu'un thème y entre, il faut le proposer — et tout se fait depuis ce studio.
📘 Plus de dépôt à forker, plus de merge request, plus de fichier à écrire : le studio prépare le paquet, prend les captures et dépose tout à votre place.
Proposer son thème
Deux portes, et elles arrivent au même endroit :
- Votre thème est dans le studio — bouton « Exporter ou publier », onglet « Publier » ;
- Votre thème est un dossier ou un zip — « Importer un thème existant », puis « le proposer à la publication ».
Dans les deux cas, vous décrivez votre thème en quelques champs et vous envoyez. C'est tout.
⚠️ Il faut en avoir le droit. Proposer un thème est ouvert aux rôles que l'équipe a désignés sur le Discord de Recalbox : si le bouton n'apparaît pas, c'est qu'il vous manque.
Ce que le studio fait pour vous
- il photographie votre thème — chaque vue, chaque résolution que vous annoncez, et une image par choix d'option — puis il garde les meilleures ;
- il écrit la fiche du thème (nom, version, auteur, description, écrans compatibles) ;
- il dépose les fichiers et prévient tout le monde.
⚠️ Votre archive part telle quelle. Le studio la regarde pour prendre les captures ; il ne la réécrit jamais. Vos dossiers, vos noms de fichiers, votre organisation : rien ne bouge.
Ce qui se passe ensuite
- votre thème part au vote — les thémeurs le regardent écran par écran et donnent leur avis, avec un mot s'ils le souhaitent ;
- un administrateur tranche. Le vote éclaire, il ne décide pas : personne n'est publié par un décompte ;
- accepté, le thème entre dans le gestionnaire de thèmes : toutes les Recalbox le voient. Refusé, vous recevez un motif — de quoi corriger et reproposer.
Vous suivez tout depuis « Mes thèmes » : la carte de votre thème porte « demande en attente », et l'onglet « Publier » vous montre le décompte des voix et ce qu'on vous a écrit (sans les noms).
Mettre à jour un thème déjà publié
Le même chemin : vous reproposez votre thème, avec un numéro de version plus élevé.
⚠️ Une mise à jour ne repasse pas par le vote — c'est votre thème, vous en connaissez l'état. Elle attend seulement qu'un administrateur la mette en ligne. Les gens n'ont alors rien à réinstaller : le gestionnaire de thèmes leur propose la mise à jour.
Ce qui vous fait refuser
- un thème incomplet : une vue vide, un écran annoncé mais pas travaillé ;
- des images qui ne vous appartiennent pas ;
- une compatibilité annoncée sans l'avoir essayée — annoncer CRT quand on ne l'a jamais regardé sur un tube, ça se voit tout de suite.