# EmulationStation Desktop Edition (ES-DE) v2.0 (development version) - Themes **Note:** This document is only relevant for the current ES-DE development version, if you would like to see the documentation for the latest stable release, refer to [THEMES.md](THEMES.md) instead. If creating theme sets specifically for ES-DE, please add `-DE` to the theme name, as in `slate-DE`. Because ES-DE theme functionality has deviated greatly from the RetroPie EmulationStation fork on which it was originally based, any newer themes will not work on such older forks. It would be confusing and annoying for users that attempt to use ES-DE theme sets in older EmulationStation forks as they would get unthemed systems, crashes, error messages or corrupted graphics. At least the -DE extension is a visual indicator that it's an ES-DE specific theme set. It's recommended to use a proper code editor for theme development, such as [VSCode](https://code.visualstudio.com) with the [Red Hat XML extension](https://github.com/redhat-developer/vscode-xml). Table of contents: [[_TOC_]] ## Introduction ES-DE allows the grouping of themes for multiple game systems into a **theme set**. A theme is a collection of **elements**, each with their own **properties** that define the way they look and behave. These elements include things like text lists, carousels, images and animations. Internally ES-DE uses the concept of **components** to actually implement the necessary building blocks to parse and render the elements, and although this is normally beyond the scope of what a theme author needs to consider, it's still good to be aware of the term as it's sometimes used in the documentation. Every game system has its own subdirectory within the theme set directory structure, and these are defined in the systems configuration file `es_systems.xml` either via the optional `` tag, or otherwise via the mandatory `` tag. When ES-DE populates a system on startup it will look for a file named `theme.xml` in each such directory. By placing a theme.xml file directly in the root of the theme set directory, that file will be processed as a default if there is no system-specific theme.xml file available. In the example below, we have a theme set named `mythemeset-DE` which includes the `snes` and `nes` systems. Assuming you have some games installed for these systems, the files `mythemeset-DE/nes/theme.xml` and `mythemeset-DE/snes/theme.xml` will be processed on startup. If there are no games available for a system, its theme.xml file will be skipped. The directory structure of our example theme set could look something like the following: ``` ... themes/ mythemeset-DE/ core/ font.ttf bold_font.ttf frame.png nes/ theme.xml background.jpg logo.svg logo_video.svg snes/ theme.xml background.jpg logo.svg logo_video.svg fonts.xml theme.xml ``` The theme set approach makes it easy for users to install different themes and choose between them from the _UI Settings_ menu. There are two places that ES-DE can load theme sets from: * `[HOME]/.emulationstation/themes/[THEME_SET]/` * `[INSTALLATION PATH]/themes/[THEME_SET]/` An example installation path would be: \ `/usr/share/emulationstation/themes/slate-DE/` If a theme set with the same name exists in both locations, the one in the home directory will be loaded and the other one will be skipped. ## Differences to legacy RetroPie themes If you are not familiar with theming for RetroPie or similar forks of EmulationStation you can skip this section as it only describes the key differences between the updated ES-DE themes and these _legacy_ theme sets. The term _legacy_ is used throughout this document to refer to this older style of themes which ES-DE still fully supports for backward compatibility reasons. The old theme format is described in [THEMES-LEGACY.md](THEMES-LEGACY.md) although this document is basically a historical artifact by now. With ES-DE v2.0 a new theme engine was introduced that fundamentally changed some aspects of how theming works. The previous design used specific view styles (basic, detailed, video and grid) and this was dropped completely and replaced with _variants_ that can accomplish the same thing while being much more powerful and flexible. In the past EmulationStation basically had hardcoded view styles with certain elements always being present and only a limited ability to manipulate these via positioning, resizing, coloring etc. As well so-called _extras_ were provided to expand theming support somehow but even this was quite limited. With the new theme engine the view presets were removed and the only views now available, _system_ and _gamelist_, were rewritten to be much more flexible. Essentially the element selection and placement is now unlimited; any number of elements of any type can be used, although with a few notable exceptions as explained throughout this document. In addition to _variants_, support for _color schemes_ and _aspect ratios_ was introduced. The former makes it possible to provide different color profiles via variable declarations, and the latter makes it possible to define different theme configurations for different display aspect ratios. That could for example be a choice between a 16:9 and a 4:3 layout, and perhaps also a vertical screen orientation layout. All these options are selectable via the _UI Settings_ menu. New theming abilities like GIF and Lottie animations were also added to the new theme engine. The NanoSVG rendering library has been replaced with [LunaSVG](https://github.com/sammycage/lunasvg) which greatly improves SVG file support as NanoSVG had issues with rendering quite some files. There might be some slight regressions with LunaSVG, but most of these are probably due to issues in NanoSVG that caused some non-conformant files to render seemingly correct. Make sure to compare any SVG files that don't seem to render correctly in ES-DE with what they look like if opened in for example Firefox or Chrome/Chromium. As for more specific changes, the following are the most important ones compared to legacy themes: * View styles are now limited to only _system_ and _gamelist_ (there is a special _all_ view style as well but that is only used for navigation sounds as explained later in this document) * The hardcoded metadata attributes like _md_image_ and _md_developer_ are gone, but a new `` property is available for populating views with metadata information * The concept of _extras_ is gone as all element can now be used however the theme author wishes * The concept of _features_ is gone * The `` tag is gone as tracking theme versions doesn't make much sense after all * The `video` element properties `showSnapshotNoVideo` and `showSnapshotDelay` have been removed * The ambiguous `alignment` property has been replaced with the `horizontalAlignment` and `verticalAlignment` properties (the same is true for `logoAlignment` for the `carousel` element) * The `forceUppercase` property has been replaced with the more versatile `letterCase` property * Many property names for the carousel have been renamed, with _logo_ being replaced by _item_ as this element can now be used in both the gamelist and system views. As well, setting the alignment will not automatically add any margins as is the case for legacy themes. These can still be set manually using the `horizontalOffset` and `verticalOffset` properties if needed. The way that alignment works in general for both carousel items and the overall carousel has also changed * The rating elements were previously not sized and overlaid consistently, this has now been fixed and rating images should now be centered on the image canvas in order for this element to render correctly rather than being left-adjusted as has previously been done by some theme authors (likely as a workaround for the previous buggy implementation). Images of any aspect ratios are now also supported where previously only square images could be used * The carousel text element hacks `systemInfo` and `logoText` have been removed and replaced with proper carousel properties * The carousel property `maxItemCount` (formerly named maxLogoCount) is now in float format for more granular control of logo placement compared to integer format for legacy themes. However some legacy theme authors thought this property supported floats (as the theme documentation incorrectly stated this) and have therefore set it to fractional values such as 3.5. This was actually rounded up when loading the theme configuration, and this logic is retained for legacy themes for backward compatibility. But for current themes the float value is correctly interpreted which means a manual rounding of the value is required in order to retain an identical layout when porting theme sets to the new theme engine. As well carousels of the wheel type now have the amount of entries controlled by the two new properties `itemsBeforeCenter` and `itemsAfterCenter`. This provides more exact control, including the ability to setup asymmetric wheels. * The full names of unthemed systems (or systems where the defined staticImage file is missing) will now be displayed in the system carousel instead of the short names shown for legacy themes. So for instance, instead of "cps" the full name "Capcom Play System" (as defined in es_systems.xml) will be displayed. * The carousel now has a zIndex value of 50 instead of 40. This means it's aligned with the textlist element which already had a zIndex value of 50. * The textlist property `selectorOffsetY` has been renamed to `selectorVerticalOffset` and a `selectorHorizontalOffset` property has been added as well. * The helpsystem `textColorDimmed` and `iconColorDimmed` properties (which apply when opening a menu) were always defined under the system view configuration which meant these properties could not be separately set for the gamelist views. Now these properties work as expected with the possibility to configure separate values for the system and gamelist views * When right-aligning the helpsystem using an X origin value of 1, the element is now aligned correctly to the defined position instead of being offset by the entrySpacing width (in RetroPie ES the offset was instead the hardcoded element entry padding) * Correct theme structure is enforced more strictly than before, and deviations will generate error log messages and make the theme loading fail * Many additional elements and properties have been added, refer to the [Reference](THEMES-DEV.md#reference) section for more information Attempting to use any of the legacy logic in the new theme structure will make the theme loading fail, for example adding the _extra="true"_ attribute to any element. Except the points mentioned above, theme configuration looks pretty similar to the legacy theme structure, so anyone having experience with these older themes should hopefully feel quite at home with the new theme engine. Probably the most important thing to keep in mind is that as there are no longer any view presets available, some more effort is needed from the theme developer to define values for some elements. This is especially true for zIndex values as elements could now be hidden by other elements if care is not taken to explicitly set the zIndex for each of them. This additional work is however a small price to pay for the much more powerful and flexible theming functionality provided by the new theme engine. Note that the legacy theme engine had quite inaccurate text sizing and font rendering and while this has been greatly improved in the new engine, for legacy themes most old bugs are retained for maximum backward compatibility. This means that you may need to revise font sizes and text placements when porting a legacy theme to the new engine. Here are some examples: * Line spacing for the textlist element was not consistently applied across different screen resolutions * Carousel text entries did not multiply the font size by the itemScale (logoScale) property value * The defined line spacing was not always applied for automatically sized text elements * Font sizes were rounded to integers, leading to imprecise text sizing across different resolutions (the rounding was also done incorrectly) ## Simple example Here is a very simple theme that changes the color of the game name text: ```xml 00FF00 0.5 0.5 0.5 0.5 0.8 0.8 ./core/frame.png 10 ``` ## How it works All configuration must be contained within a `` tag pair. That is true for each separate .xml file used to build the completely theme set. The `` tag pair refers to the available views within ES-DE, which is either _system_ or _gamelist_. There is a special _all_ view available as well, but that is only used for defining the navigation sounds as these are always applied globally to both view types. Views are defined like this: ```xml ... define elements here ... ``` An element is a particular visual component such as an image, an animation or a piece of text. It has a mandatory _name_ attribute which is used by ES-DE to track each element entry. By using this name attribute it's possible to split up the definition of an element to different locations. For example you may want to define the color properties separately from where the size and position are configured (see the example below). The name attribute can be set to any string value. This is the element structure: ```xml ... define properties here ... ``` Finally _properties_ control how a particular element looks and behaves, for example its position, size, image path, animation controls etc. The property type determines what kinds of values you can use. You can read about each type below in the [Reference](THEMES-DEV.md#reference) section. Properties are defined like this: ```xml ValueHere ``` Let's now put it all together. The following is a simple example of a text element which has its definition split across two separate XML files. `themes.xml`: ```xml 0.27 0.32 0.5 0.5 0.12 0.41 40 ``` `colors.xml`: ```xml 707070 ``` As long as the name attribute is identical, the element configuration will be combined automatically. But that is only true for elements of the same type, so for instance an image element could be defined that also uses _system_name_ for its name attribute without colliding with the text element: ```xml 0.27 0.32 0.5 0.5 0.12 0.41 40 0.49 0.8 0.4 0.28 35 ``` Whether this is a good idea is another question, it would probably be better to set the name attribute for the image to _system_logo_ or similar for this example. In addition to this, if the name is used for the same element type but for different views, then there will also not be any collision: ```xml 0.04 0.73 0.5 0.5 0.12 0.22 40 0.27 0.32 0.5 0.5 0.12 0.41 40 ``` ## Debugging during theme development If you are writing a theme it's recommended to launch ES-DE with the `--debug` flag from a terminal window. You can also pass the `--resolution` flag to avoid having the application window fill the entire screen. By doing so, you can read error messages directly in the terminal window without having to open the es_log.txt file. You can also reload the current gamelist or system view with `Ctrl+r` if the `--debug` flag has been set. There is also support for highlighting the size and position of each image and animation element by using the `Ctrl+i` key combination and likewise to highlight each text element by using the `Ctrl+t` keys. Again, both of these require that ES-DE has been launched with the `--debug` command line option, for example: ``` emulationstation --debug --resolution 1280 720 ``` Enforcement of a correct theme configuration is quite strict, and most errors will abort the theme loading, leading to an unthemed system. In each such situation the log output will be very clear of what happened, for instance: ``` Jan 28 17:17:30 Error: ThemeData::parseElement(): "/home/myusername/.emulationstation/themes/mythemeset-DE/theme.xml": Property "origin" for element "image" has no value defined (system "collections", theme "custom-collections") ``` Note that an unthemed system means precisely that, the specific system where the error occured will be unthemed but not necessarily the entire theme set. The latter can still happen if the error is global such as a missing variable used by all XML files or an error in a file included by all XML files. The approach is to only untheme relevant sections of the theme set to be able to pinpoint precisely where the problem lies. Sanitization for valid data format and structure is done in this manner, but verification that property values are actually correct (or reasonable) is handled by the individual component that takes care of creating and rendering the specific theme element. What happens in many instances is that a warning log entry is created and the invalid property is reset to its default value. So for these situations, the system will not become unthemed. Here's an example where a badges element accidentally had its horizontalAlignment property set to _leftr_ instead of _left_: ``` Jan 28 17:25:27 Warn: BadgeComponent: Invalid theme configuration, property "horizontalAlignment" for element "gamelist_badges" defined as "leftr" ``` Note however that warnings are not printed for all invalid properties as that would lead to an excessive amount of logging code. This is especially true for numeric values which are commonly just clamped to the allowable range without notifying the theme author. So make sure to check the [Reference](THEMES-DEV.md#reference) section of this document for valid values for each property. For more serious issues where it does not make sense to assign a default value or auto-adjust the configuration, an error log entry is generated and the element will in most instances not get rendered at all. Here's such an example where the imageType property for a video element was accidentally set to _covr_ instead of _cover_: ``` Jan 28 17:29:11 Error: VideoComponent: Invalid theme configuration, property "imageType" for element "gamelist_video" defined as "covr" ``` Error handling for missing files is handled a bit differently depending on whether the paths have been defined explicitly or via a variable. For explicitly defined paths a warning will be logged for element properties and an error will be triggered for include files. Here's an example of the latter case: ``` Jan 28 17:32:29 Error: ThemeData::parseIncludes(): "/home/myusername/.emulationstation/themes/mythemeset-DE/theme.xml" -> "./colors_dark.xml" not found (resolved to "/home/myusername/.emulationstation/themes/mythemeset-DE/colors_dark.xml") ``` However, if a variable has been used to define the include file, only a debug message will be generated if the file is not found: ``` Jan 28 17:34:03 Debug: ThemeData::parseIncludes(): "/home/myusername/.emulationstation/themes/mythemeset-DE/theme.xml": Couldn't find file "./${system.theme}/colors.xml" which resolves to "/home/myusername/.emulationstation/themes/mythemeset-DE/amiga/colors.xml" ``` It works essentially the same way for element path properties as for include files. This distinction between explicit values and variables makes it possible to create a theme configuration where both include files and files for fonts, images, videos etc. will be used if found, and if not found a fallback configuration can still be applied so the system will be themed. By default all debug messages regarding missing files will be logged for regular systems and automatic collections and suppressed for custom collections. This behavior can be changed by modifying the _DebugSkipMissingThemeFiles_ and _DebugSkipMissingThemeFilesCustomCollections_ settings in es_settings.xml. You can read more about those settings [here](INSTALL-DEV.md#settings-not-configurable-via-the-gui). ## Variants A core concept of ES-DE is the use of theme set _variants_ to provide different theme profiles. These are not fixed presets and a theme author can instead name and define whatever variants he wants for his theme (or possibly use no variants at all as they are optional). The variants could be purely cosmetic, such as providing different designs for a theme set, or they could provide distinctive functionality by for instance using different primary elements like a carousel or a text list. Before a variant can be used it needs to be declared, which is done in the `capabilities.xml` file that must be stored in the root of the theme set directory tree. How to setup this file is described in detail later in this document. The use of variants is straightforward, a section of the configuration that should be included for a certain variant is enclosed inside the `` tag pair. This has to be placed inside the `` tag pair, and it can only be used at this level of the hierarchy and not inside a `` tag pair for example. The mandatory _name_ attribute is used to specificy which variants to use, and multiple variants can be specified at the same time by separating them by commas or by whitespace characters (tabs, spaces or line breaks). It's also possible to use the special _all_ variant that will apply the configuration to all defined variants (although this is only a convenient shortcut and you can explicitly define every variant individually if you prefer that). Note that _all_ is a reserved name and attempting to use it in the capabilities.xml file will trigger a warning on application startup. It could sometimes be a good idea to separate the variant configuration into separate files that are then included from the main theme file as this could improve the structure and readability of the theme set configuration. It's also possible to apply only portions of the theme configuration to the variants and keep a common set of elements that are shared between all variants. This is accomplished by simply adding the shared configuration without specifying a variant, as is shown in the first example below for the `info_text_01` text element. Just be aware that the variant-specific configuration will always be loaded after the general configuration even if it's located above the general configuration in the XML file. Alternatively you could use the special _all_ variant to define common configuration used by all variants in the theme set. Here are some example uses of the `` functionality: ```xml ./gamelist_textlist.xml ./gamelist_carousel.xml ./${system.theme}/systeminfo.xml ./core/font.ttf 0.035 0.3 0.56 ``` ```xml titlescreen true 42 ``` ```xml titlescreen true 42 ``` ## Variant triggers (overrides) Variant triggers is an optional feature which can be used to replicate the automatic view style switching functionality of the legacy theme engine. This can be used to automatically override the selected variant based on two triggers, either when there are no game videos found for a system, or if there are no game media files of some specified types found for a system. These two trigger types are named `noVideos` and `noMedia` respectively. For the `noMedia` trigger there's an optional `mediaType` tag that can be used to specify precisely which media files should be checked for to determine whether to switch to the override variant. Valid values are `miximage`, `marquee`, `screenshot`, `titlescreen`, `cover`, `backcover`, `3dbox`, `physicalmedia`, `fanart` and `video`. Multiple values can be defined, in which case they are separated by a comma, or by a whitespace character (tab, space or line break). If no value is defined, it will be set to `miximage`. The `useVariant` tag specifies which variant to use to override the selected variant. You'll probably rarely need to use the `noVideos` trigger as `video` can be defined also when using the `noMedia` trigger. The reason for including both trigger types is that it makes it possible to apply a specific variant only when videos are missing and another variant when no media files at all are present. The following example (from the `capabilities.xml` file) defines a `noGameMedia` variant which is used as the override for the `withVideos` variant if no miximages, screenshots, covers and videos are found for any game in a system. For this example the `noGameMedia` variant has been set as non-selectable from the _UI Settings_ menu. As can be seen here, the overall variant trigger configuration needs to be enclosed within an `override` tag pair ```xml true noMedia miximage, screenshot, cover, video noGameMedia false ``` Note that variant triggers will only apply to the gamelist view and not the system view. Also be aware that it will add a potentially noticeable application slowdown as game media files need to be scanned for at various points when using the application, as well as during startup. The impact of the performance penalty depends on multiple factors such as the game collection size, how many games have been scraped and disk I/O and filesystem performance. So only use variant triggers if really necessary for your theme design. As well, specifying many values for the `mediaType` tag will lead to more files potentially being scanned which could introduce further lag and latency. ## Color schemes Color schemes are essentially a collection of variables that can be selected between from the _UI Settings_ menu. This makes it possible to define different values that will be applied to the overall theme configuration based on this menu selection. Only variables can be used for the color schemes, but since variables can be used for almost everything this makes the functionality very flexible. In most cases you'll probably want to apply different color values to `` properties and similar, but it's also possible to apply different images, animations, fonts etc. per color scheme. To understand the basics on how to use variables, make sure to read the _Theme variables_ section elsewhere in this document. Before a color scheme can be used it needs to be declared, which is done in the `capabilities.xml` file that must be stored in the root of the theme set directory tree. How to setup this file is described in detail later in this document. The `` tag pair can be placed directly inside the `` tags, inside the `` tags or inside the `` tags. The mandatory name attribute is used to specificy which color scheme to use, and multiple values can be specified at the same time by separating them by commas or by whitespace characters (tabs, spaces or line breaks). Note that the use of color schemes for a theme set is entirely optional. Here's an example configuration: ```xml 404040 F0F0F0 707070 262626 74747488 0 0 1 1 ./core/images/background.png true ${backgroundColor} 0.5 0.6437 1 0.056 ${defaultTextColor} ``` ## Aspect ratios The aspect ratio support works almost identically to the variants and color schemes with the main difference that the available aspect ratios are hardcoded into ES-DE. The theme set can still decide which of the aspect ratios to support (or none at all in which case the theme aspect ratio is left undefined) but it can't create entirely new aspect ratio entries. In the same manner as for the variants and color schemes, the aspect ratios that the theme set provides need to be declared in the `capabilities.xml` file that must be stored in the root of the theme set directory tree. How to setup this file is described in detailed later in this document. The `` tag pair can be placed directly inside the `` tags or inside the `` tags. Once the aspect ratios have been defined, they are applied to the theme configuration like the following examples: ```xml ./../layout_narrow.xml ./../layout_wide.xml ./../layout_ultrawide.xml ./core/font.ttf 0.035 ``` ```xml 0.3 0.56 0.42 0.31 ``` ```xml 0.3 0.56 ``` ```xml 0.3 0.56 ``` ## capabilities.xml Variants, variant triggers, color schemes and aspect ratios need to be declared before they can be used inside the actual theme set configuration files and that is done in the `capabilities.xml` file. This file needs to exist in the root of the theme directory, for example: ``` ~/.emulationstation/themes/mythemeset-DE/capabilities.xml ``` This file type was introduced with the new ES-DE theme engine in v2.0 and is an indicator that the theme set is of the new generation instead of being of the legacy type (i.e. a theme set backward compatible with RetroPie EmulationStation). In other words, if the capabilities.xml file is absent, the theme will get loaded as a legacy set. The structure of the file is simple, it just contains declarations for the variants, color schemes and aspect ratios, such as in this example: ```xml 16:9 4:3 4:3_vertical true noMedia miximage, screenshot, cover, video noGameMedia true noMedia miximage, screenshot, cover noGameMedia false ``` The file format is hopefully mostly self-explanatory; this example provides three aspect ratios, two color schemes and three variants, one of which is a variant trigger override. The `