Faire un thème Recalbox

45 pages · 8 sections
FRENESDE Tout afficher Page par page Créer mon thème

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

Ce qu’un thème ne décide pas

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

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

  1. 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.
  2. 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.
  3. Posez vos composants : faites-les glisser depuis la colonne de gauche, réglez-les dans celle de droite.
  4. 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.
  5. Exportez, copiez le dossier sur votre machine, essayez — voir Essayer son thème.

À la main

  1. 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.
  2. Créez le dossier et son theme.xml — le seul fichier obligatoire. Voir Les dossiers et les fichiers.
  3. Annoncez ce que vous visez dans la balise <theme> : compatibility pour les types d’écran, resolutions pour 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.
  4. É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.
  5. Servez les autres écrans avec des conditions plutôt qu’en recopiant tout : <include if="crt">.
  6. Copiez le dossier sur votre machine et essayez — et lisez themes.log au 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/.

📁 mon-theme/ 📄 theme.xml 📁 views/ 📁 data/ 📁 data/fonts/ …rangés comme vous voulez OBLIGATOIRE sans lui, le thème n’existe pas libre aucun nom, aucun découpage imposé la seule règle : un dossier, et un theme.xml à sa racine

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 anglaisviews/, 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

AttributRôleExempleSi absent
nameNom affiché dans la liste des thèmesname="Mon Thème"le nom du dossier
versionVersion du thèmeversion="1.2"non affichée
authorL’auteurauthor="Benoît"non affiché
recalboxVersion minimale de Recalbox exigéerecalbox="10.0"toutes versions
compatibilityTypes d’écran gérés : hdmi, crt, jamma, tatecompatibility="hdmi,crt"hdmi
resolutionsDéfinitions gérées : qvga, vga, hd, fhdresolutions="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.xmlla 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.

base.xml fond = bleu rouge.xml fond = rouge le fond est ROUGE lu de haut en bas : deux composants de même nom se remplacent, le DERNIER gagne c’est tout le mécanisme des surcouches — et donc des options

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 :

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

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

  <variable name="CheminLogo" value="${root}/data/logos/${system.name}.svg" />
  <variables if="crt">
    <variable name="TailleTitre" value="0.09" />
  </variables>

⚠️ 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 fichiervariables.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.

ÉcritureExempleCe que ça veut dire
Ratio (par défaut)0.5 0.25une proportion de l’écran, de 0 à 1
Pourcentage50% 25%la même chose, écrit autrement
Pixels960p 270pdes 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 :

ValeurCe que ça veut dire
< 1une proportion du petit côté de l’écran — 0.05 = 5 % de la hauteur en 16:9
>= 1une 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, dans l’ÉCRAN, on pose le composant. originquel 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.

l’écran, quelle que soit sa définition 00 10 01 11 pos 0.1 0.2 0.1 → 10 % de la largeur 0.2 → 20 % de la hauteur tout se mesure depuis le coin haut-gauche de l’ÉCRAN

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 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 :

votre composant000.501000.50.50.510.5010.5111les neuf valeurs possibles d’origin

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’écran (16:9) votre image (paysage) origin 1 1 le coin bas-droit de l’image… pos 0.5 0.5 …posé au centre de l’écran ➜ l’image n’est PAS centrée : elle est en haut à gauche du 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

originLe point du composant posé sur pos
0 0coin haut-gauche — la valeur par défaut
0.5 0milieu du bord haut
1 0coin haut-droit
0 0.5milieu du bord gauche
0.5 0.5le centre
1 0.5milieu du bord droit
0 1coin bas-gauche
0.5 1milieu du bord bas
1 1coin 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 veutposorigin
centré à l’écran0.5 0.50.5 0.5
collé au bord droit1 …1 …
collé au bord bas… 1… 1
centré en bas0.5 10.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 :

ÉcritureCe qui est garanti
size 0.3 0la largeur fait exactement 30 % ; la hauteur suit, quelle qu’elle soit — elle peut déborder
maxSize 0.3 0.2l’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>
size 0.3 0.2 l’image REMPLIT la boîte… image étirée ➜ déformée maxSize 0.3 0.2 …l’image TIENT dans la boîte image entière ➜ proportions gardées il reste du vide en haut et en bas le cadre pointillé jaune = la boîte demandée, dans les deux cas

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 maxSize qu’il faut. Les logos n’ont pas la même forme d’une machine à l’autre : avec size, 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.

rotationOrigin 0.5 0.5 il pivote sur son centre rotationOrigin 0 0 il pivote sur son coin haut-gauche rotation 20 — en degrés, sens horaire

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.

barre d’aide50textes et logos40voile de lisibilité20décor10fond1zIndex

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 -->

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éÀ écrireOpacitéÀ écrire
0 % — invisible0060 %99
10 %1A70 %B3
20 %3375 %BF
25 %4080 %CC
30 %4D90 %E6
40 %6695 %F2
50 %80100 % — opaqueFF

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é.

colorTop colorBottom dégradé vertical colorLeft colorRight dégradé horizontal colorTopLeft colorBottomRight dégradé aux quatre coins il suffit de donner DEUX couleurs pour obtenir un dégradé
PropriétéEffet
colorTop + colorBottomdégradé vertical
colorLeft + colorRightdégradé horizontal
colorTopLeft, colorTopRight, colorBottomLeft, colorBottomRightdé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

SuffixeCe qu’il vise
.fr .es .dela langue de la machine, en minuscules
.fr_FRla langue et le pays
.US .EU .JPla 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">

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

TypeCe qu’il fait
textun texte, sur une ou plusieurs lignes
scrolltextun texte qui défile quand il est trop long
markdownun texte mis en forme (gras, titres, listes)
imageune image
videoune vidéo
boxun bloc de couleur, ou un dégradé
datetimeune date
ratingune note, en étoiles
soundun son (aucun affichage)

Listes et navigation

TypeCe qu’il fait
textlistla liste des jeux
carouselle carrousel des systèmes
helpsystemla 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éTypeRôle
pos size origin rotation rotationOriginpaireplacement — voir Position, taille, origine
texttextele contenu, variables comprises
pathcheminlit le contenu dans un fichier, au lieu de text
fontPathcheminla police
fontSizenombre< 1 = ratio de hauteur d’écran, >= 1 = pixels
fontStyletextenormal, bold, italic, bolditalic
colorcouleurla couleur du texte
backgroundColorcouleurun fond derrière le texte
alignmenttextevoir ci-dessous
forceUppercaseoui/nontout en capitales
lineSpacingnombreinterligne, 1.2 par défaut
multilineoui/nonautoriser le retour à la ligne
zIndexnombreprofondeur
disabledoui/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À écrireSynonyme
haut-gauchetopleft
haut-centretopcentertop
haut-droitetopright
centre-gauchecenterleftleft
centrecenter
centre-droitecenterrightright
bas-gauchebottomleft
bas-centrebottomcenterbottom
bas-droitebottomright

⚠️ Une valeur inconnue ne laisse pas l’alignement précédent : elle retombe sur centre-gauche, la valeur par défaut.

toplefttopcenter= toptoprightcenterleft= leftcentercenterright= rightbottomleftbottomcenter= bottombottomrightle texte se place dans sa boîte `size` — sans size, l’alignement n’a rien pour s’exercer

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 ».

⚠️ markdown ne 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éTypeRôle
pos size origin rotation rotationOriginpaireplacement
maxSizepairetaille maximale sans déformer — voir Position, taille
keepratiooui/nonconserver les proportions (⚠️ tout en minuscules)
pathcheminle fichier image
tileoui/nonrépéter l’image en mosaïque au lieu de l’étirer
colorcouleurteinte l’image (multiplication)
colorTop colorBottom colorLeft colorRightcouleurteinte en dégradé
colorTopLeft colorTopRight colorBottomLeft colorBottomRightcouleurteinte aux quatre coins
reflectionpaireun reflet sous l’image : opacité de départ et de fin
zIndex disabledprofondeur, 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éTypeRôle
delaynombresecondes avant que la vidéo démarre
loopsnombrenombre de lectures ; 0 = en boucle
animationstextel’effet d’apparition
linktextelier la lecture à un autre composant
reflectionpairereflet, comme sur image

video n’accepte ni tile ni les couleurs de teinte.

Un delay d’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 rotationOriginplacement
colorcouleur unie
colorTop colorBottomdégradé vertical
colorLeft colorRightdégradé horizontal
colorTopLeft colorTopRight colorBottomLeft colorBottomRightdégradé aux quatre coins
zIndex disabledprofondeur, 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. textlist n’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 originplacement
primaryColorla couleur des jeux
secondaryColorla couleur des dossiers
selectedColorla couleur du texte de la ligne choisie
selectorColorla couleur du surligneur
selectorImagePathune image de surligneur, à la place de la couleur
selectorImageTilerépéter cette image en mosaïque
selectorHeightla hauteur du surligneur
selectorOffsetYson décalage vertical
fontPath fontSizela police
alignmentl’alignement des lignes
horizontalMarginla marge à gauche et à droite
forceUppercasetout en capitales
lineSpacingl’interligne — c’est lui qui espace les lignes
scrollSoundle son joué au défilement
zIndexprofondeur
Sonic the Hedgehog ▸ Un dossier Streets of Rage 2 Golden Axe Gunstar Heroes primaryColor les jeux secondaryColor les DOSSIERS (pas une ligne sur deux !) selectorColor le surligneur selectedColor le TEXTE de la ligne choisie selectorHeight l’erreur classique : croire que secondaryColor alterne une ligne sur deux

⚠️ 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.

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.

<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
typehorizontal, vertical, vertical_wheel
pos size originplacement
colorle fond du carrousel
logoSizela taille d’un logo (paire)
logoScalel’agrandissement du logo choisi
logoRotation logoRotationOriginrotation des logos (roues)
logoAlignmentl’alignement des logos dans leur case
maxLogoCountcombien de logos visibles à la fois
defaultTransitionfade ou instant ; toute autre valeur donne slide
fontPath fontSize fontColorla police du mode texte
forceUppercasele nom des systèmes tout en capitales
textOnlyécrire les noms au lieu des logos
primaryColor secondaryColorla couleur des noms
selectedColorla couleur du nom choisi
selectorColor selectorHeightle surligneur du mode texte
selectorOffsetX selectorOffsetYson décalage
textOffsetXle décalage du texte
lineSpacing horizontalMargininterligne et marges du mode texte
zIndexprofondeur
color — le fond du carrousel choisi logoSize × logoScale maxLogoCount — combien on en voit seul le logo choisi est à pleine taille : les autres sont réduits

⚠️ 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.


Dans la rubrique Liste des systèmes, « CE QU’ON VOIT DÉFILER » choisit entre :

⚠️ 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.

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 :

<carousel name="systemcarousel" type="vertical">
  <textOnly>true</textOnly>
  <textOffsetX>0.02</textOffsetX>
</carousel>

Disponible à partir de Recalbox 10.1. Sur une machine plus ancienne, textOnly est 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 rotationOriginplacement
filledPathl’image de l’étoile pleine
unfilledPathl’image de l’étoile vide
zIndex disabledprofondeur, 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

À écrireCe qui s’affiche
date1991/06/23
dateTime1991/06/23 14:05:30
year1991
time14:05:30
realTimel’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 originplacement
displayla forme de la date
color backgroundColorcouleurs
fontPath fontSizepolice
alignment forceUppercasemise en forme
zIndex disabledprofondeur, 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 :

NomCe que la machine fait
bgsoundjoue ce morceau
directorytire 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 sizeplacement
textColorla couleur des libellés
iconColorla teinte des icônes — fournissez-les en blanc
fontPath fontSizela police

Les 32 icônes

Remplacer une icône est facultatif : Recalbox fournit les siennes. Vous ne redéfinissez que celles que vous voulez.

DirectionsiconUpDown, iconLeftRight, iconUpDownLeftRight

BoutonsiconA, iconB, iconX, iconY

GâchettesiconL, iconR, iconL2, iconR2, iconL3, iconR3, iconLR, iconL2R2, iconL3R3

SystèmeiconStart, iconSelect, iconHotkey

Combinaisons avec HotkeyiconHkA, iconHkB, iconHkX, iconHkY, iconHkL, iconHkR, iconHkLeftRight

JoysticksiconJ1UpDown, 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.

iconHotkey compte 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 :

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>

iconset est 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
colorla couleur du cadre — elle teinte l’image si path est donné
pathl’image du cadre
fadePathl’image du voile qui assombrit la vue derrière

menuText (6) — les lignes du menu

PropriétéRôle
fontPath fontSizela police
colorle texte des lignes
selectedColorle texte de la ligne choisie
selectorColorle surligneur
separatorColorles 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

⚠️ iconList est 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
keyColorle fond de chaque touche au repos
keySelectedColorla touche sur laquelle on est
keyTextColorla lettre écrite sur la touche
keyDisabledColorla lettre d’un caractère que la saisie refuse
keyModifierColorMaj / Ctrl / Alt enfoncée pour une seule touche
keyModifierLockedColorMaj / Ctrl / Alt verrouillée
keyTitleColorle titre au-dessus du clavier
keyEditTextColorle texte en train d’être tapé
fontPathla 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.

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 :

  1. ses composants réservés, qu’elle construit elle-même ;
  2. les composants marqués extra="true".

Un composant libre sans extra n’apparaît jamais.

extra="true" → dessiné votre composant sans extra → invisible rien le moteur ne dessine que ses composants réservés + ceux marqués extra
<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 · markdowntextlist · 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

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

NomTypeRôle
systemcarouselcarouselle carrousel — un seul, non renommable
logoimagele logo du système dans le carrousel
systemInfotextla ligne « 510 jeux disponibles, 13 favoris »
bgsoundsoundla musique de fond du thème
directorysoundle dossier de musiques du thème

bgsound et directory sont 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éfautPrécision
typehorizontalhorizontal, vertical, vertical_wheel ; toute autre valeur retombe sur horizontal
logoSizecalculératio de l’écran, pas du carrousel
logoScale1.2agrandissement du logo choisi
maxLogoCount3arrondi à l’entier — une décimale ne sert à rien
colortransparentle fond du carrousel

L’espacement des logos se calcule ainsi :

espacement = (longueur − logoSize × maxLogoCount) / maxLogoCount + logoSize

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 basic n’est jamais demandée par le moteur, et la vue d’arcade réutilise detailed.

La liste et les médias

NomTypeRôle
gamelisttextlistla liste des jeux
logoimagele logo du système
md_imageimagela jaquette
default_image_pathimagel’image de remplacement quand le jeu n’a pas de jaquette
md_videovideola vidéo d’aperçu
md_region1md_region4imageles quatre drapeaux de région du jeu

Sur md_image, le path écrit dans le thème est ignoré : l’image vient du jeu. Sur les md_region*, seules pos, size, zIndex et path sont lues.

Les informations du jeu

NomType
md_descriptiontext, markdown ou scrolltext — au choix
md_folder_nametext
md_ratingrating
md_releasedate, md_lastplayeddatetime
md_developer, md_publisher, md_genre, md_players, md_playcount, md_favoritetext

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 sauf text : leur contenu vient du jeu.

Les couleurs de la liste

gamelist utilise cinq couleurs, dont trois ne sont pas thémables :

LigneCouleur
un jeuprimaryColor
un dossiersecondaryColor
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 triimposé

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

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 menu est é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 :

FamilleVues 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 jeuson nomsa date de sortiele fichier de la rom
un dossierle nom du dossierINCONNUle nom du dossier
un en-tête de tririenINCONNUrien

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 :

AncienneCe qu’elle donneÉquivalent actuel
$systemle nom court — « snes »${system.name}
$themele 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

À écrireCe que c’estCe qu’elle renvoie
${system}Nom du systèmeLe nom complet — ex. « Sega Megadrive »Systèmes, Jeux
${system.input.keyboard}Clavier nécessaire ?mandatory · recommended · optional · noSystèmes, Jeux
${system.input.mouse}Souris nécessaire ?mandatory · recommended · optional · noSystèmes, Jeux
${system.input.pad}Manette nécessaire ?mandatory · recommended · optional · noSystèmes, Jeux
${system.logo}Logo du systèmeLe chemin du logo fourni par RecalboxSystèmes, Jeux
${system.manufacturer}FabricantEx. « Sega », « Nintendo ». Vide si inconnuSystèmes, Jeux
${system.name}Nom court du systèmeSystèmes, Jeux
${system.releasedate}Année de sortieAnné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-arcadeSystèmes, Jeux
${system.type.name}Type de machineLe même, en clair : « Home Console », « handheld Console », « Arcade »…Systèmes, Jeux

Le jeu

À écrireCe que c’estCe qu’elle renvoie
${game.developer}DéveloppeurEx. « Konami ». « UNKNOWN » si absentVeille, Jeux
${game.file.name}Nom du fichierLe nom du fichier, extension compriseVeille, Jeux
${game.file.path}Chemin complet du fichierLe chemin complet du fichierVeille, Jeux
${game.file.stem}Nom du fichier (sans extension)Le nom du fichier, sans extensionVeille, Jeux
${game.genre.normalized}Genre (nom technique)Le genre normalisé, en anglais — « Platform », « Shoot’em Up », « Racing »…Veille, Jeux
${game.genre.raw}GenreLe genre tel qu’écrit dans la fiche du jeuVeille, Jeux
${game.isadult}Réservé aux adultes ?yes ou noVeille, Jeux
${game.isfavorite}Est un favori ?yes ou no (jamais true/false)Veille, Jeux
${game.ishidden}Est masqué ?yes ou noVeille, Jeux
${game.islastversion}Est la dernière version ?yes ou noVeille, Jeux
${game.isnotagame}N’est pas un jeu ?yes ou noVeille, Jeux
${game.ispreinstalled}Est préinstallé ?yes ou noJeux, Veille
${game.license}LicenceLa licence, souvent videVeille, Jeux
${game.name}Nom du jeuLe nom du jeuVeille, Jeux
${game.players}Nombre de joueurs« 1 », « 2 », « 1-4 », « 4+ »…Veille, Jeux
${game.players.max}Joueurs — maximumUn nombre — ex. « 4 »Veille, Jeux
${game.players.min}Joueurs — minimumUn nombre — ex. « 1 »Veille, Jeux
${game.publisher}ÉditeurEx. « Sega ». « UNKNOWN » si absentVeille, Jeux
${game.releasedate}Date de sortieDate ISO — ex. « 1991-06-23 ». « UNKNOWN » si absenteVeille, Jeux
${game.synopsis}DescriptionLe texte de présentation, souvent longVeille, Jeux

Note et statistiques

À écrireCe que c’estCe qu’elle renvoie
${game.lastplayed}Dernière partieDate ISO, ou « NEVER » si jamais jouéVeille, Jeux
${game.rating.10}Note (sur 10)Un entier de 0 à 10Veille, Jeux
${game.rating.100}Note (sur 100)Un entier de 0 à 100Veille, Jeux
${game.rating.5}Note (sur 5)Un entier de 0 à 5 — pas des étoilesVeille, Jeux
${game.timesplayed}Nombre de partiesUn nombre de partiesVeille, Jeux
${game.totalplayed}Temps de jeu totalUne durée — ex. « 3h 12m ». « NONE » si zéroVeille, Jeux

Images et vidéo du jeu

À écrireCe que c’estCe qu’elle renvoie
${game.media.boxpath}Boîtier (box art)Veille, Jeux
${game.media.imagepath}Jaquette / imageLe chemin de la jaquette. Vide si le jeu n’en a pas — voir ifexistsVeille, Jeux
${game.media.thumbpath}VignetteLe chemin de la vignetteVeille, Jeux
${game.media.videopath}VidéoLe chemin de la vidéoVeille, Jeux

Support du jeu

À écrireCe que c’estCe qu’elle renvoie
${game.support.index}Index du supportLe numéro du disque — vide s’il n’y en a qu’unVeille, Jeux
${game.support.number}Numéro du supportLe tout assemblé — ex. « 2A/3 »Veille, Jeux
${game.support.side}Face du supportLa face du support — A, B…Veille, Jeux
${game.support.total}Nombre de supportsLe nombre de supports. « UNKNOWN » si inconnuVeille, Jeux
${game.support.type}Type de supportCartridge · CD/DVD · Harddisk · Files · Tape · Quick Disc · 3" Floppy · 3".5 Floppy · 5".25 Floppy · PCB · UnknownVeille, Jeux

Système du jeu

À écrireCe que c’estCe qu’elle renvoie
${game.system}Nom du système du jeuLe nom complet du système du jeuVeille, Jeux
${game.system.input.keyboard}Clavier exigé par le système du jeumandatory · recommended · optional · noVeille, Jeux
${game.system.input.mouse}Souris exigée par le système du jeumandatory · recommended · optional · noVeille, Jeux
${game.system.input.pad}Manette exigée par le système du jeumandatory · recommended · optional · noVeille, Jeux
${game.system.logo}Logo du système du jeuLe chemin de son logoVeille, Jeux
${game.system.manufacturer}Fabricant du système du jeuSon fabricantVeille, Jeux
${game.system.name}Nom court du système du jeuSon nom interneVeille, Jeux
${game.system.releasedate}Année du système du jeuSon année de sortieVeille, 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 jeuLe même, en clairVeille, Jeux

Émulateur

À écrireCe que c’estCe qu’elle renvoie
${game.emulator.compatibility}Compatibilitéunknown · low · average · good · high · perfectVeille, Jeux
${game.emulator.extensions}Extensions géréesLes extensions gérées — ex. « .bin .gen .md »Veille, Jeux
${game.emulator.hasnetplay}Compatible jeu en ligne ?yes ou noVeille, Jeux
${game.emulator.hassoftpatching}Accepte les patchs ?yes ou noVeille, Jeux
${game.emulator.islibretro}Est un cœur Libretro ?yes ou noVeille, Jeux
${game.emulator.name}Nom de l’émulateurEx. « libretro picodrive »Veille, Jeux
${game.emulator.speed}Vitesseunknown · low · average · good · high · perfectVeille, Jeux

La machine et ses réglages

À écrireCe que c’estCe qu’elle renvoie
${display.overscan}Overscan ?yes ou noVeille, Systèmes, Menu, Jeux
${random.between(a,b,c)}Une valeur au hasard parmi…une des valeurs donnéespartout
${random.range(1,10)}Un nombre au hasard entre…un entier entre les deux bornespartout
${display.resolution}Résolutionfhd (1080p et plus) · hd (720p) · vga · qvgaVeille, Systèmes, Menu, Jeux
${display.tate}Écran vertical (TATE) ?yes ou noVeille, Systèmes, Menu, Jeux
${display.tateleft}Vertical vers la gauche ?yes ou noSystèmes, Menu, Jeux, Veille
${display.tateright}Vertical vers la droite ?yes ou noVeille, Systèmes, Menu, Jeux
${hardware.board}Modèle de machineLe modèle — « RPi 5 », « PC x64 », « RG351P/M »…Veille, Systèmes, Menu, Jeux
${hardware.crt}Écran CRT ?yes ou noMenu, Jeux, Veille, Systèmes
${hardware.isanbernic}Est-ce une Anbernic ?yes ou noVeille, Systèmes, Menu, Jeux
${hardware.isodroid}Est-ce un Odroid ?yes ou noVeille, Systèmes, Menu, Jeux
${hardware.ispc}Est-ce un PC ?yes ou noVeille, Systèmes, Menu, Jeux
${hardware.ispi}Est-ce un Raspberry Pi ?yes ou noVeille, Systèmes, Menu, Jeux
${hardware.jamma}Borne Jamma ?yes ou noSystèmes, Menu, Jeux, Veille
${recalbox.built}Date de compilationLa date de compilationVeille, Systèmes, Menu, Jeux
${recalbox.version}Version de RecalboxEx. « 10.0 »Veille, Systèmes, Menu, Jeux
${root}Dossier du thèmeLa racine du thème sélectionné — à mettre devant tous vos cheminsMenu, Jeux, Veille, Systèmes
${settings.language}LangueLa langue seule — ex. « fr »Veille, Systèmes, Menu, Jeux
${settings.locale}Langue et paysLangue et pays — ex. « fr_FR »Veille, Systèmes, Menu, Jeux
${settings.region}Région choisieeu · us · jpVeille, Systèmes, Menu, Jeux
${settings.timezone}Fuseau horaireEx. « 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

  1. ouvrez Variables dans la barre d’outils : le panneau s’ouvre à droite ;
  2. + 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) ;
  3. 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

ConditionVraie quand…
hd fhd vga qvgal’écran est de cette définition
crtl’écran est un tube cathodique
overscanl’image déborde (CRT sans Jamma)
tate tateleft taterightl’écran est vertical
jamma bartopla machine est une borne
ispc ispi isodroid isanbernicla machine est de ce modèle
nomenules 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.

ConditionVraie quand…
console handheld computer arcade engine port fantasyle système est de ce type
virtualc’est un système automatique (Favoris, Derniers joués, Tous les jeux…)
favoritec’est le système automatique Favoris
lastplayedc’est le système automatique Derniers joués

⚠️ favorite ne 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}) ou ifexists.


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 :

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 direSigneEn 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 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}
240ptestsuite240ptestsuite240ptestsuitevirtualvirtual
Acorn BBC MicrobbcmicroAcorn BBC MicroAcorncomputer
Amiga AGAamiga1200Amiga AGACommodorecomputer
Amiga CD32amigacd32Amiga CD32Commodoreconsole
Amiga CDTVamigacdtvAmiga CDTVCommodoreconsole
Amiga ECS/OCSamiga600Amiga ECS/OCSCommodorecomputer
Amstrad GX4000gx4000Amstrad GX4000Amstradconsole
AmstradCPCamstradcpcAmstradCPCAmstradcomputer
Apple IIapple2Apple IIApplecomputer
Apple IIGSapple2gsApple IIGSApplecomputer
Apple MacintoshmacintoshApple MacintoshApplecomputer
ArduboyarduboyArduboyfantasyfantasy
Atari 2600atari2600Atari 2600Atariconsole
Atari 5200atari5200Atari 5200Atariconsole
Atari 7800atari7800Atari 7800Atariconsole
Atari 8bitsatari800Atari 8bitsAtaricomputer
Atari JaguarjaguarAtari JaguarAtariconsole
Atari STataristAtari STAtaricomputer
ColecovisioncolecovisionColecovisionColecoconsole
Commodore 64c64Commodore 64Commodorecomputer
Commodore VIC-20vic20Commodore VIC-20Commodorecomputer
DaphnedaphneDaphneDaphnearcade
DICEdiceDICEDICEarcade
Dos (x86)dosDos (x86)IBMcomputer
Dragon 32/64dragonDragon 32/64DragonDatacomputer
EasyRPGeasyrpgEasyRPGvirtualengine
Elektronika BKbkElektronika BKElektronikacomputer
Epoch Cassette VisioncassettevisionEpoch Cassette VisionEpochconsole
Exelvision EXL 100exl100Exelvision EXL 100Exelvisioncomputer
Fairchild Channel FchannelfFairchild Channel FFairchildconsole
Family Computer Disk SystemfdsFamily Computer Disk SystemNintendoconsole
FinalBurn NeofbneoFinalBurn NeoFBNarcade
Game and WatchgwGame and WatchNintendohandheld
Game BoygbGame BoyNintendohandheld
Game Boy AdvancegbaGame Boy AdvanceNintendohandheld
Game Boy ColorgbcGame Boy ColorNintendohandheld
GameCubegamecubeGameCubeNintendoconsole
Infocom Z-MachinezmachineInfocom Z-MachineInfocomengine
LowRes NXlowresnxLowRes NXvirtualfantasy
LutrolutroLutrovirtualfantasy
LynxlynxLynxAtarihandheld
MamemameMameMamearcade
Mattel IntellivisionintellivisionMattel IntellivisionMattelconsole
MegaDuckmegaduckMegaDuckWelbackhandheld
MGT SAM CoupésamcoupeMGT SAM CoupéMGTcomputer
MoonlightmoonlightMoonlightNVidiavirtual
MSX1msx1MSX1Microsoftcomputer
MSX2msx2MSX2Microsoftcomputer
MSXturboRmsxturborMSXturboRMicrosoftcomputer
NEC PC-88pc88NEC PC-88NECcomputer
NEC PC-98pc98NEC PC-98NECcomputer
NEC PC-FXpcfxNEC PC-FXNECconsole
Neo-Geo AESneogeoNeo-Geo AESSNKconsole
Neo-Geo CDneogeocdNeo-Geo CDSNKconsole
Neo-Geo PocketngpNeo-Geo PocketSNKhandheld
Neo-Geo Pocket ColorngpcNeo-Geo Pocket ColorSNKhandheld
Nintendo 64n64Nintendo 64Nintendoconsole
Nintendo 64DD64ddNintendo 64DDNintendoconsole
Nintendo DSndsNintendo DSNintendohandheld
Nintendo Entertainment SystemnesNintendo Entertainment SystemNintendoconsole
Odyssey2o2emOdyssey2Magnavoxconsole
OpenBORopenborOpenBORSenile Teamengine
Oric/AtmosoricatmosOric/AtmosTangerinecomputer
Othello MultivisionmultivisionOthello MultivisionTsukudaconsole
PalmpalmPalmPalmhandheld
Panasonic 3DO3doPanasonic 3DOPanasonicconsole
PC EnginepcenginePC EngineNECconsole
PC Engine CDpcenginecdPC Engine CDNECconsole
Philips CD-IcdiPhilips CD-IPhillipsconsole
Philips P2000Tp2000tPhilips P2000TPhilipscomputer
Philips VG 5000vg5000Philips VG 5000Philipscomputer
PICO-8pico8PICO-8virtualfantasy
Pocket Challenge v2pcv2Pocket Challenge v2Benessehandheld
Pokémon MinipokeminiPokémon MiniNintendohandheld
Sammy AtomiswaveatomiswaveSammy AtomiswaveSammyarcade
SatellaviewsatellaviewSatellaviewNintendoconsole
ScreenshotsimageviewerScreenshotsvirtualvirtual
ScummVMscummvmScummVMLudvig Strigeusengine
Sega 32Xsega32xSega 32XSegaconsole
Sega CDsegacdSega CDSegaconsole
Sega DreamcastdreamcastSega DreamcastSegaconsole
Sega Game GeargamegearSega Game GearSegahandheld
Sega Master System / Mark IIImastersystemSega Master System / Mark IIISegaconsole
Sega MegadrivemegadriveSega MegadriveSegaconsole
Sega Model3model3Sega Model3Segaarcade
Sega NAOMInaomiSega NAOMISegaarcade
Sega NAOMI 2naomi2Sega NAOMI 2Segaarcade
Sega NAOMI GD-ROM SystemnaomigdSega NAOMI GD-ROM SystemSegaarcade
Sega PicopicoSega PicoSegaconsole
Sega SaturnsaturnSega SaturnSegaconsole
Sega SG1000sg1000Sega SG1000Segaconsole
Sharp X1x1Sharp X1Sharpcomputer
Sharp X68000x68000Sharp X68000Sharpcomputer
SolarussolarusSolarusSolarusengine
Sony Playstation 1psxSony Playstation 1Sonyconsole
Sony Playstation 2ps2Sony Playstation 2Sonyconsole
Sony Playstation PortablepspSony Playstation PortableSonyhandheld
SpectravideospectravideoSpectravideoSpectravideocomputer
ST-VstvST-VSegaarcade
SuFami TurbosufamiSuFami TurboBandaiconsole
Super Cassette VisionscvSuper Cassette VisionEpochconsole
Super Nintendo Entertainment SystemsnesSuper Nintendo Entertainment SystemNintendoconsole
SupergrafxsupergrafxSupergrafxNECconsole
Texas Instrument TI-99/4Ati994aTexas Instrument TI-99/4ATexas Instrumentcomputer
ThomsonthomsonThomsonThomsoncomputer
TIC-80tic80TIC-80portfantasy
TRS-80 Color Computertrs80cocoTRS-80 Color ComputerTandycomputer
UzeboxuzeboxUzeboxportconsole
VectrexvectrexVectrexMBconsole
Videopac+ G7400videopacplusVideopac+ G7400Philipsconsole
Vircon32vircon32Vircon32virtualconsole
Virtual BoyvirtualboyVirtual BoyNintendoconsole
Visual Pinball StandalonevpinballVisual Pinball StandaloneRandy Davisengine
WASM-4wasm4WASM-4Bruno Garciafantasy
Watara SupervisionsupervisionWatara SupervisionWatarahandheld
WiiwiiWiiNintendoconsole
WonderSwanwswanWonderSwanBandaihandheld
WonderSwan ColorwswancWonderSwan ColorBandaihandheld
XboxxboxXboxMicrosoftconsole
ZX81zx81ZX81Sinclaircomputer
ZXSpectrumzxspectrumZXSpectrumSinclaircomputer

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)dospc
GameCubegamecubegc
Odyssey2o2emodyssey2
Oric/Atmosoricatmosoric
Thomsonthomsonto8
WonderSwanwswanwonderswan
WonderSwan Colorwswancwonderswancolor

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}
PortsportsPortsvirtual
FavoritesfavoritesFavoritesvirtual
Last playedlastplayedLast playedvirtual
All gamesallgamesAll gamesvirtual
MultiplayermultiplayerMultiplayervirtual
ArcadearcadeArcadevirtual-arcade
LightgunlightgunLightgunvirtual
TatetateTatevirtual
DialdialDialvirtual
TrackballtrackballTrackballvirtual
ChallengeschallengesChallengesvirtual

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 playedlastplayedauto-lastplayed
All gamesallgamesauto-allgames
Multiplayermultiplayerauto-multiplayer
Lightgunlightgunauto-lightgun
Tatetateauto-tate
Dialdialauto-dial
Trackballtrackballauto-trackball
Challengeschallengesauto-challenges

Arcade par constructeur (54) (${system.type} = virtual-arcade)

Le système${system.name}${system}
Acclaimarcade-manufacturer-acclaimAcclaim
Atariarcade-manufacturer-atariAtari
Atlusarcade-manufacturer-atlusAtlus
Banprestoarcade-manufacturer-banprestoBanpresto
Capcom Cps1arcade-manufacturer-capcom-cps1Capcom Cps1
Capcom Cps2arcade-manufacturer-capcom-cps2Capcom Cps2
Capcom Cps3arcade-manufacturer-capcom-cps3Capcom Cps3
Capcomarcade-manufacturer-capcomCapcom
Cavearcade-manufacturer-caveCave
Data eastarcade-manufacturer-data eastData east
Exidyarcade-manufacturer-exidyExidy
Hng64arcade-manufacturer-hng64Hng64
Igsarcade-manufacturer-igsIgs
Irem M72arcade-manufacturer-irem-m72Irem M72
Irem M92arcade-manufacturer-irem-m92Irem M92
Iremarcade-manufacturer-iremIrem
Itecharcade-manufacturer-itechItech
Jalecoarcade-manufacturer-jalecoJaleco
Kanekoarcade-manufacturer-kanekoKaneko
Konami Gxarcade-manufacturer-konami-gxKonami Gx
Konamiarcade-manufacturer-konamiKonami
Midwayarcade-manufacturer-midwayMidway
Mitchellarcade-manufacturer-mitchellMitchell
Namco Naarcade-manufacturer-namco-naNamco Na
Namco Nbarcade-manufacturer-namco-nbNamco Nb
Namco System1arcade-manufacturer-namco-system1Namco System1
Namco System10arcade-manufacturer-namco-system10Namco System10
Namco System11arcade-manufacturer-namco-system11Namco System11
Namco System12arcade-manufacturer-namco-system12Namco System12
Namco System18arcade-manufacturer-namco-system18Namco System18
Namco System2arcade-manufacturer-namco-system2Namco System2
Namcoarcade-manufacturer-namcoNamco
Neogeoarcade-manufacturer-neogeoNeogeo
Nichibutsuarcade-manufacturer-nichibutsuNichibutsu
Nintendoarcade-manufacturer-nintendoNintendo
Nmkarcade-manufacturer-nmkNmk
Psikyoarcade-manufacturer-psikyoPsikyo
Raizingarcade-manufacturer-raizingRaizing
Sammyarcade-manufacturer-sammySammy
Sega Stvarcade-manufacturer-sega-stvSega Stv
Sega System16arcade-manufacturer-sega-system16Sega System16
Sega System18arcade-manufacturer-sega-system18Sega System18
Sega System32arcade-manufacturer-sega-system32Sega System32
Segaarcade-manufacturer-segaSega
Seibuarcade-manufacturer-seibuSeibu
Setaarcade-manufacturer-setaSeta
Snkarcade-manufacturer-snkSnk
Taito F3arcade-manufacturer-taito-f3Taito F3
Taito Gnetarcade-manufacturer-taito-gnetTaito Gnet
Taitoarcade-manufacturer-taitoTaito
Technosarcade-manufacturer-technosTechnos
Tecmoarcade-manufacturer-tecmoTecmo
Toaplanarcade-manufacturer-toaplanToaplan
Viscoarcade-manufacturer-viscoVisco

Par genre (56) (${system.type} = virtual)

Le système${system.name}${system}
Actiongenre-actionAction
Platformgenre-actionplatformerPlatform
Platform Shootergenre-actionplatformshooterPlatform Shooter
First Person Shootergenre-actionfirstpersonshooterFirst Person Shooter
Shoot'em Upgenre-actionshootemupShoot'em Up
Shoot with Gungenre-actionshootwithgunShoot with Gun
Fightinggenre-actionfightingFighting
Beat'em Allgenre-actionbeatemupBeat'em All
Infiltrationgenre-actionstealthInfiltration
Battle Royalegenre-actionbattleroyaleBattle Royale
Rythm & Musicgenre-actionrythmRythm & Music
Adventuregenre-adventureAdventure
Textual Adventuregenre-adventuretextTextual Adventure
Graphical Adventuregenre-adventuregraphicsGraphical Adventure
Visual Novelgenre-adventurevisualnovelsVisual Novel
Interactive Moviegenre-adventureinteractivemovieInteractive Movie
Real Time 3D Adventuregenre-adventurerealtime3dReal Time 3D Adventure
Survivalgenre-adventuresurvivalhorrorSurvival
RPGgenre-rpgRPG
Action RPGgenre-rpgactionAction RPG
MMORPGgenre-rpgmmoMMORPG
Dungeon Crawlergenre-rpgdungeoncrawlerDungeon Crawler
Tactical RPGgenre-rpgtacticalTactical RPG
JRPGgenre-rpgjapaneseJRPG
Party based RPGgenre-rpgfirstpersonpartybasedParty based RPG
Simulationgenre-simulationSimulation
Build & Managementgenre-simulationbuildandmanagementBuild & Management
Life Simulationgenre-simulationlifeLife Simulation
Fishing & Huntinggenre-simulationfishandhuntFishing & Hunting
Vehicle Simulationgenre-simulationvehicleVehicle Simulation
Science Fiction Simulationgenre-simulationscifiScience Fiction Simulation
Strategygenre-strategyStrategy
eXplore, eXpand, eXploit & eXterminategenre-strategy4xeXplore, eXpand, eXploit & eXterminate
Artillerygenre-strategyartilleryArtillery
Auto-battlergenre-strategyautobattlerAuto-battler
Multiplayer Online Battle Arenagenre-strategymobaMultiplayer Online Battle Arena
Real Time Strategygenre-strategyrtsReal Time Strategy
Turn Based Strategygenre-strategytbsTurn Based Strategy
Tower Defensegenre-strategytowerdefenseTower Defense
Wargamegenre-strategywargameWargame
Sportsgenre-sportsSports
Racinggenre-sportracingRacing
Sport Simulationgenre-sportsimulationSport Simulation
Competition Sportgenre-sportcompetitiveCompetition Sport
Fighting/Violent Sportgenre-sportfightFighting/Violent Sport
Pinballgenre-pinballPinball
Board gamegenre-boardBoard game
Casual gamegenre-casualCasual game
Digital Cardsgenre-digitalcardDigital Cards
Puzzle & Logicgenre-puzzleandlogicPuzzle & Logic
Multiplayer Party Gamegenre-partyMultiplayer Party Game
Triviagenre-triviaTrivia
Casinogenre-casinoCasino
Multi Game Compilationgenre-compilationMulti Game Compilation
Demo from Demo Screnegenre-demosceneDemo from Demo Screne
Educativegenre-educativeEducative

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 :

OptionCe qu’elle change
systemViewla mise en page de la liste des systèmes
gameListcelle de la liste de jeux
gameclipviewcelle de l’écran de veille
SysInfosles informations des systèmes : complètes, minimales, masquées
gameInfosles informations des jeux
colorTheme12 palettes de couleurs
shaderle filtre rétro : CRT, scanlines, nid d’abeille, aucun
shadowl’ombrage
bandsles bandes de couleur : fines, épaisses, aucune
iconesetThemeles 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.

<subset subset="colorTheme" déclare le GROUPE + son titre <include … name="1 - Bleu"> <include … name="2 - Rouge"> chaque choix = un fichier chargé par-dessus Réglages ▸ Thème Couleurs du thème ◁ Bleu ▷ ce que voit l’utilisateur sur sa machine le préfixe « 1 - » sert à ORDONNER les choix : il ne s’affiche pas

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" />
AttributRôle
subsetl’identifiant du groupe — c’est lui qui relie les choix entre eux
titlele libellé lu par l’utilisateur dans le menu
helpla 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>
AttributRôle
subsetà quel groupe ce choix appartient
namele 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 :

  1. si TOUS les choix ont un numéro en tête de leur name, il trie par ce numéro ;
  2. 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.

  1. Exporter le thème depuis le studio ;
  2. copier le dossier dans /recalbox/share/themes/ ;
  3. 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 :

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 :

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

⚠️ 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

  1. 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 ;
  2. un administrateur tranche. Le vote éclaire, il ne décide pas : personne n'est publié par un décompte ;
  3. 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