Was ist ein Theme?
Was ein Theme ändert — und was nicht.
Ein Recalbox-Theme kleidet die Oberfläche ein: die Liste deiner Systeme beim Start, die deiner Spiele, die Menüs, den Bildschirmschoner.
Was ein Theme entscheidet
- wo die Elemente auf dem Bildschirm sitzen, und wie groß;
- welche Bilder angezeigt werden: Hintergründe, Logos, Rahmen, Illustrationen;
- die Farben und die Schriften, auch die der Menüs;
- was je nach Kontext erscheint: die Bildschirmauflösung, die Art der Maschine, das markierte Spiel.
Was ein Theme nicht entscheidet
- den Inhalt der Menüs: Recalbox baut sie selbst. Ein Theme gestaltet nur ihr Aussehen;
- die Spiele, ihre Daten, die Emulatoren;
- das Verhalten der Tasten.
Das Prinzip, in einem Satz
Recalbox liest XML-Dateien, die Komponenten deklarieren — ein Bild, einen Text, eine Liste — jede mit ihrer Position, ihrer Größe und ihrem Aussehen.
<image name="fond">
<pos>0 0</pos>
<size>1 1</size>
<path>./data/fond.jpg</path>
</image>
Das Studio schreibt diese Dateien für dich. Diese Dokumentation erklärt die vollständige Syntax: Sie soll dir erlauben, ein Theme ganz von Hand zu schreiben, wenn du möchtest.
Das minimale funktionierende Theme
Die vier Ansichten, in einer einzigen Datei.
Ein Theme fügt sich immer auf dieselbe Weise zusammen, und das ist der Plan dieser ganzen Dokumentation:
ein Theme enthält Ansichten — je ein Bildschirm; eine Ansicht enthält Komponenten — ein Bild, einen Text, eine Liste; eine Komponente trägt Eigenschaften — ihre Position, ihre Größe, ihre Farbe.
Hier ist ein vollständiges, funktionierendes Theme: alle vier Ansichten sind da. Lege einen Ordner mon-theme/ in /recalbox/share/themes/ an, lege dieses theme.xml hinein, und es erscheint in der Theme-Liste.
<?xml version="1.0" encoding="UTF-8"?>
<theme name="Mein Theme" version="1.0" author="Ich"
recalbox="10.0" compatibility="hdmi,crt" resolutions="hd,fhd">
<!-- ── 1. DIE SYSTEMLISTE ──────────────────────────────────── -->
<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. DIE SPIELELISTE ──────────────────────────────────── -->
<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. DIE MENÜGESTALTUNG ───────────────────────────────── -->
<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. DER BILDSCHIRMSCHONER ────────────────────────────── -->
<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>
Was daran auffallen soll
extra="true"auf dem Hintergrund: Ohne ihn würde er gar nicht angezeigt. Das ist die Regel, die man sich merken muss — erklärt in Die wichtigste Regel;systemcarousel,logo,gamelist,md_imagesind reservierte Namen: dieser Name — nicht der Typ des Tags — verbindet sie mit der Engine. Es gibt etwa dreißig davon, und jede Ansicht hat ihre eigenen: die vollständige Liste steht in Die Ansichten und ihre Komponenten;${system}und${game.name}sind Variablen: Recalbox ersetzt sie beim Anzeigen durch den Namen des aktuellen Systems oder des markierten Spiels. Das macht ein Theme lebendig, ohne etwas Besonderes zu schreiben — siehe Variablen verwenden;- die Ansicht
menuwird nicht zusammengesetzt: Man platziert dort nichts, man gestaltet, was Recalbox baut; - die Ansicht
gameclipist der Bildschirmschoner — Recalbox spielt dort Spielausschnitte ab, und das Theme fügt kaum mehr hinzu als Informationen über das gezeigte Spiel.
Hier passt alles in eine Datei, damit es auf einen Blick lesbar ist. Sobald das Theme wächst, teilt man es auf — siehe Das Theme aufteilen.
Dein erstes Theme erstellen
Der Weg — mit dem Studio, oder von Hand.
Das Ergebnis ist dasselbe: ein Theme-Ordner. Zwei Wege führen dorthin.
Mit dem Studio
- Starte von einer Vorlage. Die leere Seite ist der schlechteste Anfang. Eine Vorlage kommt mit fertig platzierten Komponenten: Du ersetzt sie.
- Wähle deine Bildschirmauflösungen — HD 16:9, CRT 4:3, vertikaler Bildschirm (TATE). Was auf einen Breitbildschirm passt, passt nicht auf einen Röhrenfernseher. Beginne mit einer, ergänze die anderen später.
- Platziere deine Komponenten: Ziehe sie aus der linken Spalte, stelle sie in der rechten ein.
- Wechsle das System in Ansichtsoptionen und schau hin: Der häufigste Fehler ist ein Theme, das auf einer einzigen Maschine eingestellt wurde und überall sonst zerbricht. Nicht alle Logos haben dieselbe Form, nicht alle Namen dieselbe Länge.
- Exportiere, kopiere den Ordner auf deine Maschine, probiere es aus — siehe Das Theme ausprobieren.
Von Hand
- Starte von einem bestehenden Theme. Öffne eines, lies es: Das ist der schnellste Weg zu verstehen, wie es gemacht wird. Das offizielle Recalbox-Theme ist ein guter Anfang.
- Lege den Ordner und sein
theme.xmlan — die einzige Pflichtdatei. Siehe Ordner und Dateien. - Kündige an, was du abdeckst, im
<theme>-Tag:compatibilityfür die Bildschirmtypen,resolutionsfür die Auflösungen. Diese beiden Attribute schalten die Piktogramme des Theme-Managers ein — sie falsch anzugeben heißt versprechen, was das Theme nicht hält. Siehe Der Kopf. - Schreibe deine Ansichten, eine pro Bildschirm, und teile in mehrere Dateien auf, sobald es wächst — siehe Das minimale funktionierende Theme, dann Das Theme aufteilen.
- Bediene die anderen Bildschirme mit Bedingungen statt alles zu kopieren:
<include if="crt">. - Kopiere den Ordner auf deine Maschine und probiere es aus — und lies
themes.logbei der ersten unsichtbaren Komponente.
In beiden Fällen lauert derselbe Fehler: nur auf DEINEM Bildschirm und DEINEM System zu prüfen.
Ordner und Dateien
Eine einzige Regel, und viel Freiheit.
Ein Theme ist ein Ordner in /recalbox/share/themes/.
Die einzige Regel
Dieser Ordner muss eine Datei
theme.xmlenthalten.
Das ist alles. theme.xml ist der Einstiegspunkt: Recalbox sucht sie in der Wurzel des Ordners, und wenn sie fehlt, existiert das Theme nicht.
Nichts anderes ist vorgeschrieben. Keine Unterordnernamen, keine Aufteilung, keine Organisation. Du kannst ein ganzes Theme in dieser einen Datei schreiben.
Aufteilen, weil es praktischer ist
Ein ernsthaftes Theme kommt schnell auf Tausende Zeilen. Man teilt es daher in mehrere XML-Dateien auf, geordnet wie man will, die theme.xml einbindet:
mon-theme/
theme.xml ← der einzige vorgeschriebene Name
variables.xml
views/
system.xml
detailed.xml
menu.xml
data/
fonts/
images/
Diese Namen gehören dir: Nenne sie, wie du willst. Üblich ist, in veröffentlichten Themes wie in den Exporten des Studios, sie auf Englisch zu schreiben — views/, data/, fonts/ — weil das die Sprache der Tags ist, die sie enthalten.
Der Einbindungsmechanismus wird in Das Theme aufteilen erklärt.
Die Pfade
Ein in einer Datei geschriebener Pfad ist relativ zu dieser Datei. Sobald man in Unterordner aufteilt, wird das zur Fehlerquelle.
Geh über ${root}, das immer die Wurzel des Themes bezeichnet:
<path>${root}/data/images/fond.jpg</path> <!-- ✅ funktioniert aus jeder Datei -->
<path>../data/images/fond.jpg</path> <!-- fragil: hängt davon ab, wo die Zeile steht --> Der Kopf: das <theme>-Tag
Was dein Theme identifiziert — und NUR in theme.xml gehört.
<theme> ist die Wurzel jeder XML-Datei eines Themes. Aber seine Identitätsattribute haben nur in theme.xml einen Sinn: Diese Datei liest der Theme-Manager, um zu wissen, worum es geht.
Merken In
theme.xmlfüllst du sie alle aus: Ohne sie erscheint dein Theme ohne Namen, ohne Version, und der Manager weiß nicht, auf welchen Bildschirmen es funktioniert. In den anderen Dateien schreibst du ein nacktes<theme>: Sie zu wiederholen bringt nichts und kann verwirren.
Die Attribute
| Attribut | Rolle | Beispiel | Wenn es fehlt |
|---|---|---|---|
name | Name in der Theme-Liste | name="Mein Theme" | der Ordnername |
version | Version des Themes | version="1.2" | nicht angezeigt |
author | Der Autor | author="Benoît" | nicht angezeigt |
recalbox | Mindestversion von Recalbox | recalbox="10.0" | alle Versionen |
compatibility | Unterstützte Bildschirmtypen: hdmi, crt, jamma, tate | compatibility="hdmi,crt" | hdmi |
resolutions | Unterstützte Auflösungen: qvga, vga, hd, fhd | resolutions="hd,fhd" | fhd,hd |
compatibility und resolutions sind genau die Piktogramme, die der Theme-Manager zeigt: HDMI / CRT / JAMMA / TATE auf der einen Seite, SD 240p / SD+ 480p / HD 720p / FULLHD 1080p auf der anderen. Sie falsch anzugeben heißt versprechen, was das Theme nicht hält.
⚠️ Ohne sie kann dein Theme nicht veröffentlicht werden im Theme-Manager: Das offizielle Repository verlangt einen Namen, eine Version und einen Autor. Siehe Das Theme teilen.
Die zwei Fälle
In theme.xml — das vollständige Tag:
<theme name="Mein Theme" version="1.2" author="Benoît"
recalbox="10.0" compatibility="hdmi,crt" resolutions="hd,fhd">
…
</theme>
In allen anderen Dateien des Themes — das nackte Tag:
<theme>
<view name="system"> … </view>
</theme> Das Theme aufteilen: <include>
Ein ernsthaftes Theme passt nicht in eine einzige Datei.
<include> lädt eine andere Datei an genau dieser Stelle, als wäre ihr Inhalt dorthin kopiert.
<include>${root}/views/system.xml</include>
<include path="${root}/views/detailed.xml" />
Beide Schreibweisen funktionieren: der Pfad als Inhalt des Tags, oder als Attribut path.
Die Reihenfolge zählt
Recalbox liest von oben nach unten, und zwei Komponenten mit demselben Namen ersetzen einander: die letzte gewinnt.
Das ist der ganze Overlay-Mechanismus — und so sind die Theme-Optionen gebaut: Jede Wahl ist eine obendrauf geladene Datei, die nur neu definiert, was sich ändert. Siehe Was eine Option ist.
<include>${root}/views/base.xml</include> <!-- malt den Hintergrund blau -->
<include>${root}/options/rouge.xml</include> <!-- malt ihn rot um -->
Bedingt einbinden
Ein <include> akzeptiert if=, wie eine Komponente: Die Datei wird nur geladen, wenn die Bedingung wahr ist.
<include if="crt">${root}/views/system-crt.xml</include>
So bedient man je Bildschirm ein anderes Layout, ohne alles andere zu duplizieren. Die Liste der Bedingungen steht in Bedingt anzeigen.
Freie Komponenten: extra="true" und <extras>
Warum eine hinzugefügte Komponente nicht erscheint — und die zwei Arten, sie zu deklarieren.
Zwei Familien von Komponenten
Innerhalb einer Ansicht unterscheidet Recalbox:
- die Elemente, die es selbst baut — das Karussell, die Spieleliste, die Hilfeleiste, das Cover des Spiels… Sie haben einen reservierten Namen, und das Theme stellt sie nur ein;
- die Komponenten, die du hinzufügst — ein Text, ein Bild, ein Farbblock, ein Video. Das sind die freien Komponenten.
Eine freie Komponente muss als solche deklariert werden
Eine freie Komponente direkt in der Ansicht wird nicht gezeichnet: Die Maschine baut nur die mit extra markierten Elemente. Zwei mögliche Schreibweisen, die genau dasselbe tun:
<image name="monLogo" extra="true">…</image>
<extras>
<image name="monLogo">…</image>
<text name="maMention">…</text>
</extras>
<extras> ist ein Container: Jedes seiner Kinder erhält extra="true", auf einen Schlag. Kürzer und lesbarer, sobald es mehrere Komponenten gibt — das schreibt auch das Studio.
Was man sich merken muss
<extras>ist keine Komponente: Es wird nicht gezeichnet, nicht positioniert.- Es verschachtelt sich nicht in sich selbst: Ein
<extras>in einem<extras>hat keine zusätzliche Wirkung. - Es funktioniert in der Ansicht Systeme, der Spieleliste und dem Bildschirmschoner. ⚠️ Nicht im Menü: Die Engine sucht dort keine freie Komponente. Ein für die Menü-Ansicht geschriebenes
<extras>wird fehlerfrei gelesen… und zeichnet nie etwas. - Elemente mit reserviertem Namen bleiben außerhalb: Sie existieren bereits, man stellt sie nur ein.
Im Studio musst du nichts tun: Die Komponenten, die du hinzufügst, werden automatisch in
<extras>geschrieben, die reservierten Elemente außerhalb.
Deine eigenen Variablen: <variables>
Was in die Datei kommt: das Tag, die Namensregeln, der Geltungsbereich.
Diese Seite beschreibt, was in die Datei kommt. Um sie ohne eine Zeile XML anzulegen, siehe Deine maßgeschneiderten Variablen unter „Dynamische Daten“.
Es ist der nützlichste Mechanismus eines ernsthaften Themes — und doch der unbekannteste.
<variables>
<variable name="CouleurPrincipale" value="2E447C" />
<variable name="PoliceTitre" value="${root}/data/fonts/Exo2.otf" />
<variable name="Alpha50" value="80" />
</variables>
Dann, überall im Theme:
<box name="fond">
<color>${CouleurPrincipale}</color>
</box>
<text name="titre">
<fontPath>${PoliceTitre}</fontPath>
<color>${CouleurPrincipale}${Alpha50}</color>
</text>
Ändere den Wert an einer Stelle, das ganze Theme folgt. Das macht Farboptionen möglich: Eine Overlay-Datei definiert die Variable neu, und sonst nichts.
Die Regeln
nameundvaluesind beide Pflicht; fehlt eines, wird die Zeile ignoriert und im Protokoll gemeldet;- ein leerer
namewird abgelehnt; - eine Variable kann eine andere enthalten, auch eine Recalbox-Variable:
<variable name="CheminLogo" value="${root}/data/logos/${system.name}.svg" />
- der
<variables>-Block akzeptiert eine Bedingung, und jede<variable>ebenfalls:
<variables if="crt">
<variable name="TailleTitre" value="0.09" />
</variables>
- es kann mehrere
<variables>-Blöcke geben, in jeder Datei; - eine weiter unten neu definierte Variable überschreibt die vorige — wie Komponenten.
⚠️ Der Block steht ganz oben
<variables>gilt für alles, was NACH ihm gelesen wird.
Recalbox ersetzt jedes ${nom} beim Lesen der Dateien. Eine oben im Theme deklarierte Variable gilt also überall; in der Mitte deklariert, gilt sie nur für den Rest.
Daher die Regel, gültig für alle Themes: der Variablenblock zuerst, vor den Ansichten, vor allem anderen.
Eine später neu definierte Variable ersetzt die vorige für den Rest des Lesens — das erlaubt einer Optionswahl, ein ganzes Theme umzufärben, sofern sie vor den Ansichten geladen wird. Siehe Eine Option deklarieren.
Wo sie deklarieren
In theme.xml, vor den <include>-Tags, die sie benutzen: Die Engine liest die Variablen einer Datei, bevor sie ihre Einbindungen verarbeitet.
Üblich, vom offiziellen Theme übernommen, ist eine eigene Datei — variables.xml — als allererstes eingebunden:
<include>${root}/variables.xml</include> Die drei Schreibweisen einer Eigenschaft
Tag, Tag mit Wert, oder Attribut — und warum das alles ändert.
Dieselbe Eigenschaft kann auf drei Arten geschrieben werden. Sie sind gleichwertig… außer in einem entscheidenden Punkt.
1. Als Kind-Tag
<image name="fond">
<path>./data/fond.jpg</path>
</image>
Die historische Form. Am lesbarsten, sobald eine Komponente mehrere Eigenschaften hat.
2. Als Kind-Tag mit value-Attribut
<image name="fond">
<path value="./data/fond.jpg" />
</image>
Streng gleichwertig zur ersten.
3. Als Attribut der Komponente
<image name="fond" path="./data/fond.jpg" pos="0 0" size="1 1" />
Alles passt in eine Zeile. Sehr praktisch für einfache Komponenten.
Der Unterschied, der zählt
Nur die Formen 1 und 2 akzeptieren eine Bedingung auf EINER Eigenschaft.
<image name="fond">
<path if="crt">./data/fond-crt.jpg</path>
<path if="!crt">./data/fond-hd.jpg</path>
</image>
In Form 3 unmöglich: if= auf dem Eltern-Tag würde die ganze Komponente bedingen, nicht eine ihrer Eigenschaften.
Sie mischen sich frei
Das ist der wichtige Punkt: Du musst dich nicht für eine Art entscheiden und dabei bleiben. Recalbox' Leser akzeptiert alle drei, auch in derselben Komponente.
<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>
Drei Schreibweisen in derselben Komponente: die einfachen Werte als Attribute in der ersten Zeile, die mit Varianten als bedingte Tags, und eine letzte als Tag mit value. Vollkommen gültig — und genau das schreibt man in der Praxis.
➡️ Einfache Regel: ein einziger Wert → Attribut; Varianten → Kind-Tag.
Ratio, Prozent oder Pixel
Die drei akzeptierten Schreibweisen für eine Position oder eine Größe.
Mehrere Eigenschaften werden mit zwei durch ein Leerzeichen getrennten Zahlen geschrieben: x y. Das gilt für pos, size, maxSize, origin, rotationOrigin, logoSize und reflection.
Jede Zahl akzeptiert drei Schreibweisen, und man darf sie im selben Paar mischen.
| Schreibweise | Beispiel | Bedeutung |
|---|---|---|
| Ratio (Standard) | 0.5 0.25 | ein Anteil des Bildschirms, von 0 bis 1 |
| Prozent | 50% 25% | dasselbe, anders geschrieben |
| Pixel | 960p 270p | echte Pixel, Suffix p |
<pos>0.5 0.5</pos> <!-- die Mitte -->
<pos>50% 50%</pos> <!-- genau dasselbe -->
<pos>960p 540p</pos> <!-- die Mitte… nur eines 1920×1080-Bildschirms -->
Ratio oder Prozent: vorzuziehen
x ist ein Anteil der Breite, y der Höhe. Eine Komponente bei 0.5 0.5 ist auf einem 1920×1080 wie auf einem 640×480 in der Mitte. Das lässt ein Theme auf mehrere Bildschirme passen.
Pixel: eine feste Position
Ein Pixel ist ein Pixel. 960p sind 960 Pixel vom linken Rand, Punkt.
Das passt sich an nichts an: Auf einem Bildschirm mit weniger als 960 Pixeln Breite liegt die Komponente außerhalb des Bildschirms. Eine absolute Position, das Gegenteil des Ratios.
Pixel rechtfertigen sich nur für das, was unabhängig von der Auflösung fest bleiben muss: die Dicke einer Linie, ein Versatz von wenigen Punkten. Für alles andere nimm das Ratio.
Der Sonderfall fontSize
fontSize nimmt nur eine Zahl, und ihre Einheit hängt vom Wert ab:
| Wert | Bedeutung |
|---|---|
< 1 | ein Anteil der kurzen Seite des Bildschirms — 0.05 = 5 % der Höhe in 16:9 |
>= 1 | eine 240p-Referenzgröße, die Recalbox je nach Bildschirm multipliziert |
Über 1 ist der Wert keine Pixel. Es ist eine Größe für einen Bildschirm mit 240 Zeilen, bei jedem Auflösungsschritt eine Stufe multipliziert: ×1 bis 288 Pixel kurze Seite, ×2 bis 576, ×4 bei 1080p. <fontSize>8</fontSize> ergibt also 16 Pixel auf einem 480p-Bildschirm und 32 Pixel bei 1080p. Dieselbe Regel gilt überall — Menütexte eingeschlossen.
<fontSize>0.045</fontSize> <!-- 4,5 % der kurzen Seite: passt sich überall an, vorzuziehen -->
<fontSize>8</fontSize> <!-- „240p“-Größe: 16 px bei 480p, 32 px bei 1080p --> Position, Größe, Ursprung, Rotation
Eine Komponente genau dort platzieren, wo man sie will.
pos und origin gehören immer zusammen
Das ist der Punkt, den man vor allem anderen verstanden haben muss, denn die beiden sprechen nicht über dasselbe:
pos— wo, auf dem BILDSCHIRM, die Komponente platziert wird.origin— welcher Punkt DER KOMPONENTE dort platziert wird.
Das eine ist eine Position auf dem Bildschirm, das andere ein Punkt der Komponente. Das Zusammentreffen beider entscheidet, wo die Komponente erscheint.
pos — eine Position auf dem Bildschirm
<pos>0.1 0.2</pos>
Diese zwei Zahlen messen sich von der oberen linken Ecke des Bildschirms: 0 0 ist diese Ecke, 1 1 die untere rechte.
Am einfachsten liest man sie als Prozentwerte — es ist genau dasselbe: 0.1 = 10 %, 0.2 = 20 %, 0.5 = 50 %. pos 0.1 0.2 heißt also 10 % der Breite und 20 % der Höhe.
pos sagt nichts über die Komponente selbst: Es ist ein bloßer Punkt auf dem Bildschirm. Was entscheidet, welcher Teil der Komponente dort landet, ist origin.
origin — welcher Punkt DER KOMPONENTE
origin beantwortet die andere Hälfte der Frage: Jetzt, da wir wissen wo auf dem Bildschirm — welcher Teil der Komponente legt sich dorthin?
Diese neun Werte sind die gebräuchlichsten, aber jedes Paar zwischen 0 und 1 funktioniert:
Standardmäßig ist origin 0 0 — die obere linke Ecke der Komponente. Deshalb erstreckt sich eine Komponente ohne origin von ihrer pos aus nach rechts und nach unten.
Das Beispiel, das alles erklärt
Nehmen wir ein Querformat-Bild auf einem 16:9-Bildschirm:
<pos>0.5 0.5</pos>
<origin>1 1</origin>
pos 0.5 0.5 ist die Bildschirmmitte. Man könnte glauben, das Bild werde dort zentriert. Ist es nicht: origin 1 1 bezeichnet die untere rechte Ecke des Bildes, und diese Ecke wird in die Mitte gelegt.
Das Bild landet also vollständig oben links von der Mitte.
Um wirklich zu zentrieren
<pos>0.5 0.5</pos>
<origin>0.5 0.5</origin>
Diesmal wird die Bildmitte in die Bildschirmmitte gelegt.
Die vollständige Tabelle
origin | Der auf pos gelegte Punkt der Komponente |
|---|---|
0 0 | obere linke Ecke — der Standardwert |
0.5 0 | Mitte der Oberkante |
1 0 | obere rechte Ecke |
0 0.5 | Mitte der linken Kante |
0.5 0.5 | die Mitte |
1 0.5 | Mitte der rechten Kante |
0 1 | untere linke Ecke |
0.5 1 | Mitte der Unterkante |
1 1 | untere rechte Ecke |
Wofür das wirklich gut ist
Ohne origin wird jede Komponente an ihrer oberen linken Ecke platziert: Um etwas zu zentrieren, müsste man 0.5 − Breite/2 rechnen — und jedes Mal neu rechnen, wenn sich die Breite ändert.
Mit origin rechnet man gar nichts mehr:
| Was man will | pos | origin |
|---|---|---|
| auf dem Bildschirm zentriert | 0.5 0.5 | 0.5 0.5 |
| am rechten Rand angedockt | 1 … | 1 … |
| am unteren Rand angedockt | … 1 | … 1 |
| unten zentriert | 0.5 1 | 0.5 1 |
Besonders nützlich für ein System-Logo, dessen Breite von Maschine zu Maschine wechselt: Mit origin bleibt es ausgerichtet, egal was kommt.
size — die vorgegebene Größe
<size>0.3 0.2</size>
Die Komponente hat genau diese Größe. Für ein Bild heißt das: Es wird verzerrt, um die Box zu füllen.
Nur eine angeben
Setze die andere auf 0: Die fehlende Dimension wird berechnet, um die Proportionen zu erhalten.
<size>0.3 0</size> <!-- 30 % breit, proportionale Höhe -->
⚠️ Das ist nicht dasselbe wie keepratio oder maxSize, auch wenn beide die Proportionen erhalten:
| Schreibweise | Was garantiert ist |
|---|---|
size 0.3 0 | die Breite beträgt genau 30 %; die Höhe folgt, wie auch immer — sie kann überlaufen |
maxSize 0.3 0.2 | das Bild passt in die Box: die einschränkendere Dimension gewinnt, die Breite kann also schrumpfen |
Anders gesagt: size mit einer Null setzt eine Schranke, maxSize setzt zwei.
maxSize — die Maximalgröße, ohne Verzerrung
<maxSize>0.3 0.2</maxSize>
Das Bild wird unter Erhalt seiner Proportionen vergrößert oder verkleinert, um in die Box zu passen. Es füllt sie daher selten ganz aus.
Für ein System-Logo ist fast immer
maxSizerichtig. Logos haben nicht von Maschine zu Maschine dieselbe Form: Mitsizewürden manche plattgedrückt.
size zusammen mit keepratio ergibt dasselbe wie maxSize — und ist oft die bevorzugte Schreibweise, weil sie die gewollte Größe nennt statt einer Grenze:
<size>0.3 0.2</size>
<keepratio>true</keepratio> <!-- ⚠️ alles klein geschrieben -->
maxSize existiert nur auf image und video.
rotation und rotationOrigin
<rotation>90</rotation>
<rotationOrigin>0.5 0.5</rotationOrigin>
rotation ist in Grad, im Uhrzeigersinn. rotationOrigin bezeichnet den Punkt, um den die Komponente dreht, in ihren eigenen Proportionen. Ohne rotationOrigin ist der Drehpunkt die obere linke Ecke (0 0) — schreibe 0.5 0.5, um um die Mitte zu drehen.
Die Rotation gilt für Bilder, Farbblöcke, Videos und Markdown-Blöcke. Ein gedrehter Text zeichnet sich bei kleinen Neigungen, verschwindet aber bei 90°; ein gedrehter Lauftext zeichnet nur seinen Hintergrund, nie seinen Text. Für einen vertikalen Titel nimm ein Bild oder einen Markdown-Block.
Die Tiefe (
zIndex) und das Abschalten (disabled) haben ihre eigene Seite: Tiefe und Sichtbarkeit.
Tiefe und Sichtbarkeit
Was vor was liegt, und wie man eine Komponente abschaltet.
Zwei Eigenschaften, die jede Komponente akzeptiert und die weder von Position noch von Größe sprechen: welche vor der anderen liegt, und welche gar nicht erscheint.
zIndex — die Ebenen
<zIndex>40</zIndex>
Es ist ein Ebenensystem, genau wie in einem Zeichenprogramm: Jede Komponente ist ein Blatt, und zIndex sagt, in welcher Reihenfolge sie gestapelt sind. Je größer die Zahl, desto näher bei dir liegt die Komponente.
Reservierte Komponenten haben ihre Standardwerte, freie Komponenten erhalten 10, wenn du nichts sagst. Lass Platz zwischen deinen — 10, 20, 30 — um später eine dazwischenschieben zu können.
disabled — eine Komponente abschalten
<disabled>true</disabled>
Die Komponente wird gelesen, aber nicht angezeigt. Praktisch, um eine reservierte Komponente auszublenden, die man nicht will, ohne sie ganz neu definieren zu müssen.
⚠️ Weder carousel noch textlist akzeptieren es.
Farben und Verläufe
RRGGBB, Transparenz, und die acht Ecken.
Die Schreibweise
Eine Farbe wird hexadezimal geschrieben, ohne Raute:
<color>2E447C</color> <!-- deckend -->
<color>2E447C80</color> <!-- halbtransparent -->
- 6 Zeichen:
RRGGBB, deckend; - 8 Zeichen:
RRGGBBAA, die letzten beiden geben die Deckkraft —00unsichtbar,80halb,FFdeckend.
Die nützlichsten Transparenzwerte
Den Code einer Farbe kennt man. Die zwei Zeichen der Deckkraft viel weniger:
| Deckkraft | Schreiben | Deckkraft | Schreiben |
|---|---|---|---|
| 0 % — unsichtbar | 00 | 60 % | 99 |
| 10 % | 1A | 70 % | B3 |
| 20 % | 33 | 75 % | BF |
| 25 % | 40 | 80 % | CC |
| 30 % | 4D | 90 % | E6 |
| 40 % | 66 | 95 % | F2 |
| 50 % | 80 | 100 % — deckend | FF |
Die Rechnung, falls dein Wert fehlt: der Prozentsatz × 255, hexadezimal geschrieben.
Die Verläufe
box und image akzeptieren eine Farbe pro Kante oder pro Ecke. Zwei genügen für einen Verlauf.
| Eigenschaft | Wirkung |
|---|---|
colorTop + colorBottom | vertikaler Verlauf |
colorLeft + colorRight | horizontaler Verlauf |
colorTopLeft, colorTopRight, colorBottomLeft, colorBottomRight | Verlauf über die vier Ecken |
<box name="ombre-du-haut">
<pos>0 0</pos>
<size>1 0.3</size>
<colorTop>00000080</colorTop>
<colorBottom>00000000</colorBottom>
</box>
Ein Verlauf von halbtransparentem Schwarz zu voller Transparenz: der klassische Schleier, unter dem ein Titel lesbar bleibt, egal welches Bild dahinterliegt.
Ein Bild einfärben
Auf einer image-Komponente füllt color nicht: Es multipliziert das Bild. Ein weißes Bild nimmt also genau die angegebene Farbe an — so färbt man ein Icon um, ohne die Datei neu zu machen.
<image name="etoile">
<path>${root}/data/arts/etoile-blanche.svg</path>
<color>FFC24B</color> <!-- der Stern wird golden -->
</image> Bedingen, übersetzen, regionalisieren
Ein anderer Wert je nach Bildschirm, Maschine oder Sprache.
Eine Bedingung auf einer Eigenschaft
<text name="titre">
<fontSize if="crt">0.09</fontSize>
<fontSize if="!crt">0.05</fontSize>
<color>FFFFFF</color>
</text>
Eine Komponente, zwei Größen je nach Bildschirm. Die Liste der Bedingungen steht auf der Seite Bedingt anzeigen.
Eine Bedingung auf der ganzen Komponente
<image name="filtre" if="crt">
<path>${root}/data/arts/scanlines.png</path>
</image>
Die Komponente existiert nur, wenn die Bedingung wahr ist. Die vollständige Liste steht in Bedingt anzeigen, und Mehrere Bedingungen kombinieren erklärt und, oder und die Klammern.
ifexists und ifnotexists
Diese beiden prüfen nicht die Maschine, sondern das Vorhandensein einer Datei:
<image name="jaquette">
<path ifexists="${game.media.imagepath}">${game.media.imagepath}</path>
<path ifnotexists="${game.media.imagepath}">${root}/images/pas-dimage.png</path>
</image>
Das ist der saubere Weg für Spiele ohne Cover — sonst bleibt das Feld leer.
Einen Text übersetzen
Ein Sprachsuffix auf der Eigenschaft genügt:
<text name="bienvenue">
<text>Bienvenue</text>
<text.en>Welcome</text.en>
<text.es>Bienvenido</text.es>
</text>
Recalbox nimmt die passende Variante — und die suffixlose Version, wenn keine passt.
Was ein Suffix akzeptiert
| Suffix | Was es anspricht |
|---|---|
.fr .es .de | die Sprache der Maschine, in Kleinbuchstaben |
.fr_FR | die Sprache und das Land |
.US .EU .JP | die Region, in GROSSBUCHSTABEN |
Die Region
Damit schreibt man „Genesis“ in den USA und „Mega Drive“ in Europa. Derselbe Mechanismus wie bei der Sprache, mit einem Suffix in Großbuchstaben — genau das nutzt das offizielle Recalbox-Theme:
<box name="fond"
color.US="2E447C"
color.EU="7C2E44"
color.JP="447C2E" />
Die drei Werte sind US, EU und JP. Eine Eigenschaft ohne Suffix gilt für alle Regionen.
⚠️ Es gibt immer eine aktive Region. Auf einer frischen Maschine ist es US: Was du ohne Suffix schreibst, sehen die meisten Leute — .EU oder .JP dienen nur dazu, davon abzuweichen.
Die Region wird auf der Maschine geändert, in den Oberflächeneinstellungen — und Recalbox liest dann das ganze Theme neu, wie bei einer Option. Das Studio bietet dieselbe Wahl, um zu sehen, was jedes Publikum sieht.
Worauf das funktioniert: auf ALLEM
Es gibt keine Liste übersetzbarer Eigenschaften. Das Suffix wird auf jeder Eigenschaft geprüft, bevor die Engine überhaupt weiß, um welche es sich handelt — also akzeptieren es alle:
<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" /> <!-- Deutsch läuft länger -->
<box name="bandeau" color.JP="D62828" />
Beide Schreibweisen unterstützen es — als Attribut (size.fr="…") wie als Unterknoten (<size.fr>…</size.fr>) — und sogar der Tag-Name (<text.fr name="…">). Es gilt auch für title und help einer Option.
⚠️ Die lokalisierte Version gewinnt endgültig. Sobald eine Variante angewendet wurde, wird die suffixlose Version derselben Eigenschaft abgelehnt, auch in einer später gelesenen Datei. Ein Overlay kann eine bereits lokalisierte Eigenschaft also nicht „zurückerobern“: Es muss seine eigene lokalisierte Variante liefern.
Einen Text aus einer Datei laden
text, scrolltext und markdown akzeptieren path anstelle von text: Der Inhalt wird dann aus der Datei gelesen. Praktisch für einen langen Vorstellungstext.
<markdown name="apropos">
<path>${root}/data/textes/apropos.md</path>
</markdown> Benennen und wiederverwenden
Der Name einer Komponente, und wie man mehrere auf einmal anlegt.
Zwei Dinge, die man nicht verwechseln darf
<image name="mon-fond">
imageist der Komponententyp: Er entscheidet, was die Komponente kann und welche Eigenschaften sie akzeptiert. Er muss aus der Liste der existierenden Typen gewählt werden — siehe Die Komponenten. Ein erfundener Typ wird ignoriert;nameist dein Etikett. Es ist frei… außer du greifst einen der von Recalbox reservierten Namen auf — dann ist die Komponente mit der Engine verdrahtet — siehe Die Ansichten und ihre Komponenten.
Zwei Komponenten mit demselben name in derselben Ansicht sind eine: Die zweite ergänzt oder ersetzt die erste. Das ist gewollt — es ist der Overlay-Mechanismus — aber eine Fehlerquelle, wenn man zwei Hintergründe gedankenlos „fond“ nennt.
Mehrere Komponenten auf einmal anlegen
<box name="bande1, bande2, bande3, bande4">
<size>0.01 1</size>
<color>FFFFFF20</color>
</box>
Vier Komponenten, dieselben Eigenschaften. Bleibt, jeder weiter unten ihre eigene Position zu geben:
<box name="bande1"><pos>0.90 0</pos></box>
<box name="bande2"><pos>0.92 0</pos></box>
Ändern, ohne alles neu zu schreiben
Da der letzte gewinnt, genügt es, die eine Eigenschaft neu zu deklarieren, die sich ändert:
<include>${root}/views/base.xml</include>
<view name="system">
<box name="fond"><color>7C2E44</color></box> <!-- der Rest bleibt erhalten -->
</view> Alle Komponenten
Was es gibt, und was jede einzelne kann.
Eine Komponente wird über ihren Typ deklariert — den Namen des Tags. Ein Typ, der nicht in dieser Liste steht, wird von Recalbox ignoriert und im Protokoll vermerkt.
Etwas anzeigen
| Typ | Was er macht |
|---|---|
text | ein Text, ein- oder mehrzeilig |
scrolltext | ein Text, der durchläuft, wenn er zu lang ist |
markdown | ein formatierter Text (fett, Überschriften, Listen) |
image | ein Bild |
video | ein Video |
box | ein Farbblock oder ein Verlauf |
datetime | ein Datum |
rating | eine Bewertung, in Sternen |
sound | ein Klang (zeigt nichts an) |
Listen und Navigation
| Typ | Was er macht |
|---|---|
textlist | die Spieleliste |
carousel | das System-Karussell |
helpsystem | die Hilfeleiste unten am Bildschirm |
Gestaltung der Menüs
Diese neun werden nicht platziert: sie passen das Aussehen der Menüs an, die Recalbox selbst aufbaut.
menuBackground · menuIcons · menuText · menuTextSmall · menuSection · menuSwitch · menuSlider · menuButton · menuSize
Gestaltung der virtuellen Tastatur
keyboard wird ebenfalls nicht platziert: es gibt der Tastatur, die Recalbox für eine Suche oder eine Eingabe öffnet, ihre Farben und ihre Schrift. Eingerichtet wird es unter Globale Komponenten.
Achtung Groß- und Kleinschreibung
Die Namen unterscheiden Groß- und Kleinschreibung. menuswitch funktioniert nicht, es muss menuSwitch heißen. Eine einzige Eigenschaft ist die Ausnahme und wird komplett klein geschrieben: keepratio.
text, scrolltext, markdown
Die drei Arten, Text anzuzeigen.
text — der normale Text (18 Eigenschaften)
<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>
| Eigenschaft | Typ | Aufgabe |
|---|---|---|
pos size origin rotation rotationOrigin | Paar | Platzierung — siehe Position, Größe, Ursprung |
text | Text | der Inhalt, Variablen eingeschlossen |
path | Pfad | liest den Inhalt aus einer Datei, statt text |
fontPath | Pfad | die Schrift |
fontSize | Zahl | < 1 = Anteil der Bildschirmhöhe, >= 1 = Pixel |
fontStyle | Text | normal, bold, italic, bolditalic |
color | Farbe | die Textfarbe |
backgroundColor | Farbe | ein Hintergrund hinter dem Text |
alignment | Text | siehe unten |
forceUppercase | ja/nein | alles in Großbuchstaben |
lineSpacing | Zahl | der Zeilenabstand, standardmäßig 1.2 |
multiline | ja/nein | Zeilenumbrüche erlauben |
zIndex | Zahl | die Tiefe |
disabled | ja/nein | die Komponente abschalten |
alignment wirkt auf zwei Achsen
Der Wert kombiniert die Horizontale und die Vertikale.
Neun Positionen, dreizehn Schreibweisen — vier Werte sind Synonyme:
| Position | Schreibweise | Synonym |
|---|---|---|
| oben-links | topleft | |
| oben-mittig | topcenter | top |
| oben-rechts | topright | |
| mittig-links | centerleft | left |
| mittig | center | |
| mittig-rechts | centerright | right |
| unten-links | bottomleft | |
| unten-mittig | bottomcenter | bottom |
| unten-rechts | bottomright |
⚠️ Ein unbekannter Wert behält nicht die vorherige Ausrichtung: er fällt auf mittig-links zurück, den Standardwert.
Der Text wird innerhalb seiner size-Box ausgerichtet: ohne size hat die Ausrichtung nichts, worauf sie wirken kann.
scrolltext — der durchlaufende Text (16 Eigenschaften)
Dieselben Eigenschaften wie text, ohne multiline und lineSpacing. Der Text läuft horizontal durch, wenn er über seine Box hinausragt.
<scrolltext name="titre-long">
<size>0.4 0.06</size>
<text>${game.name}</text>
<fontSize>0.04</fontSize>
</scrolltext>
Vorbehalten für Werte, deren Länge du nicht kontrollierst — ein Spielname, ein Entwickler.
markdown — der formatierte Text (15 Eigenschaften)
<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>
Dieselben Eigenschaften wie text, ohne fontStyle, backgroundColor und multiline.
Er versteht einfache Formatierung im Text: **fett**, *kursiv*, # Überschrift, Listen mit Bindestrichen. Nützlich für eine Beschreibung oder eine „Über“-Seite.
⚠️
markdownläuft nicht durch: ein Text, der länger ist als seine Box, wird abgeschnitten.
image und video
Ein Bild oder ein Video anzeigen, und sie einfärben.
image (21 Eigenschaften)
<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>
| Eigenschaft | Typ | Aufgabe |
|---|---|---|
pos size origin rotation rotationOrigin | Paar | Platzierung |
maxSize | Paar | Maximalgröße ohne Verzerrung — siehe Position, Größe |
keepratio | ja/nein | die Proportionen bewahren (⚠️ komplett klein geschrieben) |
path | Pfad | die Bilddatei |
tile | ja/nein | das Bild als Kacheln wiederholen, statt es zu strecken |
color | Farbe | färbt das Bild ein (Multiplikation) |
colorTop colorBottom colorLeft colorRight | Farbe | Einfärbung als Verlauf |
colorTopLeft colorTopRight colorBottomLeft colorBottomRight | Farbe | Einfärbung über die vier Ecken |
reflection | Paar | eine Spiegelung unter dem Bild: Anfangs- und Enddeckkraft |
zIndex disabled | Tiefe, Abschalten |
Formate
PNG, JPG und SVG. SVG empfohlen für Logos: es bleibt in jeder Größe scharf.
tile — die wiederholte Textur
<image name="grille">
<size>1 1</size>
<path>${root}/data/arts/motif.png</path>
<tile>true</tile>
</image>
Das Bild behält seine Originalgröße und wiederholt sich, bis es die Box füllt.
reflection
<reflection>0.5 0.0</reflection>
Fügt eine umgedrehte Spiegelung unter dem Bild hinzu, von 50 % Deckkraft oben bis 0 % unten.
video (15 Eigenschaften)
<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>
Dieselben Platzierungseigenschaften wie image, plus:
| Eigenschaft | Typ | Aufgabe |
|---|---|---|
delay | Zahl | Sekunden, bevor das Video startet |
loops | Zahl | Anzahl der Wiedergaben; 0 = Endlosschleife |
animations | Text | der Erscheinungseffekt |
link | Text | die Wiedergabe an eine andere Komponente koppeln |
reflection | Paar | die Spiegelung, wie bei image |
video akzeptiert weder tile noch die Einfärbefarben.
Ein
delayvon ein bis zwei Sekunden verhindert, dass das Video bei jedem Spiel losgeht, an dem man beim Durchblättern der Liste nur vorbeikommt.
box — der Farbblock
Hintergründe, Schleier, Bänder und Verläufe.
Die einfachste Komponente, und eine der nützlichsten: Hintergründe, Schleier, Bänder, Trennlinien. 16 Eigenschaften.
<box name="fond" extra="true">
<pos>0 0</pos>
<size>1 1</size>
<color>101820</color>
<zIndex>1</zIndex>
</box>
| Eigenschaft | Aufgabe |
|---|---|
pos size origin rotation rotationOrigin | Platzierung |
color | einheitliche Farbe |
colorTop colorBottom | vertikaler Verlauf |
colorLeft colorRight | horizontaler Verlauf |
colorTopLeft colorTopRight colorBottomLeft colorBottomRight | Verlauf über die vier Ecken |
zIndex disabled | Tiefe, Abschalten |
Kein path: eine box zeigt kein Bild an. Für einen Bildhintergrund nimm image.
Der Lesbarkeitsschleier
Der häufigste Anwendungsfall: einen Text über jedem beliebigen Bild lesbar machen.
<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>
Von transparent zu 75 % Schwarz: der untere Bildschirmrand verdunkelt sich allmählich, und der Text darüber bleibt lesbar, egal welches Artwork dahinterliegt.
Ein dünnes Band
<box name="filet" extra="true">
<pos>0.06 0.18</pos>
<size>0.3 2p</size>
<color>FFFFFF40</color>
</box>
2p = zwei Pixel hoch, egal bei welcher Auflösung: einer der seltenen Fälle, in denen die Pixeleinheit die richtige Wahl ist.
textlist — die Spieleliste
Die Liste, in der du dein Spiel auswählst: ihre Farben, ihre Markierung, ihre Schrift.
⚠️ Diese Liste wird nicht beliebig platziert.
textlistexistiert nur in der Spiele-Ansicht, unter dem reservierten Namengamelist. Anderswo — oder unter einem anderen Namen — wird sie nicht aufgebaut, und eine zweite lässt sich nicht hinzufügen.
Die Eigenschaften (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>
| Eigenschaft | Aufgabe |
|---|---|
pos size origin | Platzierung |
primaryColor | die Farbe der Spiele |
secondaryColor | die Farbe der Ordner |
selectedColor | die Textfarbe der gewählten Zeile |
selectorColor | die Farbe der Markierung |
selectorImagePath | ein Markierungs-Bild, statt der Farbe |
selectorImageTile | dieses Bild als Kacheln wiederholen |
selectorHeight | die Höhe der Markierung |
selectorOffsetY | ihr vertikaler Versatz |
fontPath fontSize | die Schrift |
alignment | die Ausrichtung der Zeilen |
horizontalMargin | der linke und rechte Rand |
forceUppercase | alles in Großbuchstaben |
lineSpacing | der Zeilenabstand — er ist es, der die Zeilen auseinanderrückt |
scrollSound | der Klang beim Durchblättern |
zIndex | die Tiefe |
⚠️ Kein disabled: eine Spieleliste schaltet sich nicht ab.
primaryColor und secondaryColor sind die häufigste Quelle der Verwirrung: die zweite ist nicht die Wechselfarbe jeder zweiten Zeile, sie ist die Farbe der Ordner.
carousel — das System-Karussell
Das Band, das die Systeme vorbeiziehen lässt: Richtung, Logogröße, und der Textmodus.
Das Karussell zeigt die Systeme, Logo für Logo. Es existiert nur in der Systeme-Ansicht, unter dem reservierten Namen systemcarousel, und die Engine baut nur eines. Anderswo, oder unter einem anderen Namen, wird es gar nicht aufgebaut.
Die Laufrichtung
type akzeptiert horizontal (Standard), vertical und vertical_wheel — das Rad. Ein horizontales Rad gibt es nicht: jeder andere Wert fällt stillschweigend auf horizontal zurück.
Die Eigenschaften (28)
<carousel name="systemcarousel">
<type>vertical</type>
<pos>0 0</pos>
<size>0.25 1</size>
<color>00000020</color>
<logoSize>0.12 0.075</logoSize>
<logoScale>1.5</logoScale>
<maxLogoCount>7</maxLogoCount>
<logoAlignment>center</logoAlignment>
<defaultTransition>instant</defaultTransition>
</carousel>
| Eigenschaft | Aufgabe |
|---|---|
type | horizontal, vertical, vertical_wheel |
pos size origin | Platzierung |
color | der Hintergrund des Karussells |
logoSize | die Größe eines Logos (Paar) |
logoScale | die Vergrößerung des gewählten Logos |
logoRotation logoRotationOrigin | die Rotation der Logos (Räder) |
logoAlignment | die Ausrichtung der Logos in ihrem Feld |
maxLogoCount | wie viele Logos gleichzeitig sichtbar sind |
defaultTransition | fade oder instant; jeder andere Wert ergibt slide |
fontPath fontSize fontColor | die Schrift des Textmodus |
forceUppercase | die Systemnamen in Großbuchstaben |
textOnly | die Namen schreiben statt der Logos |
primaryColor secondaryColor | die Farbe der Namen |
selectedColor | die Farbe des gewählten Namens |
selectorColor selectorHeight | die Markierung des Textmodus |
selectorOffsetX selectorOffsetY | ihr Versatz |
textOffsetX | der Versatz des Textes |
lineSpacing horizontalMargin | Zeilenabstand und Ränder des Textmodus |
zIndex | die Tiefe |
⚠️ Kein disabled: das Karussell schaltet sich nicht ab.
⚠️ maxLogoCount ist keine Anzahl angezeigter Logos. Es steuert nur Abstand und Zentrierung: die Engine zeichnet mehr davon, die überstehen und abgeschnitten werden. Und der Wert wird auf die ganze Zahl gerundet — eine Dezimalzahl (2.5) ist daher nutzlos.
Logos, oder Namen
Im Abschnitt Systemliste wählt „WAS VORBEIZIEHT“ zwischen:
- Logos — das übliche Verhalten;
- Namen — das Karussell schreibt den Namen jedes Systems.
⚠️ Es ist keine vierte Laufrichtung: der Textmodus kombiniert sich mit horizontal, vertikal und Rad. Ein Karussell aus Namen kann also in jede Richtung laufen.
Die Eigenschaften, die dazugehören
Im Textmodus werden die Schrift, ihre Größe und ihre Farbe im Abschnitt Text eingestellt. Zwei karusselleigene Versätze kommen hinzu:
- Horizontaler Versatz der Markierung (
selectorOffsetX); - Horizontaler Versatz des Textes (
textOffsetX).
Was es schreibt
<carousel name="systemcarousel" type="vertical">
<textOnly>true</textOnly>
<textOffsetX>0.02</textOffsetX>
</carousel>
Verfügbar ab Recalbox 10.1. Auf einer älteren Maschine wird
textOnlyignoriert und das Karussell zeigt die Logos.
rating — die Sternebewertung
Die Note des Spiels, gezeichnet in Sternen: die zwei Bilder, aus denen sie besteht.
Die Eigenschaften (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>
| Eigenschaft | Aufgabe |
|---|---|
pos size origin rotation rotationOrigin | Platzierung |
filledPath | das Bild des vollen Sterns |
unfilledPath | das Bild des leeren Sterns |
zIndex disabled | Tiefe, Abschalten |
size bezeichnet die Gesamtheit der fünf Sterne. Eine Breite von fünfmal der Höhe ergibt quadratische Sterne.
Keine Farbe: um den Farbton zu ändern, ändere die Bilder — oder liefere weiße Bilder und färbe sie ein… was rating nicht erlaubt. Es braucht also zwei Dateien.
datetime — ein Datum
Das Erscheinungsdatum eines Spiels, oder das der letzten Partie.
Ein datetime zeigt ein Datum, das Recalbox kennt, nie freien Text. Sein Name sagt, welches Datum — md_releasedate das Erscheinen des Spiels, md_lastplayed die letzte Partie — und seine Eigenschaft display sagt, in welcher Form es geschrieben wird.
<datetime name="md_releasedate">
<pos>0.06 0.66</pos>
<fontSize>0.028</fontSize>
<color>C6CBD8</color>
<display>date</display>
</datetime>
display — die Form des Datums
| Schreibweise | Was angezeigt wird |
|---|---|
date | 1991/06/23 |
dateTime | 1991/06/23 14:05:30 |
year | 1991 |
time | 14:05:30 |
realTime | die aktuelle Uhrzeit — kein Spieldatum |
RelativeToNow | „vor 3 Tagen“ |
⚠️ Die Schreibweise zählt. datetime funktioniert nicht, es muss dateTime heißen; relativeToNow auch nicht, es muss RelativeToNow heißen. Ein unbekannter Wert behält die vorherige Form und landet in themes.log.
Die Eigenschaften (12)
| Eigenschaft | Aufgabe |
|---|---|
pos size origin | Platzierung |
display | die Form des Datums |
color backgroundColor | die Farben |
fontPath fontSize | die Schrift |
alignment forceUppercase | die Formatierung |
zIndex disabled | Tiefe, Abschalten |
⚠️ Ein datetime ohne color ist unsichtbar: es ist die einzige Komponente, die die Farbe auf null setzt, statt die vorherige zu behalten. Und von alignment wird nur der horizontale Teil behalten — vertikal bleibt es immer zentriert.
Ein Spiel ohne Datum zeigt eine leere Zeile: es sind die Daten, die fehlen, nicht das Theme.
sound — die Musik des Themes
Ein Titel, oder ein Ordner voller Titel, abgespielt beim Stöbern.
Ein sound wird nicht gezeichnet: er hat weder Position, noch Größe, noch Tiefe. Er trägt einen Pfad, und sonst nichts.
Zwei Namen, zwei Verhalten:
| Name | Was die Maschine macht |
|---|---|
bgsound | spielt diesen Titel |
directory | wählt zufällig aus diesem Ordner |
⚠️ Die Maschine liest ihn nur in der Systeme-Ansicht. Anderswo platziert, spielt er nie. Dafür kann eine Systemdatei ihn neu definieren: so gibt man eine Musik pro System.
Die eigene Musik des Benutzers, falls vorhanden, hat Vorrang vor der des Themes.
helpsystem — die Hilfeleiste
Die 32 Button-Symbole, eines nach dem anderen.
Die Leiste unten am Bildschirm, die daran erinnert, wozu die Buttons dienen. 38 Eigenschaften: sechs für die Formatierung, und 32 Symbole.
Recalbox entscheidet selbst, was die Leiste ankündigt, Bildschirm für Bildschirm, und übersetzt jede Beschriftung. Dein Theme legt nur die Form fest: die Platzierung, die Schrift, die Farben, und das Bild jedes Piktogramms.
<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>
Die Formatierung
| Eigenschaft | Aufgabe |
|---|---|
pos size | Platzierung |
textColor | die Farbe der Beschriftungen |
iconColor | die Einfärbung der Symbole — liefere sie in Weiß |
fontPath fontSize | die Schrift |
Die 32 Symbole
Ein Symbol zu ersetzen ist optional: Recalbox bringt seine eigenen mit. Du definierst nur die neu, die du willst.
Richtungen — iconUpDown, iconLeftRight, iconUpDownLeftRight
Buttons — iconA, iconB, iconX, iconY
Schultertasten — iconL, iconR, iconL2, iconR2, iconL3, iconR3, iconLR, iconL2R2, iconL3R3
System — iconStart, iconSelect, iconHotkey
Hotkey-Kombinationen — iconHkA, iconHkB, iconHkX, iconHkY, iconHkL, iconHkR, iconHkLeftRight
Joysticks — iconJ1UpDown, iconJ1LeftRight, iconJ1UpDownLeftRight, iconJ2UpDown, iconJ2LeftRight, iconJ2UpDownLeftRight
Eine Kombination sind zwei Piktogramme
Wenn die Hilfe eine Kombination betrifft, zeichnet Recalbox iconHotkey, dann die Taste.
[HK] [A] Spiel starten
iconHkA ist also das Bild des A-Buttons in einer Kombination — keine Zeichnung von „HK + A“. Setze dort einen schlichten Button hin: das Kürzel steht schon davor.
iconHotkeyzählt doppelt: es erscheint vor jeder Kombination der Leiste. Es ist das Symbol, das du zuerst polieren solltest, wenn dein Theme welche zeigt.
Der Name ist vorgegeben: help
Recalbox sucht das Bauteil namens help, und nur dieses. <helpsystem name="barre"> wird in deiner Datei existieren, ohne je etwas zu steuern.
Die Leiste wird Ansicht für Ansicht eingerichtet
Recalbox liest das <helpsystem> der Ansicht bei jedem Bildschirmwechsel neu, und beginnt wieder bei seinen eigenen Symbolen. Was eine Ansicht nicht deklariert, fällt daher auf Recalbox' Standard zurück — nie auf das, was eine andere Ansicht eingestellt hatte.
Zwei Wege:
- überall derselbe Satz — die Leiste in einer Ansicht deklarieren, die alles abdeckt:
<view name="system, detailed, menu">; - eine andere Leiste pro Bildschirm — ein
<helpsystem>in jeder Ansicht, mit eigenen Bildern.
Liefere weiße Symbole und nutze
iconColor: ein einziger Dateisatz genügt dann für alle Farbvarianten deines Themes.
Mehrere Symbolsätze anbieten
Das machen die großen Themes: ein SNES-Satz, ein Xbox-Satz, ein PlayStation-Satz… und der Benutzer wählt in Menü → Oberfläche → Theme.
Jeder Satz ist eine Datei, die nur das <helpsystem> und seine Bilder enthält:
<!-- ./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>
Und angeboten werden sie so, ohne irgendetwas anderes zu deklarieren:
<include subset="iconset" name="1 - SNES">${root}/options/icones-snes.xml</include>
<include subset="iconset" name="2 - Xbox">${root}/options/icones-xbox.xml</include>
iconsetist ein reservierter Name: Recalbox zeigt „SELECT THEME'S ICONSET“, übersetzt in die Sprache der Maschine. Kein<subset>-Tag ist nötig, um ihn zu benennen.
Die Gestaltung der Menüs
Die neun Komponenten, die Recalbox' Menüs anpassen.
Recalbox baut seine Menüs selbst: ihr Inhalt gehört nicht dir. Das Theme passt nur ihr Aussehen an, über neun Komponenten, die nicht platziert werden.
menuBackground (3)
<menuBackground>
<color>101820F0</color>
<path>${root}/data/arts/cadre-menu.png</path>
<fadePath>${root}/data/arts/voile.png</fadePath>
</menuBackground>
| Eigenschaft | Aufgabe |
|---|---|
color | die Farbe des Rahmens — sie färbt das Bild ein, wenn path angegeben ist |
path | das Bild des Rahmens |
fadePath | das Schleierbild, das die Ansicht dahinter verdunkelt |
menuText (6) — die Menüzeilen
| Eigenschaft | Aufgabe |
|---|---|
fontPath fontSize | die Schrift |
color | der Text der Zeilen |
selectedColor | der Text der gewählten Zeile |
selectorColor | die Markierung |
separatorColor | die Linien zwischen den Zeilen |
menuTextSmall (5) — der kleine Text
fontPath, fontSize, color, selectedColor, selectorColor. Verwendet für Einstellwerte und Auswahllisten.
menuSection (5) — die Abschnittstitel
fontPath, fontSize, color, selectedColor, alignment.
menuSize (1)
<menuSize><height>0.85</height></menuSize>
Die maximale Höhe des Menürahmens, als Anteil des Bildschirms. Es ist das einzige Maß, das ein Theme den Menüs vorschreibt.
menuSwitch (2) — die Schalter
pathOn, pathOff — die zwei Bilder einer Ja/Nein-Einstellung.
menuSlider (1) — die Schieberegler
path — das Bild des Reglers.
menuButton (2) — die Buttons
path, filledPath — der normale und der gedrückte Zustand.
menuIcons (23) — die Abschnittssymbole
Ein Symbol pro Menüabschnitt:
iconSystem · iconUpdates · iconThemes · iconGames · iconUI · iconTate · iconControllers · iconSound · iconNetwork · iconScraper · iconBios · iconDownload · iconLicense · iconAdvanced · iconArcade · iconKodi · iconCardReader · iconRecalboxRGBDual · iconQuit · iconRestart · iconShutdown · iconFastShutdown · iconList
⚠️
iconListsteht im Singular, anders als der Abschnitt „Listen“, den es vertritt. Im Plural wird es ignoriert.
Die Gestaltung der virtuellen Tastatur
Die Farben und die Schrift der Tastatur, die sich öffnet, um ein Spiel zu suchen oder einen Text einzugeben.
Wie die Menüs wird sie nicht platziert: die Tastatur bestimmt ihre eigene Geometrie, das Theme wählt nur ihre Farben und ihre Schrift.
Sie öffnet sich über dem Bildschirm, auf dem du bist, egal welcher — eingerichtet wird sie also ein einziges Mal, unter Globale Komponenten.
| Eigenschaft | Was sie färbt |
|---|---|
keyColor | der Hintergrund jeder Taste in Ruhe |
keySelectedColor | die Taste, auf der du stehst |
keyTextColor | der Buchstabe auf der Taste |
keyDisabledColor | der Buchstabe eines Zeichens, das die Eingabe ablehnt |
keyModifierColor | Umschalt / Strg / Alt gedrückt für eine einzelne Taste |
keyModifierLockedColor | Umschalt / Strg / Alt festgestellt |
keyTitleColor | der Titel über der Tastatur |
keyEditTextColor | der Text, der gerade getippt wird |
fontPath | die Schrift — die Größe bestimmt weiterhin die Tastatur |
Drei Tastaturen, und der Benutzer wählt
Das Theme entscheidet nicht, welche erscheint: das ist eine Einstellung der Konsole. Und nicht alle lesen deine Farben.
- Arcade-Rad — nur vier: angepeilte Taste, Buchstabe, Titel, getippter Text. Weder Tastenhintergrund noch Schrift.
- Klassische Tastatur — alle acht, plus die Schrift.
- Vereinfachte Tastatur — alle acht außer den zwei Farben Umschalt / Strg / Alt (gedrückt und festgestellt): diese Tastatur hat solche Tasten nicht.
Stelle also zuerst die vier ein, die alle lesen: deine Gestaltung hält dann, egal welche Tastatur.
Was es schreibt
<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>
Der name muss exakt keyboard sein, und es akzeptiert weder pos noch size.
Verfügbar ab Recalbox 10.1. Auf einer älteren Maschine wird der Block ignoriert und die Tastatur behält ihre Werksfarben.
⚠️ Die wichtigste Regel
Warum deine Komponente nicht erscheint.
Das ist die Ursache von „ich habe mein Bild platziert und sehe es nicht“.
Eine Ansicht zeichnet nur zwei Dinge:
- ihre reservierten Komponenten, die sie selbst aufbaut;
- die mit
extra="true"markierten Komponenten.Eine freie Komponente ohne
extraerscheint nie.
<view name="system">
<image name="mon-decor" extra="true"> <!-- ✅ wird angezeigt -->
<path>${root}/data/arts/decor.png</path>
</image>
<image name="autre-decor"> <!-- ❌ unsichtbar -->
<path>${root}/data/arts/decor.png</path>
</image>
</view>
Die andere, gleichwertige Schreibweise ist, sie zu gruppieren:
<view name="system">
<extras>
<image name="mon-decor"> … </image>
<text name="ma-legende"> … </text>
</extras>
</view>
Nur SECHS Typen können frei platziert werden
Die Fabrik der freien Komponenten kann nur diese bauen:
✅ Als extra verwendbar | ❌ Abgelehnt |
|---|---|
image · box · video · text · scrolltext · markdown | textlist · carousel · datetime · rating · sound · helpsystem · keyboard · container · ninepatch · alle menu* |
Ein abgelehnter Typ schreibt Extra type unknown: Rating ins Protokoll und nichts wird gezeichnet.
➡️ rating, datetime, textlist und carousel werden nur unter ihrem reservierten Namen verwendet, in einer Ansicht, die sie vorsieht. Du kannst keine zweite Bewertung oder eine zweite Liste hinzufügen.
➡️ ninepatch hat nirgends einen reservierten Namen: als freie Komponente abgelehnt, und keine Ansicht baut ihn. Er ist daher in der Praxis unbrauchbar, obwohl er in der Engine vorhanden ist.
➡️ container wird ebenfalls nicht platziert, aber er ist nicht nutzlos: die Engine wendet ihn über einer anderen Komponente an. In der Spieleliste stellt md_description, als <text> deklariert, zugleich den scrollenden Rahmen darum ein — pos, size und zIndex gehen an den Rahmen, der Rest an den Text. Du schreibst ihn nie selbst.
Drei weitere Grenzen
- nur ein
<video>pro Ansicht — das zweite wird ohne Meldung ignoriert; - ein Extra bekommt standardmäßig einen
zIndexvon 10; - die Extras werden vor der Anzeige nach
zIndexsortiert.
Die Ausnahme
helpsystem funktioniert weiter, selbst in einem <extras>-Block geschrieben: die Ansicht findet es über seinen Namen wieder. Die Warnung im Protokoll ist folgenlos.
Die system-Ansicht — die Maschinen
Das Karussell, das Logo, die Informationszeile.
Der erste Bildschirm: die Liste deiner Maschinen.
Die reservierten Komponenten
| Name | Typ | Aufgabe |
|---|---|---|
systemcarousel | carousel | das Karussell — nur eines, nicht umbenennbar |
logo | image | das Logo des Systems im Karussell |
systemInfo | text | die Zeile „510 Spiele verfügbar, 13 Favoriten“ |
bgsound | sound | die Hintergrundmusik des Themes |
directory | sound | der Musikordner des Themes |
bgsoundunddirectorysind die einzigen Namen, die<sound>akzeptiert, und nur in dieser Ansicht.
Zwei Fallen
systemInfo hat standardmäßig einen grauen Hintergrund. Um ihn zu entfernen:
<text name="systemInfo">
<backgroundColor>00000000</backgroundColor>
</text>
Das Karussell ist einmalig. Ein zweites <carousel> wird ohne Fehler gelesen, aber nie gezeichnet.
Das Standard-Logo
Wenn das Theme kein Logo liefert, sucht Recalbox sein eigenes in dieser Reihenfolge:
<system>-<sprache_LAND>.svg → <system>-<sprache>.svg
→ <system>-<region>.svg → <system>.svg
Das ermöglicht ein „Genesis“-Logo in den USA und „Mega Drive“ in Europa, ohne irgendetwas zu schreiben.
Das Karussell im Detail
| Eigenschaft | Standard | Genauer |
|---|---|---|
type | horizontal | horizontal, vertical, vertical_wheel; jeder andere Wert fällt auf horizontal zurück |
logoSize | berechnet | Verhältnis zum Bildschirm, nicht zum Karussell |
logoScale | 1.2 | Vergrößerung des gewählten Logos |
maxLogoCount | 3 | auf die ganze Zahl gerundet — eine Dezimalzahl bringt nichts |
color | transparent | der Hintergrund des Karussells |
Der Abstand der Logos berechnet sich so:
abstand = (laenge − logoSize × maxLogoCount) / maxLogoCount + logoSize
wobei laenge vertikal size.y ist, horizontal size.x.
Die detailed-Ansicht — die Spiele
Die Liste, die Spielkarte, und ihre dreißig reservierten Komponenten.
Die Spieleliste eines Systems, mit der Karte des markierten Spiels.
Es ist die Ansicht aller Spielelisten: die
basic-Ansicht wird von der Engine nie angefordert, und die Arcade-Ansicht verwendetdetailedwieder.
Die Liste und die Medien
| Name | Typ | Aufgabe |
|---|---|---|
gamelist | textlist | die Spieleliste |
logo | image | das Logo des Systems |
md_image | image | das Cover |
default_image_path | image | das Ersatzbild, wenn das Spiel kein Cover hat |
md_video | video | das Vorschauvideo |
md_region1 … md_region4 | image | die vier Regionsflaggen des Spiels |
Bei
md_imagewird der im Theme geschriebenepathignoriert: das Bild kommt vom Spiel. Bei denmd_region*werden nurpos,size,zIndexundpathgelesen.
Die Informationen des Spiels
| Name | Typ |
|---|---|
md_description | text, markdown oder scrolltext — nach Wahl |
md_folder_name | text |
md_rating | rating |
md_releasedate, md_lastplayed | datetime |
md_developer, md_publisher, md_genre, md_players, md_playcount, md_favorite | text |
Die Beschriftungen
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
⚠️ Ihr Text ist vorgegeben. Recalbox schreibt „Bewertung:“, „Erschienen:“, „Entwickler:“… übersetzt in die Sprache der Maschine, nachdem das Theme angewendet wurde. Ein
text=im Theme wird überschrieben. Du stellst ihre Formatierung ein, nie ihren Inhalt.Ebenso akzeptieren die
md_*-Werte alle Eigenschaften außertext: ihr Inhalt kommt vom Spiel.
Die Farben der Liste
gamelist verwendet fünf Farben, von denen drei nicht durch das Theme gestaltbar sind:
| Zeile | Farbe |
|---|---|
| ein Spiel | primaryColor |
| ein Ordner | secondaryColor |
| ein abgeblendetes Spiel | berechnet: primaryColor mit halbierter Deckkraft |
| ein abgeblendeter Ordner | ebenso berechnet |
| der Hintergrund einer Sortierüberschrift | vorgegeben |
Vorhandene, aber wirkungslose Komponenten
template_flag, template_genre und template_players werden von der Engine gelesen, aber nie verwendet: die Farben der Verzierungen sind nicht gestaltbar. Verliere keine Zeit damit.
Die gameclip-Ansicht — der Bildschirmschoner
Was erscheint, wenn die Maschine nichts tut.
Der Bildschirmschoner spielt Spielausschnitte ab. Das Theme kleidet ihn drumherum ein.
Was gestaltbar ist
Diese Ansicht wird fast vollständig aus deinen Extras aufgebaut: platziere deine Komponenten mit extra="true" und sie erscheinen.
Nur eine reservierte Komponente ist hier gestaltbar: das Video. Die Informationskomponenten (md_rating, md_developer, md_genre…) existieren in dieser Ansicht, sind aber themeseitig deaktiviert: anders als in detailed kannst du sie nicht formatieren.
➡️ Um den Namen oder den Entwickler des Spiels im Bildschirmschoner anzuzeigen, platziere deine eigenen text mit den Variablen:
<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>
Die Spielvariablen funktionieren hier
Der Kontext dieser Ansicht enthält das System UND das Spiel: alle ${game.*} werden aufgelöst. Das macht den Bildschirmschoner interessant zum Einkleiden.
Die menu-Ansicht — die Menüs
Was ein Theme an den Menüs ändern kann, und was nicht.
Recalbox baut seine Menüs selbst: ihr Inhalt, ihre Reihenfolge und ihre Beschriftungen gehören nicht dir.
Was das Theme liefert, sind Stile — und ein einziges Maß.
<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>
Die neun verfügbaren Komponenten sind in Die Gestaltung der Menüs beschrieben.
Was du nicht tun kannst
- eine Menüzeile hinzufügen, entfernen oder umbenennen;
- die Reihenfolge der Abschnitte ändern;
- eine freie Komponente in ein Menü setzen: die Extras werden in dieser Ansicht nicht gelesen.
Woran du denken solltest
Die Menüs erscheinen über der aktuellen Ansicht. Der Schleier (fadePath von menuBackground) verdunkelt, was dahinterliegt: ohne ihn wird ein Menü mit transparentem Hintergrund auf einem hellen Theme unlesbar.
Eine Ebene auf allen Ansichten
Ein gemeinsames Dekor, einmal geschrieben, statt in jede Ansicht kopiert.
Das Problem
Ein CRT-Schleier, ein Markenlogo, ein Rahmen: du willst ihn auf der Systemliste und auf der Spieleliste und auf dem Menü. Ihn in jede Ansicht zu kopieren funktioniert… bis zu dem Tag, an dem du nur einen davon änderst. Die anderen bleiben zurück, und nichts weist darauf hin.
Die Lösung
Rechtsklick auf die Ebene (oder das ⋯ des Inspektors) → „Auf allen Ansichten vorhanden“.
Die Ebene verlässt ihre Ansicht und wechselt zu den gemeinsamen Ebenen: es gibt nur noch eine einzige, geteilte. Sie von irgendwo einzustellen, stellt sie überall ein.
Um zurückzugehen: „Nur auf dieser Ansicht behalten“.
Was das ins Theme schreibt
Ein einziger Block, dessen Name die Ansichten auflistet:
<!-- global.xml -->
<view name="system, basic, detailed, menu, gameclip">
<extras>
<image name="voileCRT" extra="true">…</image>
</extras>
</view>
Die Maschine zerlegt diesen Namen an den Kommas und setzt das Element in jede Ansicht. Die Datei wird vor den Ansichten geladen: was überall gilt, ist ein Sockel, den eine Ansicht noch korrigieren kann.
Nicht zu verwechseln
„In ein anderes Layout duplizieren…“ ist etwas anderes: es kopiert die Ebene in ein anderes Layout derselben Ansicht (zum Beispiel „Vertikal links“ und „Horizontal“), und die zwei Kopien sind danach unabhängig.
⚠️ Der Ansichtsname
menuwird geschrieben, weil das Theme es verlangt, aber die Engine liest dort keine freie Komponente: die Ebene ist in den Menüs nicht zu sehen.
Beim Import findet ein Theme, das bereits einen Block über alle Ansichten schreibt, seine gemeinsame Ebene wieder. Ein Block, der nur einen Teil abdeckt, bleibt pro Ansicht aufgeteilt: er gilt nur dort, wo das Theme ihn hingesetzt hat.
Variablen verwenden
Den Namen eines Spiels anzeigen, ein Bild je nach System wählen.
Eine Variable wird ${…} geschrieben und Recalbox ersetzt sie beim Anzeigen.
<text name="titre">
<text>Bienvenue sur ${system}</text>
</text>
→ „Bienvenue sur Super Nintendo“.
In einem Bildpfad: am nützlichsten
<image name="console" extra="true">
<path>${root}/data/arts/consoles/${system.name}.png</path>
</image>
Eine einzige Zeile, und jedes System zeigt sein eigenes Bild. Die Dateien müssen nur den internen Namen des Systems tragen: snes.png, megadrive.png…
Wo jede Variable funktioniert
Das ist die Regel, die am meisten überrascht, und sie kommt aus der Engine:
| Familie | Ansichten, in denen sie aufgelöst wird |
|---|---|
${system…} | Systeme und Spieleliste — eine Liste gehört immer zu einem System |
${game…} | Spieleliste und Bildschirmschoner — dort, wo es ein markiertes Spiel gibt |
${recalbox…} ${settings…} ${hardware…} ${display…} | überall |
Eine Variable, die dort verwendet wird, wo sie nicht existiert, wird nicht ersetzt: der rohe Text erscheint unverändert, ${game.name} eingeschlossen. Das Studio bietet nur die an, die auf der aktuellen Ansicht funktionieren.
Wenn die markierte Zeile kein Spiel ist
In der Spieleliste durchläuft der Cursor nicht nur Spiele: Er landet auch auf Ordnern und auf Sortier-Überschriften, jenen Zwischentiteln, die Recalbox einfügt, sobald die Liste anders als alphabetisch sortiert ist. Die ${game…} antworten weiterhin, beschreiben dann aber eine Zeile ohne Spiel:
| Markierte Zeile | ${game.name} | ${game.releasedate} | ${game.file.name} |
|---|---|---|---|
| ein Spiel | sein Name | sein Erscheinungsdatum | die Rom-Datei |
| ein Ordner | der Ordnername | UNBEKANNT | der Ordnername |
| eine Sortier-Überschrift | nichts | UNBEKANNT | nichts |
Ein Bauteil, das Spieldaten anzeigt, hat auf diesen Zeilen also nichts mehr zu sagen — und bleibt trotzdem auf dem Bildschirm, über dem Ordnernamen, den Recalbox im selben Moment schreibt. Beschränken Sie es auf Spielzeilen:
<text name="sortie" extra="true" showIf="game">
<text>Erschienen: ${game.releasedate}</text>
</text>
Das Studio übernimmt das: Sobald ein Bauteil Spieldaten verwendet, wechselt seine Sichtbarkeit auf „ein Spiel“. Über den Reiter Sichtbarkeit des Bauteils lässt sie sich jederzeit wieder für Ordner und Überschriften öffnen.
${root} — nie vergessen
${root} bezeichnet die Wurzel des ausgewählten Themes. Ohne ihn sind die Pfade relativ zur Datei, die sie schreibt, und dein Theme geht kaputt, sobald es anders abgelegt wird.
<path>${root}/data/arts/fond.jpg</path> <!-- ✅ -->
<path>../data/arts/fond.jpg</path> <!-- zerbrechlich -->
Die alten $…-Variablen
In alten Themes wirst du Variablen ohne Klammern begegnen:
| Alt | Was sie ergibt | Aktuelles Äquivalent |
|---|---|---|
$system | der kurze Name — „snes“ | ${system.name} |
$theme | der Ordner des Themes | ${root} |
⚠️ $system und ${system} ergeben nicht dasselbe: die erste liefert „snes“, die zweite „Super Nintendo“. Sie werden noch akzeptiert, sind aber veraltet: schreibe nur noch die Form mit Klammern.
Die Zufallsauswahl
<path>${random.between(fond1.jpg,fond2.jpg,fond3.jpg)}</path>
<fontSize>${random.range(1,10)}</fontSize>
random.between wählt zufällig einen Wert aus der Liste, random.range eine Zahl zwischen zwei Grenzen. Die Auswahl findet beim Laden des Themes statt, nicht bei jeder Anzeige.
Alle Variablen
Die vollständige Liste, und wo jede funktioniert.
Hier sind alle Variablen, die Recalbox ersetzen kann, erhoben aus der Engine.
Eine Variable wird ${…} geschrieben, und Recalbox ersetzt sie beim Anzeigen.
Das System
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${system} | Name des Systems | Der vollständige Name — z. B. „Sega Megadrive“ | Systeme, Spiele |
${system.input.keyboard} | Tastatur nötig? | mandatory · recommended · optional · no | Systeme, Spiele |
${system.input.mouse} | Maus nötig? | mandatory · recommended · optional · no | Systeme, Spiele |
${system.input.pad} | Controller nötig? | mandatory · recommended · optional · no | Systeme, Spiele |
${system.logo} | Logo des Systems | Der Pfad des von Recalbox gelieferten Logos | Systeme, Spiele |
${system.manufacturer} | Hersteller | Z. B. „Sega“, „Nintendo“. Leer, wenn unbekannt | Systeme, Spiele |
${system.name} | Kurzname des Systems | — | Systeme, Spiele |
${system.releasedate} | Erscheinungsjahr | Jahr und Monat — z. B. „1988-10“ | Systeme, Spiele |
${system.type} | Maschinentyp (technischer Name) | arcade · console · handheld · computer · engine · port · fantasy · virtual · virtual-arcade | Systeme, Spiele |
${system.type.name} | Maschinentyp | Derselbe, im Klartext: „Home Console“, „handheld Console“, „Arcade“… | Systeme, Spiele |
Das Spiel
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${game.developer} | Entwickler | Z. B. „Konami“. „UNKNOWN“, wenn nicht vorhanden | Bildschirmschoner, Spiele |
${game.file.name} | Dateiname | Der Dateiname, Erweiterung eingeschlossen | Bildschirmschoner, Spiele |
${game.file.path} | Vollständiger Dateipfad | Der vollständige Pfad der Datei | Bildschirmschoner, Spiele |
${game.file.stem} | Dateiname (ohne Erweiterung) | Der Dateiname, ohne Erweiterung | Bildschirmschoner, Spiele |
${game.genre.normalized} | Genre (technischer Name) | Das normalisierte Genre, auf Englisch — „Platform“, „Shoot’em Up“, „Racing“… | Bildschirmschoner, Spiele |
${game.genre.raw} | Genre | Das Genre so, wie es in der Spielkarte steht | Bildschirmschoner, Spiele |
${game.isadult} | Nur für Erwachsene? | yes oder no | Bildschirmschoner, Spiele |
${game.isfavorite} | Ist ein Favorit? | yes oder no (nie true/false) | Bildschirmschoner, Spiele |
${game.ishidden} | Ist versteckt? | yes oder no | Bildschirmschoner, Spiele |
${game.islastversion} | Ist die neueste Version? | yes oder no | Bildschirmschoner, Spiele |
${game.isnotagame} | Ist kein Spiel? | yes oder no | Bildschirmschoner, Spiele |
${game.ispreinstalled} | Ist vorinstalliert? | yes oder no | Spiele, Bildschirmschoner |
${game.license} | Lizenz | Die Lizenz, oft leer | Bildschirmschoner, Spiele |
${game.name} | Name des Spiels | Der Name des Spiels | Bildschirmschoner, Spiele |
${game.players} | Anzahl der Spieler | „1“, „2“, „1-4“, „4+“… | Bildschirmschoner, Spiele |
${game.players.max} | Spieler — Maximum | Eine Zahl — z. B. „4“ | Bildschirmschoner, Spiele |
${game.players.min} | Spieler — Minimum | Eine Zahl — z. B. „1“ | Bildschirmschoner, Spiele |
${game.publisher} | Herausgeber | Z. B. „Sega“. „UNKNOWN“, wenn nicht vorhanden | Bildschirmschoner, Spiele |
${game.releasedate} | Erscheinungsdatum | ISO-Datum — z. B. „1991-06-23“. „UNKNOWN“, wenn nicht vorhanden | Bildschirmschoner, Spiele |
${game.synopsis} | Beschreibung | Der Vorstellungstext, oft lang | Bildschirmschoner, Spiele |
Bewertung und Statistiken
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${game.lastplayed} | Letzte Partie | ISO-Datum, oder „NEVER“, wenn nie gespielt | Bildschirmschoner, Spiele |
${game.rating.10} | Bewertung (von 10) | Eine ganze Zahl von 0 bis 10 | Bildschirmschoner, Spiele |
${game.rating.100} | Bewertung (von 100) | Eine ganze Zahl von 0 bis 100 | Bildschirmschoner, Spiele |
${game.rating.5} | Bewertung (von 5) | Eine ganze Zahl von 0 bis 5 — keine Sterne | Spiele, Bildschirmschoner |
${game.timesplayed} | Anzahl der Partien | Eine Anzahl von Partien | Bildschirmschoner, Spiele |
${game.totalplayed} | Gesamte Spielzeit | Eine Dauer — z. B. „3h 12m“. „NONE“, wenn null | Bildschirmschoner, Spiele |
Bilder und Video des Spiels
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${game.media.boxpath} | Verpackung (Box Art) | — | Spiele, Bildschirmschoner |
${game.media.imagepath} | Cover / Bild | Der Pfad des Covers. Leer, wenn das Spiel keines hat — siehe ifexists | Bildschirmschoner, Spiele |
${game.media.thumbpath} | Miniaturbild | Der Pfad des Miniaturbilds | Bildschirmschoner, Spiele |
${game.media.videopath} | Video | Der Pfad des Videos | Bildschirmschoner, Spiele |
Datenträger des Spiels
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${game.support.index} | Index des Datenträgers | Die Nummer der Disc — leer, wenn es nur eine gibt | Bildschirmschoner, Spiele |
${game.support.number} | Nummer des Datenträgers | Alles zusammengesetzt — z. B. „2A/3“ | Bildschirmschoner, Spiele |
${game.support.side} | Seite des Datenträgers | Die Seite des Datenträgers — A, B… | Bildschirmschoner, Spiele |
${game.support.total} | Anzahl der Datenträger | Die Anzahl der Datenträger. „UNKNOWN“, wenn unbekannt | Bildschirmschoner, Spiele |
${game.support.type} | Art des Datenträgers | Cartridge · CD/DVD · Harddisk · Files · Tape · Quick Disc · 3" Floppy · 3".5 Floppy · 5".25 Floppy · PCB · Unknown | Bildschirmschoner, Spiele |
System des Spiels
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${game.system} | Name des Systems des Spiels | Der vollständige Name des Systems des Spiels | Bildschirmschoner, Spiele |
${game.system.input.keyboard} | Vom System des Spiels verlangte Tastatur | mandatory · recommended · optional · no | Bildschirmschoner, Spiele |
${game.system.input.mouse} | Vom System des Spiels verlangte Maus | mandatory · recommended · optional · no | Bildschirmschoner, Spiele |
${game.system.input.pad} | Vom System des Spiels verlangter Controller | mandatory · recommended · optional · no | Bildschirmschoner, Spiele |
${game.system.logo} | Logo des Systems des Spiels | Der Pfad seines Logos | Bildschirmschoner, Spiele |
${game.system.manufacturer} | Hersteller des Systems des Spiels | Sein Hersteller | Bildschirmschoner, Spiele |
${game.system.name} | Kurzname des Systems des Spiels | Sein interner Name | Bildschirmschoner, Spiele |
${game.system.releasedate} | Jahr des Systems des Spiels | Sein Erscheinungsjahr | Bildschirmschoner, Spiele |
${game.system.type} | Typ des Systems des Spiels (technischer Name) | Wie ${system.type}: console · handheld · arcade… | Bildschirmschoner, Spiele |
${game.system.type.name} | Typ des Systems des Spiels | Derselbe, im Klartext | Bildschirmschoner, Spiele |
Emulator
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${game.emulator.compatibility} | Kompatibilität | unknown · low · average · good · high · perfect | Bildschirmschoner, Spiele |
${game.emulator.extensions} | Unterstützte Erweiterungen | Die unterstützten Erweiterungen — z. B. „.bin .gen .md“ | Bildschirmschoner, Spiele |
${game.emulator.hasnetplay} | Online-Spiel möglich? | yes oder no | Bildschirmschoner, Spiele |
${game.emulator.hassoftpatching} | Akzeptiert Patches? | yes oder no | Spiele, Bildschirmschoner |
${game.emulator.islibretro} | Ist ein Libretro-Kern? | yes oder no | Bildschirmschoner, Spiele |
${game.emulator.name} | Name des Emulators | Z. B. „libretro picodrive“ | Bildschirmschoner, Spiele |
${game.emulator.speed} | Geschwindigkeit | unknown · low · average · good · high · perfect | Bildschirmschoner, Spiele |
Die Maschine und ihre Einstellungen
| Schreibweise | Was es ist | Was sie liefert | Wo |
|---|---|---|---|
${display.overscan} | Overscan? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${random.between(a,b,c)} | Ein zufälliger Wert aus… | einer der angegebenen Werte | überall |
${random.range(1,10)} | Eine Zufallszahl zwischen… | eine ganze Zahl zwischen den beiden Grenzen | überall |
${display.resolution} | Auflösung | fhd (1080p und mehr) · hd (720p) · vga · qvga | Bildschirmschoner, Systeme, Menü, Spiele |
${display.tate} | Hochkant-Bildschirm (TATE)? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${display.tateleft} | Hochkant nach links? | yes oder no | Systeme, Menü, Spiele, Bildschirmschoner |
${display.tateright} | Hochkant nach rechts? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${hardware.board} | Maschinenmodell | Das Modell — „RPi 5“, „PC x64“, „RG351P/M“… | Bildschirmschoner, Systeme, Menü, Spiele |
${hardware.crt} | CRT-Bildschirm? | yes oder no | Menü, Spiele, Bildschirmschoner, Systeme |
${hardware.isanbernic} | Ist es ein Anbernic? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${hardware.isodroid} | Ist es ein Odroid? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${hardware.ispc} | Ist es ein PC? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${hardware.ispi} | Ist es ein Raspberry Pi? | yes oder no | Bildschirmschoner, Systeme, Menü, Spiele |
${hardware.jamma} | Jamma-Automat? | yes oder no | Systeme, Menü, Spiele, Bildschirmschoner |
${recalbox.built} | Build-Datum | Das Build-Datum | Bildschirmschoner, Systeme, Menü, Spiele |
${recalbox.version} | Recalbox-Version | Z. B. „10.0“ | Bildschirmschoner, Systeme, Menü, Spiele |
${root} | Ordner des Themes | Die Wurzel des ausgewählten Themes — vor alle deine Pfade zu setzen | Menü, Spiele, Bildschirmschoner, Systeme |
${settings.language} | Sprache | Die Sprache allein — z. B. „fr“ | Bildschirmschoner, Systeme, Menü, Spiele |
${settings.locale} | Sprache und Land | Sprache und Land — z. B. „fr_FR“ | Bildschirmschoner, Systeme, Menü, Spiele |
${settings.region} | Gewählte Region | eu · us · jp | Bildschirmschoner, Systeme, Menü, Spiele |
${settings.timezone} | Zeitzone | Z. B. „Europe/Paris“ | Bildschirmschoner, Systeme, Menü, Spiele |
Deine maßgeschneiderten Variablen
Deine eigenen benannten Werte — eine Farbe, eine Schrift — erstellt im Studio.
Die Variablen der vorherigen Seite sind die von Recalbox: ${system}, ${game.developer}… Sie werden geliefert, du verwendest sie nur.
Diese hier sind deine. Du gibst einem Wert einen Namen — einer Farbe, einer Schrift, einem Pfad — du verwendest diesen Namen überall, und an dem Tag, an dem du den Wert änderst, folgt das ganze Theme. Das macht die Farboptionen möglich: eine Wahl definiert die Variable neu, und sonst nichts.
Sie im Studio erstellen
- öffne Variablen in der Werkzeugleiste: das Panel öffnet sich rechts;
- + Neue Variable, gib ihr einen Namen, und sage, was es ist — eine Farbe (du bekommst den Farbwähler, Transparenz eingeschlossen), eine Schrift (gewählt aus denen des Themes), oder ein anderer Wert (ein Text, ein Pfad, oder eine andere Variable);
- in einem Farbfeld bietet das kleine { } neben dem Feld deine an, und in der Schriftenliste erscheinen sie ganz oben.
Das Studio zeigt unter dem Feld den berechneten Wert: eine Variable, die eine andere verwendet, lässt sich auf einen Blick lesen.
Von Hand, ohne das Studio
Sie werden in einem <variables>-Block geschrieben. Das Tag, die Namensregeln, der Geltungsbereich und die Bedingungen werden auf Deine eigenen Variablen erklärt, in „Die Struktur eines Themes“.
Bedingt anzeigen
Die 26 Bedingungen: die Maschine, der Bildschirm, und das angezeigte System.
Eine if=-Bedingung lässt eine Komponente nur in bestimmten Fällen erscheinen.
<image name="fond" if="crt">…</image>
Sie lassen sich mit ! (nicht), & (und), | (oder) kombinieren — oder, wenn du Wörter bevorzugst, not, and, or: if="crt and not tate". Siehe Mehrere Bedingungen kombinieren.
⚠️ Ein Bezeichner, der in dieser Liste fehlt, wird von Recalbox ignoriert und in themes.log als unbekannt gemeldet.
Überall — die Maschine und der Bildschirm
| Bedingung | Wahr, wenn… |
|---|---|
hd fhd vga qvga | der Bildschirm diese Auflösung hat |
crt | der Bildschirm eine Bildröhre ist |
overscan | das Bild übersteuert (CRT ohne Jamma) |
tate tateleft tateright | der Bildschirm vertikal steht |
jamma bartop | die Maschine ein Arcade-Automat ist |
ispc ispi isodroid isanbernic | die Maschine dieses Modell ist |
nomenu | die Menüs deaktiviert sind |
Nur dort, wo ein System angezeigt wird
Diese acht fragen das aktuelle System ab. Sie gelten daher nur in der Ansicht Systeme und in der Spieleliste — anderswo gibt es kein System, und die Bedingung ist immer falsch.
| Bedingung | Wahr, wenn… |
|---|---|
console handheld computer arcade engine port fantasy | das System von diesem Typ ist |
virtual | es ein automatisches System ist (Favoriten, Zuletzt gespielt, Alle Spiele…) |
favorite | es das automatische System Favoriten ist |
lastplayed | es das automatische System Zuletzt gespielt ist |
⚠️
favoritebedeutet nicht, dass das markierte Spiel ein Favorit ist: Es geht um das System. Es gibt keine Bedingung für ein Spiel oder einen Ordner — um auf den Inhalt eines Spiels zu reagieren, nutzt man seine Variablen (${game.isfavorite}) oderifexists.
Mehrere Bedingungen kombinieren
UND, ODER, „nicht das“, und die Gruppen in Klammern.
Ein Etikett pro Bedingung
Du wählst eine Bedingung aus der Liste, sie wird zu einem Etikett. Der kleine Button davor sagt, in welche Richtung sie zählt:
- wenn — nur in diesem Fall;
- wenn nicht — überall außer in diesem Fall.
Auf einem bereits gesetzten Etikett kehrt derselbe Button die Richtung um; das ✕ entfernt es.
Die Gruppen
Eine Gruppe ist eine Klammer. Darin sagst du, ob alle Bedingungen nötig sind, oder mindestens eine. Zwischen zwei Gruppen stellt sich dieselbe Frage: UND oder ODER.
Drei Schreibweisen für dieselben Operatoren
| Was man sagen will | Zeichen | Ausgeschrieben |
|---|---|---|
| und | & | AND |
| oder | | | OR |
| nicht | ! | NOT |
Die beiden Schreibweisen mischen sich, und Groß- und Kleinschreibung spielt keine Rolle: if="crt AND !tate" entspricht if="crt & !tate".
⚠️ && und || funktionieren nicht. Zwei Zeichen hintereinander sind ein Syntaxfehler, und ein fehlerhafter Ausdruck ist falsch: die Komponente verschwindet, ohne dass irgendetwas auf dem Bildschirm darauf hinweist.
Beispiel: „auf einem Röhrenbildschirm, und in Arcade oder in den Favoriten“ schreibt man
crt & (arcade | favorite)
⚠️ Die Reihenfolge der Gruppen zählt
Die Maschine liest von links nach rechts, ohne Vorrang. a | b & c ist dort (a | b) & c, und nicht a | (b & c). Deshalb setzt das Studio systematisch die Klammern: was du liest, ist genau das, was die Maschine verstehen wird.
Innerhalb einer Gruppe dagegen spielt die Reihenfolge keine Rolle: „nicht Full HD und Arcade“ sagt dasselbe wie „Arcade und nicht Full HD“.
Eine Bedingung wird an zwei Stellen geschrieben
Dieselbe Bedingung, an der Komponente selbst oder an nur einer ihrer Eigenschaften, sagt nicht dasselbe:
<image name="filtre" if="crt">…</image> <!-- ① die Komponente existiert NUR auf einem CRT -->
<text name="titre"> <!-- ② die Komponente existiert immer… -->
<fontSize if="crt">0.09</fontSize> <!-- …aber ihre Größe ändert sich auf einem CRT -->
<fontSize if="!crt">0.05</fontSize>
</text>
① an der Komponente: sie erscheint, oder sie existiert gar nicht. ② an einer Eigenschaft: die Komponente ist immer da, nur einer ihrer Werte ändert sich.
Im Studio ist es in beiden Fällen derselbe Button und dasselbe Fenster — was du hier baust, gilt dort identisch.
Die Systeme
Die internen Namen, die in deinen Ordnern und Dateien erwartet werden.
Jedes System trägt mehrere Namen, und man darf sie nicht verwechseln:
- Das System — sein üblicher Name, von dem man spricht. Er taucht in keiner Datei auf;
- die Variablen, die die Engine beim Anzeigen füllt.
Der Ordner, den Sie in Ihrem Theme anlegen, trägt den Namen von ${system.name}. Einige Systeme bilden eine Ausnahme: Sie stehen unter ihrer Tabelle.
Die Maschinen (121)
| System | ${system.name} | ${system} | ${system.manufacturer} | ${system.type} |
|---|---|---|---|---|
| 240ptestsuite | 240ptestsuite | 240ptestsuite | virtual | virtual |
| Acorn BBC Micro | bbcmicro | Acorn BBC Micro | Acorn | computer |
| Amiga AGA | amiga1200 | Amiga AGA | Commodore | computer |
| Amiga CD32 | amigacd32 | Amiga CD32 | Commodore | console |
| Amiga CDTV | amigacdtv | Amiga CDTV | Commodore | console |
| Amiga ECS/OCS | amiga600 | Amiga ECS/OCS | Commodore | computer |
| Amstrad GX4000 | gx4000 | Amstrad GX4000 | Amstrad | console |
| AmstradCPC | amstradcpc | AmstradCPC | Amstrad | computer |
| Apple II | apple2 | Apple II | Apple | computer |
| Apple IIGS | apple2gs | Apple IIGS | Apple | computer |
| Apple Macintosh | macintosh | Apple Macintosh | Apple | computer |
| Arduboy | arduboy | Arduboy | fantasy | fantasy |
| Atari 2600 | atari2600 | Atari 2600 | Atari | console |
| Atari 5200 | atari5200 | Atari 5200 | Atari | console |
| Atari 7800 | atari7800 | Atari 7800 | Atari | console |
| Atari 8bits | atari800 | Atari 8bits | Atari | computer |
| Atari Jaguar | jaguar | Atari Jaguar | Atari | console |
| Atari ST | atarist | Atari ST | Atari | computer |
| Colecovision | colecovision | Colecovision | Coleco | console |
| Commodore 64 | c64 | Commodore 64 | Commodore | computer |
| Commodore VIC-20 | vic20 | Commodore VIC-20 | Commodore | computer |
| Daphne | daphne | Daphne | Daphne | arcade |
| DICE | dice | DICE | DICE | arcade |
| Dos (x86) | dos | Dos (x86) | IBM | computer |
| Dragon 32/64 | dragon | Dragon 32/64 | DragonData | computer |
| EasyRPG | easyrpg | EasyRPG | virtual | engine |
| Elektronika BK | bk | Elektronika BK | Elektronika | computer |
| Epoch Cassette Vision | cassettevision | Epoch Cassette Vision | Epoch | console |
| Exelvision EXL 100 | exl100 | Exelvision EXL 100 | Exelvision | computer |
| Fairchild Channel F | channelf | Fairchild Channel F | Fairchild | console |
| Family Computer Disk System | fds | Family Computer Disk System | Nintendo | console |
| FinalBurn Neo | fbneo | FinalBurn Neo | FBN | arcade |
| Game and Watch | gw | Game and Watch | Nintendo | handheld |
| Game Boy | gb | Game Boy | Nintendo | handheld |
| Game Boy Advance | gba | Game Boy Advance | Nintendo | handheld |
| Game Boy Color | gbc | Game Boy Color | Nintendo | handheld |
| GameCube | gamecube | GameCube | Nintendo | console |
| Infocom Z-Machine | zmachine | Infocom Z-Machine | Infocom | engine |
| LowRes NX | lowresnx | LowRes NX | virtual | fantasy |
| Lutro | lutro | Lutro | virtual | fantasy |
| Lynx | lynx | Lynx | Atari | handheld |
| Mame | mame | Mame | Mame | arcade |
| Mattel Intellivision | intellivision | Mattel Intellivision | Mattel | console |
| MegaDuck | megaduck | MegaDuck | Welback | handheld |
| MGT SAM Coupé | samcoupe | MGT SAM Coupé | MGT | computer |
| Moonlight | moonlight | Moonlight | NVidia | virtual |
| MSX1 | msx1 | MSX1 | Microsoft | computer |
| MSX2 | msx2 | MSX2 | Microsoft | computer |
| MSXturboR | msxturbor | MSXturboR | Microsoft | computer |
| NEC PC-88 | pc88 | NEC PC-88 | NEC | computer |
| NEC PC-98 | pc98 | NEC PC-98 | NEC | computer |
| NEC PC-FX | pcfx | NEC PC-FX | NEC | console |
| Neo-Geo AES | neogeo | Neo-Geo AES | SNK | console |
| Neo-Geo CD | neogeocd | Neo-Geo CD | SNK | console |
| Neo-Geo Pocket | ngp | Neo-Geo Pocket | SNK | handheld |
| Neo-Geo Pocket Color | ngpc | Neo-Geo Pocket Color | SNK | handheld |
| Nintendo 64 | n64 | Nintendo 64 | Nintendo | console |
| Nintendo 64DD | 64dd | Nintendo 64DD | Nintendo | console |
| Nintendo DS | nds | Nintendo DS | Nintendo | handheld |
| Nintendo Entertainment System | nes | Nintendo Entertainment System | Nintendo | console |
| Odyssey2 | o2em | Odyssey2 | Magnavox | console |
| OpenBOR | openbor | OpenBOR | Senile Team | engine |
| Oric/Atmos | oricatmos | Oric/Atmos | Tangerine | computer |
| Othello Multivision | multivision | Othello Multivision | Tsukuda | console |
| Palm | palm | Palm | Palm | handheld |
| Panasonic 3DO | 3do | Panasonic 3DO | Panasonic | console |
| PC Engine | pcengine | PC Engine | NEC | console |
| PC Engine CD | pcenginecd | PC Engine CD | NEC | console |
| Philips CD-I | cdi | Philips CD-I | Phillips | console |
| Philips P2000T | p2000t | Philips P2000T | Philips | computer |
| Philips VG 5000 | vg5000 | Philips VG 5000 | Philips | computer |
| PICO-8 | pico8 | PICO-8 | virtual | fantasy |
| Pocket Challenge v2 | pcv2 | Pocket Challenge v2 | Benesse | handheld |
| Pokémon Mini | pokemini | Pokémon Mini | Nintendo | handheld |
| Sammy Atomiswave | atomiswave | Sammy Atomiswave | Sammy | arcade |
| Satellaview | satellaview | Satellaview | Nintendo | console |
| Screenshots | imageviewer | Screenshots | virtual | virtual |
| ScummVM | scummvm | ScummVM | Ludvig Strigeus | engine |
| Sega 32X | sega32x | Sega 32X | Sega | console |
| Sega CD | segacd | Sega CD | Sega | console |
| Sega Dreamcast | dreamcast | Sega Dreamcast | Sega | console |
| Sega Game Gear | gamegear | Sega Game Gear | Sega | handheld |
| Sega Master System / Mark III | mastersystem | Sega Master System / Mark III | Sega | console |
| Sega Megadrive | megadrive | Sega Megadrive | Sega | console |
| Sega Model3 | model3 | Sega Model3 | Sega | arcade |
| Sega NAOMI | naomi | Sega NAOMI | Sega | arcade |
| Sega NAOMI 2 | naomi2 | Sega NAOMI 2 | Sega | arcade |
| Sega NAOMI GD-ROM System | naomigd | Sega NAOMI GD-ROM System | Sega | arcade |
| Sega Pico | pico | Sega Pico | Sega | console |
| Sega Saturn | saturn | Sega Saturn | Sega | console |
| Sega SG1000 | sg1000 | Sega SG1000 | Sega | console |
| Sharp X1 | x1 | Sharp X1 | Sharp | computer |
| Sharp X68000 | x68000 | Sharp X68000 | Sharp | computer |
| Solarus | solarus | Solarus | Solarus | engine |
| Sony Playstation 1 | psx | Sony Playstation 1 | Sony | console |
| Sony Playstation 2 | ps2 | Sony Playstation 2 | Sony | console |
| Sony Playstation Portable | psp | Sony Playstation Portable | Sony | handheld |
| Spectravideo | spectravideo | Spectravideo | Spectravideo | computer |
| ST-V | stv | ST-V | Sega | arcade |
| SuFami Turbo | sufami | SuFami Turbo | Bandai | console |
| Super Cassette Vision | scv | Super Cassette Vision | Epoch | console |
| Super Nintendo Entertainment System | snes | Super Nintendo Entertainment System | Nintendo | console |
| Supergrafx | supergrafx | Supergrafx | NEC | console |
| Texas Instrument TI-99/4A | ti994a | Texas Instrument TI-99/4A | Texas Instrument | computer |
| Thomson | thomson | Thomson | Thomson | computer |
| TIC-80 | tic80 | TIC-80 | port | fantasy |
| TRS-80 Color Computer | trs80coco | TRS-80 Color Computer | Tandy | computer |
| Uzebox | uzebox | Uzebox | port | console |
| Vectrex | vectrex | Vectrex | MB | console |
| Videopac+ G7400 | videopacplus | Videopac+ G7400 | Philips | console |
| Vircon32 | vircon32 | Vircon32 | virtual | console |
| Virtual Boy | virtualboy | Virtual Boy | Nintendo | console |
| Visual Pinball Standalone | vpinball | Visual Pinball Standalone | Randy Davis | engine |
| WASM-4 | wasm4 | WASM-4 | Bruno Garcia | fantasy |
| Watara Supervision | supervision | Watara Supervision | Watara | handheld |
| Wii | wii | Wii | Nintendo | console |
| WonderSwan | wswan | WonderSwan | Bandai | handheld |
| WonderSwan Color | wswanc | WonderSwan Color | Bandai | handheld |
| Xbox | xbox | Xbox | Microsoft | console |
| ZX81 | zx81 | ZX81 | Sinclair | computer |
| ZXSpectrum | zxspectrum | ZXSpectrum | Sinclair | computer |
Die Ordner, die nicht den Namen des Systems tragen. Nur für diese heißt der Ordner in Ihrem Theme anders als ${system.name}.
| System | ${system.name} | Anzulegender Ordner |
|---|---|---|
| Dos (x86) | dos | pc |
| GameCube | gamecube | gc |
| Odyssey2 | o2em | odyssey2 |
| Oric/Atmos | oricatmos | oric |
| Thomson | thomson | to8 |
| WonderSwan | wswan | wonderswan |
| WonderSwan Color | wswanc | wonderswancolor |
Die virtuellen Systeme (11)
Recalbox baut sie selbst, aus deinen Spielen: sie haben keine Systemdatei, aber sehr wohl einen Theme-Ordner, und du kannst sie wie alle anderen einkleiden.
| System | ${system.name} | ${system} | ${system.type} |
|---|---|---|---|
| Ports | ports | Ports | virtual |
| Favorites | favorites | Favorites | virtual |
| Last played | lastplayed | Last played | virtual |
| All games | allgames | All games | virtual |
| Multiplayer | multiplayer | Multiplayer | virtual |
| Arcade | arcade | Arcade | virtual-arcade |
| Lightgun | lightgun | Lightgun | virtual |
| Tate | tate | Tate | virtual |
| Dial | dial | Dial | virtual |
| Trackball | trackball | Trackball | virtual |
| Challenges | challenges | Challenges | virtual |
Die Ordner, die nicht den Namen des Systems tragen. Nur für diese heißt der Ordner in Ihrem Theme anders als ${system.name}.
| System | ${system.name} | Anzulegender Ordner |
|---|---|---|
| Last played | lastplayed | auto-lastplayed |
| All games | allgames | auto-allgames |
| Multiplayer | multiplayer | auto-multiplayer |
| Lightgun | lightgun | auto-lightgun |
| Tate | tate | auto-tate |
| Dial | dial | auto-dial |
| Trackball | trackball | auto-trackball |
| Challenges | challenges | auto-challenges |
Arcade nach Hersteller (54) (${system.type} = virtual-arcade)
| System | ${system.name} | ${system} |
|---|---|---|
| Acclaim | arcade-manufacturer-acclaim | Acclaim |
| Atari | arcade-manufacturer-atari | Atari |
| Atlus | arcade-manufacturer-atlus | Atlus |
| Banpresto | arcade-manufacturer-banpresto | Banpresto |
| Capcom Cps1 | arcade-manufacturer-capcom-cps1 | Capcom Cps1 |
| Capcom Cps2 | arcade-manufacturer-capcom-cps2 | Capcom Cps2 |
| Capcom Cps3 | arcade-manufacturer-capcom-cps3 | Capcom Cps3 |
| Capcom | arcade-manufacturer-capcom | Capcom |
| Cave | arcade-manufacturer-cave | Cave |
| Data east | arcade-manufacturer-data east | Data east |
| Exidy | arcade-manufacturer-exidy | Exidy |
| Hng64 | arcade-manufacturer-hng64 | Hng64 |
| Igs | arcade-manufacturer-igs | Igs |
| Irem M72 | arcade-manufacturer-irem-m72 | Irem M72 |
| Irem M92 | arcade-manufacturer-irem-m92 | Irem M92 |
| Irem | arcade-manufacturer-irem | Irem |
| Itech | arcade-manufacturer-itech | Itech |
| Jaleco | arcade-manufacturer-jaleco | Jaleco |
| Kaneko | arcade-manufacturer-kaneko | Kaneko |
| Konami Gx | arcade-manufacturer-konami-gx | Konami Gx |
| Konami | arcade-manufacturer-konami | Konami |
| Midway | arcade-manufacturer-midway | Midway |
| Mitchell | arcade-manufacturer-mitchell | Mitchell |
| Namco Na | arcade-manufacturer-namco-na | Namco Na |
| Namco Nb | arcade-manufacturer-namco-nb | Namco Nb |
| Namco System1 | arcade-manufacturer-namco-system1 | Namco System1 |
| Namco System10 | arcade-manufacturer-namco-system10 | Namco System10 |
| Namco System11 | arcade-manufacturer-namco-system11 | Namco System11 |
| Namco System12 | arcade-manufacturer-namco-system12 | Namco System12 |
| Namco System18 | arcade-manufacturer-namco-system18 | Namco System18 |
| Namco System2 | arcade-manufacturer-namco-system2 | Namco System2 |
| Namco | arcade-manufacturer-namco | Namco |
| Neogeo | arcade-manufacturer-neogeo | Neogeo |
| Nichibutsu | arcade-manufacturer-nichibutsu | Nichibutsu |
| Nintendo | arcade-manufacturer-nintendo | Nintendo |
| Nmk | arcade-manufacturer-nmk | Nmk |
| Psikyo | arcade-manufacturer-psikyo | Psikyo |
| Raizing | arcade-manufacturer-raizing | Raizing |
| Sammy | arcade-manufacturer-sammy | Sammy |
| Sega Stv | arcade-manufacturer-sega-stv | Sega Stv |
| Sega System16 | arcade-manufacturer-sega-system16 | Sega System16 |
| Sega System18 | arcade-manufacturer-sega-system18 | Sega System18 |
| Sega System32 | arcade-manufacturer-sega-system32 | Sega System32 |
| Sega | arcade-manufacturer-sega | Sega |
| Seibu | arcade-manufacturer-seibu | Seibu |
| Seta | arcade-manufacturer-seta | Seta |
| Snk | arcade-manufacturer-snk | Snk |
| Taito F3 | arcade-manufacturer-taito-f3 | Taito F3 |
| Taito Gnet | arcade-manufacturer-taito-gnet | Taito Gnet |
| Taito | arcade-manufacturer-taito | Taito |
| Technos | arcade-manufacturer-technos | Technos |
| Tecmo | arcade-manufacturer-tecmo | Tecmo |
| Toaplan | arcade-manufacturer-toaplan | Toaplan |
| Visco | arcade-manufacturer-visco | Visco |
Nach Genre (56) (${system.type} = virtual)
| System | ${system.name} | ${system} |
|---|---|---|
| Action | genre-action | Action |
| Platform | genre-actionplatformer | Platform |
| Platform Shooter | genre-actionplatformshooter | Platform Shooter |
| First Person Shooter | genre-actionfirstpersonshooter | First Person Shooter |
| Shoot'em Up | genre-actionshootemup | Shoot'em Up |
| Shoot with Gun | genre-actionshootwithgun | Shoot with Gun |
| Fighting | genre-actionfighting | Fighting |
| Beat'em All | genre-actionbeatemup | Beat'em All |
| Infiltration | genre-actionstealth | Infiltration |
| Battle Royale | genre-actionbattleroyale | Battle Royale |
| Rythm & Music | genre-actionrythm | Rythm & Music |
| Adventure | genre-adventure | Adventure |
| Textual Adventure | genre-adventuretext | Textual Adventure |
| Graphical Adventure | genre-adventuregraphics | Graphical Adventure |
| Visual Novel | genre-adventurevisualnovels | Visual Novel |
| Interactive Movie | genre-adventureinteractivemovie | Interactive Movie |
| Real Time 3D Adventure | genre-adventurerealtime3d | Real Time 3D Adventure |
| Survival | genre-adventuresurvivalhorror | Survival |
| RPG | genre-rpg | RPG |
| Action RPG | genre-rpgaction | Action RPG |
| MMORPG | genre-rpgmmo | MMORPG |
| Dungeon Crawler | genre-rpgdungeoncrawler | Dungeon Crawler |
| Tactical RPG | genre-rpgtactical | Tactical RPG |
| JRPG | genre-rpgjapanese | JRPG |
| Party based RPG | genre-rpgfirstpersonpartybased | Party based RPG |
| Simulation | genre-simulation | Simulation |
| Build & Management | genre-simulationbuildandmanagement | Build & Management |
| Life Simulation | genre-simulationlife | Life Simulation |
| Fishing & Hunting | genre-simulationfishandhunt | Fishing & Hunting |
| Vehicle Simulation | genre-simulationvehicle | Vehicle Simulation |
| Science Fiction Simulation | genre-simulationscifi | Science Fiction Simulation |
| Strategy | genre-strategy | Strategy |
| eXplore, eXpand, eXploit & eXterminate | genre-strategy4x | eXplore, eXpand, eXploit & eXterminate |
| Artillery | genre-strategyartillery | Artillery |
| Auto-battler | genre-strategyautobattler | Auto-battler |
| Multiplayer Online Battle Arena | genre-strategymoba | Multiplayer Online Battle Arena |
| Real Time Strategy | genre-strategyrts | Real Time Strategy |
| Turn Based Strategy | genre-strategytbs | Turn Based Strategy |
| Tower Defense | genre-strategytowerdefense | Tower Defense |
| Wargame | genre-strategywargame | Wargame |
| Sports | genre-sports | Sports |
| Racing | genre-sportracing | Racing |
| Sport Simulation | genre-sportsimulation | Sport Simulation |
| Competition Sport | genre-sportcompetitive | Competition Sport |
| Fighting/Violent Sport | genre-sportfight | Fighting/Violent Sport |
| Pinball | genre-pinball | Pinball |
| Board game | genre-board | Board game |
| Casual game | genre-casual | Casual game |
| Digital Cards | genre-digitalcard | Digital Cards |
| Puzzle & Logic | genre-puzzleandlogic | Puzzle & Logic |
| Multiplayer Party Game | genre-party | Multiplayer Party Game |
| Trivia | genre-trivia | Trivia |
| Casino | genre-casino | Casino |
| Multi Game Compilation | genre-compilation | Multi Game Compilation |
| Demo from Demo Screne | genre-demoscene | Demo from Demo Screne |
| Educative | genre-educative | Educative |
Die Schriften
Die der Maschine, und deine.
Eine Schrift kommt von zwei Orten, und das ändert, was mitgeliefert werden muss.
Die der Maschine
<fontPath>:/ubuntu_condensed.ttf</fontPath>
Das :/ bezeichnet die Ressourcen von EmulationStation. Nichts zu kopieren in dein Theme: die Datei ist auf jeder Recalbox vorhanden.
Deine
<fontPath>${root}/data/fonts/Exo2.otf</fontPath>
Die Datei muss in deinem Theme sein und wird mit ihm ausgeliefert. TTF und OTF funktionieren.
Die Größe
fontSize wechselt die Einheit je nach Wert: unter 1 ist es ein Anteil der Bildschirmhöhe; ab 1 sind es Pixel. Alles wird in Verhältnis, Prozent oder Pixel erklärt. Bevorzuge das Verhältnis: 0.045 ergibt überall denselben Anteil.
Die Pixelschriften
f8bitfortressplus ist nur bei Größen scharf, die Vielfache von 7 in Pixeln sind. Zwischen diesen Werten verschmiert sie. Das gilt für jede Pixel für Pixel gezeichnete Schrift: wenn du eine hinzufügst, prüfe, bei welchen Größen sie sauber ist.
fontStyle
normal · bold · italic · bolditalic
⚠️ Funktioniert nur, wenn die Schrift diese Schnitte enthält. Eine Schrift, die als einzelne „Regular“-Datei geliefert wird, wird nicht fett: du musst die „Bold“-Datei liefern und sie mit einem eigenen fontPath bezeichnen.
Mit Recalbox ausgeliefert
Nichts in dein Theme zu kopieren.
| Schrift | Schreibweise |
|---|---|
| Ubuntu Condensed | :/ubuntu_condensed.ttf |
| DejaVu Sans Condensed | :/dejavusanscondensed.ttf |
| Ubuntu Mono | :/UbuntuMonoR.ttf |
| 8-bit Fortress Plus — Pixelschrift, scharf in Vielfachen von 7 | :/f8bitfortressplus.ttf |
Was eine Option ist
Dem Benutzer deines Themes Auswahlmöglichkeiten bieten.
Eine Option ist eine Eigenschaft, die der Benutzer in den Menüs seiner Maschine findet: „Themefarben: Blau / Grün / Rot“, „Retro-Filter: CRT / Scanlines / keiner“.
Sie ist es, die ein reiches Theme von einem starren unterscheidet.
Was eine Option wirklich tut
Eine Option ist kein „zeigen oder verstecken“-Schalter. Sie ist eine Datei, die über das Theme geladen wird und neu definiert, was sie will: eine Farbe, eine Schrift, ein ganzes Layout.
Bei den offiziellen Themes ändert die große Mehrheit der Optionen nur Farben — oft durch das Neudefinieren einer einfachen <variable>. Ausblendungen sind selten.
Eine Option wirkt auf alle Ansichten zugleich: sie ist eine Eigenschaft des Themes, nicht einer Ansicht.
Die Optionen des offiziellen Themes
recalbox-next-2025 bietet zehn davon, was eine gute Vorstellung davon gibt, was üblich ist:
| Option | Was sie ändert |
|---|---|
systemView | das Layout der Systemliste |
gameList | das der Spieleliste |
gameclipview | das des Bildschirmschoners |
SysInfos | die Informationen der Systeme: vollständig, minimal, versteckt |
gameInfos | die Informationen der Spiele |
colorTheme | 12 Farbpaletten |
shader | der Retro-Filter: CRT, Scanlines, Wabe, keiner |
shadow | die Schattierung |
bands | die Farbbänder: dünn, dick, keine |
iconesetTheme | die Symbole der Hilfeleiste: 8 Sätze |
Eine Option deklarieren: <subset>
Die genaue Syntax, in zwei Schritten.
Eine Option wird in zwei Schritten gebaut: man deklariert sie, dann listet man ihre Auswahlmöglichkeiten auf.
1. Die Gruppe deklarieren
<subset subset="colorTheme"
title="THEME : Colors" title.fr="THÈME : Couleurs"
help="Choose the color set" help.fr="Choisissez la palette" />
| Attribut | Aufgabe |
|---|---|
subset | die Kennung der Gruppe — sie verbindet die Auswahlmöglichkeiten miteinander |
title | die Beschriftung, die der Benutzer im Menü liest |
help | der Erklärungssatz unter der Beschriftung |
title und help akzeptieren ein Sprachsuffix: title.fr, title.es… Die Version ohne Suffix dient als Rückfall.
2. Die Auswahlmöglichkeiten auflisten
Jede Auswahl ist ein <include>, das dasselbe subset trägt:
<include subset="colorTheme" name="Blue" name.fr="Bleu">${root}/options/couleurs/bleu.xml</include>
<include subset="colorTheme" name="Green" name.fr="Vert">${root}/options/couleurs/vert.xml</include>
<include subset="colorTheme" name="Red" name.fr="Rouge">${root}/options/couleurs/rouge.xml</include>
| Attribut | Aufgabe |
|---|---|
subset | zu welcher Gruppe diese Auswahl gehört |
name | die Beschriftung der Auswahl in der Liste (übersetzbar: name.fr) |
Die Auswahl „keine“
Ein leeres <include> ergibt die Option „keine“ — nützlich, um das Theme in seinem Ursprungszustand zu lassen:
<include subset="shader" name="None" name.fr="Aucun"></include>
Eine Auswahl nur auf bestimmten Bildschirmen anbieten
<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>
Der Benutzer sieht nur die für seine Hardware relevanten Auswahlmöglichkeiten. So erscheint der Retro-Filter nicht auf einem Röhrenbildschirm, der ihn nicht braucht.
Der Inhalt einer Auswahldatei
Es ist eine gewöhnliche Theme-Datei, die nur neu definiert, was sich ändert:
<?xml version="1.0" encoding="UTF-8"?>
<theme>
<variables>
<variable name="CouleurPrincipale" value="7C2E44" />
</variables>
</theme>
Drei nützliche Zeilen, und das ganze Theme wird rot — vorausgesetzt, das Theme wurde auf Variablen aufgebaut statt auf fest geschriebenen Farben.
⚠️ Die Optionen werden VOR den Ansichten geladen. Eine Variable gilt für das, was nach ihr gelesen wird: dort muss die Auswahl durchlaufen, damit die Ansichten davon profitieren.
Die Reihenfolge, die in theme.xml zu schreiben ist:
<theme name="Mon Thème" …>
<include>${root}/variables.xml</include> <!-- 1. die Standardwerte -->
<include>${root}/options.xml</include> <!-- 2. die Auswahl definiert sie neu -->
<include>${root}/views/system.xml</include> <!-- 3. die Ansichten, die sie verwenden -->
</theme>
Was passiert, wenn die Person die Auswahl wechselt
Recalbox liest das ganze Theme neu — alle Dateien, ab theme.xml, mit der neuen aktiven Auswahl. Deshalb erscheint in diesem Moment kurz „Theme wird aktualisiert…“.
Eine Option, die nur Farben ändert, definiert ausschließlich Variablen neu. Das ist die kürzeste Art, einen Farbsatz zu schreiben — und der Hauptgrund,
<variables>zu verwenden.
Die Reihenfolge der Auswahlmöglichkeiten
Das Zahlenpräfix — und warum es nicht angezeigt wird.
Recalbox behält nicht die Reihenfolge, in der du deine <include> schreibst. Es sortiert die Auswahlmöglichkeiten selbst, in zwei Schritten:
- wenn ALLE Auswahlmöglichkeiten eine Zahl am Anfang ihres
namehaben, sortiert es nach dieser Zahl; - sonst sortiert es nach der alphabetischen Reihenfolge des
name.
Deshalb nummeriert man.
Die richtige Schreibweise
<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>
Das Präfix wird nicht angezeigt. Recalbox erkennt es, nutzt es zum Sortieren, und entfernt es dann, bevor die Beschriftung angezeigt wird. Der Benutzer liest „Dünn“, „Dick“, „Keine“.
Was die Engine als Präfix akzeptiert
Eine Zahl, dann ein Leerzeichen, ein Bindestrich oder ein Punkt, alles innerhalb der ersten acht Zeichen. Diese drei Schreibweisen funktionieren:
1 - Dünn
1. Dünn
1 Dünn
Die Falle
Die numerische Sortierung wird nur verwendet, wenn alle Auswahlmöglichkeiten nummeriert sind. Ein einziges Versäumnis, und Recalbox fällt auf die alphabetische Reihenfolge zurück — deine Auswahlmöglichkeiten ordnen sich von selbst neu, ohne Meldung.
<include subset="bands" name="1 - Fines">…</include>
<include subset="bands" name="2 - Épaisses">…</include>
<include subset="bands" name="Aucune">…</include> <!-- ❌ bricht die Sortierung aller drei -->
Der Name der DATEIEN hingegen ist frei
Nicht zu verwechseln: die Zahl gehört in das Attribut name, nicht in den Dateinamen. Recalbox schaut nie, wie deine Datei heißt.
options/
bandes/
fines.xml ← benenne sie, wie du willst
epaisses.xml
aucune.xml
Was zählt: ein Ordner pro Option, und Namen, die sagen, was sie tun.
Sein Theme ausprobieren
Es auf die Maschine kopieren, und im Problemfall das Protokoll lesen.
Ein Theme lässt sich nur eingeschaltet, auf einem Bildschirm beurteilen. Die Vorschau des Studios ist originalgetreu, aber nichts ersetzt die Maschine.
- das Theme aus dem Studio exportieren;
- den Ordner nach
/recalbox/share/themes/kopieren; - auf der Maschine: Menü → Eigenschaften der Oberfläche → Theme, und deines auswählen.
📄 Das Protokoll: themes.log
Das ist die erste Stelle, an der man nachsieht, wenn etwas nicht erscheint.
/recalbox/share/system/logs/themes.log
Es vermerkt, mit Datei und Zeile als Beleg:
- eine unbekannte Eigenschaft — oft ein Fehler bei der Groß- und Kleinschreibung (
keepRatiostattkeepratio); - eine Komponente, deren Typ nicht existiert;
- ein
Extra type unknown: …— du hast alsextraeinen Typ gesetzt, der das nicht akzeptiert; - eine Variable ohne
nameoder ohnevalue; - ein falsch geschriebenes Paar (
pos,size).
Eine unsichtbare Komponente ohne Eintrag im Protokoll bedeutet fast immer, dass extra="true" fehlt: siehe Die wichtigste Regel.
Dein Theme teilen
Dein Theme dem Theme-Manager von Recalbox vorschlagen — direkt aus dem Studio.
Recalbox hat einen Theme-Manager: Themes werden dort installiert, ohne dass man etwas von Hand kopiert. Damit ein Theme hineinkommt, muss man es vorschlagen — und alles geschieht aus diesem Studio heraus.
📘 Kein Repository zum Forken, keine Merge Request, keine Datei zum Schreiben: Das Studio baut das Paket, macht die Bildschirmfotos und reicht alles für dich ein.
Dein Theme vorschlagen
Zwei Wege, ein Ziel:
- Dein Theme liegt im Studio — Schaltfläche „Exportieren oder veröffentlichen“, Reiter „Veröffentlichen“;
- Dein Theme ist ein Ordner oder eine ZIP-Datei — „Bestehendes Theme importieren“, dann „zur Veröffentlichung vorschlagen“.
In beiden Fällen beschreibst du dein Theme in wenigen Feldern und schickst es ab. Das war's.
⚠️ Du brauchst das Recht dazu. Ein Theme vorzuschlagen steht den Rollen offen, die das Team im Recalbox-Discord bestimmt hat: Fehlt die Schaltfläche, fehlt dir das Recht.
Was das Studio für dich erledigt
- es fotografiert dein Theme — jede Ansicht, jede angekündigte Auflösung und ein Bild je Theme-Option — und behält die besten;
- es schreibt die Themenkarte (Name, Version, Autor, Beschreibung, unterstützte Bildschirme);
- es lädt die Dateien hoch und sagt allen Bescheid.
⚠️ Dein Archiv geht unverändert raus. Das Studio schaut nur hinein, um die Bildschirmfotos zu machen; es schreibt es nie um. Deine Ordner, deine Dateinamen, deine Struktur: nichts verrutscht.
Was danach passiert
- dein Theme geht zur Abstimmung — die Theme-Macher sehen es sich Bildschirm für Bildschirm an und geben ihre Meinung ab, auf Wunsch mit ein paar Worten;
- eine Administratorin oder ein Administrator entscheidet. Die Abstimmung erhellt, sie entscheidet nicht: Niemand wird durch eine Zählung veröffentlicht;
- angenommen landet das Theme im Theme-Manager und jede Recalbox sieht es. Abgelehnt bekommst du eine Begründung — genug, um nachzubessern und erneut vorzuschlagen.
Verfolgen kannst du alles unter „Meine Themes“: Auf der Karte deines Themes steht „Anfrage offen“, und der Reiter „Veröffentlichen“ zeigt die Stimmen und die Rückmeldungen (ohne Namen).
Ein veröffentlichtes Theme aktualisieren
Derselbe Weg: Schlage dein Theme erneut vor, mit einer höheren Versionsnummer.
⚠️ Eine Aktualisierung geht nicht noch einmal durch die Abstimmung — es ist dein Theme, du kennst seinen Zustand. Sie wartet nur darauf, online gestellt zu werden. Niemand muss etwas neu installieren: Der Theme-Manager bietet die Aktualisierung an.
Woran es scheitert
- ein unvollständiges Theme: eine leere Ansicht, ein angekündigter Bildschirm ohne Arbeit daran;
- Bilder, die dir nicht gehören;
- eine Kompatibilität, die nie ausprobiert wurde — CRT anzukündigen, ohne es je auf einer Röhre gesehen zu haben, fällt sofort auf.