Merge branch '587-make-the-help-component-more-configurable-by-the-theme' into '575-theme-add-a-modern-clean-switch-like-theme-as-an-official-theme-in-es-de-to-choose-from'

Merge helpsystem PR into modern theme draft.

See merge request leonstyhre/emulationstation-de!9
This commit is contained in:
Sophia Hadash 2021-08-31 11:57:43 +00:00
commit b4a15e2211
155 changed files with 5027 additions and 3973 deletions

View file

@ -66,7 +66,7 @@ CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
@ -119,7 +119,7 @@ SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements

View file

@ -12,6 +12,11 @@
* Added a menu option to change the application exit key combination
* Added the GLM (OpenGL Mathematics) library as a Git subtree
* Replaced all built-in matrix and vector data types and functions with GLM library equivalents
* Replaced some additional math functions and moved the remaining built-in functions to a math utility namespace
* Added a function to generate MD5 hashes
* Changed two clang-format rules related to braced lists and reformatted the codebase
* Changed the language standard from C++14 to C++17
### Bug fixes
@ -293,6 +298,4 @@ Many bugs have been fixed, and numerous features that were only partially implem
* On macOS Big Sur (and possibly other OS versions) when connecting a DualShock 4 controller either via Bluetooth or using a USB cable, two separate controller devices are registered in parallel. This is a bug in either macOS or the DualShock driver and it makes it seem as if ES-DE is registering double button presses when actually two separate controller devices are generating identical input. A workaround if using Bluetooth mode is to plug in the USB cable just after connecting the controller, wait a second or two and then remove the cable again. This will remove the cabled device, leaving only the Bluetooth device active. Another workaround is to enable the setting "Only accept input from first controller" in the ES-DE input device settings. The reason why this bug may not be visible in some other games and applications is that ES-DE enables and auto-configures all connected controllers.
* Some screen tearing can be seen in the upper part of the screen when using the slide transitions with certain graphics drivers and resolutions. This problem will hopefully be resolved in ES-DE v1.2 when moving to the GLM library.
* On Windows when using high DPI displays, if not running ES-DE on the primary monitor and the display where it runs does not have the same scaling percentage as the primary monitor, then the ES-DE resolution will not be properly set. The application will still work and if running in fullscreen mode it may not even be noticeable. This issue is caused by a bug in SDL where the primary display scaling is always used for calculating the display bounds and as such it needs to be fixed in that library. If using the same scaling percentage across all monitors, or if not using high DPI monitors at all, then this issue will not occur.

View file

@ -122,11 +122,11 @@ endif()
# Set up compiler and linker flags for debug, profiling or release builds.
if(CMAKE_BUILD_TYPE MATCHES Debug)
# Enable the C++14 standard and disable optimizations as it's a debug build.
# Enable the C++17 standard and disable optimizations as it's a debug build.
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++14 /Od /DEBUG:FULL")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /Od /DEBUG:FULL")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0")
endif()
# If using Clang, then add additional debug data needed by GDB.
@ -139,18 +139,18 @@ if(CMAKE_BUILD_TYPE MATCHES Debug)
elseif(CMAKE_BUILD_TYPE MATCHES Profiling)
# For the profiling build, we enable optimizations and supply the required profiler flags.
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++14 /O2 /DEBUG:FULL")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /O2 /DEBUG:FULL")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O2 -pg -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O2 -pg -g")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O2 -pg")
endif()
else()
# Enable the C++14 standard and enable optimizations as it's a release build.
# Enable the C++17 standard and enable optimizations as it's a release build.
# This will also disable all assert() macros. Strip the binary too.
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG /std:c++14 /O2 /DEBUG:NONE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG /std:c++17 /O2 /DEBUG:NONE")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O2 -DNDEBUG")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O2 -DNDEBUG")
if(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O2")
else()
@ -196,6 +196,10 @@ if(DEFINED libCEC_FOUND)
add_definitions(-DHAVE_LIBCEC)
endif()
# GLM library options.
add_definitions(-DGLM_FORCE_CXX17)
add_definitions(-DGLM_FORCE_XYZW_ONLY)
# For Unix systems, assign the installation prefix. If it's not explicitly set,
# we use /usr on Linux, /usr/pkg on NetBSD and /usr/local on FreeBSD and OpenBSD.
if(NOT WIN32 AND NOT APPLE)

View file

@ -33,7 +33,7 @@ This plan is under constant review so expect it to change from time to time. Sti
* Improved full-screen support, removing the temporary full-screen hacks
* On-screen keyboard
* Support for the Raspberry Pi 4 with OpenGL ES 2.0 and GLSL shaders (Raspberry Pi OS)
* Add GLM library dependency for matrix and vector operations, start to decommission the built-in functions
* Add GLM library dependency for matrix and vector operations, decommission the built-in functions
* Add to more Linux repositories, BSD ports collections etc.
* Flatpak and Snap releases on Linux
@ -90,6 +90,7 @@ But as clang-format won't change the actual code content or fix all code style c
* As a general rule, use C++ syntax instead of C syntax, for example `static_cast<int>(someFloatVariable)` instead of `(int)someFloatVariable`
* Always declare one variable per line, never combine multiple declarations of the same type
* Name member variables starting with an `m` such as `mMyMemberVariable` and name static variables starting with an `s` such as `sMyStaticVariable`
* Use braced initializations when possible, e.g. `float myFloat{1.5f}` as this is generally the safest way to do it
* Short function definitions can be placed in either the .h or .cpp file depending on the situation
* Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone
* Try to be coherent with the existing codebase regarding names, structure etc., it should not be obvious what person wrote which parts

View file

@ -1,4 +1,4 @@
# EmulationStation Desktop Edition (ES-DE) - Building and advanced configuration (development version)
# EmulationStation Desktop Edition (ES-DE) v1.2 (development version) - Building and advanced configuration
**Note:** This is a quite technical document intended for those that are interested in compiling ES-DE from source code, or would like to customize the configuration. If you just want to start using the software, check out [USERGUIDE-DEV.md](USERGUIDE-DEV.md) instead.

View file

@ -1,4 +1,4 @@
# EmulationStation Desktop Edition (ES-DE) - Building and advanced configuration
# EmulationStation Desktop Edition (ES-DE) v1.1 - Building and advanced configuration
**Note:** This is a quite technical document intended for those that are interested in compiling ES-DE from source code, or would like to customize the configuration. If you just want to start using the software, check out [USERGUIDE.md](USERGUIDE.md) instead.

956
THEMES-DEV.md Normal file
View file

@ -0,0 +1,956 @@
# EmulationStation Desktop Edition (ES-DE) v1.2 (development version) - Themes
**Note:** If creating theme sets specifically for ES-DE, please add `-DE` to the theme name, as in `rbsimple-DE`. Because the ES-DE theme support has already deviated somehow from the RetroPie EmulationStation fork and will continue to deviate further in the future, the theme set will likely not be backwards compatible. It would be confusing and annoying for a user that downloads and attempts to use an ES-DE theme set in another EmulationStation fork only to get crashes, error messages or corrupted graphics. At least the -DE extension is a visual indicator that it's an ES-DE specific theme set.
Also note that 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.
ES-DE allows each system to have its own "theme." A theme is a collection **views** that define some **elements**, each with their own **properties**.
The first place ES-DE will check for a theme is in the system's `<path>` folder, for a theme.xml file:
* `[SYSTEM_PATH]/theme.xml`
If that file doesn't exist, ES-DE will try to find the theme in the current **theme set**. Theme sets are just a collection of individual system themes arranged in the "themes" folder under some name. A theme set can provide a default theme that will be used if there is no matching system theme. Here's an example:
```
...
themes/
my_theme_set/
snes/
theme.xml
my_cool_background.jpg
nes/
theme.xml
my_other_super_cool_background.jpg
common_resources/
my_font.ttf
theme.xml (Default theme)
another_theme_set/
snes/
theme.xml
some_resource.svg
```
The theme set system makes it easy for users to try different themes and allows distributions to include multiple theme options. Users can change the currently active theme set in the "UI Settings" menu. The option is only visible if at least one theme set exists.
There are two places ES-DE can load theme sets from:
* `[HOME]/.emulationstation/themes/[CURRENT_THEME_SET]/[SYSTEM_THEME]/theme.xml`
* `[INSTALLATION PATH]/themes/[CURRENT_THEME_SET]/[SYSTEM_THEME]/theme.xml`
An example installation path would be: \
`/usr/share/emulationstation/themes/[CURRENT_THEME_SET]/[SYSTEM_THEME]/theme.xml`
`[SYSTEM_THEME]` is the `<theme>` tag for the system, as defined in `es_systems.cfg`. If the `<theme>` tag is not set, ES-DE will use the system's `<name>`.
If both files happen to exist, ES-DE will pick the first one (the one located in the home directory).
Again, the `[CURRENT_THEME_SET]` value is set in the "UI Settings" menu. If it has not been set yet or the previously selected theme set is missing, the first available theme set will be used as the default.
# Simple Example
Here is a very simple theme that changes the description text's color:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="detailed">
<text name="description">
<color>00FF00</color>
</text>
<image name="my_image" extra="true">
<pos>0.5 0.5</pos>
<origin>0.5 0.5</origin>
<size>0.8 0.8</size>
<path>./my_art/my_awesome_image.jpg</path>
</image>
</view>
</theme>
```
# How it works
Everything must be inside a `<theme>` tag.
**The `<formatVersion>` tag *must* be specified**. This is the version of the theming system the theme was designed for.
The current version is 7.
A *view* can be thought of as a particular "screen" within EmulationStation. Views are defined like this:
```xml
<view name="ViewNameHere">
... define elements here ...
</view>
```
An *element* is a particular visual element, such as an image or a piece of text. You can either modify an element that already exists for a particular view (as is done in the "description" example), like this:
```xml
<elementTypeHere name="ExistingElementNameHere">
... define properties here ...
</elementTypeHere>
```
Or, you can create your own elements by adding `extra="true"` (as is done in the "my_image" example) like this:
```xml
<elementTypeHere name="YourUniqueElementNameHere" extra="true">
... define properties here ...
</elementTypeHere>
```
"Extra" elements will be drawn in the order they are defined (so define backgrounds first!). When they get drawn relative to the pre-existing elements depends on the view. Make sure "extra" element names do not clash with existing element names! An easy way to protect against this is to just start all your extra element names with some prefix like "e_".
*Properties* control how a particular *element* looks - for example, its position, size, image path, etc. The type of the property determines what kinds of values you can use. You can read about the types below in the "Reference" section. Properties are defined like this:
```xml
<propertyNameHere>ValueHere</propertyNameHere>
```
# Advanced Features
It is recommended that if you are writing a theme you launch EmulationStation with the `--debug` and `--windowed` switches. This way you can read error messages without having to check the log file. You can also reload the current gamelist view and system view with `Ctrl-R` if `--debug` is specified.
### The `<include>` tag
You can include theme files within theme files, similar to `#include` in C (though the internal mechanism is different, the effect is the same). Example:
`~/.emulationstation/all_themes.xml`:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="detailed">
<text name="description">
<fontPath>./all_themes/myfont.ttf</fontPath>
<color>00FF00</color>
</text>
</view>
</theme>
```
`~/.emulationstation/snes/theme.xml`:
```xml
<theme>
<formatVersion>7</formatVersion>
<include>./../all_themes.xml</include>
<view name="detailed">
<text name="description">
<color>FF0000</color>
</text>
</view>
</theme>
```
Is equivalent to this `snes/theme.xml`:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="detailed">
<text name="description">
<fontPath>./all_themes/myfont.ttf</fontPath>
<color>FF0000</color>
</text>
</view>
</theme>
```
Notice that properties that were not specified got merged (`<fontPath>`) and the `snes/theme.xml` could overwrite the included files' values (`<color>`). Also notice the included file still needed the `<formatVersion>` tag.
### Theming multiple views simultaneously
Sometimes you want to apply the same properties to the same elements across multiple views. The `name` attribute actually works as a list (delimited by any characters of `\t\r\n ,` - that is, whitespace and commas). So, for example, to easily apply the same header to the basic, grid, and system views:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="basic, grid, system">
<image name="logo">
<path>./snes_art/snes_header.png</path>
</image>
</view>
<view name="detailed">
<image name="logo">
<path>./snes_art/snes_header_detailed.png</path>
</image>
</view>
</theme>
```
This is equivalent to:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="basic">
<image name="logo">
<path>./snes_art/snes_header.png</path>
</image>
</view>
<view name="detailed">
<image name="logo">
<path>./snes_art/snes_header_detailed.png</path>
</image>
</view>
<view name="grid">
<image name="logo">
<path>./snes_art/snes_header.png</path>
</image>
</view>
<view name="system">
<image name="logo">
<path>./snes_art/snes_header.png</path>
</image>
</view>
... and any other view that might try to look up "logo" ...
</theme>
```
### Theming multiple elements simultaneously
You can theme multiple elements *of the same type* simultaneously. The `name` attribute actually works as a list (delimited by any characters of `\t\r\n ,` - that is, whitespace and commas), just like it does for views, as long as the elements have the same type. This is useful if you want to, say, apply the same color to all the metadata labels:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="detailed">
<!-- Weird spaces/newline on purpose! -->
<text name="md_lbl_rating, md_lbl_releasedate, md_lbl_developer, md_lbl_publisher,
md_lbl_genre, md_lbl_players, md_lbl_lastplayed, md_lbl_playcount">
<color>48474D</color>
</text>
</view>
</theme>
```
Which is equivalent to:
```xml
<theme>
<formatVersion>7</formatVersion>
<view name="detailed">
<text name="md_lbl_rating">
<color>48474D</color>
</text>
<text name="md_lbl_releasedate">
<color>48474D</color>
</text>
<text name="md_lbl_developer">
<color>48474D</color>
</text>
<text name="md_lbl_publisher">
<color>48474D</color>
</text>
<text name="md_lbl_genre">
<color>48474D</color>
</text>
<text name="md_lbl_players">
<color>48474D</color>
</text>
<text name="md_lbl_lastplayed">
<color>48474D</color>
</text>
<text name="md_lbl_playcount">
<color>48474D</color>
</text>
</view>
</theme>
```
Just remember, *this only works if the elements have the same type!*
### Element rendering order with z-index
You can now change the order in which elements are rendered by setting `zIndex` values. Default values correspond to the default rendering order while allowing elements to easily be shifted without having to set `zIndex` values for every element. Elements will be rendered in order from smallest z-index to largest.
#### Navigation sounds
The navigation sounds are configured globally per theme set, so it needs to be defined as a feature and with the view set to the special 'all' category.
It's recommended to put these elements in a separate file and include it from the main theme file (e.g. `<include>./navigationsounds.xml</include>`).
There are seven different navigation sounds that can be configured. The names as well as the element structure should be self-explanatory based
on the example below.
Starting EmulationStation with the --debug flag will provide feedback on whether any navigation sound elements were read from the theme set. If no navigation sound is provided by the theme, ES-DE will use the bundled navigation sound file as a fallback. This is done per sound, so the theme could provide for example one or two custom sound files while using the bundled ES-DE sounds for the other samples.
Example debug output:
```
Jul 12 11:28:58 Debug: NavigationSounds::loadThemeNavigationSounds(): Theme set includes navigation sound support, loading custom sounds
Jul 12 11:28:58 Debug: Sound::getFromTheme(): Looking for tag <sound name="systembrowse">
Jul 12 11:28:58 Debug: Sound::getFromTheme(): Tag found, ready to load theme sound file
Jul 12 11:28:58 Debug: Sound::getFromTheme(): Looking for tag <sound name="quicksysselect">
Jul 12 11:28:58 Debug: Sound::getFromTheme(): Tag not found, using fallback sound file
```
Example `navigationsounds.xml`, to be included from the main theme file:
```xml
<theme>
<formatVersion>7</formatVersion>
<feature supported="navigationsounds">
<view name="all">
<sound name="systembrowse">
<path>./core/sounds/systembrowse.wav</path>
</sound>
<sound name="quicksysselect">
<path>./core/sounds/quicksysselect.wav</path>
</sound>
<sound name="select">
<path>./core/sounds/select.wav</path>
</sound>
<sound name="back">
<path>./core/sounds/back.wav</path>
</sound>
<sound name="scroll">
<path>./core/sounds/scroll.wav</path>
</sound>
<sound name="favorite">
<path>./core/sounds/favorite.wav</path>
</sound>
<sound name="launch">
<path>./core/sounds/launch.wav</path>
</sound>
</view>
</feature>
</theme>
```
#### Defaults
##### system
* Extra Elements `extra="true"` - 10
* `carousel name="systemcarousel"` - 40
* `text name="systemInfo"` - 50
##### basic, detailed, grid, video
* `image name="background"` - 0
* Extra Elements `extra="true"` - 10
* `textlist name="gamelist"` - 20
* `imagegrid name="gamegrid"` - 20
* Media
* `image name="md_image"` - 30
* `video name="md_video"` - 30
* `image name="md_marquee"` - 35
* Metadata - 40
* Labels
* `text name="md_lbl_rating"`
* `text name="md_lbl_releasedate"`
* `text name="md_lbl_developer"`
* `text name="md_lbl_publisher"`
* `text name="md_lbl_genre"`
* `text name="md_lbl_players"`
* `text name="md_lbl_lastplayed"`
* `text name="md_lbl_playcount"`
* Values
* `rating name="md_rating"`
* `datetime name="md_releasedate"`
* `text name="md_developer"`
* `text name="md_publisher"`
* `text name="md_genre"`
* `text name="md_players"`
* `datetime name="md_lastplayed"`
* `text name="md_playcount"`
* `text name="md_description"`
* `text name="md_name"`
* System Logo/Text - 50
* `text name="logoText"`
* `image name="logo"`
* Gamelist information - 50
* `text name="gamelistInfo"`
### Theme variables
Theme variables can be used to simplify theme construction. There are 2 types of variables available.
* System Variables
* Theme Defined Variables
#### System Variables
System variables are system specific and are derived from the values in es_systems.cfg.
* `system.name`
* `system.fullName`
* `system.theme`
#### Theme Defined Variables
Variables can also be defined in the theme.
```
<variables>
<themeColor>8b0000</themeColor>
</variables>
```
#### Usage in themes
Variables can be used to specify the value of a theme property:
```
<color>${themeColor}</color>
```
or to specify only a portion of the value of a theme property:
```
<color>${themeColor}c0</color>
<path>./art/logo/${system.theme}.svg</path>
````
# Reference
## Views, their elements, and themeable properties:
#### basic
* `helpsystem name="help"` - ALL
- The help system style for this view.
* `image name="background"` - ALL
- This is a background image that exists for convenience. It goes from (0, 0) to (1, 1).
* `text name="logoText"` - ALL
- Displays the name of the system. Only present if no "logo" image is specified. Displayed at the top of the screen, centered by default.
* `image name="logo"` - ALL
- A header image. If a non-empty `path` is specified, `text name="logoText"` will be hidden and this image will be, by default, displayed roughly in its place.
* `textlist name="gamelist"` - ALL
- The gamelist. `primaryColor` is for games, `secondaryColor` is for folders. Centered by default.
---
#### detailed
* `helpsystem name="help"` - ALL
- The help system style for this view.
* `image name="background"` - ALL
- This is a background image that exists for convenience. It goes from (0, 0) to (1, 1).
* `text name="logoText"` - ALL
- Displays the name of the system. Only present if no "logo" image is specified. Displayed at the top of the screen, centered by default.
* `image name="logo"` - ALL
- A header image. If a non-empty `path` is specified, `text name="logoText"` will be hidden and this image will be, by default, displayed roughly in its place.
* `textlist name="gamelist"` - ALL
- The gamelist. `primaryColor` is for games, `secondaryColor` is for folders. Left aligned by default.
* `text name="gamelistInfo"` - ALL
- Displays the game count (all games as well as favorites), any applied filters, and a folder icon if a folder has been entered. If this text is left aligned, the folder icon will be placed to the right of the other information, and if it's right aligned, the folder icon will be placed to the left. Left aligned by default.
* Metadata
* Labels
* `text name="md_lbl_rating"` - ALL
* `text name="md_lbl_releasedate"` - ALL
* `text name="md_lbl_developer"` - ALL
* `text name="md_lbl_publisher"` - ALL
* `text name="md_lbl_genre"` - ALL
* `text name="md_lbl_players"` - ALL
* `text name="md_lbl_lastplayed"` - ALL
* `text name="md_lbl_playcount"` - ALL
* Values
* All values will follow to the right of their labels if a position isn't specified.
* `image name="md_image"` - POSITION | SIZE | Z_INDEX
- Path is the "image" metadata for the currently selected game.
* `rating name="md_rating"` - ALL
- The "rating" metadata.
* `datetime name="md_releasedate"` - ALL
- The "releasedate" metadata.
* `text name="md_developer"` - ALL
- The "developer" metadata.
* `text name="md_publisher"` - ALL
- The "publisher" metadata.
* `text name="md_genre"` - ALL
- The "genre" metadata.
* `text name="md_players"` - ALL
- The "players" metadata (number of players the game supports).
* `datetime name="md_lastplayed"` - ALL
- The "lastplayed" metadata. Displayed as a string representing the time relative to "now" (e.g. "3 hours ago").
* `text name="md_playcount"` - ALL
- The "playcount" metadata (number of times the game has been played).
* `text name="md_description"` - POSITION | SIZE | FONT_PATH | FONT_SIZE | COLOR | Z_INDEX
- Text is the "desc" metadata. If no `pos`/`size` is specified, will move and resize to fit under the lowest label and reach to the bottom of the screen.
* `text name="md_name"` - ALL
- The "name" metadata (the game name). Unlike the others metadata fields, the name is positioned offscreen by default
#### video
* `helpsystem name="help"` - ALL
- The help system style for this view.
* `image name="background"` - ALL
- This is a background image that exists for convenience. It goes from (0, 0) to (1, 1).
* `text name="logoText"` - ALL
- Displays the name of the system. Only present if no "logo" image is specified. Displayed at the top of the screen, centered by default.
* `image name="logo"` - ALL
- A header image. If a non-empty `path` is specified, `text name="logoText"` will be hidden and this image will be, by default, displayed roughly in its place.
* `textlist name="gamelist"` - ALL
- The gamelist. `primaryColor` is for games, `secondaryColor` is for folders. Left aligned by default.
* `text name="gamelistInfo"` - ALL
- Displays the game count (all games as well as favorites), any applied filters, and a folder icon if a folder has been entered. If this text is left aligned, the folder icon will be placed to the right of the other information, and if it's right aligned, the folder icon will be placed to the left. Left aligned by default.
* Metadata
* Labels
* `text name="md_lbl_rating"` - ALL
* `text name="md_lbl_releasedate"` - ALL
* `text name="md_lbl_developer"` - ALL
* `text name="md_lbl_publisher"` - ALL
* `text name="md_lbl_genre"` - ALL
* `text name="md_lbl_players"` - ALL
* `text name="md_lbl_lastplayed"` - ALL
* `text name="md_lbl_playcount"` - ALL
* Values
* All values will follow to the right of their labels if a position isn't specified.
* `image name="md_image"` - POSITION | SIZE | Z_INDEX
- Path is the "image" metadata for the currently selected game.
* `image name="md_marquee"` - POSITION | SIZE | Z_INDEX
- Path is the "marquee" metadata for the currently selected game.
* `video name="md_video"` - POSITION | SIZE | Z_INDEX
- Path is the "video" metadata for the currently selected game.
* `rating name="md_rating"` - ALL
- The "rating" metadata.
* `datetime name="md_releasedate"` - ALL
- The "releasedate" metadata.
* `text name="md_developer"` - ALL
- The "developer" metadata.
* `text name="md_publisher"` - ALL
- The "publisher" metadata.
* `text name="md_genre"` - ALL
- The "genre" metadata.
* `text name="md_players"` - ALL
- The "players" metadata (number of players the game supports).
* `datetime name="md_lastplayed"` - ALL
- The "lastplayed" metadata. Displayed as a string representing the time relative to "now" (e.g. "3 hours ago").
* `text name="md_playcount"` - ALL
- The "playcount" metadata (number of times the game has been played).
* `text name="md_description"` - POSITION | SIZE | FONT_PATH | FONT_SIZE | COLOR | Z_INDEX
- Text is the "desc" metadata. If no `pos`/`size` is specified, will move and resize to fit under the lowest label and reach to the bottom of the screen.
* `text name="md_name"` - ALL
- The "name" metadata (the game name). Unlike the others metadata fields, the name is positioned offscreen by default
---
#### grid
* `helpsystem name="help"` - ALL
- The help system style for this view.
* `image name="background"` - ALL
- This is a background image that exists for convenience. It goes from (0, 0) to (1, 1).
* `text name="logoText"` - ALL
- Displays the name of the system. Only present if no "logo" image is specified. Displayed at the top of the screen, centered by default.
* `image name="logo"` - ALL
- A header image. If a non-empty `path` is specified, `text name="logoText"` will be hidden and this image will be, by default, displayed roughly in its place.
* `imagegrid name="gamegrid"` - ALL
- The gamegrid. The number of tile displayed is controlled by its size, margin and the default tile max size.
* `gridtile name="default"` - ALL
- Note that many of the default gridtile parameters change the selected gridtile parameters if they are not explicitly set by the theme. For example, changing the background image of the default gridtile also change the background image of the selected gridtile. Refer to the gridtile documentation for more informations.
* `gridtile name="selected"` - ALL
- See default gridtile description right above.
* `text name="gamelistInfo"` - ALL
- Displays the game count (all games as well as favorites), any applied filters, and a folder icon if a folder has been entered. If this text is left aligned, the folder icon will be placed to the right of the other information, and if it's right aligned, the folder icon will be placed to the left. Left aligned by default.
* Metadata
* Labels
* `text name="md_lbl_rating"` - ALL
* `text name="md_lbl_releasedate"` - ALL
* `text name="md_lbl_developer"` - ALL
* `text name="md_lbl_publisher"` - ALL
* `text name="md_lbl_genre"` - ALL
* `text name="md_lbl_players"` - ALL
* `text name="md_lbl_lastplayed"` - ALL
* `text name="md_lbl_playcount"` - ALL
* Values
* All values will follow to the right of their labels if a position isn't specified.
* `rating name="md_rating"` - ALL
- The "rating" metadata.
* `datetime name="md_releasedate"` - ALL
- The "releasedate" metadata.
* `text name="md_developer"` - ALL
- The "developer" metadata.
* `text name="md_publisher"` - ALL
- The "publisher" metadata.
* `text name="md_genre"` - ALL
- The "genre" metadata.
* `text name="md_players"` - ALL
- The "players" metadata (number of players the game supports).
* `datetime name="md_lastplayed"` - ALL
- The "lastplayed" metadata. Displayed as a string representing the time relative to "now" (e.g. "3 hours ago").
* `text name="md_playcount"` - ALL
- The "playcount" metadata (number of times the game has been played).
* `text name="md_description"` - POSITION | SIZE | FONT_PATH | FONT_SIZE | COLOR | Z_INDEX
- Text is the "desc" metadata. If no `pos`/`size` is specified, will move and resize to fit under the lowest label and reach to the bottom of the screen.
* `text name="md_name"` - ALL
- The "name" metadata (the game name). Unlike the others metadata fields, the name is positioned offscreen by default
---
#### system
* `helpsystem name="help"` - ALL
- The help system style for this view.
* `carousel name="systemcarousel"` -ALL
- The system logo carousel
* `image name="logo"` - PATH | COLOR
- A logo image, to be displayed in the system logo carousel.
* `text name="logoText"` - FONT_PATH | COLOR | FORCE_UPPERCASE | LINE_SPACING | TEXT
- A logo text, to be displayed system name in the system logo carousel when no logo is available.
* `text name="systemInfo"` - ALL
- Displays details of the system currently selected in the carousel.
* You can use extra elements (elements with `extra="true"`) to add your own backgrounds, etc. They will be displayed behind the carousel, and scroll relative to the carousel.
## Types of properties:
* NORMALIZED_PAIR - two decimals, in the range [0..1], delimited by a space. For example, `0.25 0.5`. Most commonly used for position (x and y coordinates) and size (width and height).
* NORMALIZED_RECT - four decimals, in the range [0..1], delimited by a space. For example, `0.25 0.5 0.10 0.30`. Most commonly used for padding to store top, left, bottom and right coordinates.
* PATH - a path. If the first character is a `~`, it will be expanded into the environment variable for the home path (`$HOME` for Linux or `%HOMEPATH%` for Windows) unless overridden using the --home command line option. If the first character is a `.`, it will be expanded to the theme file's directory, allowing you to specify resources relative to the theme file, like so: `./../general_art/myfont.ttf`.
* BOOLEAN - `true`/`1` or `false`/`0`.
* COLOR - a hexidecimal RGB or RGBA color (6 or 8 digits). If 6 digits, will assume the alpha channel is `FF` (not transparent).
* FLOAT - a decimal.
* STRING - a string of text.
## Types of elements and their properties:
Common to almost all elements is a `pos` and `size` property of the NORMALIZED_PAIR type. They are normalized in terms of their "parent" object's size; 99% of the time, this is just the size of the screen. In this case, `<pos>0 0</pos>` would correspond to the top left corner, and `<pos>1 1</pos>` the bottom right corner (a positive Y value points further down). `pos` almost always refers to the top left corner of your element. You *can* use numbers outside of the [0..1] range if you want to place an element partially or completely off-screen.
The order you define properties in does not matter.
Remember, you do *not* need to specify every property!
*Note that a view may choose to only make only certain properties on a particular element themeable!*
#### image
Can be created as an extra.
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
- If only one axis is specified (and the other is zero), the other will be automatically calculated in accordance with the image's aspect ratio.
* `maxSize` - type: NORMALIZED_PAIR.
- The image will be resized as large as possible so that it fits within this size and maintains its aspect ratio. Use this instead of `size` when you don't know what kind of image you're using so it doesn't get grossly oversized on one axis (e.g. with a game's image metadata).
* `origin` - type: NORMALIZED_PAIR.
- Where on the image `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the image exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `rotation` - type: FLOAT.
- angle in degrees that the image should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise.
* `rotationOrigin` - type: NORMALIZED_PAIR.
- Point around which the image will be rotated. Defaults to `0.5 0.5`.
* `path` - type: PATH.
- Path to the image file. Most common extensions are supported (including .jpg, .png, and unanimated .gif).
* `default` - type: PATH.
- Path to default image file. Default image will be displayed when selected game does not have an image.
* `tile` - type: BOOLEAN.
- If true, the image will be tiled instead of stretched to fit its size. Useful for backgrounds.
* `color` - type: COLOR.
- Multiply each pixel's color by this color. For example, an all-white image with `<color>FF0000</color>` would become completely red. You can also control the transparency of an image with `<color>FFFFFFAA</color>` - keeping all the pixels their normal color and only affecting the alpha channel.
* `visible` - type: BOOLEAN.
- If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
#### imagegrid
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
- The size of the grid. Take care the selected tile can go out of the grid size, so don't position the grid too close to another element or the screen border.
* `margin` - type: NORMALIZED_PAIR. Margin between tiles.
* `padding` - type: NORMALIZED_RECT.
- NEW : Padding for displaying tiles.
* `autoLayout` - type: NORMALIZED_PAIR.
- NEW : Number of column and rows in the grid (integer values).
* `autoLayoutSelectedZoom` - type: FLOAT.
- NEW : Zoom factor to apply when a tile is selected.
* `gameImage` - type: PATH.
- The default image used for games which doesn't have an image.
* `folderImage` - type: PATH.
- The default image used for folders which doesn't have an image.
* `imageSource` - type: STRING.
- Selects the image to display. `thumbnail` by default, can also be set to `image`, `miximage`, `screenshot`, `cover`, `marquee` or `3dbox`. If selecting `image`, the media type `miximage` will be tried first, with fallback to `screenshot` and then `cover`.
* `scrollDirection` - type: STRING.
- `vertical` by default, can also be set to `horizontal`. Not that in `horizontal` mod, the tiles are ordered from top to bottom, then from left to right.
* `centerSelection` - type: BOOLEAN.
- `false` by default, when `true` the selected tile will be locked to the center of the grid.
* `scrollLoop` - type: BOOLEAN.
- `false` by default, when `true` the grid will seamlessly loop around when scrolling reaches the end of the list. Only works when `centerSelection` is `true`.
* `animate` - type : BOOLEAN.
- `true` by default, when `false` the grid scrolling will not be animated.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
#### gridtile
* `size` - type: NORMALIZED_PAIR.
- The size of the default gridtile is used to calculate how many tiles can fit in the imagegrid. If not explicitly set, the size of the selected gridtile is equal the size of the default gridtile * 1.2
* `padding` - type: NORMALIZED_PAIR.
- The padding around the gridtile content. Default `16 16`. If not explicitly set, the selected tile padding will be equal to the default tile padding.
* `imageColor` - type: COLOR.
- The default tile image color and selected tile image color have no influence on each others.
* `backgroundImage` - type: PATH.
- If not explicitly set, the selected tile background image will be the same as the default tile background image.
* `backgroundCornerSize` - type: NORMALIZED_PAIR.
- The corner size of the ninepatch used for the tile background. Default is `16 16`.
* `backgroundColor` - type: COLOR.
- A shortcut to define both the center color and edge color at the same time. The default tile background color and selected tile background color have no influence on each others.
* `backgroundCenterColor` - type: COLOR.
- Set the color of the center part of the ninepatch. The default tile background center color and selected tile background center color have no influence on each others.
* `backgroundEdgeColor` - type: COLOR.
- Set the color of the edge parts of the ninepatch. The default tile background edge color and selected tile background edge color have no influence on each others.
#### video
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
- If only one axis is specified (and the other is zero), the other will be automatically calculated in accordance with the video's aspect ratio.
* `maxSize` - type: NORMALIZED_PAIR.
- The video will be resized as large as possible so that it fits within this size and maintains its aspect ratio. Use this instead of `size` when you don't know what kind of video you're using so it doesn't get grossly oversized on one axis (e.g. with a game's video metadata).
* `origin` - type: NORMALIZED_PAIR.
- Where on the image `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the image exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `rotation` - type: FLOAT.
- angle in degrees that the text should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise.
* `rotationOrigin` - type: NORMALIZED_PAIR.
- Point around which the text will be rotated. Defaults to `0.5 0.5`.
* `delay` - type: FLOAT. Default is false.
- Delay in seconds before video will start playing.
* `default` - type: PATH.
- Path to default video file. Default video will be played when selected game does not have a video.
* `showSnapshotNoVideo` - type: BOOLEAN
- If true, image will be shown when selected game does not have a video and no `default` video is configured.
* `showSnapshotDelay` - type: BOOLEAN
- If true, playing of video will be delayed for `delayed` seconds, when game is selected.
* `visible` - type: BOOLEAN.
- If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
#### text
Can be created as an extra.
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
- Possible combinations:
- `0 0` - automatically size so text fits on one line (expanding horizontally).
- `w 0` - automatically wrap text so it doesn't go beyond `w` (expanding vertically).
- `w h` - works like a "text box." If `h` is non-zero and `h` <= `fontSize` (implying it should be a single line of text), text that goes beyond `w` will be truncated with an elipses (...).
* `origin` - type: NORMALIZED_PAIR.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the component exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `rotation` - type: FLOAT.
- angle in degrees that the text should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise.
* `rotationOrigin` - type: NORMALIZED_PAIR.
- Point around which the text will be rotated. Defaults to `0.5 0.5`.
* `text` - type: STRING.
* `color` - type: COLOR.
* `backgroundColor` - type: COLOR;
* `fontPath` - type: PATH.
- Path to a truetype font (.ttf).
* `fontSize` - type: FLOAT.
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height).
* `alignment` - type: STRING.
- Valid values are "left", "center", or "right". Controls alignment on the X axis. "center" will also align vertically.
* `forceUppercase` - type: BOOLEAN. Draw text in uppercase.
* `lineSpacing` - type: FLOAT. Controls the space between lines (as a multiple of font height). Default is 1.5.
* `visible` - type: BOOLEAN.
- If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
#### textlist
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
* `origin` - type: NORMALIZED_PAIR.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the component exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `selectorColor` - type: COLOR.
- Color of the "selector bar."
* `selectorImagePath` - type: PATH.
- Path to image to render in place of "selector bar."
* `selectorImageTile` - type: BOOLEAN.
- If true, the selector image will be tiled instead of stretched to fit its size.
* `selectorHeight` - type: FLOAT.
- Height of the "selector bar".
* `selectorOffsetY` - type: FLOAT.
- Allows moving of the "selector bar" up or down from its computed position. Useful for fine tuning the position of the "selector bar" relative to the text.
* `selectedColor` - type: COLOR.
- Color of the highlighted entry text.
* `primaryColor` - type: COLOR.
- Primary color; what this means depends on the text list. For example, for game lists, it is the color of a game.
* `secondaryColor` - type: COLOR.
- Secondary color; what this means depends on the text list. For example, for game lists, it is the color of a folder.
* `fontPath` - type: PATH.
* `fontSize` - type: FLOAT.
* `alignment` - type: STRING.
- Valid values are "left", "center", or "right". Controls alignment on the X axis.
* `horizontalMargin` - type: FLOAT.
- Horizontal offset for text from the alignment point. If `alignment` is "left", offsets the text to the right. If `alignment` is "right", offsets text to the left. No effect if `alignment` is "center". Given as a percentage of the element's parent's width (same unit as `size`'s X value).
* `forceUppercase` - type: BOOLEAN. Draw text in uppercase.
* `lineSpacing` - type: FLOAT. Controls the space between lines (as a multiple of font height). Default is 1.5.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
#### ninepatch
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
* `path` - type: PATH.
* `visible` - type: BOOLEAN.
- If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
EmulationStation borrows the concept of "nine patches" from Android (or "9-Slices"). Currently the implementation is very simple and hard-coded to only use 48x48px images (16x16px for each "patch"). Check the `data/resources` directory for some examples (button.png, frame.png).
#### rating
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
- Only one value is actually used. The other value should be zero. (e.g. specify width OR height, but not both. This is done to maintain the aspect ratio.)
* `origin` - type: NORMALIZED_PAIR.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the component exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `rotation` - type: FLOAT.
- angle in degrees that the rating should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise.
* `rotationOrigin` - type: NORMALIZED_PAIR.
- Point around which the rating will be rotated. Defaults to `0.5 0.5`.
* `filledPath` - type: PATH.
- Path to the "filled star" image. Image must be square (width equals height).
* `unfilledPath` - type: PATH.
- Path to the "unfilled star" image. Image must be square (width equals height).
* `color` - type: COLOR.
- Multiply each pixel's color by this color. For example, an all-white image with `<color>FF0000</color>` would become completely red. You can also control the transparency of an image with `<color>FFFFFFAA</color>` - keeping all the pixels their normal color and only affecting the alpha channel.
* `visible` - type: BOOLEAN.
- If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
#### datetime
* `pos` - type: NORMALIZED_PAIR.
* `size` - type: NORMALIZED_PAIR.
- Possible combinations:
- `0 0` - automatically size so text fits on one line (expanding horizontally).
- `w 0` - automatically wrap text so it doesn't go beyond `w` (expanding vertically).
- `w h` - works like a "text box." If `h` is non-zero and `h` <= `fontSize` (implying it should be a single line of text), text that goes beyond `w` will be truncated with an elipses (...).
* `origin` - type: NORMALIZED_PAIR.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the component exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `rotation` - type: FLOAT.
- angle in degrees that the text should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise.
* `rotationOrigin` - type: NORMALIZED_PAIR.
- Point around which the text will be rotated. Defaults to `0.5 0.5`.
* `color` - type: COLOR.
* `backgroundColor` - type: COLOR;
* `fontPath` - type: PATH.
- Path to a truetype font (.ttf).
* `fontSize` - type: FLOAT.
- Size of the font as a percentage of screen height (e.g. for a value of `0.1`, the text's height would be 10% of the screen height).
* `alignment` - type: STRING.
- Valid values are "left", "center", or "right". Controls alignment on the X axis. "center" will also align vertically.
* `forceUppercase` - type: BOOLEAN. Draw text in uppercase.
* `lineSpacing` - type: FLOAT. Controls the space between lines (as a multiple of font height). Default is 1.5.
* `visible` - type: BOOLEAN.
- If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view.
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
* `displayRelative` - type: BOOLEAN. Renders the datetime as a a relative string (ex: 'x days ago')
* `format` - type: STRING. Specifies format for rendering datetime.
- %Y: The year, including the century (1900)
- %m: The month number [01,12]
- %d: The day of the month [01,31]
- %H: The hour (24-hour clock) [00,23]
- %M: The minute [00,59]
- %S: The second [00,59]
#### helpsystem
* `pos` - type: NORMALIZED_PAIR. Default is "0.012 0.9515"
* `origin` - type: NORMALIZED_PAIR.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place
the component exactly in the middle of the screen.
* `textColor` - type: COLOR. Default is 777777FF.
* `textColorDimmed` - type: COLOR. Default is 777777FF.
* `iconColor` - type: COLOR. Default is 777777FF.
* `iconColorDimmed` - type: COLOR. Default is 777777FF.
* `fontPath` - type: PATH.
* `fontSize` - type: FLOAT.
* `entrySpacing` - type: FLOAT. Default is 16.0.
- Spacing in pixels between the help system components.
* `iconTextSpacing` - type: FLOAT. Default is 8.0.
- Spacing in pixels within a help system component between it's icon and text.
* `textStyle` - type: STRING. Default is `uppercase`.
- The style of the text. Options: `uppercase`, `lowercase`, `camelcase`.
* `customButtonIcon` - type: PATH.
- A button icon override. Specify the button type in the attribute `button`. The available buttons are:
`dpad_updown`,
`dpad_leftright`,
`dpad_all`,
`thumbstick_click`,
`button_l`,
`button_r`,
`button_lr`,
`button_a_SNES`,
`button_b_SNES`,
`button_x_SNES`,
`button_y_SNES`,
`button_back_SNES`,
`button_start_SNES`,
`button_a_PS`,
`button_b_PS`,
`button_x_PS`,
`button_y_PS`,
`button_back_PS4`,
`button_start_PS4`,
`button_back_PS5`,
`button_start_PS5`,
`button_a_XBOX`,
`button_b_XBOX`,
`button_x_XBOX`,
`button_y_XBOX`,
`button_back_XBOX`,
`button_start_XBOX`,
`button_back_XBOX360`,
`button_start_XBOX360`.
#### carousel
* `type` - type: STRING.
- Sets the scoll direction of the carousel.
- Accepted values are "horizontal", "vertical", "horizontal_wheel" or "vertical_wheel".
- Default is "horizontal".
* `size` - type: NORMALIZED_PAIR. Default is "1 0.2325"
* `pos` - type: NORMALIZED_PAIR. Default is "0 0.38375".
* `origin` - type: NORMALIZED_PAIR.
- Where on the carousel `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the carousel exactly in the middle of the screen. If the "POSITION" and "SIZE" attributes are themeable, "ORIGIN" is implied.
* `color` - type: COLOR.
- Controls the color of the carousel background.
- Default is FFFFFFD8
* `logoSize` - type: NORMALIZED_PAIR. Default is "0.25 0.155"
* `logoScale` - type: FLOAT.
- Selected logo is increased in size by this scale
- Default is 1.2
* `logoRotation` - type: FLOAT.
- Angle in degrees that the logos should be rotated. Value should be positive.
- Default is 7.5
- This property only applies when `type` is "horizontal_wheel" or "vertical_wheel".
* `logoRotationOrigin` - type: NORMALIZED_PAIR.
- Point around which the logos will be rotated. Defaults to `-5 0.5`.
- This property only applies when `type` is "horizontal_wheel" or "vertical_wheel".
* `logoAlignment` - type: STRING.
- Sets the alignment of the logos relative to the carousel.
- Accepted values are "top", "bottom" or "center" when `type` is "horizontal" or "horizontal_wheel".
- Accepted values are "left", "right" or "center" when `type` is "vertical" or "vertical_wheel".
- Default is "center"
* `maxLogoCount` - type: FLOAT.
- Sets the number of logos to display in the carousel.
- Default is 3
* `zIndex` - type: FLOAT.
- z-index value for component. Components will be rendered in order of z-index value from low to high.
The help system is a special element that displays a context-sensitive list of actions the user can take at any time. You should try and keep the position constant throughout every screen. Keep in mind the "default" settings (including position) are used whenever the user opens a menu.
To see some examples of EmulationStation themes, the following resources are recommended:
https://aloshi.com/emulationstation#themes
https://github.com/RetroPie
https://gitlab.com/recalbox/recalbox-themes
https://wiki.batocera.org/themes

View file

@ -1,4 +1,4 @@
# EmulationStation Desktop Edition (ES-DE) - Themes
# EmulationStation Desktop Edition (ES-DE) v1.1 - Themes
**Note:** If creating theme sets specifically for ES-DE, please add `-DE` to the theme name, as in `rbsimple-DE`. Because the ES-DE theme support has already deviated somehow from the RetroPie EmulationStation fork and will continue to deviate further in the future, the theme set will likely not be backwards compatible. It would be confusing and annoying for a user that downloads and attempts to use an ES-DE theme set in another EmulationStation fork only to get crashes, error messages or corrupted graphics. At least the -DE extension is a visual indicator that it's an ES-DE specific theme set.
@ -71,20 +71,18 @@ Here is a very simple theme that changes the description text's color:
Everything must be inside a `<theme>` tag.
**The `<formatVersion>` tag *must* be specified**. This is the version of the theming system the theme was designed for. The current version is 6.
**The `<formatVersion>` tag *must* be specified**. This is the version of the theming system the theme was designed for.
The current version is 6.
A *view* can be thought of as a particular "screen" within EmulationStation. Views are defined like this:
A *view* can be thought of as a particular "screen" within EmulationStation. Views are defined like this:
```xml
<view name="ViewNameHere">
... define elements here ...
</view>
```
An *element* is a particular visual element, such as an image or a piece of text. You can either modify an element that already exists for a particular view (as is done in the "description" example), like this:
```xml
@ -122,6 +120,7 @@ You can include theme files within theme files, similar to `#include` in C (thou
`~/.emulationstation/all_themes.xml`:
```xml
<theme>
<formatVersion>6</formatVersion>
<view name="detailed">
@ -135,6 +134,7 @@ You can include theme files within theme files, similar to `#include` in C (thou
`~/.emulationstation/snes/theme.xml`:
```xml
<theme>
<formatVersion>6</formatVersion>
<include>./../all_themes.xml</include>
@ -148,6 +148,7 @@ You can include theme files within theme files, similar to `#include` in C (thou
Is equivalent to this `snes/theme.xml`:
```xml
<theme>
<formatVersion>6</formatVersion>
<view name="detailed">
@ -167,6 +168,7 @@ Notice that properties that were not specified got merged (`<fontPath>`) and the
Sometimes you want to apply the same properties to the same elements across multiple views. The `name` attribute actually works as a list (delimited by any characters of `\t\r\n ,` - that is, whitespace and commas). So, for example, to easily apply the same header to the basic, grid, and system views:
```xml
<theme>
<formatVersion>6</formatVersion>
<view name="basic, grid, system">
@ -184,6 +186,7 @@ Sometimes you want to apply the same properties to the same elements across mult
This is equivalent to:
```xml
<theme>
<formatVersion>6</formatVersion>
<view name="basic">
@ -216,6 +219,7 @@ This is equivalent to:
You can theme multiple elements *of the same type* simultaneously. The `name` attribute actually works as a list (delimited by any characters of `\t\r\n ,` - that is, whitespace and commas), just like it does for views, as long as the elements have the same type. This is useful if you want to, say, apply the same color to all the metadata labels:
```xml
<theme>
<formatVersion>6</formatVersion>
<view name="detailed">
@ -230,6 +234,7 @@ You can theme multiple elements *of the same type* simultaneously. The `name` a
Which is equivalent to:
```xml
<theme>
<formatVersion>6</formatVersion>
<view name="detailed">
@ -288,6 +293,7 @@ Jul 12 11:28:58 Debug: Sound::getFromTheme(): Tag not found, using fallback sou
Example `navigationsounds.xml`, to be included from the main theme file:
```xml
<theme>
<formatVersion>6</formatVersion>
<feature supported="navigationsounds">
@ -849,11 +855,12 @@ EmulationStation borrows the concept of "nine patches" from Android (or "9-Slice
#### helpsystem
* `pos` - type: NORMALIZED_PAIR. Default is "0.012 0.9515"
* `pos` - type: NORMALIZED_PAIR. Default is "0.012 0.9515"
* `origin` - type: NORMALIZED_PAIR.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place the component exactly in the middle of the screen.
* `textColor` - type: COLOR. Default is 777777FF.
* `iconColor` - type: COLOR. Default is 777777FF.
- Where on the component `pos` refers to. For example, an origin of `0.5 0.5` and a `pos` of `0.5 0.5` would place
the component exactly in the middle of the screen.
* `textColor` - type: COLOR. Default is 777777FF.
* `iconColor` - type: COLOR. Default is 777777FF.
* `fontPath` - type: PATH.
* `fontSize` - type: FLOAT.

View file

@ -1,4 +1,4 @@
# EmulationStation Desktop Edition (ES-DE) - User guide (development version)
# EmulationStation Desktop Edition (ES-DE) v1.2 (development version) - User guide
This document is intended as a quick start guide and as a reference for the application settings and general functionality. For details on how to build ES-DE from source code and to perform more advanced configuration, please refer to [INSTALL-DEV.md](INSTALL-DEV.md).
@ -26,6 +26,7 @@ For additional details, read on below.
There are also installation videos available at the ES-DE YouTube channel:\
[https://www.youtube.com/channel/UCosLuC9yIMQPKFBJXgDpvVQ](https://www.youtube.com/channel/UCosLuC9yIMQPKFBJXgDpvVQ)
## Installation and first startup
To install ES-DE, just download the package or installer from [https://es-de.org](https://es-de.org) and follow the brief instructions below.
@ -78,7 +79,7 @@ On Unix this means /home/\<username\>/.emulationstation/, on macOS /Users/\<user
It's also possible to override the home directory path using the --home command line option, but this is normally required only for very special situations so we can safely ignore that option for now.
Also on first startup the configuration file `es_settings.xml` will be generated in the ES-DE home directory, containing all the application settings at their default values. Following this, a file named `es_systems.xml` will be loaded from the resources directory (which is part of the ES-DE installation). This file contains the game system definitions including which emulator to use per platform, and it can be customized if needed. The customized version needs to be placed in the `custom_systems` folder in the ES-DE home directory, i.e. `~/.emulationstation/custom_systems/es_systems.xml`. You can find information on how to customize the systems configuration file in the [INSTALL-DEV.md](INSTALL-DEV.md#es_systemsxml) document.
Also on first startup the configuration file `es_settings.xml` will be generated in the ES-DE home directory, containing all the application settings at their default values. Following this, a file named `es_systems.xml` will be loaded from the resources directory (which is part of the ES-DE installation). This file contains the game system definitions including which emulator to use per platform. The systems configuration file can be customized, as described in the next section.
There's an application log file created in the ES-DE home directory named `es_log.txt`, please refer to this in case of any issues as it should hopefully provide information on what went wrong. Starting ES-DE with the --debug flag provides even more detailed information.
@ -125,6 +126,11 @@ There will be a lot of directories created if using the es_systems.xml file bund
_This is the dialog shown if no game files were found. It lets you configure the ROM directory if you don't want to use the default one, and you can also generate the game systems directory structure. Note that the directory is the physical path, and that your operating system may present this as a localized path if you are using a language other than English._
## Customizing the systems configuration file
The `es_systems.xml` file is located in the ES-DE resources directory which is part of the application installation. As such this file is not intended to be modified directly. If a customized file is needed, this should instead be placed in the `custom_systems` folder in the ES-DE home directory, i.e. `~/.emulationstation/custom_systems/es_systems.xml`. You can find information on the file structure and how to adapt the configuration in the [INSTALL-DEV.md](INSTALL-DEV.md#es_systemsxml) document.
## Migrating from other EmulationStation forks
**IMPORTANT!!! IMPORTANT!!! IMPORTANT!!!**
@ -138,6 +144,7 @@ Due to this, always make backups of at least the following directories before te
~/.emulationstation/collections/
```
## Running on high resolution displays
ES-DE fully supports high resolution displays such as 4K, 6K, 1440p, ultrawide monitors etc. But some emulators such as RetroArch will also run using the same resolution which may cause performance problems on slower machines or when using resource intensive shaders. Although some emulator cores will have options to set their internal resolution, they still need to be scaled up to the screen resolution. On Unix it's possible to start ES-DE with the `--resolution` option to set a lower screen resolution (this will also affect the emulators) but this is not really recommended as it's highly dependent on well-written graphics drivers for proper behavior.
@ -159,6 +166,7 @@ If you have issues with your input configuration, as a last resort you can reset
If you experience double button presses with your DualShock 4 controller on macOS, please read about the workaround for this issue in the [Known issues](CHANGELOG.md#known-issues) section of the changelog.
## System view (main screen)
When starting ES-DE with the default settings, you will see the System view first. From here you can navigate your game systems and enter their respective gamelists.
@ -170,6 +178,7 @@ The game systems are sorted by their full names, as defined in the es_systems.xm
![alt text](images/current/es-de_system_view.png "ES-DE System View")
_The **System view** is the default starting point for the application, it's here that you browse through your game systems._
## Gamelist view
The gamelist view is where you browse and start your games, and it's where you will spend most of your time using ES-DE.
@ -190,6 +199,7 @@ _The **Gamelist view** is where you browse the games for a specific system._
![alt text](images/current/es-de_basic_view_style.png "ES-DE Basic View Style")
_Here's an example of what the Basic view style looks like. Needless to say, ES-DE is not intended to be used like this. After scraping some game media for the system, the view style will automatically change to Detailed or Video (assuming the Automatic view style option has been selected)._
## UI modes
ES-DE supports three separate modes, **Full**, **Kiosk** and **Kid**.
@ -204,6 +214,7 @@ There is an unlock code available to revert to the Full mode from the Kiosk or K
The application can also be forced into any of the three modes via the command line options `--force-full`, `--force-kiosk` and `--force-kid`. This is only temporary until the restart of the application, unless the settings menu is entered and the setting is saved to the configuration file (this assumes that the main menu is available in the selected UI mode of course).
## Help system
There is a help system available throughout the application that provides an overview of the possible actions and buttons that can be used. But some general actions are never shown, such as the ability to quick jump in gamelists, menus and text input fields using the shoulder and trigger buttons. It's also possible to disable the help system using a menu option for a somewhat cleaner look.

View file

@ -1,4 +1,4 @@
# EmulationStation Desktop Edition (ES-DE) - User guide
# EmulationStation Desktop Edition (ES-DE) v1.1 - User guide
This document is intended as a quick start guide and as a reference for the application settings and general functionality. For details on how to build ES-DE from source code and to perform more advanced configuration, please refer to [INSTALL.md](INSTALL.md).
@ -24,6 +24,7 @@ For additional details, read on below.
There are also installation videos available at the ES-DE YouTube channel:\
[https://www.youtube.com/channel/UCosLuC9yIMQPKFBJXgDpvVQ](https://www.youtube.com/channel/UCosLuC9yIMQPKFBJXgDpvVQ)
## Installation and first startup
To install ES-DE, just download the package or installer from [https://es-de.org](https://es-de.org) and follow the brief instructions below.
@ -76,7 +77,7 @@ On Unix this means /home/\<username\>/.emulationstation/, on macOS /Users/\<user
It's also possible to override the home directory path using the --home command line option, but this is normally required only for very special situations so we can safely ignore that option for now.
Also on first startup the configuration file `es_settings.xml` will be generated in the ES-DE home directory, containing all the application settings at their default values. Following this, a file named `es_systems.xml` will be loaded from the resources directory (which is part of the ES-DE installation). This file contains the game system definitions including which emulator to use per platform, and it can be customized if needed. The customized version needs to be placed in the `custom_systems` folder in the ES-DE home directory, i.e. `~/.emulationstation/custom_systems/es_systems.xml`. You can find information on how to customize the systems configuration file in the [INSTALL.md](INSTALL.md#es_systemsxml) document.
Also on first startup the configuration file `es_settings.xml` will be generated in the ES-DE home directory, containing all the application settings at their default values. Following this, a file named `es_systems.xml` will be loaded from the resources directory (which is part of the ES-DE installation). This file contains the game system definitions including which emulator to use per platform. The systems configuration file can be customized, as described in the next section.
There's an application log file created in the ES-DE home directory named `es_log.txt`, please refer to this in case of any issues as it should hopefully provide information on what went wrong. Starting ES-DE with the --debug flag provides even more detailed information.
@ -123,6 +124,11 @@ There will be a lot of directories created if using the es_systems.xml file bund
_This is the dialog shown if no game files were found. It lets you configure the ROM directory if you don't want to use the default one, and you can also generate the game systems directory structure. Note that the directory is the physical path, and that your operating system may present this as a localized path if you are using a language other than English._
## Customizing the systems configuration file
The `es_systems.xml` file is located in the ES-DE resources directory which is part of the application installation. As such this file is not intended to be modified directly. If a customized file is needed, this should instead be placed in the `custom_systems` folder in the ES-DE home directory, i.e. `~/.emulationstation/custom_systems/es_systems.xml`. You can find information on the file structure and how to adapt the configuration in the [INSTALL.md](INSTALL.md#es_systemsxml) document.
## Migrating from other EmulationStation forks
**IMPORTANT!!! IMPORTANT!!! IMPORTANT!!!**
@ -136,6 +142,7 @@ Due to this, always make backups of at least the following directories before te
~/.emulationstation/collections/
```
## Running on high resolution displays
ES-DE fully supports high resolution displays such as 4K, 6K, 1440p, ultrawide monitors etc. But some emulators such as RetroArch will also run using the same resolution which may cause performance problems on slower machines or when using resource intensive shaders. Although some emulator cores will have options to set their internal resolution, they still need to be scaled up to the screen resolution. On Unix it's possible to start ES-DE with the `--resolution` option to set a lower screen resolution (this will also affect the emulators) but this is not really recommended as it's highly dependent on well-written graphics drivers for proper behavior.
@ -157,6 +164,7 @@ If you have issues with your input configuration, as a last resort you can reset
If you experience double button presses with your DualShock 4 controller on macOS, please read about the workaround for this issue in the [Known issues](CHANGELOG.md#known-issues) section of the changelog.
## System view (main screen)
When starting ES-DE with the default settings, you will see the System view first. From here you can navigate your game systems and enter their respective gamelists.
@ -168,6 +176,7 @@ The game systems are sorted by their full names, as defined in the es_systems.xm
![alt text](images/current/es-de_system_view.png "ES-DE System View")
_The **System view** is the default starting point for the application, it's here that you browse through your game systems._
## Gamelist view
The gamelist view is where you browse and start your games, and it's where you will spend most of your time using ES-DE.
@ -188,6 +197,7 @@ _The **Gamelist view** is where you browse the games for a specific system._
![alt text](images/current/es-de_basic_view_style.png "ES-DE Basic View Style")
_Here's an example of what the Basic view style looks like. Needless to say, ES-DE is not intended to be used like this. After scraping some game media for the system, the view style will automatically change to Detailed or Video (assuming the Automatic view style option has been selected)._
## UI modes
ES-DE supports three separate modes, **Full**, **Kiosk** and **Kid**.
@ -202,6 +212,7 @@ There is an unlock code available to revert to the Full mode from the Kiosk or K
The application can also be forced into any of the three modes via the command line options `--force-full`, `--force-kiosk` and `--force-kid`. This is only temporary until the restart of the application, unless the settings menu is entered and the setting is saved to the configuration file (this assumes that the main menu is available in the selected UI mode of course).
## Help system
There is a help system available throughout the application that provides an overview of the possible actions and buttons that can be used. But some general actions are never shown, such as the ability to quick jump in gamelists, menus and text input fields using the shoulder and trigger buttons. It's also possible to disable the help system using a menu option for a somewhat cleaner look.

View file

@ -16,6 +16,7 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.h
# GUIs
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiAlternativeEmulators.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiCollectionSystemsOptions.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistFilter.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistOptions.h
@ -48,9 +49,6 @@ set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/views/SystemView.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/UIModeController.h
${CMAKE_CURRENT_SOURCE_DIR}/src/views/ViewController.h
# Animations
${CMAKE_CURRENT_SOURCE_DIR}/src/animations/MoveCameraAnimation.h
)
set(ES_SOURCES
@ -69,6 +67,7 @@ set(ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.cpp
# GUIs
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiAlternativeEmulators.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiCollectionSystemsOptions.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistFilter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistOptions.cpp

View file

@ -52,11 +52,11 @@ CollectionSystemsManager::CollectionSystemsManager(Window* window)
{
// clang-format off
CollectionSystemDecl systemDecls[] = {
// Type Name Long name Theme folder isCustom
{ AUTO_ALL_GAMES, "all", "all games", "auto-allgames", false },
{ AUTO_LAST_PLAYED, "recent", "last played", "auto-lastplayed", false },
{ AUTO_FAVORITES, "favorites", "favorites", "auto-favorites", false },
{ CUSTOM_COLLECTION, myCollectionsName, "collections", "custom-collections", true }
// Type Name Long name Theme folder isCustom
{AUTO_ALL_GAMES, "all", "all games", "auto-allgames", false},
{AUTO_LAST_PLAYED, "recent", "last played", "auto-lastplayed", false},
{AUTO_FAVORITES, "favorites", "favorites", "auto-favorites", false},
{CUSTOM_COLLECTION, myCollectionsName, "collections", "custom-collections", true }
};
// clang-format on
@ -73,7 +73,8 @@ CollectionSystemsManager::CollectionSystemsManager(Window* window)
mCollectionEnvData->mStartPath = "";
std::vector<std::string> exts;
mCollectionEnvData->mSearchExtensions = exts;
mCollectionEnvData->mLaunchCommand = "";
std::vector<std::pair<std::string, std::string>> commands;
mCollectionEnvData->mLaunchCommands = commands;
std::vector<PlatformIds::PlatformId> allPlatformIds;
allPlatformIds.push_back(PlatformIds::PLATFORM_IGNORE);
mCollectionEnvData->mPlatformIds = allPlatformIds;
@ -791,7 +792,7 @@ FileData* CollectionSystemsManager::updateCollectionFolderMetadata(SystemData* s
if (gameCount > 1) {
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine { randDev() };
std::mt19937 engine{randDev()};
unsigned int target;
for (unsigned int i = 0; i < 3; i++) {

View file

@ -212,7 +212,7 @@ const std::string FileData::getMediaDirectory()
const std::string FileData::getMediafilePath(std::string subdirectory, std::string mediatype) const
{
const std::vector<std::string> extList = { ".png", ".jpg" };
const std::vector<std::string> extList = {".png", ".jpg"};
std::string subFolders;
// Extract possible subfolders from the path.
@ -287,7 +287,7 @@ const std::string FileData::getThumbnailPath() const
const std::string FileData::getVideoPath() const
{
const std::vector<std::string> extList = { ".avi", ".mkv", ".mov", ".mp4", ".wmv" };
const std::vector<std::string> extList = {".avi", ".mkv", ".mov", ".mp4", ".wmv"};
std::string subFolders;
// Extract possible subfolders from the path.
@ -756,7 +756,22 @@ void FileData::launchGame(Window* window)
command = metadata.get("launchcommand");
}
else {
command = mEnvData->mLaunchCommand;
std::string alternativeEmulator = getSystem()->getAlternativeEmulator();
for (auto launchCommand : mEnvData->mLaunchCommands) {
if (launchCommand.second == alternativeEmulator) {
command = launchCommand.first;
break;
}
}
if (!alternativeEmulator.empty() && command.empty()) {
LOG(LogWarning) << "The alternative emulator configured for system \""
<< getSystem()->getName()
<< "\" is invalid, falling back to the default command \""
<< getSystem()->getSystemEnvData()->mLaunchCommands.front().first << "\"";
}
if (command.empty())
command = mEnvData->mLaunchCommands.front().first;
}
std::string commandRaw = command;
@ -1158,7 +1173,7 @@ std::string FileData::findEmulatorPath(std::string& command)
HKEY registryKey;
LSTATUS keyStatus = -1;
LSTATUS pathStatus = -1;
char registryPath[1024] {};
char registryPath[1024]{};
DWORD pathSize = 1024;
// First look in HKEY_CURRENT_USER.

View file

@ -11,7 +11,6 @@
#include "FileData.h"
#include "Log.h"
#include "Settings.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "views/UIModeController.h"
@ -37,15 +36,15 @@ FileFilterIndex::FileFilterIndex()
// clang-format off
FilterDataDecl filterDecls[] = {
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
{ FAVORITES_FILTER, &mFavoritesIndexAllKeys, &mFilterByFavorites, &mFavoritesIndexFilteredKeys, "favorite", false, "", "FAVORITES" },
{ GENRE_FILTER, &mGenreIndexAllKeys, &mFilterByGenre, &mGenreIndexFilteredKeys, "genre", true, "genre", "GENRE" },
{ PLAYER_FILTER, &mPlayersIndexAllKeys, &mFilterByPlayers, &mPlayersIndexFilteredKeys, "players", false, "", "PLAYERS" },
{ PUBDEV_FILTER, &mPubDevIndexAllKeys, &mFilterByPubDev, &mPubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" },
{ RATINGS_FILTER, &mRatingsIndexAllKeys, &mFilterByRatings, &mRatingsIndexFilteredKeys, "rating", false, "", "RATING" },
{ KIDGAME_FILTER, &mKidGameIndexAllKeys, &mFilterByKidGame, &mKidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME" },
{ COMPLETED_FILTER, &mCompletedIndexAllKeys, &mFilterByCompleted, &mCompletedIndexFilteredKeys, "completed", false, "", "COMPLETED" },
{ BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN" },
{ HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" }
{FAVORITES_FILTER, &mFavoritesIndexAllKeys, &mFilterByFavorites, &mFavoritesIndexFilteredKeys, "favorite", false, "", "FAVORITES"},
{GENRE_FILTER, &mGenreIndexAllKeys, &mFilterByGenre, &mGenreIndexFilteredKeys, "genre", true, "genre", "GENRE"},
{PLAYER_FILTER, &mPlayersIndexAllKeys, &mFilterByPlayers, &mPlayersIndexFilteredKeys, "players", false, "", "PLAYERS"},
{PUBDEV_FILTER, &mPubDevIndexAllKeys, &mFilterByPubDev, &mPubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER"},
{RATINGS_FILTER, &mRatingsIndexAllKeys, &mFilterByRatings, &mRatingsIndexFilteredKeys, "rating", false, "", "RATING"},
{KIDGAME_FILTER, &mKidGameIndexAllKeys, &mFilterByKidGame, &mKidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME"},
{COMPLETED_FILTER, &mCompletedIndexAllKeys, &mFilterByCompleted, &mCompletedIndexFilteredKeys, "completed", false, "", "COMPLETED"},
{BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN"},
{HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN"}
};
// clang-format on
@ -67,15 +66,15 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
};
IndexImportStructure indexStructDecls[] = {
{ &mFavoritesIndexAllKeys, &(indexToImport->mFavoritesIndexAllKeys) },
{ &mGenreIndexAllKeys, &(indexToImport->mGenreIndexAllKeys) },
{ &mPlayersIndexAllKeys, &(indexToImport->mPlayersIndexAllKeys) },
{ &mPubDevIndexAllKeys, &(indexToImport->mPubDevIndexAllKeys) },
{ &mRatingsIndexAllKeys, &(indexToImport->mRatingsIndexAllKeys) },
{ &mKidGameIndexAllKeys, &(indexToImport->mKidGameIndexAllKeys) },
{ &mCompletedIndexAllKeys, &(indexToImport->mCompletedIndexAllKeys) },
{ &mBrokenIndexAllKeys, &(indexToImport->mBrokenIndexAllKeys) },
{ &mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys) },
{&mFavoritesIndexAllKeys, &(indexToImport->mFavoritesIndexAllKeys)},
{&mGenreIndexAllKeys, &(indexToImport->mGenreIndexAllKeys)},
{&mPlayersIndexAllKeys, &(indexToImport->mPlayersIndexAllKeys)},
{&mPubDevIndexAllKeys, &(indexToImport->mPubDevIndexAllKeys)},
{&mRatingsIndexAllKeys, &(indexToImport->mRatingsIndexAllKeys)},
{&mKidGameIndexAllKeys, &(indexToImport->mKidGameIndexAllKeys)},
{&mCompletedIndexAllKeys, &(indexToImport->mCompletedIndexAllKeys)},
{&mBrokenIndexAllKeys, &(indexToImport->mBrokenIndexAllKeys)},
{&mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys)},
};
std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(
@ -304,7 +303,7 @@ void FileFilterIndex::setKidModeFilters()
{
if (UIModeController::getInstance()->isUIModeKid()) {
mFilterByKidGame = true;
std::vector<std::string> val = { "TRUE" };
std::vector<std::string> val = {"TRUE"};
setFilter(KIDGAME_FILTER, &val);
}
}
@ -419,14 +418,13 @@ bool FileFilterIndex::isFiltered()
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type)
{
const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER,
PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER,
COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER };
const FilterIndexType filterTypes[9] = {FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER,
PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER,
COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER};
std::vector<std::string> filterKeysList[9] = {
mFavoritesIndexFilteredKeys, mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys,
mPubDevIndexFilteredKeys, mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys,
mCompletedIndexFilteredKeys, mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys
};
mCompletedIndexFilteredKeys, mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys};
for (int i = 0; i < 9; i++) {
if (filterTypes[i] == type) {

View file

@ -45,8 +45,7 @@ namespace FileSorts
FileData::SortType(&compareTimesPlayedDescending, "times played, descending"),
FileData::SortType(&compareSystem, "system, ascending"),
FileData::SortType(&compareSystemDescending, "system, descending")
};
FileData::SortType(&compareSystemDescending, "system, descending")};
const std::vector<FileData::SortType> SortTypes(typesArr,
typesArr +

View file

@ -117,11 +117,35 @@ void parseGamelist(SystemData* system)
return;
}
pugi::xml_node alternativeEmulator = doc.child("alternativeEmulator");
if (alternativeEmulator) {
std::string label = alternativeEmulator.child("label").text().get();
if (label != "") {
bool validLabel = false;
for (auto command : system->getSystemEnvData()->mLaunchCommands) {
if (command.second == label)
validLabel = true;
}
if (validLabel) {
system->setAlternativeEmulator(label);
LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName()
<< "\" has a valid alternativeEmulator entry: \"" << label << "\"";
}
else {
system->setAlternativeEmulator("<INVALID>");
LOG(LogWarning) << "System \"" << system->getName()
<< "\" has an invalid alternativeEmulator entry that does "
"not match any command tag in es_systems.xml: \""
<< label << "\"";
}
}
}
std::string relativeTo = system->getStartPath();
bool showHiddenFiles = Settings::getInstance()->getBool("ShowHiddenFiles");
std::vector<std::string> tagList = { "game", "folder" };
FileType typeList[2] = { GAME, FOLDER };
std::vector<std::string> tagList = {"game", "folder"};
FileType typeList[2] = {GAME, FOLDER};
for (int i = 0; i < 2; i++) {
std::string tag = tagList[i];
FileType type = typeList[i];
@ -221,7 +245,7 @@ void addFileDataNode(pugi::xml_node& parent,
}
}
void updateGamelist(SystemData* system)
void updateGamelist(SystemData* system, bool updateAlternativeEmulator)
{
// We do this by reading the XML again, adding changes and then writing them back,
// because there might be information missing in our systemdata which we would otherwise
@ -256,8 +280,39 @@ void updateGamelist(SystemData* system)
LOG(LogError) << "Couldn't find <gameList> node in gamelist \"" << xmlReadPath << "\"";
return;
}
if (updateAlternativeEmulator) {
pugi::xml_node alternativeEmulator = doc.child("alternativeEmulator");
if (system->getAlternativeEmulator() != "") {
if (!alternativeEmulator) {
doc.prepend_child("alternativeEmulator");
alternativeEmulator = doc.child("alternativeEmulator");
}
pugi::xml_node label = alternativeEmulator.child("label");
if (label && system->getAlternativeEmulator() !=
alternativeEmulator.child("label").text().get()) {
alternativeEmulator.remove_child(label);
alternativeEmulator.prepend_child("label").text().set(
system->getAlternativeEmulator().c_str());
}
else if (!label) {
alternativeEmulator.prepend_child("label").text().set(
system->getAlternativeEmulator().c_str());
}
}
else if (alternativeEmulator) {
doc.remove_child("alternativeEmulator");
}
}
}
else {
if (updateAlternativeEmulator && system->getAlternativeEmulator() != "") {
pugi::xml_node alternativeEmulator = doc.prepend_child("alternativeEmulator");
alternativeEmulator.prepend_child("label").text().set(
system->getAlternativeEmulator().c_str());
}
// Set up an empty gamelist to append to.
root = doc.append_child("gameList");
}
@ -312,15 +367,31 @@ void updateGamelist(SystemData* system)
}
// Now write the file.
if (numUpdated > 0) {
if (numUpdated > 0 || updateAlternativeEmulator) {
// Make sure the folders leading up to this path exist (or the write will fail).
std::string xmlWritePath(system->getGamelistPath(true));
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated
<< (numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath
<< "\"";
if (updateAlternativeEmulator) {
if (system->getAlternativeEmulator() == "") {
LOG(LogDebug) << "Gamelist::updateGamelist(): Removed the "
"alternativeEmulator tag for system \""
<< system->getName() << "\" as the default emulator \""
<< system->getSystemEnvData()->mLaunchCommands.front().second
<< "\" was selected";
}
else {
LOG(LogDebug) << "Gamelist::updateGamelist(): "
"Added/updated the alternativeEmulator tag for system \""
<< system->getName() << "\" to \""
<< system->getAlternativeEmulator() << "\"";
}
}
if (numUpdated > 0) {
LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated
<< (numUpdated == 1 ? " entity in \"" : " entities in \"")
<< xmlWritePath << "\"";
}
#if defined(_WIN64)
if (!doc.save_file(Utils::String::stringToWideString(xmlWritePath).c_str())) {
#else

View file

@ -15,6 +15,6 @@ class SystemData;
void parseGamelist(SystemData* system);
// Writes currently loaded metadata for a SystemData to gamelist.xml.
void updateGamelist(SystemData* system);
void updateGamelist(SystemData* system, bool updateAlternativeEmulator = false);
#endif // ES_APP_GAME_LIST_H

View file

@ -83,19 +83,19 @@ void MediaViewer::update(int deltaTime)
void MediaViewer::render()
{
Transform4x4f transform = Transform4x4f::Identity();
Renderer::setMatrix(transform);
glm::mat4 trans{Renderer::getIdentity()};
Renderer::setMatrix(trans);
// Render a black background below the game media.
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
if (mVideo && !mDisplayingImage) {
mVideo->render(transform);
mVideo->render(trans);
#if defined(USE_OPENGL_21)
Renderer::shaderParameters videoParameters;
unsigned int shaders = 0;
unsigned int shaders{0};
if (Settings::getInstance()->getBool("MediaViewerVideoScanlines"))
shaders = Renderer::SHADER_SCANLINES;
if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) {
@ -121,8 +121,8 @@ void MediaViewer::render()
Renderer::shaderPostprocessing(shaders, videoParameters);
#endif
}
else if (mImage && mImage->hasImage() && mImage->getSize() != 0) {
mImage->render(transform);
else if (mImage && mImage->hasImage() && mImage->getSize() != glm::vec2{}) {
mImage->render(trans);
#if defined(USE_OPENGL_21)
if (mCurrentImageIndex == mScreenShotIndex &&

View file

@ -12,7 +12,6 @@
#include "Log.h"
#include "Settings.h"
#include "SystemData.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include <chrono>
@ -472,7 +471,7 @@ bool MiximageGenerator::generateImage()
frameImageAlpha.draw_image(xPosMarquee, yPosMarquee, marqueeImageAlpha);
// Set a frame color based on an average of the screenshot contents.
unsigned char frameColor[] = { 0, 0, 0, 0 };
unsigned char frameColor[] = {0, 0, 0, 0};
sampleFrameColor(screenshotImage, frameColor);
// Upper / lower frame.
@ -564,11 +563,11 @@ void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth,
// an approximately equivalent amount of space on the miximage.
float widthRatio = static_cast<float>(width) / static_cast<float>(height);
widthModifier = Math::clamp(widthModifier + widthRatio / 6.5f, 0.0f, 1.0f);
widthModifier = glm::clamp(widthModifier + widthRatio / 6.5f, 0.0f, 1.0f);
// Hack to increase the size slightly for wider and shorter images.
if (widthRatio >= 4)
widthModifier += Math::clamp(widthRatio / 40.0f, 0.0f, 0.3f);
widthModifier += glm::clamp(widthRatio / 40.0f, 0.0f, 0.3f);
adjustedTargetWidth =
static_cast<unsigned int>(static_cast<float>(targetWidth) * widthModifier);
@ -620,9 +619,9 @@ void MiximageGenerator::sampleFrameColor(CImg<unsigned char>& screenshotImage,
}
}
unsigned char redC = Math::clamp(static_cast<int>(redLine / 255), 0, 255);
unsigned char greenC = Math::clamp(static_cast<int>(greenLine / 255), 0, 255);
unsigned char blueC = Math::clamp(static_cast<int>(blueLine / 255), 0, 255);
unsigned char redC = glm::clamp(static_cast<int>(redLine / 255), 0, 255);
unsigned char greenC = glm::clamp(static_cast<int>(greenLine / 255), 0, 255);
unsigned char blueC = glm::clamp(static_cast<int>(blueLine / 255), 0, 255);
// Convert to the HSL color space to be able to modify saturation and lightness.
CImg<float> colorHSL = CImg<>(1, 1, 1, 3).fill(redC, greenC, blueC).RGBtoHSL();
@ -635,8 +634,8 @@ void MiximageGenerator::sampleFrameColor(CImg<unsigned char>& screenshotImage,
// makes the end result look better than the raw average pixel value. Also clamp
// the lightness to a low value so we don't get a frame that is nearly pitch black
// if the screenshot mostly contains blacks or dark colors.
colorHSL(0, 0, 0, 1) = Math::clamp(saturation * 0.9f, 0.0f, 1.0f);
colorHSL(0, 0, 0, 2) = Math::clamp(lightness * 1.25f, 0.10f, 1.0f);
colorHSL(0, 0, 0, 1) = glm::clamp(saturation * 0.9f, 0.0f, 1.0f);
colorHSL(0, 0, 0, 2) = glm::clamp(lightness * 1.25f, 0.10f, 1.0f);
const CImg<unsigned char> colorRGB = colorHSL.HSLtoRGB();

View file

@ -417,13 +417,27 @@ bool SystemData::loadConfig()
std::string name;
std::string fullname;
std::string path;
std::string cmd;
std::string themeFolder;
name = system.child("name").text().get();
fullname = system.child("fullname").text().get();
path = system.child("path").text().get();
auto nameFindFunc = [&] {
for (auto system : sSystemVector) {
if (system->mName == name) {
LOG(LogWarning) << "A system with the name \"" << name
<< "\" has already been loaded, skipping duplicate entry";
return true;
}
}
return false;
};
// If the name is matching a system that has already been loaded, then skip the entry.
if (nameFindFunc())
continue;
// If there is a %ROMPATH% variable set for the system, expand it. By doing this
// it's possible to use either absolute ROM paths in es_systems.xml or to utilize
// the ROM path configured as ROMDirectory in es_settings.xml. If it's set to ""
@ -461,7 +475,41 @@ bool SystemData::loadConfig()
// Convert extensions list from a string into a vector of strings.
std::vector<std::string> extensions = readList(system.child("extension").text().get());
cmd = system.child("command").text().get();
// Load all launch command tags for the system and if there are multiple tags, then
// the label attribute needs to be set on all entries as it's a requirement for the
// alternative emulator logic.
std::vector<std::pair<std::string, std::string>> commands;
for (pugi::xml_node entry = system.child("command"); entry;
entry = entry.next_sibling("command")) {
if (!entry.attribute("label")) {
if (commands.size() == 1) {
// The first command tag had a label but the second one doesn't.
LOG(LogError)
<< "Missing mandatory label attribute for alternative emulator "
"entry, only the default command tag will be processed for system \""
<< name << "\"";
break;
}
else if (commands.size() > 1) {
// At least two command tags had a label but this one doesn't.
LOG(LogError)
<< "Missing mandatory label attribute for alternative emulator "
"entry, no additional command tags will be processed for system \""
<< name << "\"";
break;
}
}
else if (!commands.empty() && commands.back().second == "") {
// There are more than one command tags and the first tag did not have a label.
LOG(LogError)
<< "Missing mandatory label attribute for alternative emulator "
"entry, only the default command tag will be processed for system \""
<< name << "\"";
break;
}
commands.push_back(
std::make_pair(entry.text().get(), entry.attribute("label").as_string()));
}
// Platform ID list
const std::string platformList =
@ -504,7 +552,7 @@ bool SystemData::loadConfig()
<< "A system in the es_systems.xml file has no name defined, skipping entry";
continue;
}
else if (fullname.empty() || path.empty() || extensions.empty() || cmd.empty()) {
else if (fullname.empty() || path.empty() || extensions.empty() || commands.empty()) {
LOG(LogError) << "System \"" << name
<< "\" is missing the fullname, path, "
"extension, or command tag, skipping entry";
@ -526,7 +574,7 @@ bool SystemData::loadConfig()
SystemEnvironmentData* envData = new SystemEnvironmentData;
envData->mStartPath = path;
envData->mSearchExtensions = extensions;
envData->mLaunchCommand = cmd;
envData->mLaunchCommands = commands;
envData->mPlatformIds = platformIds;
SystemData* newSys = new SystemData(name, fullname, envData, themeFolder);
@ -679,7 +727,7 @@ bool SystemData::createSystemDirectories()
std::string fullname;
std::string path;
std::string extensions;
std::string command;
std::vector<std::string> commands;
std::string platform;
std::string themeFolder;
const std::string systemInfoFileName = "/systeminfo.txt";
@ -690,7 +738,10 @@ bool SystemData::createSystemDirectories()
fullname = system.child("fullname").text().get();
path = system.child("path").text().get();
extensions = system.child("extension").text().get();
command = system.child("command").text().get();
for (pugi::xml_node entry = system.child("command"); entry;
entry = entry.next_sibling("command")) {
commands.push_back(entry.text().get());
}
platform = Utils::String::toLower(system.child("platform").text().get());
themeFolder = system.child("theme").text().as_string(name.c_str());
@ -757,7 +808,16 @@ bool SystemData::createSystemDirectories()
systemInfoFile << "Supported file extensions:" << std::endl;
systemInfoFile << extensions << std::endl << std::endl;
systemInfoFile << "Launch command:" << std::endl;
systemInfoFile << command << std::endl << std::endl;
systemInfoFile << commands.front() << std::endl << std::endl;
// Alternative emulator configuration entries.
if (commands.size() > 1) {
systemInfoFile << (commands.size() == 2 ? "Alternative launch command:" :
"Alternative launch commands:")
<< std::endl;
for (auto it = commands.cbegin() + 1; it != commands.cend(); it++)
systemInfoFile << (*it) << std::endl;
systemInfoFile << std::endl;
}
systemInfoFile << "Platform (for scraping):" << std::endl;
systemInfoFile << platform << std::endl << std::endl;
systemInfoFile << "Theme folder:" << std::endl;
@ -919,7 +979,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem)
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine { randDev() };
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0, total - 1);
int target = uniform_dist(engine);
@ -998,7 +1058,7 @@ FileData* SystemData::getRandomGame(const FileData* currentGame)
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine { randDev() };
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0, total - 1);
target = uniform_dist(engine);
} while (currentGame && gameList.at(target) == currentGame);

View file

@ -27,7 +27,7 @@ class ThemeData;
struct SystemEnvironmentData {
std::string mStartPath;
std::vector<std::string> mSearchExtensions;
std::string mLaunchCommand;
std::vector<std::pair<std::string, std::string>> mLaunchCommands;
std::vector<PlatformIds::PlatformId> mPlatformIds;
};
@ -97,6 +97,9 @@ public:
bool getScrapeFlag() { return mScrapeFlag; }
void setScrapeFlag(bool scrapeflag) { mScrapeFlag = scrapeflag; }
std::string getAlternativeEmulator() { return mAlternativeEmulator; }
void setAlternativeEmulator(const std::string& command) { mAlternativeEmulator = command; }
static void deleteSystems();
// Loads the systems configuration file at getConfigPath() and creates the systems.
static bool loadConfig();
@ -153,6 +156,7 @@ private:
std::string mName;
std::string mFullName;
SystemEnvironmentData* mEnvData;
std::string mAlternativeEmulator;
std::string mThemeFolder;
std::shared_ptr<ThemeData> mTheme;

View file

@ -248,19 +248,19 @@ void SystemScreensaver::renderScreensaver()
if (mVideoScreensaver && screensaverType == "video") {
// Render a black background below the video.
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::setMatrix(Renderer::getIdentity());
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
// Only render the video if the state requires it.
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
Transform4x4f transform = Transform4x4f::Identity();
mVideoScreensaver->render(transform);
glm::mat4 trans{Renderer::getIdentity()};
mVideoScreensaver->render(trans);
}
}
else if (mImageScreensaver && screensaverType == "slideshow") {
// Render a black background below the image.
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::setMatrix(Renderer::getIdentity());
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
@ -268,15 +268,14 @@ void SystemScreensaver::renderScreensaver()
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
if (mImageScreensaver->hasImage()) {
mImageScreensaver->setOpacity(255 - static_cast<unsigned char>(mOpacity * 255));
Transform4x4f transform = Transform4x4f::Identity();
mImageScreensaver->render(transform);
glm::mat4 trans{Renderer::getIdentity()};
mImageScreensaver->render(trans);
}
}
}
if (isScreensaverActive()) {
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::setMatrix(Renderer::getIdentity());
if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") {
if (mHasMediaFiles) {
#if defined(USE_OPENGL_21)
@ -292,13 +291,13 @@ void SystemScreensaver::renderScreensaver()
0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn);
}
mRectangleFadeIn =
Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
glm::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn);
if (mTextFadeIn > 50)
mGameOverlayFont.at(0)->renderTextCache(mGameOverlay.get());
if (mTextFadeIn < 255)
mTextFadeIn = Math::clamp(mTextFadeIn + 2 + mTextFadeIn / 6, 0, 255);
mTextFadeIn = glm::clamp(mTextFadeIn + 2 + mTextFadeIn / 6, 0, 255);
}
}
else {
@ -345,13 +344,13 @@ void SystemScreensaver::renderScreensaver()
0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn);
}
mRectangleFadeIn =
Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
glm::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170);
mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn);
if (mTextFadeIn > 50)
mGameOverlayFont.at(0)->renderTextCache(mGameOverlay.get());
if (mTextFadeIn < 255)
mTextFadeIn = Math::clamp(mTextFadeIn + 2 + mTextFadeIn / 6, 0, 255);
mTextFadeIn = glm::clamp(mTextFadeIn + 2 + mTextFadeIn / 6, 0, 255);
}
}
else {
@ -365,11 +364,11 @@ void SystemScreensaver::renderScreensaver()
dimParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters);
if (mDimValue > 0.4)
mDimValue = Math::clamp(mDimValue - 0.021f, 0.4f, 1.0f);
mDimValue = glm::clamp(mDimValue - 0.021f, 0.4f, 1.0f);
dimParameters.fragmentSaturation = mSaturationAmount;
Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters);
if (mSaturationAmount > 0.0)
mSaturationAmount = Math::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f);
mSaturationAmount = glm::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f);
#else
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
0x000000A0, 0x000000A0);
@ -381,7 +380,7 @@ void SystemScreensaver::renderScreensaver()
blackParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters);
if (mDimValue > 0.0)
mDimValue = Math::clamp(mDimValue - 0.045f, 0.0f, 1.0f);
mDimValue = glm::clamp(mDimValue - 0.045f, 0.0f, 1.0f);
#else
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(),
0x000000FF, 0x000000FF);
@ -515,7 +514,7 @@ void SystemScreensaver::pickRandomImage(std::string& path)
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine { randDev() };
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0,
static_cast<int>(mImageFiles.size()) - 1);
index = uniform_dist(engine);
@ -549,7 +548,7 @@ void SystemScreensaver::pickRandomVideo(std::string& path)
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine { randDev() };
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0,
static_cast<int>(mVideoFiles.size()) - 1);
index = uniform_dist(engine);
@ -577,7 +576,7 @@ void SystemScreensaver::pickRandomCustomImage(std::string& path)
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine { randDev() };
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(
0, static_cast<int>(mImageCustomFiles.size()) - 1);
index = uniform_dist(engine);
@ -609,18 +608,18 @@ void SystemScreensaver::generateOverlayInfo()
mGameOverlayFont.at(0)->buildTextCache(overlayText, posX, posY, 0xFFFFFFFF));
float textSizeX;
float textSizeY = mGameOverlayFont[0].get()->sizeText(overlayText).y();
float textSizeY = mGameOverlayFont[0].get()->sizeText(overlayText).y;
// There is a weird issue with sizeText() where the X size value is returned
// as too large if there are two rows in a string and the second row is longer
// than the first row. Possibly it's the newline character that is somehow
// injected in the size calculation. Regardless, this workaround is working
// fine for the time being.
if (mGameOverlayFont[0].get()->sizeText(gameName).x() >
mGameOverlayFont[0].get()->sizeText(systemName).x())
textSizeX = mGameOverlayFont[0].get()->sizeText(gameName).x();
if (mGameOverlayFont[0].get()->sizeText(gameName).x >
mGameOverlayFont[0].get()->sizeText(systemName).x)
textSizeX = mGameOverlayFont[0].get()->sizeText(gameName).x;
else
textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x();
textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x;
float marginX = Renderer::getWindowWidth() * 0.01f;

View file

@ -9,7 +9,7 @@
#include "VolumeControl.h"
#include "Log.h"
#include "math/Misc.h"
#include "utils/MathUtil.h"
#if defined(_WIN64)
#include <cmath>
@ -212,13 +212,13 @@ int VolumeControl::getVolume() const
}
#endif
volume = Math::clamp(volume, 0, 100);
volume = glm::clamp(volume, 0, 100);
return volume;
}
void VolumeControl::setVolume(int volume)
{
volume = Math::clamp(volume, 0, 100);
volume = glm::clamp(volume, 0, 100);
#if defined(__linux__)
if (mixerElem != nullptr) {

View file

@ -1,41 +0,0 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// MoveCameraAnimation.h
//
// Animation to play when moving the camera, used by the slide transition style.
//
#ifndef ES_APP_ANIMATIONS_MOVE_CAMERA_ANIMATION_H
#define ES_APP_ANIMATIONS_MOVE_CAMERA_ANIMATION_H
#include "animations/Animation.h"
class MoveCameraAnimation : public Animation
{
public:
MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target)
: mCameraStart(camera)
, mTarget(target)
, cameraOut(camera)
{
}
int getDuration() const override { return 400; }
void apply(float t) override
{
// Cubic ease out.
t -= 1;
cameraOut.translation() =
-Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1);
}
private:
Transform4x4f mCameraStart;
Vector3f mTarget;
Transform4x4f& cameraOut;
};
#endif // ES_APP_ANIMATIONS_MOVE_CAMERA_ANIMATION_H

View file

@ -0,0 +1,231 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiAlternativeEmulators.cpp
//
// User interface to select between alternative emulators per system
// based on configuration entries in es_systems.xml.
//
#include "guis/GuiAlternativeEmulators.h"
#include "Gamelist.h"
#include "SystemData.h"
#include "views/ViewController.h"
GuiAlternativeEmulators::GuiAlternativeEmulators(Window* window)
: GuiComponent{window}
, mMenu{window, "ALTERNATIVE EMULATORS"}
, mHasSystems(false)
{
addChild(&mMenu);
mMenu.addButton("BACK", "back", [this] { delete this; });
// Horizontal sizes for the system and label entries.
float systemSizeX = mMenu.getSize().x / 3.27f;
float labelSizeX = mMenu.getSize().x / 1.53;
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); it++) {
// Only include systems that have at least two command entries, unless the system
// has an invalid entry.
if ((*it)->getAlternativeEmulator() != "<INVALID>" &&
(*it)->getSystemEnvData()->mLaunchCommands.size() < 2)
continue;
ComponentListRow row;
// This transparent bracket is only added to generate a left margin.
auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg");
bracket->setOpacity(0);
bracket->setSize(bracket->getSize() / 3.0f);
row.addElement(bracket, false);
std::string name = (*it)->getName();
std::shared_ptr<TextComponent> systemText =
std::make_shared<TextComponent>(mWindow, name, Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
systemText->setSize(systemSizeX, systemText->getSize().y);
row.addElement(systemText, false);
std::string configuredLabel = (*it)->getAlternativeEmulator();
std::string label;
if (configuredLabel == "") {
label = (*it)->getSystemEnvData()->mLaunchCommands.front().second;
}
else {
for (auto command : (*it)->getSystemEnvData()->mLaunchCommands) {
if (command.second == configuredLabel) {
label = command.second;
break;
}
}
}
bool invalidEntry = false;
if (label.empty()) {
label = "<INVALID ENTRY>";
invalidEntry = true;
}
std::shared_ptr<TextComponent> labelText;
if (label == (*it)->getSystemEnvData()->mLaunchCommands.front().second) {
labelText = std::make_shared<TextComponent>(
mWindow, label, Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT), 0x777777FF,
ALIGN_RIGHT);
}
else {
// Mark any non-default value with bold and add a gear symbol as well.
labelText = std::make_shared<TextComponent>(
mWindow, label + (!invalidEntry ? " " + ViewController::GEAR_CHAR : ""),
Font::get(FONT_SIZE_MEDIUM, FONT_PATH_BOLD), 0x777777FF, ALIGN_RIGHT);
}
// Mark invalid entries with red color.
if (invalidEntry)
labelText->setColor(TEXTCOLOR_SCRAPERMARKED);
mCommandRows[name] = labelText;
labelText->setSize(labelSizeX, labelText->getSize().y);
row.addElement(labelText, false);
row.makeAcceptInputHandler([this, it] { selectorWindow(*it); });
mMenu.addRow(row);
mHasSystems = true;
}
// Add a dummy row if no enabled systems have any alternative emulators defined in
// es_systems.xml.
if (!mHasSystems) {
ComponentListRow row;
std::shared_ptr<TextComponent> systemText =
std::make_shared<TextComponent>(mWindow, "<NO ALTERNATIVE EMULATORS DEFINED>",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
row.addElement(systemText, true);
mMenu.addRow(row);
}
float width =
static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() * 1.05f),
static_cast<int>(Renderer::getScreenWidth() * 0.90f)));
setSize(mMenu.getSize());
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, Renderer::getScreenHeight() * 0.13f);
mMenu.setSize(width, Renderer::getScreenHeight() * 0.76f);
}
void GuiAlternativeEmulators::updateMenu(const std::string& systemName,
const std::string& label,
bool defaultEmulator)
{
// If another label was selected, then update the menu accordingly.
if (defaultEmulator) {
mCommandRows[systemName].get()->setFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT));
mCommandRows[systemName].get()->setValue(label);
}
else {
// Mark any non-default value with bold and add a gear symbol as well.
mCommandRows[systemName].get()->setFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_BOLD));
mCommandRows[systemName].get()->setValue(label + " " + ViewController::GEAR_CHAR);
}
mCommandRows[systemName].get()->setColor(DEFAULT_TEXTCOLOR);
}
void GuiAlternativeEmulators::selectorWindow(SystemData* system)
{
auto s = new GuiSettings(mWindow, system->getFullName());
std::string selectedLabel = system->getAlternativeEmulator();
std::string label;
for (auto entry : system->getSystemEnvData()->mLaunchCommands) {
ComponentListRow row;
if (entry.second == "")
label = "<REMOVE INVALID ENTRY>";
else
label = entry.second;
std::shared_ptr<TextComponent> labelText = std::make_shared<TextComponent>(
mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
row.addElement(labelText, true);
row.makeAcceptInputHandler([this, s, system, labelText, entry, selectedLabel] {
if (entry.second != selectedLabel) {
if (entry.second == system->getSystemEnvData()->mLaunchCommands.front().second)
system->setAlternativeEmulator("");
else
system->setAlternativeEmulator(entry.second);
updateGamelist(system, true);
updateMenu(
system->getName(), labelText->getValue(),
(entry.second == system->getSystemEnvData()->mLaunchCommands.front().second));
}
delete s;
});
// This transparent bracket is only added to generate the correct help prompts.
auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg");
bracket->setOpacity(0);
bracket->setSize(bracket->getSize() / 3.0f);
row.addElement(bracket, false);
// Select the row that corresponds to the selected label.
if (selectedLabel == label)
s->addRow(row, true);
else
s->addRow(row, false);
}
// Adjust the width depending on the aspect ratio of the screen, to make the screen look
// somewhat coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9
// reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float maxWidthModifier = glm::clamp(0.70f * aspectValue, 0.50f, 0.92f);
float maxWidth = static_cast<float>(Renderer::getScreenWidth()) * maxWidthModifier;
s->setMenuSize(glm::vec2{maxWidth, s->getMenuSize().y});
auto menuSize = s->getMenuSize();
auto menuPos = s->getMenuPosition();
s->setMenuPosition(glm::vec3{(s->getSize().x - menuSize.x) / 2.0f,
(s->getSize().y - menuSize.y) / 3.0f, menuPos.z});
mWindow->pushGui(s);
}
bool GuiAlternativeEmulators::input(InputConfig* config, Input input)
{
if (input.value != 0 && (config->isMappedTo("b", input) || config->isMappedTo("back", input))) {
delete this;
return true;
}
return mMenu.input(config, input);
}
std::vector<HelpPrompt> GuiAlternativeEmulators::getHelpPrompts()
{
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
prompts.push_back(HelpPrompt("b", "back"));
if (mHasSystems)
prompts.push_back(HelpPrompt("a", "select"));
return prompts;
}
HelpStyle GuiAlternativeEmulators::getHelpStyle()
{
HelpStyle style = HelpStyle();
style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system");
return style;
}

View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiAlternativeEmulators.h
//
// User interface to select between alternative emulators per system
// based on configuration entries in es_systems.xml.
//
#ifndef ES_APP_GUIS_GUI_ALTERNATIVE_EMULATORS_H
#define ES_APP_GUIS_GUI_ALTERNATIVE_EMULATORS_H
#include "GuiComponent.h"
#include "guis/GuiSettings.h"
template <typename T> class OptionListComponent;
class GuiAlternativeEmulators : public GuiComponent
{
public:
GuiAlternativeEmulators(Window* window);
private:
void updateMenu(const std::string& systemName, const std::string& label, bool defaultEmulator);
void selectorWindow(SystemData* system);
virtual bool input(InputConfig* config, Input input) override;
virtual std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override;
MenuComponent mMenu;
bool mHasSystems;
std::map<std::string, std::shared_ptr<TextComponent>> mCommandRows;
std::shared_ptr<OptionListComponent<std::string>> mCommandSelection;
};
#endif // ES_APP_GUIS_GUI_ALTERNATIVE_EMULATORS_H

View file

@ -158,7 +158,7 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::st
auto bracketThemeCollection = std::make_shared<ImageComponent>(mWindow);
bracketThemeCollection->setImage(":/graphics/arrow.svg");
bracketThemeCollection->setResize(
Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
glm::vec2{0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
row.addElement(themeCollection, true);
row.addElement(bracketThemeCollection, false);
row.makeAcceptInputHandler([this, unusedFolders] {
@ -195,7 +195,8 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::st
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketNewCollection = std::make_shared<ImageComponent>(mWindow);
bracketNewCollection->setImage(":/graphics/arrow.svg");
bracketNewCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
bracketNewCollection->setResize(
glm::vec2{0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
row.addElement(newCollection, true);
row.addElement(bracketNewCollection, false);
auto createCollectionCall = [this](const std::string& newVal) {
@ -219,7 +220,8 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::st
mWindow, "DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketDeleteCollection = std::make_shared<ImageComponent>(mWindow);
bracketDeleteCollection->setImage(":/graphics/arrow.svg");
bracketDeleteCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
bracketDeleteCollection->setResize(
glm::vec2{0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
row.addElement(deleteCollection, true);
row.addElement(bracketDeleteCollection, false);
row.makeAcceptInputHandler([this, customSystems] {

View file

@ -22,7 +22,7 @@ GuiGameScraper::GuiGameScraper(Window* window,
ScraperSearchParams params,
std::function<void(const ScraperSearchResult&)> doneFunc)
: GuiComponent(window)
, mGrid(window, Vector2i(1, 7))
, mGrid(window, glm::ivec2{1, 7})
, mBox(window, ":/graphics/frame.svg")
, mSearchParams(params)
, mClose(false)
@ -52,20 +52,20 @@ GuiGameScraper::GuiGameScraper(Window* window,
scrapeName +
((mSearchParams.game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : ""),
Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER);
mGrid.setEntry(mGameName, Vector2i(0, 1), false, true);
mGrid.setEntry(mGameName, glm::ivec2{0, 1}, false, true);
// Row 2 is a spacer.
mSystemName = std::make_shared<TextComponent>(
mWindow, Utils::String::toUpper(mSearchParams.system->getFullName()),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
mGrid.setEntry(mSystemName, Vector2i(0, 3), false, true);
mGrid.setEntry(mSystemName, glm::ivec2{0, 3}, false, true);
// Row 4 is a spacer.
// GuiScraperSearch.
mSearch = std::make_shared<GuiScraperSearch>(window, GuiScraperSearch::NEVER_AUTO_ACCEPT, 1);
mGrid.setEntry(mSearch, Vector2i(0, 5), true);
mGrid.setEntry(mSearch, glm::ivec2{0, 5}, true);
// Buttons
std::vector<std::shared_ptr<ButtonComponent>> buttons;
@ -92,7 +92,7 @@ GuiGameScraper::GuiGameScraper(Window* window,
}));
mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 6}, true, false);
mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) {
doneFunc(result);
@ -103,11 +103,11 @@ GuiGameScraper::GuiGameScraper(Window* window,
// Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is
// the 16:9 reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth();
float width = glm::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth();
setSize(width, Renderer::getScreenHeight() * 0.747f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f);
mGrid.resetCursor();
mSearch->search(params); // Start the search.
@ -115,16 +115,14 @@ GuiGameScraper::GuiGameScraper(Window* window,
void GuiGameScraper::onSizeChanged()
{
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBox.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
mGrid.setRowHeightPerc(0, 0.04f, false);
mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y(),
false); // Game name.
mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y, false);
mGrid.setRowHeightPerc(2, 0.04f, false);
mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y(),
false); // System name.
mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y, false);
mGrid.setRowHeightPerc(4, 0.04f, false);
mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // Buttons.
mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y / mSize.y, false);
mGrid.setSize(mSize);
}

View file

@ -50,7 +50,7 @@ void GuiGamelistFilter::initializeMenu()
mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this));
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f,
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x) / 2.0f,
Renderer::getScreenHeight() * 0.13f);
// Save the initial filter values to be able to check later if any changes were made.
@ -101,12 +101,12 @@ void GuiGamelistFilter::addFiltersToMenu()
row.addElement(mTextFilterField, true);
auto spacer = std::make_shared<GuiComponent>(mWindow);
spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0);
spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f);
row.addElement(spacer, false);
auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg");
bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight()));
bracket->setResize(glm::vec2{0.0f, lbl->getFont()->getLetterHeight()});
row.addElement(bracket, false);
mTextFilterField->setValue(mFilterIndex->getTextFilter());

View file

@ -240,8 +240,7 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system)
// Center the menu.
setSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f,
(mSize.y() - mMenu.getSize().y()) / 2.0f);
mMenu.setPosition((mSize.x - mMenu.getSize().x) / 2.0f, (mSize.y - mMenu.getSize().y) / 2.0f);
}
GuiGamelistOptions::~GuiGamelistOptions()

View file

@ -33,36 +33,36 @@ GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration)
mSize = s->getSize();
// Confirm that the size isn't larger than the screen width, otherwise cap it.
if (mSize.x() > maxWidth) {
s->setSize(maxWidth, mSize[1]);
mSize[0] = maxWidth;
if (mSize.x > maxWidth) {
s->setSize(maxWidth, mSize.y);
mSize.x = maxWidth;
}
if (mSize.y() > maxHeight) {
s->setSize(mSize[0], maxHeight);
mSize[1] = maxHeight;
if (mSize.y > maxHeight) {
s->setSize(mSize.x, maxHeight);
mSize.y = maxHeight;
}
// Add a padding to the box.
int paddingX = static_cast<int>(Renderer::getScreenWidth() * 0.03f);
int paddingY = static_cast<int>(Renderer::getScreenHeight() * 0.02f);
mSize[0] = mSize.x() + paddingX;
mSize[1] = mSize.y() + paddingY;
mSize.x = mSize.x + paddingX;
mSize.y = mSize.y + paddingY;
float posX = Renderer::getScreenWidth() * 0.5f - mSize.x() * 0.5f;
float posX = Renderer::getScreenWidth() * 0.5f - mSize.x * 0.5f;
float posY = Renderer::getScreenHeight() * 0.02f;
setPosition(posX, posY, 0);
mFrame->setImagePath(":/graphics/frame.svg");
mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mFrame->fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
addChild(mFrame);
// We only initialize the actual time when we first start to render.
mStartTime = 0;
mGrid = new ComponentGrid(window, Vector2i(1, 3));
mGrid = new ComponentGrid(window, glm::ivec2{1, 3});
mGrid->setSize(mSize);
mGrid->setEntry(s, Vector2i(0, 1), false, true);
mGrid->setEntry(s, glm::ivec2{0, 1}, false, true);
addChild(mGrid);
}
@ -72,10 +72,10 @@ GuiInfoPopup::~GuiInfoPopup()
delete mFrame;
}
void GuiInfoPopup::render(const Transform4x4f& /*parentTrans*/)
void GuiInfoPopup::render(const glm::mat4& /*parentTrans*/)
{
// We use Identity() as we want to render on a specific window position, not on the view.
Transform4x4f trans = getTransform() * Transform4x4f::Identity();
// We use getIdentity() as we want to render on a specific window position, not on the view.
glm::mat4 trans{getTransform() * Renderer::getIdentity()};
if (mRunning && updateState()) {
// If we're still supposed to be rendering it.
Renderer::setMatrix(trans);

View file

@ -21,7 +21,7 @@ public:
GuiInfoPopup(Window* window, std::string message, int duration);
~GuiInfoPopup();
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void stop() override { mRunning = false; }
private:

View file

@ -12,7 +12,6 @@
#include "SystemData.h"
#include "components/ComponentGrid.h"
#include "components/TextComponent.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
GuiLaunchScreen::GuiLaunchScreen(Window* window)
@ -34,7 +33,7 @@ GuiLaunchScreen::~GuiLaunchScreen()
void GuiLaunchScreen::displayLaunchScreen(FileData* game)
{
mGrid = new ComponentGrid(mWindow, Vector2i(3, 8));
mGrid = new ComponentGrid(mWindow, glm::ivec2{3, 8});
addChild(mGrid);
mImagePath = game->getMarqueePath();
@ -51,8 +50,8 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
const float gameNameFontSize = 0.073f;
// Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), false, false,
Vector2i(1, 1));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 0}, false, false,
glm::ivec2{1, 1});
// Title.
mTitle = std::make_shared<TextComponent>(
@ -60,19 +59,19 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
Font::get(static_cast<int>(
titleFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))),
0x666666FF, ALIGN_CENTER);
mGrid->setEntry(mTitle, Vector2i(1, 1), false, true, Vector2i(1, 1));
mGrid->setEntry(mTitle, glm::ivec2{1, 1}, false, true, glm::ivec2{1, 1});
// Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 2), false, false,
Vector2i(1, 1));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 2}, false, false,
glm::ivec2{1, 1});
// Row for the marquee.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 3), false, false,
Vector2i(1, 1));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 3}, false, false,
glm::ivec2{1, 1});
// Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 4), false, false,
Vector2i(1, 1));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 4}, false, false,
glm::ivec2{1, 1});
// Game name.
mGameName = std::make_shared<TextComponent>(
@ -80,32 +79,32 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
Font::get(static_cast<int>(
gameNameFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))),
0x444444FF, ALIGN_CENTER);
mGrid->setEntry(mGameName, Vector2i(1, 5), false, true, Vector2i(1, 1));
mGrid->setEntry(mGameName, glm::ivec2{1, 5}, false, true, glm::ivec2{1, 1});
// System name.
mSystemName = std::make_shared<TextComponent>(
mWindow, "SYSTEM NAME", Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER);
mGrid->setEntry(mSystemName, Vector2i(1, 6), false, true, Vector2i(1, 1));
mGrid->setEntry(mSystemName, glm::ivec2{1, 6}, false, true, glm::ivec2{1, 1});
// Spacer row.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 7), false, false,
Vector2i(1, 1));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 7}, false, false,
glm::ivec2{1, 1});
// Left spacer.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false, false,
Vector2i(1, 8));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{0, 0}, false, false,
glm::ivec2{1, 8});
// Right spacer.
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(2, 0), false, false,
Vector2i(1, 8));
mGrid->setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{2, 0}, false, false,
glm::ivec2{1, 8});
// Adjust the width depending on the aspect ratio of the screen, to make the screen look
// somewhat coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9
// reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float maxWidthModifier = Math::clamp(0.78f * aspectValue, 0.78f, 0.90f);
float minWidthModifier = Math::clamp(0.50f * aspectValue, 0.50f, 0.65f);
float maxWidthModifier = glm::clamp(0.78f * aspectValue, 0.78f, 0.90f);
float minWidthModifier = glm::clamp(0.50f * aspectValue, 0.50f, 0.65f);
float maxWidth = static_cast<float>(Renderer::getScreenWidth()) * maxWidthModifier;
float minWidth = static_cast<float>(Renderer::getScreenWidth()) * minWidthModifier;
@ -114,12 +113,12 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
Font::get(static_cast<int>(gameNameFontSize * std::min(Renderer::getScreenHeight(),
Renderer::getScreenWidth())))
->sizeText(Utils::String::toUpper(game->getName()))
.x();
.x;
// Add a bit of width to compensate for the left and right spacers.
fontWidth += static_cast<float>(Renderer::getScreenWidth()) * 0.05f;
float width = Math::clamp(fontWidth, minWidth, maxWidth);
float width = glm::clamp(fontWidth, minWidth, maxWidth);
if (mImagePath != "")
setSize(width, static_cast<float>(Renderer::getScreenHeight()) * 0.60f);
@ -131,15 +130,15 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
mGrid->setRowHeightPerc(0, 0.09f, false);
else
mGrid->setRowHeightPerc(0, 0.15f, false);
mGrid->setRowHeightPerc(1, mTitle->getFont()->getLetterHeight() * 1.70f / mSize.y(), false);
mGrid->setRowHeightPerc(1, mTitle->getFont()->getLetterHeight() * 1.70f / mSize.y, false);
mGrid->setRowHeightPerc(2, 0.05f, false);
if (mImagePath != "")
mGrid->setRowHeightPerc(3, 0.35f, false);
else
mGrid->setRowHeightPerc(3, 0.01f, false);
mGrid->setRowHeightPerc(4, 0.05f, false);
mGrid->setRowHeightPerc(5, mGameName->getFont()->getHeight() * 0.80f / mSize.y(), false);
mGrid->setRowHeightPerc(6, mSystemName->getFont()->getHeight() * 0.90f / mSize.y(), false);
mGrid->setRowHeightPerc(5, mGameName->getFont()->getHeight() * 0.80f / mSize.y, false);
mGrid->setRowHeightPerc(6, mSystemName->getFont()->getHeight() * 0.90f / mSize.y, false);
// Set left and right spacers column widths.
mGrid->setColWidthPerc(0, 0.025f);
@ -153,7 +152,7 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
for (int i = 0; i < 7; i++)
totalRowHeight += mGrid->getRowHeight(i);
setSize(mSize.x(), totalRowHeight);
setSize(mSize.x, totalRowHeight);
mGameName->setText(Utils::String::toUpper(game->getName()));
mSystemName->setText(Utils::String::toUpper(game->getSystem()->getFullName()));
@ -163,29 +162,29 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game)
// width so that the sizes look somewhat consistent regardless of the aspect ratio
// of the images.
if (mImagePath != "") {
mMarquee->setImage(game->getMarqueePath(), false);
mMarquee->setImage(game->getMarqueePath(), false, true);
mMarquee->cropTransparentPadding(static_cast<float>(Renderer::getScreenWidth()) *
(0.25f * (1.778f / Renderer::getScreenAspectRatio())),
mGrid->getRowHeight(3));
mMarquee->setOrigin(0.5f, 0.5f);
Vector3f currentPos = mMarquee->getPosition();
Vector2f currentSize = mMarquee->getSize();
glm::vec3 currentPos{mMarquee->getPosition()};
glm::vec2 currentSize{mMarquee->getSize()};
// Position the image in the middle of row four.
currentPos.x() = mSize.x() / 2.0f;
currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + mGrid->getRowHeight(2) +
mGrid->getRowHeight(3) / 2.0f;
currentPos.x = mSize.x / 2.0f;
currentPos.y = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + mGrid->getRowHeight(2) +
mGrid->getRowHeight(3) / 2.0f;
mMarquee->setPosition(currentPos);
}
setOrigin({ 0.5f, 0.5f });
setOrigin({0.5f, 0.5f});
// Center on the X axis and keep slightly off-center on the Y axis.
setPosition(static_cast<float>(Renderer::getScreenWidth()) / 2.0f,
static_cast<float>(Renderer::getScreenHeight()) / 2.25f);
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
mBackground.setEdgeColor(0xEEEEEEFF);
}
@ -219,7 +218,7 @@ void GuiLaunchScreen::update(int deltaTime)
if (Settings::getInstance()->getString("MenuOpeningEffect") == "none")
mScaleUp = 1.0f;
else if (mScaleUp < 1.0f)
mScaleUp = Math::clamp(mScaleUp + 0.07f, 0.0f, 1.0f);
mScaleUp = glm::clamp(mScaleUp + 0.07f, 0.0f, 1.0f);
}
void GuiLaunchScreen::render()
@ -228,7 +227,7 @@ void GuiLaunchScreen::render()
if (mScaleUp < 1.0f)
setScale(mScaleUp);
Transform4x4f trans = Transform4x4f::Identity() * getTransform();
glm::mat4 trans{Renderer::getIdentity() * getTransform()};
Renderer::setMatrix(trans);
GuiComponent::renderChildren(trans);

View file

@ -20,6 +20,7 @@
#include "components/OptionListComponent.h"
#include "components/SliderComponent.h"
#include "components/SwitchComponent.h"
#include "guis/GuiAlternativeEmulators.h"
#include "guis/GuiCollectionSystemsOptions.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiDetectDevice.h"
@ -75,8 +76,7 @@ GuiMenu::GuiMenu(Window* window)
addChild(&mMenu);
addVersionInfo();
setSize(mMenu.getSize());
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
Renderer::getScreenHeight() * 0.13f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, Renderer::getScreenHeight() * 0.13f);
}
GuiMenu::~GuiMenu()
@ -87,7 +87,11 @@ GuiMenu::~GuiMenu()
ViewController::get()->stopScrolling();
}
void GuiMenu::openScraperOptions() { mWindow->pushGui(new GuiScraperMenu(mWindow, "SCRAPER")); }
void GuiMenu::openScraperOptions()
{
// Open the scraper menu.
mWindow->pushGui(new GuiScraperMenu(mWindow, "SCRAPER"));
}
void GuiMenu::openUIOptions()
{
@ -97,17 +101,16 @@ void GuiMenu::openUIOptions()
auto startup_system = std::make_shared<OptionListComponent<std::string>>(
mWindow, getHelpStyle(), "GAMELIST ON STARTUP", false);
startup_system->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == "");
float dotsSize = Font::get(FONT_SIZE_MEDIUM)->sizeText("...").x();
float dotsSize = Font::get(FONT_SIZE_MEDIUM)->sizeText("...").x;
for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend();
it++) {
if ((*it)->getName() != "retropie") {
// If required, abbreviate the system name so it doesn't overlap the setting name.
std::string abbreviatedString =
Font::get(FONT_SIZE_MEDIUM)
->getTextMaxWidth((*it)->getFullName(), mSize.x() * 0.47f);
float sizeDifference = Font::get(FONT_SIZE_MEDIUM)->sizeText((*it)->getFullName()).x() -
Font::get(FONT_SIZE_MEDIUM)->sizeText(abbreviatedString).x();
if (sizeDifference > 0) {
Font::get(FONT_SIZE_MEDIUM)->getTextMaxWidth((*it)->getFullName(), mSize.x * 0.47f);
float sizeDifference = Font::get(FONT_SIZE_MEDIUM)->sizeText((*it)->getFullName()).x -
Font::get(FONT_SIZE_MEDIUM)->sizeText(abbreviatedString).x;
if (sizeDifference > 0.0f) {
// It doesn't make sense to abbreviate if the number of pixels removed by
// the abbreviation is less or equal to the size of the three dots that
// would be appended to the string.
@ -374,6 +377,28 @@ void GuiMenu::openUIOptions()
}
});
// Media viewer.
ComponentListRow media_viewer_row;
media_viewer_row.elements.clear();
media_viewer_row.addElement(std::make_shared<TextComponent>(mWindow, "MEDIA VIEWER SETTINGS",
Font::get(FONT_SIZE_MEDIUM),
0x777777FF),
true);
media_viewer_row.addElement(makeArrow(mWindow), false);
media_viewer_row.makeAcceptInputHandler(std::bind(&GuiMenu::openMediaViewerOptions, this));
s->addRow(media_viewer_row);
// Screensaver.
ComponentListRow screensaver_row;
screensaver_row.elements.clear();
screensaver_row.addElement(std::make_shared<TextComponent>(mWindow, "SCREENSAVER SETTINGS",
Font::get(FONT_SIZE_MEDIUM),
0x777777FF),
true);
screensaver_row.addElement(makeArrow(mWindow), false);
screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this));
s->addRow(screensaver_row);
#if defined(USE_OPENGL_21)
// Blur background when the menu is open.
auto menu_blur_background = std::make_shared<SwitchComponent>(mWindow);
@ -556,28 +581,6 @@ void GuiMenu::openUIOptions()
}
});
// Media viewer.
ComponentListRow media_viewer_row;
media_viewer_row.elements.clear();
media_viewer_row.addElement(std::make_shared<TextComponent>(mWindow, "MEDIA VIEWER SETTINGS",
Font::get(FONT_SIZE_MEDIUM),
0x777777FF),
true);
media_viewer_row.addElement(makeArrow(mWindow), false);
media_viewer_row.makeAcceptInputHandler(std::bind(&GuiMenu::openMediaViewerOptions, this));
s->addRow(media_viewer_row);
// Screensaver.
ComponentListRow screensaver_row;
screensaver_row.elements.clear();
screensaver_row.addElement(std::make_shared<TextComponent>(mWindow, "SCREENSAVER SETTINGS",
Font::get(FONT_SIZE_MEDIUM),
0x777777FF),
true);
screensaver_row.addElement(makeArrow(mWindow), false);
screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this));
s->addRow(screensaver_row);
mWindow->pushGui(s);
}
@ -770,6 +773,49 @@ void GuiMenu::openOtherOptions()
{
auto s = new GuiSettings(mWindow, "OTHER SETTINGS");
// Alternative emulators GUI.
ComponentListRow alternativeEmulatorsRow;
alternativeEmulatorsRow.elements.clear();
alternativeEmulatorsRow.addElement(
std::make_shared<TextComponent>(mWindow, "ALTERNATIVE EMULATORS",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
true);
alternativeEmulatorsRow.addElement(makeArrow(mWindow), false);
alternativeEmulatorsRow.makeAcceptInputHandler(
std::bind([this] { mWindow->pushGui(new GuiAlternativeEmulators(mWindow)); }));
s->addRow(alternativeEmulatorsRow);
// Game media directory.
ComponentListRow rowMediaDir;
auto media_directory = std::make_shared<TextComponent>(mWindow, "GAME MEDIA DIRECTORY",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketMediaDirectory = std::make_shared<ImageComponent>(mWindow);
bracketMediaDirectory->setImage(":/graphics/arrow.svg");
bracketMediaDirectory->setResize(
glm::vec2{0.0f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()});
rowMediaDir.addElement(media_directory, true);
rowMediaDir.addElement(bracketMediaDirectory, false);
std::string titleMediaDir = "ENTER GAME MEDIA DIRECTORY";
std::string mediaDirectoryStaticText = "Default directory:";
std::string defaultDirectoryText = "~/.emulationstation/downloaded_media/";
std::string initValueMediaDir = Settings::getInstance()->getString("MediaDirectory");
bool multiLineMediaDir = false;
auto updateValMediaDir = [this](const std::string& newVal) {
Settings::getInstance()->setString("MediaDirectory", newVal);
Settings::getInstance()->saveFile();
ViewController::get()->reloadAll();
mWindow->invalidateCachedBackground();
};
rowMediaDir.makeAcceptInputHandler([this, titleMediaDir, mediaDirectoryStaticText,
defaultDirectoryText, initValueMediaDir, updateValMediaDir,
multiLineMediaDir] {
mWindow->pushGui(new GuiComplexTextEditPopup(
mWindow, getHelpStyle(), titleMediaDir, mediaDirectoryStaticText, defaultDirectoryText,
Settings::getInstance()->getString("MediaDirectory"), updateValMediaDir,
multiLineMediaDir, "SAVE", "SAVE CHANGES?"));
});
s->addRow(rowMediaDir);
// Maximum VRAM.
auto max_vram = std::make_shared<SliderComponent>(mWindow, 80.f, 1024.f, 8.f, "MiB");
max_vram->setValue(static_cast<float>(Settings::getInstance()->getInt("MaxVRAM")));
@ -893,36 +939,6 @@ void GuiMenu::openOtherOptions()
}
});
// Game media directory.
ComponentListRow rowMediaDir;
auto media_directory = std::make_shared<TextComponent>(mWindow, "GAME MEDIA DIRECTORY",
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
auto bracketMediaDirectory = std::make_shared<ImageComponent>(mWindow);
bracketMediaDirectory->setImage(":/graphics/arrow.svg");
bracketMediaDirectory->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
rowMediaDir.addElement(media_directory, true);
rowMediaDir.addElement(bracketMediaDirectory, false);
std::string titleMediaDir = "ENTER GAME MEDIA DIRECTORY";
std::string mediaDirectoryStaticText = "Default directory:";
std::string defaultDirectoryText = "~/.emulationstation/downloaded_media/";
std::string initValueMediaDir = Settings::getInstance()->getString("MediaDirectory");
bool multiLineMediaDir = false;
auto updateValMediaDir = [this](const std::string& newVal) {
Settings::getInstance()->setString("MediaDirectory", newVal);
Settings::getInstance()->saveFile();
ViewController::get()->reloadAll();
mWindow->invalidateCachedBackground();
};
rowMediaDir.makeAcceptInputHandler([this, titleMediaDir, mediaDirectoryStaticText,
defaultDirectoryText, initValueMediaDir, updateValMediaDir,
multiLineMediaDir] {
mWindow->pushGui(new GuiComplexTextEditPopup(
mWindow, getHelpStyle(), titleMediaDir, mediaDirectoryStaticText, defaultDirectoryText,
Settings::getInstance()->getString("MediaDirectory"), updateValMediaDir,
multiLineMediaDir, "SAVE", "SAVE CHANGES?"));
});
s->addRow(rowMediaDir);
#if defined(_WIN64)
// Hide taskbar during the ES program session.
auto hide_taskbar = std::make_shared<SwitchComponent>(mWindow);
@ -1259,8 +1275,8 @@ void GuiMenu::openCollectionSystemOptions()
void GuiMenu::onSizeChanged()
{
mVersion.setSize(mSize.x(), 0);
mVersion.setPosition(0, mSize.y() - mVersion.getSize().y());
mVersion.setSize(mSize.x, 0.0f);
mVersion.setPosition(0.0f, mSize.y - mVersion.getSize().y);
}
void GuiMenu::addEntry(const std::string& name,

View file

@ -42,7 +42,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
: GuiComponent(window)
, mScraperParams(scraperParams)
, mBackground(window, ":/graphics/frame.svg")
, mGrid(window, Vector2i(1, 3))
, mGrid(window, glm::ivec2{1, 3})
, mMetaDataDecl(mdd)
, mMetaData(md)
, mSavedCallback(saveCallback)
@ -53,7 +53,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
addChild(&mBackground);
addChild(&mGrid);
mHeaderGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i(1, 5));
mHeaderGrid = std::make_shared<ComponentGrid>(mWindow, glm::ivec2{1, 5});
mTitle = std::make_shared<TextComponent>(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE),
0x555555FF, ALIGN_CENTER);
@ -78,15 +78,15 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
folderPath + Utils::FileSystem::getFileName(scraperParams.game->getPath()) + " [" +
Utils::String::toUpper(scraperParams.system->getName()) + "]" +
(scraperParams.game->getType() == FOLDER ? " " + ViewController::FOLDER_CHAR : ""),
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f),
Vector2f(0.0f, 0.0f), 0x00000000, 0.05f);
mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), false, true);
mHeaderGrid->setEntry(mSubtitle, Vector2i(0, 3), false, true);
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, glm::vec3{}, glm::vec2{}, 0x00000000,
0.05f);
mHeaderGrid->setEntry(mTitle, glm::ivec2{0, 1}, false, true);
mHeaderGrid->setEntry(mSubtitle, glm::ivec2{0, 3}, false, true);
mGrid.setEntry(mHeaderGrid, Vector2i(0, 0), false, true);
mGrid.setEntry(mHeaderGrid, glm::ivec2{0, 0}, false, true);
mList = std::make_shared<ComponentList>(mWindow);
mGrid.setEntry(mList, Vector2i(0, 1), true, true);
mGrid.setEntry(mList, glm::ivec2{0, 1}, true, true);
// Populate list.
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) {
@ -123,8 +123,8 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
case MD_BOOL: {
ed = std::make_shared<SwitchComponent>(window);
// Make the switches slightly smaller.
auto switchSize = ed->getSize() * 0.9f;
ed->setResize(switchSize.x(), switchSize.y());
glm::vec2 switchSize{ed->getSize() * 0.9f};
ed->setResize(switchSize.x, switchSize.y);
ed->setOrigin(-0.05f, -0.09f);
ed->setChangedColor(ICONCOLOR_USERMARKED);
@ -138,8 +138,8 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
ed = std::make_shared<RatingComponent>(window, true);
ed->setChangedColor(ICONCOLOR_USERMARKED);
const float height = lbl->getSize().y() * 0.71f;
ed->setSize(0, height);
const float height = lbl->getSize().y * 0.71f;
ed->setSize(0.0f, height);
row.addElement(ed, false, true);
auto ratingSpacer = std::make_shared<GuiComponent>(mWindow);
@ -182,7 +182,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg");
bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight()));
bracket->setResize(glm::vec2{0.0f, lbl->getFont()->getLetterHeight()});
row.addElement(bracket, false);
bool multiLine = false;
@ -198,8 +198,28 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
};
std::string staticTextString = "Default value from es_systems.xml:";
std::string defaultLaunchCommand =
scraperParams.system->getSystemEnvData()->mLaunchCommand;
std::string defaultLaunchCommand;
std::string alternativeEmulator = scraperParams.system->getAlternativeEmulator();
for (auto launchCommand :
scraperParams.system->getSystemEnvData()->mLaunchCommands) {
if (launchCommand.second == alternativeEmulator) {
defaultLaunchCommand = launchCommand.first;
break;
}
}
if (!alternativeEmulator.empty() && defaultLaunchCommand.empty()) {
LOG(LogWarning)
<< "The alternative emulator defined for system \""
<< scraperParams.system->getName()
<< "\" is invalid, falling back to the default command \""
<< scraperParams.system->getSystemEnvData()->mLaunchCommands.front().first
<< "\"";
}
if (defaultLaunchCommand.empty())
defaultLaunchCommand =
scraperParams.system->getSystemEnvData()->mLaunchCommands.front().first;
row.makeAcceptInputHandler([this, title, staticTextString, defaultLaunchCommand, ed,
updateVal, multiLine] {
@ -223,7 +243,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg");
bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight()));
bracket->setResize(glm::vec2{0.0f, lbl->getFont()->getLetterHeight()});
row.addElement(bracket, false);
bool multiLine = iter->type == MD_MULTILINE_STRING;
@ -346,15 +366,15 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
}
mButtons = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtons, Vector2i(0, 2), true, false);
mGrid.setEntry(mButtons, glm::ivec2{0, 2}, true, false);
// Resize + center.
float width =
static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() * 1.05f),
static_cast<int>(Renderer::getScreenWidth() * 0.90f)));
setSize(width, Renderer::getScreenHeight() * 0.83f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f);
}
void GuiMetaDataEd::onSizeChanged()
@ -363,19 +383,19 @@ void GuiMetaDataEd::onSizeChanged()
const float titleHeight = mTitle->getFont()->getLetterHeight();
const float subtitleHeight = mSubtitle->getFont()->getLetterHeight();
const float titleSubtitleSpacing = mSize.y() * 0.03f;
const float titleSubtitleSpacing = mSize.y * 0.03f;
mGrid.setRowHeightPerc(
0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / mSize.y());
mGrid.setRowHeightPerc(2, mButtons->getSize().y() / mSize.y());
0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / mSize.y);
mGrid.setRowHeightPerc(2, mButtons->getSize().y / mSize.y);
// Snap list size to the row height to prevent a fraction of a row from being displayed.
float listHeight = 0;
float listSize = mList->getSize().y();
float listHeight = 0.0f;
float listSize = mList->getSize().y;
int i = 0;
while (i < mList->size()) {
// Add the separator height to the row height so that it also gets properly rendered.
float rowHeight = mList->getRowHeight(i) + (1 * Renderer::getScreenHeightModifier());
float rowHeight = mList->getRowHeight(i) + Renderer::getScreenHeightModifier();
if (listHeight + rowHeight < listSize)
listHeight += rowHeight;
else
@ -385,19 +405,19 @@ void GuiMetaDataEd::onSizeChanged()
// Adjust the size of the list and window.
float heightAdjustment = listSize - listHeight;
mList->setSize(mList->getSize().x(), listHeight);
Vector2f newWindowSize = mSize;
newWindowSize.y() -= heightAdjustment;
mBackground.fitTo(newWindowSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mList->setSize(mList->getSize().x, listHeight);
glm::vec2 newWindowSize{mSize};
newWindowSize.y -= heightAdjustment;
mBackground.fitTo(newWindowSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
// Move the buttons up as well to make the layout align correctly after the resize.
Vector3f newButtonPos = mButtons->getPosition();
newButtonPos.y() -= heightAdjustment;
glm::vec3 newButtonPos{mButtons->getPosition()};
newButtonPos.y -= heightAdjustment;
mButtons->setPosition(newButtonPos);
mHeaderGrid->setRowHeightPerc(1, titleHeight / mHeaderGrid->getSize().y());
mHeaderGrid->setRowHeightPerc(2, titleSubtitleSpacing / mHeaderGrid->getSize().y());
mHeaderGrid->setRowHeightPerc(3, subtitleHeight / mHeaderGrid->getSize().y());
mHeaderGrid->setRowHeightPerc(1, titleHeight / mHeaderGrid->getSize().y);
mHeaderGrid->setRowHeightPerc(2, titleSubtitleSpacing / mHeaderGrid->getSize().y);
mHeaderGrid->setRowHeightPerc(3, subtitleHeight / mHeaderGrid->getSize().y);
}
void GuiMetaDataEd::save()

View file

@ -16,7 +16,7 @@
GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue<FileData*>& gameQueue)
: GuiComponent(window)
, mBackground(window, ":/graphics/frame.svg")
, mGrid(window, Vector2i(6, 13))
, mGrid(window, glm::ivec2{6, 13})
, mGameQueue(gameQueue)
{
addChild(&mBackground);
@ -38,100 +38,100 @@ GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue<FileDa
// Header.
mTitle = std::make_shared<TextComponent>(mWindow, "MIXIMAGE OFFLINE GENERATOR",
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(6, 1));
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{6, 1});
mStatus = std::make_shared<TextComponent>(mWindow, "NOT STARTED", Font::get(FONT_SIZE_MEDIUM),
0x777777FF, ALIGN_CENTER);
mGrid.setEntry(mStatus, Vector2i(0, 1), false, true, Vector2i(6, 1));
mGrid.setEntry(mStatus, glm::ivec2{0, 1}, false, true, glm::ivec2{6, 1});
mGameCounter = std::make_shared<TextComponent>(
mWindow,
std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) +
(mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED",
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
mGrid.setEntry(mGameCounter, Vector2i(0, 2), false, true, Vector2i(6, 1));
mGrid.setEntry(mGameCounter, glm::ivec2{0, 2}, false, true, glm::ivec2{6, 1});
// Spacer row with top border.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 3), false, false,
Vector2i(6, 1), GridFlags::BORDER_TOP);
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{0, 3}, false, false,
glm::ivec2{6, 1}, GridFlags::BORDER_TOP);
// Left spacer.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 4), false, false,
Vector2i(1, 7));
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{0, 4}, false, false,
glm::ivec2{1, 7});
// Generated label.
mGeneratedLbl = std::make_shared<TextComponent>(
mWindow, "Generated:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mGeneratedLbl, Vector2i(1, 4), false, true, Vector2i(1, 1));
mGrid.setEntry(mGeneratedLbl, glm::ivec2{1, 4}, false, true, glm::ivec2{1, 1});
// Generated value/counter.
mGeneratedVal =
std::make_shared<TextComponent>(mWindow, std::to_string(mGamesProcessed),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mGeneratedVal, Vector2i(2, 4), false, true, Vector2i(1, 1));
mGrid.setEntry(mGeneratedVal, glm::ivec2{2, 4}, false, true, glm::ivec2{1, 1});
// Overwritten label.
mOverwrittenLbl = std::make_shared<TextComponent>(
mWindow, "Overwritten:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mOverwrittenLbl, Vector2i(1, 5), false, true, Vector2i(1, 1));
mGrid.setEntry(mOverwrittenLbl, glm::ivec2{1, 5}, false, true, glm::ivec2{1, 1});
// Overwritten value/counter.
mOverwrittenVal =
std::make_shared<TextComponent>(mWindow, std::to_string(mImagesOverwritten),
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mOverwrittenVal, Vector2i(2, 5), false, true, Vector2i(1, 1));
mGrid.setEntry(mOverwrittenVal, glm::ivec2{2, 5}, false, true, glm::ivec2{1, 1});
// Skipping label.
mSkippedLbl = std::make_shared<TextComponent>(
mWindow, "Skipped (existing):", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mSkippedLbl, Vector2i(1, 6), false, true, Vector2i(1, 1));
mGrid.setEntry(mSkippedLbl, glm::ivec2{1, 6}, false, true, glm::ivec2{1, 1});
// Skipping value/counter.
mSkippedVal = std::make_shared<TextComponent>(
mWindow, std::to_string(mGamesSkipped), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mSkippedVal, Vector2i(2, 6), false, true, Vector2i(1, 1));
mGrid.setEntry(mSkippedVal, glm::ivec2{2, 6}, false, true, glm::ivec2{1, 1});
// Failed label.
mFailedLbl = std::make_shared<TextComponent>(mWindow, "Failed:", Font::get(FONT_SIZE_SMALL),
0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mFailedLbl, Vector2i(1, 7), false, true, Vector2i(1, 1));
mGrid.setEntry(mFailedLbl, glm::ivec2{1, 7}, false, true, glm::ivec2{1, 1});
// Failed value/counter.
mFailedVal = std::make_shared<TextComponent>(
mWindow, std::to_string(mGamesFailed), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mFailedVal, Vector2i(2, 7), false, true, Vector2i(1, 1));
mGrid.setEntry(mFailedVal, glm::ivec2{2, 7}, false, true, glm::ivec2{1, 1});
// Processing label.
mProcessingLbl = std::make_shared<TextComponent>(
mWindow, "Processing: ", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mProcessingLbl, Vector2i(3, 4), false, true, Vector2i(1, 1));
mGrid.setEntry(mProcessingLbl, glm::ivec2{3, 4}, false, true, glm::ivec2{1, 1});
// Processing value.
mProcessingVal = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL),
0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mProcessingVal, Vector2i(4, 4), false, true, Vector2i(1, 1));
mGrid.setEntry(mProcessingVal, glm::ivec2{4, 4}, false, true, glm::ivec2{1, 1});
// Spacer row.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 8), false, false,
Vector2i(4, 1));
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 8}, false, false,
glm::ivec2{4, 1});
// Last error message label.
mLastErrorLbl = std::make_shared<TextComponent>(
mWindow, "Last error message:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mLastErrorLbl, Vector2i(1, 9), false, true, Vector2i(4, 1));
mGrid.setEntry(mLastErrorLbl, glm::ivec2{1, 9}, false, true, glm::ivec2{4, 1});
// Last error message value.
mLastErrorVal = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL),
0x888888FF, ALIGN_LEFT);
mGrid.setEntry(mLastErrorVal, Vector2i(1, 10), false, true, Vector2i(4, 1));
mGrid.setEntry(mLastErrorVal, glm::ivec2{1, 10}, false, true, glm::ivec2{4, 1});
// Right spacer.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(5, 4), false, false,
Vector2i(1, 7));
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{5, 4}, false, false,
glm::ivec2{1, 7});
// Spacer row with bottom border.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 11), false, false,
Vector2i(6, 1), GridFlags::BORDER_BOTTOM);
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{0, 11}, false, false,
glm::ivec2{6, 1}, GridFlags::BORDER_BOTTOM);
// Buttons.
std::vector<std::shared_ptr<ButtonComponent>> buttons;
@ -177,7 +177,7 @@ GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue<FileDa
buttons.push_back(mCloseButton);
mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 12), true, false, Vector2i(6, 1));
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 12}, true, false, glm::ivec2{6, 1});
// For narrower displays (e.g. in 4:3 ratio), allow the window to fill 95% of the screen
// width rather than the 85% allowed for wider displays.
@ -185,8 +185,8 @@ GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue<FileDa
Renderer::getScreenWidth() * ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f);
setSize(width, Renderer::getScreenHeight() * 0.75f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f);
}
GuiOfflineGenerator::~GuiOfflineGenerator()
@ -203,13 +203,13 @@ GuiOfflineGenerator::~GuiOfflineGenerator()
void GuiOfflineGenerator::onSizeChanged()
{
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
// Set row heights.
mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y(), false);
mGrid.setRowHeightPerc(1, (mStatus->getFont()->getLetterHeight() + 2.0f) / mSize.y(), false);
mGrid.setRowHeightPerc(2, mGameCounter->getFont()->getHeight() * 1.75f / mSize.y(), false);
mGrid.setRowHeightPerc(3, (mStatus->getFont()->getLetterHeight() + 3.0f) / mSize.y(), false);
mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y, false);
mGrid.setRowHeightPerc(1, (mStatus->getFont()->getLetterHeight() + 2.0f) / mSize.y, false);
mGrid.setRowHeightPerc(2, mGameCounter->getFont()->getHeight() * 1.75f / mSize.y, false);
mGrid.setRowHeightPerc(3, (mStatus->getFont()->getLetterHeight() + 3.0f) / mSize.y, false);
mGrid.setRowHeightPerc(4, 0.07f, false);
mGrid.setRowHeightPerc(5, 0.07f, false);
mGrid.setRowHeightPerc(6, 0.07f, false);
@ -217,7 +217,7 @@ void GuiOfflineGenerator::onSizeChanged()
mGrid.setRowHeightPerc(8, 0.02f, false);
mGrid.setRowHeightPerc(9, 0.07f, false);
mGrid.setRowHeightPerc(10, 0.07f, false);
mGrid.setRowHeightPerc(12, mButtonGrid->getSize().y() / mSize.y(), false);
mGrid.setRowHeightPerc(12, mButtonGrid->getSize().y / mSize.y, false);
// Set column widths.
mGrid.setColWidthPerc(0, 0.03f);

View file

@ -140,8 +140,7 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
setSize(mMenu.getSize());
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
Renderer::getScreenHeight() * 0.13f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, Renderer::getScreenHeight() * 0.13f);
}
GuiScraperMenu::~GuiScraperMenu()

View file

@ -28,7 +28,7 @@ GuiScraperMulti::GuiScraperMulti(Window* window,
bool approveResults)
: GuiComponent(window)
, mBackground(window, ":/graphics/frame.svg")
, mGrid(window, Vector2i(1, 5))
, mGrid(window, glm::ivec2{1, 5})
, mSearchQueue(searches)
, mApproveResults(approveResults)
{
@ -47,15 +47,15 @@ GuiScraperMulti::GuiScraperMulti(Window* window,
// Set up grid.
mTitle = std::make_shared<TextComponent>(mWindow, "SCRAPING IN PROGRESS",
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true);
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true);
mSystem = std::make_shared<TextComponent>(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM),
0x777777FF, ALIGN_CENTER);
mGrid.setEntry(mSystem, Vector2i(0, 1), false, true);
mGrid.setEntry(mSystem, glm::ivec2{0, 1}, false, true);
mSubtitle = std::make_shared<TextComponent>(
mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true);
mGrid.setEntry(mSubtitle, glm::ivec2{0, 2}, false, true);
if (mApproveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic"))
mSearchComp = std::make_shared<GuiScraperSearch>(
@ -70,7 +70,7 @@ GuiScraperMulti::GuiScraperMulti(Window* window,
std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1));
mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this));
mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this));
mGrid.setEntry(mSearchComp, Vector2i(0, 3),
mGrid.setEntry(mSearchComp, glm::ivec2{0, 3},
mSearchComp->getSearchType() != GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT,
true);
@ -102,16 +102,16 @@ GuiScraperMulti::GuiScraperMulti(Window* window,
std::bind(&GuiScraperMulti::finish, this)));
mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 4}, true, false);
// Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is
// the 16:9 reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth();
float width = glm::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth();
setSize(width, Renderer::getScreenHeight() * 0.849f);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f);
doNextSearch();
}
@ -130,12 +130,12 @@ GuiScraperMulti::~GuiScraperMulti()
void GuiScraperMulti::onSizeChanged()
{
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y(), false);
mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2.0f) / mSize.y(), false);
mGrid.setRowHeightPerc(2, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y(), false);
mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y() / mSize.y(), false);
mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y, false);
mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2.0f) / mSize.y, false);
mGrid.setRowHeightPerc(2, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y, false);
mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y / mSize.y, false);
mGrid.setSize(mSize);
}

View file

@ -38,7 +38,7 @@
GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int scrapeCount)
: GuiComponent(window)
, mGrid(window, Vector2i(4, 3))
, mGrid(window, glm::ivec2{4, 3})
, mBusyAnim(window)
, mSearchType(type)
, mScrapeCount(scrapeCount)
@ -54,8 +54,8 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int
mRetryCount = 0;
// Left spacer (empty component, needed for borders).
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false, false,
Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{0, 0}, false, false,
glm::ivec2{1, 3}, GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// Selected result name.
mResultName = std::make_shared<TextComponent>(mWindow, "Result name",
@ -63,7 +63,7 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int
// Selected result thumbnail.
mResultThumbnail = std::make_shared<ImageComponent>(mWindow);
mGrid.setEntry(mResultThumbnail, Vector2i(1, 1), false, false, Vector2i(1, 1));
mGrid.setEntry(mResultThumbnail, glm::ivec2{1, 1}, false, false, glm::ivec2{1, 1});
// Selected result description and container.
mDescContainer = std::make_shared<ScrollableContainer>(mWindow);
@ -87,18 +87,14 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int
mMD_ReleaseDate = std::make_shared<DateTimeEditComponent>(mWindow);
mMD_ReleaseDate->setColor(mdColor);
mMD_ReleaseDate->setUppercase(true);
mMD_Developer =
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Publisher =
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Genre =
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Players =
std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(),
Vector2f::Zero(), 0x00000000, 0.02f);
mMD_Developer = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT,
glm::vec3{}, glm::vec2{}, 0x00000000, 0.02f);
mMD_Publisher = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT,
glm::vec3{}, glm::vec2{}, 0x00000000, 0.02f);
mMD_Genre = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT, glm::vec3{},
glm::vec2{}, 0x00000000, 0.02f);
mMD_Players = std::make_shared<TextComponent>(mWindow, "", font, mdColor, ALIGN_LEFT,
glm::vec3{}, glm::vec2{}, 0x00000000, 0.02f);
mMD_Filler = std::make_shared<TextComponent>(mWindow, "", font, mdColor);
if (Settings::getInstance()->getString("Scraper") != "thegamesdb")
@ -127,15 +123,15 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int
std::make_shared<TextComponent>(mWindow, "", font, mdLblColor), mMD_Filler));
mMD_Grid = std::make_shared<ComponentGrid>(
mWindow, Vector2i(2, static_cast<int>(mMD_Pairs.size() * 2 - 1)));
mWindow, glm::ivec2{2, static_cast<int>(mMD_Pairs.size() * 2 - 1)});
unsigned int i = 0;
for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) {
mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true);
mMD_Grid->setEntry(it->second, Vector2i(1, i), false, it->resize);
mMD_Grid->setEntry(it->first, glm::ivec2{0, i}, false, true);
mMD_Grid->setEntry(it->second, glm::ivec2{1, i}, false, it->resize);
i += 2;
}
mGrid.setEntry(mMD_Grid, Vector2i(2, 1), false, false);
mGrid.setEntry(mMD_Grid, glm::ivec2{2, 1}, false, false);
// Result list.
mResultList = std::make_shared<ComponentList>(mWindow);
@ -184,7 +180,7 @@ void GuiScraperSearch::onSizeChanged()
{
mGrid.setSize(mSize);
if (mSize.x() == 0 || mSize.y() == 0)
if (mSize.x == 0 || mSize.y == 0)
return;
// Column widths.
@ -203,7 +199,7 @@ void GuiScraperSearch::onSizeChanged()
// Row heights.
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name.
mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) /
mGrid.getSize().y()); // Result name.
mGrid.getSize().y); // Result name.
else
mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding.
@ -230,11 +226,11 @@ void GuiScraperSearch::onSizeChanged()
mResultDesc->getFont()->getHeight() * 6.0f);
// Make description text wrap at edge of container.
mResultDesc->setSize(mDescContainer->getSize().x(), 0);
mResultDesc->setSize(mDescContainer->getSize().x, 0.0f);
// Set the width of mResultName to the cell width so that text abbreviation will work correctly.
Vector2f resultNameSize = mResultName->getSize();
mResultName->setSize(mGrid.getColWidth(3), resultNameSize.y());
glm::vec2 resultNameSize{mResultName->getSize()};
mResultName->setSize(mGrid.getColWidth(3), resultNameSize.y);
mGrid.onSizeChanged();
mBusyAnim.setSize(mSize);
@ -243,8 +239,8 @@ void GuiScraperSearch::onSizeChanged()
void GuiScraperSearch::resizeMetadata()
{
mMD_Grid->setSize(mGrid.getColWidth(2), mGrid.getRowHeight(1));
if (mMD_Grid->getSize().y() > mMD_Pairs.size()) {
const int fontHeight = static_cast<int>(mMD_Grid->getSize().y() / mMD_Pairs.size() * 0.8f);
if (mMD_Grid->getSize().y > mMD_Pairs.size()) {
const int fontHeight = static_cast<int>(mMD_Grid->getSize().y / mMD_Pairs.size() * 0.8f);
auto fontLbl = Font::get(fontHeight, FONT_PATH_REGULAR);
auto fontComp = Font::get(fontHeight, FONT_PATH_LIGHT);
@ -253,15 +249,14 @@ void GuiScraperSearch::resizeMetadata()
for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) {
it->first->setFont(fontLbl);
it->first->setSize(0, 0);
if (it->first->getSize().x() > maxLblWidth)
maxLblWidth =
it->first->getSize().x() + (16.0f * Renderer::getScreenWidthModifier());
if (it->first->getSize().x > maxLblWidth)
maxLblWidth = it->first->getSize().x + (16.0f * Renderer::getScreenWidthModifier());
}
for (unsigned int i = 0; i < mMD_Pairs.size(); i++)
mMD_Grid->setRowHeightPerc(
i * 2, (fontLbl->getLetterHeight() + (2.0f * Renderer::getScreenHeightModifier())) /
mMD_Grid->getSize().y());
mMD_Grid->getSize().y);
// Update component fonts.
mMD_ReleaseDate->setFont(fontComp);
@ -270,7 +265,7 @@ void GuiScraperSearch::resizeMetadata()
mMD_Genre->setFont(fontComp);
mMD_Players->setFont(fontComp);
mMD_Grid->setColWidthPerc(0, maxLblWidth / mMD_Grid->getSize().x());
mMD_Grid->setColWidthPerc(0, maxLblWidth / mMD_Grid->getSize().x);
if (mScrapeRatings) {
// Rating is manually sized.
@ -293,33 +288,33 @@ void GuiScraperSearch::updateViewStyle()
// Add them back depending on search type.
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) {
// Show name.
mGrid.setEntry(mResultName, Vector2i(1, 0), false, false, Vector2i(2, 1),
mGrid.setEntry(mResultName, glm::ivec2{1, 0}, false, false, glm::ivec2{2, 1},
GridFlags::BORDER_TOP);
// Need a border on the bottom left.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 2), false, false,
Vector2i(3, 1), GridFlags::BORDER_BOTTOM);
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{0, 2}, false, false,
glm::ivec2{3, 1}, GridFlags::BORDER_BOTTOM);
// Show description on the right.
mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3),
mGrid.setEntry(mDescContainer, glm::ivec2{3, 0}, false, false, glm::ivec2{1, 3},
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// Make description text wrap at edge of container.
mResultDesc->setSize(mDescContainer->getSize().x(), 0.0f);
mResultDesc->setSize(mDescContainer->getSize().x, 0.0f);
}
else {
// Fake row where name would be.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(1, 0), false, true,
Vector2i(2, 1), GridFlags::BORDER_TOP);
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), glm::ivec2{1, 0}, false, true,
glm::ivec2{2, 1}, GridFlags::BORDER_TOP);
// Show result list on the right.
mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3),
mGrid.setEntry(mResultList, glm::ivec2{3, 0}, true, true, glm::ivec2{1, 3},
GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
// Show description under image/info.
mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1),
mGrid.setEntry(mDescContainer, glm::ivec2{1, 2}, false, false, glm::ivec2{2, 1},
GridFlags::BORDER_BOTTOM);
// Make description text wrap at edge of container.
mResultDesc->setSize(mDescContainer->getSize().x(), 0);
mResultDesc->setSize(mDescContainer->getSize().x, 0);
}
}
@ -565,12 +560,12 @@ bool GuiScraperSearch::input(InputConfig* config, Input input)
return GuiComponent::input(config, input);
}
void GuiScraperSearch::render(const Transform4x4f& parentTrans)
void GuiScraperSearch::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
renderChildren(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x00000009, 0x00000009);
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x00000009, 0x00000009);
if (mBlockAccept) {
Renderer::setMatrix(trans);

View file

@ -75,7 +75,7 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override;
void onSizeChanged() override;

View file

@ -41,8 +41,7 @@ GuiSettings::GuiSettings(Window* window, std::string title)
setSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f,
Renderer::getScreenHeight() * 0.13f);
mMenu.setPosition((mSize.x - mMenu.getSize().x) / 2.0f, Renderer::getScreenHeight() * 0.13f);
}
GuiSettings::~GuiSettings()
@ -171,7 +170,7 @@ void GuiSettings::addEditableTextComponent(const std::string label,
auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg");
bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight()));
bracket->setResize(glm::vec2{0.0f, lbl->getFont()->getLetterHeight()});
row.addElement(bracket, false);
// OK callback (apply new value to ed).

View file

@ -22,7 +22,10 @@ public:
virtual ~GuiSettings();
void save();
void addRow(const ComponentListRow& row) { mMenu.addRow(row); }
void addRow(const ComponentListRow& row, bool setCursorHere = false)
{
mMenu.addRow(row, setCursorHere);
}
void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp)
{
mMenu.addWithLabel(label, comp);
@ -34,6 +37,11 @@ public:
bool isPassword = false);
void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }
glm::vec2 getMenuSize() { return mMenu.getSize(); }
void setMenuSize(glm::vec2 size) { mMenu.setSize(size); }
glm::vec3 getMenuPosition() { return mMenu.getPosition(); }
void setMenuPosition(glm::vec3 position) { mMenu.setPosition(position); }
void setNeedsSaving(bool state = true) { mNeedsSaving = state; }
void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; }
void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; }

View file

@ -572,7 +572,7 @@ int main(int argc, char* argv[])
bool splashScreen = Settings::getInstance()->getBool("SplashScreen");
bool splashScreenProgress = Settings::getInstance()->getBool("SplashScreenProgress");
SDL_Event event {};
SDL_Event event{};
if (!window.init()) {
LOG(LogError) << "Window failed to initialize";
@ -613,6 +613,16 @@ int main(int argc, char* argv[])
}
}
// Check if any of the enabled systems has an invalid alternative emulator entry,
// which means that a label is present in the gamelist.xml file which is not matching
// any command tag in es_systems.xml.
for (auto system : SystemData::sSystemVector) {
if (system->getAlternativeEmulator() == "<INVALID>") {
ViewController::get()->invalidAlternativeEmulatorDialog();
break;
}
}
// Don't generate controller events while we're loading.
SDL_GameControllerEventState(SDL_DISABLE);

View file

@ -7,6 +7,10 @@
// Called from Scraper.
//
#if defined(_MSC_VER) // MSVC compiler.
#define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING
#endif
#include "scrapers/GamesDBJSONScraper.h"
#include "scrapers/GamesDBJSONScraperResources.h"
@ -33,90 +37,90 @@ namespace
TheGamesDBJSONRequestResources resources;
}
const std::map<PlatformId, std::string> gamesdb_new_platformid_map {
{ THREEDO, "25" },
{ COMMODORE_AMIGA, "4911" },
{ COMMODORE_AMIGA_CD32, "4947" },
{ AMSTRAD_CPC, "4914" },
{ APPLE_II, "4942" },
{ ARCADE, "23" },
{ ATARI_800, "4943" },
{ ATARI_2600, "22" },
{ ATARI_5200, "26" },
{ ATARI_7800, "27" },
{ ATARI_JAGUAR, "28" },
{ ATARI_JAGUAR_CD, "29" },
{ ATARI_LYNX, "4924" },
{ ATARI_ST, "4937" },
{ ATARI_XE, "30" },
{ CAVESTORY, "1" },
{ COLECOVISION, "31" },
{ COMMODORE_64, "40" },
{ DAPHNE, "23" },
{ INTELLIVISION, "32" },
{ APPLE_MACINTOSH, "37" },
{ MICROSOFT_XBOX, "14" },
{ MICROSOFT_XBOX_360, "15" },
{ MOONLIGHT, "1" },
{ MSX, "4929" },
{ MSX2, "4929" },
{ MSX_TURBO_R, "4929" },
{ SNK_NEO_GEO, "24" },
{ SNK_NEO_GEO_CD, "24" },
{ SNK_NEO_GEO_POCKET, "4922" },
{ SNK_NEO_GEO_POCKET_COLOR, "4923" },
{ NINTENDO_3DS, "4912" },
{ NINTENDO_64, "3" },
{ NINTENDO_DS, "8" },
{ NINTENDO_FAMICOM, "7" },
{ NINTENDO_FAMICOM_DISK_SYSTEM, "4936" },
{ NINTENDO_ENTERTAINMENT_SYSTEM, "7" },
{ NINTENDO_GAME_BOY, "4" },
{ NINTENDO_GAME_BOY_ADVANCE, "5" },
{ NINTENDO_GAME_BOY_COLOR, "41" },
{ NINTENDO_GAMECUBE, "2" },
{ NINTENDO_WII, "9" },
{ NINTENDO_WII_U, "38" },
{ NINTENDO_VIRTUAL_BOY, "4918" },
{ NINTENDO_GAME_AND_WATCH, "4950" },
{ NINTENDO_POKEMON_MINI, "4957" },
{ NINTENDO_SATELLAVIEW, "6" },
{ NINTENDO_SWITCH, "4971" },
{ BANDAI_SUFAMI_TURBO, "6" },
{ DOS, "1" },
{ PC, "1" },
{ VALVE_STEAM, "1" },
{ NEC_PCFX, "4930" },
{ SEGA_32X, "33" },
{ SEGA_CD, "21" },
{ SEGA_DREAMCAST, "16" },
{ SEGA_GAME_GEAR, "20" },
{ SEGA_GENESIS, "18" },
{ SEGA_MASTER_SYSTEM, "35" },
{ SEGA_MEGA_DRIVE, "36" },
{ SEGA_SATURN, "17" },
{ SEGA_SG1000, "4949" },
{ SONY_PLAYSTATION, "10" },
{ SONY_PLAYSTATION_2, "11" },
{ SONY_PLAYSTATION_3, "12" },
{ SONY_PLAYSTATION_4, "4919" },
{ SONY_PLAYSTATION_VITA, "39" },
{ SONY_PLAYSTATION_PORTABLE, "13" },
{ SUPER_NINTENDO, "6" },
{ SHARP_X1, "4977" },
{ SHARP_X68000, "4931" },
{ NEC_SUPERGRAFX, "34" },
{ NEC_PC_8800, "4933" },
{ NEC_PC_9800, "4934" },
{ NEC_PC_ENGINE, "34" },
{ NEC_PC_ENGINE_CD, "4955" },
{ BANDAI_WONDERSWAN, "4925" },
{ BANDAI_WONDERSWAN_COLOR, "4926" },
{ SINCLAIR_ZX_SPECTRUM, "4913" },
{ VIDEOPAC_ODYSSEY2, "4927" },
{ VECTREX, "4939" },
{ TANDY_COLOR_COMPUTER, "4941" },
{ TANDY_TRS80, "4941" },
const std::map<PlatformId, std::string> gamesdb_new_platformid_map{
{THREEDO, "25"},
{COMMODORE_AMIGA, "4911"},
{COMMODORE_AMIGA_CD32, "4947"},
{AMSTRAD_CPC, "4914"},
{APPLE_II, "4942"},
{ARCADE, "23"},
{ATARI_800, "4943"},
{ATARI_2600, "22"},
{ATARI_5200, "26"},
{ATARI_7800, "27"},
{ATARI_JAGUAR, "28"},
{ATARI_JAGUAR_CD, "29"},
{ATARI_LYNX, "4924"},
{ATARI_ST, "4937"},
{ATARI_XE, "30"},
{CAVESTORY, "1"},
{COLECOVISION, "31"},
{COMMODORE_64, "40"},
{DAPHNE, "23"},
{INTELLIVISION, "32"},
{APPLE_MACINTOSH, "37"},
{MICROSOFT_XBOX, "14"},
{MICROSOFT_XBOX_360, "15"},
{MOONLIGHT, "1"},
{MSX, "4929"},
{MSX2, "4929"},
{MSX_TURBO_R, "4929"},
{SNK_NEO_GEO, "24"},
{SNK_NEO_GEO_CD, "24"},
{SNK_NEO_GEO_POCKET, "4922"},
{SNK_NEO_GEO_POCKET_COLOR, "4923"},
{NINTENDO_3DS, "4912"},
{NINTENDO_64, "3"},
{NINTENDO_DS, "8"},
{NINTENDO_FAMICOM, "7"},
{NINTENDO_FAMICOM_DISK_SYSTEM, "4936"},
{NINTENDO_ENTERTAINMENT_SYSTEM, "7"},
{NINTENDO_GAME_BOY, "4"},
{NINTENDO_GAME_BOY_ADVANCE, "5"},
{NINTENDO_GAME_BOY_COLOR, "41"},
{NINTENDO_GAMECUBE, "2"},
{NINTENDO_WII, "9"},
{NINTENDO_WII_U, "38"},
{NINTENDO_VIRTUAL_BOY, "4918"},
{NINTENDO_GAME_AND_WATCH, "4950"},
{NINTENDO_POKEMON_MINI, "4957"},
{NINTENDO_SATELLAVIEW, "6"},
{NINTENDO_SWITCH, "4971"},
{BANDAI_SUFAMI_TURBO, "6"},
{DOS, "1"},
{PC, "1"},
{VALVE_STEAM, "1"},
{NEC_PCFX, "4930"},
{SEGA_32X, "33"},
{SEGA_CD, "21"},
{SEGA_DREAMCAST, "16"},
{SEGA_GAME_GEAR, "20"},
{SEGA_GENESIS, "18"},
{SEGA_MASTER_SYSTEM, "35"},
{SEGA_MEGA_DRIVE, "36"},
{SEGA_SATURN, "17"},
{SEGA_SG1000, "4949"},
{SONY_PLAYSTATION, "10"},
{SONY_PLAYSTATION_2, "11"},
{SONY_PLAYSTATION_3, "12"},
{SONY_PLAYSTATION_4, "4919"},
{SONY_PLAYSTATION_VITA, "39"},
{SONY_PLAYSTATION_PORTABLE, "13"},
{SUPER_NINTENDO, "6"},
{SHARP_X1, "4977"},
{SHARP_X68000, "4931"},
{NEC_SUPERGRAFX, "34"},
{NEC_PC_8800, "4933"},
{NEC_PC_9800, "4934"},
{NEC_PC_ENGINE, "34"},
{NEC_PC_ENGINE_CD, "4955"},
{BANDAI_WONDERSWAN, "4925"},
{BANDAI_WONDERSWAN_COLOR, "4926"},
{SINCLAIR_ZX_SPECTRUM, "4913"},
{VIDEOPAC_ODYSSEY2, "4927"},
{VECTREX, "4939"},
{TANDY_COLOR_COMPUTER, "4941"},
{TANDY_TRS80, "4941"},
};
void thegamesdb_generate_json_scraper_requests(

View file

@ -12,6 +12,10 @@
// gamesdb_publishers.json
//
#if defined(_MSC_VER) // MSVC compiler.
#define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING
#endif
#include "scrapers/GamesDBJSONScraperResources.h"
#include "Log.h"

View file

@ -26,10 +26,9 @@
#include <cmath>
#include <fstream>
const std::map<std::string, generate_scraper_requests_func> scraper_request_funcs {
{ "thegamesdb", &thegamesdb_generate_json_scraper_requests },
{ "screenscraper", &screenscraper_generate_scraper_requests }
};
const std::map<std::string, generate_scraper_requests_func> scraper_request_funcs{
{"thegamesdb", &thegamesdb_generate_json_scraper_requests},
{"screenscraper", &screenscraper_generate_scraper_requests}};
std::unique_ptr<ScraperSearchHandle> startScraperSearch(const ScraperSearchParams& params)
{

View file

@ -14,7 +14,6 @@
#include "PlatformId.h"
#include "Settings.h"
#include "SystemData.h"
#include "math/Misc.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
@ -26,109 +25,108 @@ using namespace PlatformIds;
// List of systems and their IDs from:
// https://www.screenscraper.fr/api/systemesListe.php?devid=xxx&devpassword=yyy&softname=zzz&output=XML
const std::map<PlatformId, unsigned short> screenscraper_platformid_map {
{ THREEDO, 29 },
{ COMMODORE_AMIGA, 64 },
{ COMMODORE_AMIGA_CD32, 130 },
{ AMSTRAD_CPC, 65 },
{ AMSTRAD_GX4000, 87 },
{ APPLE_II, 86 },
{ APPLE_IIGS, 217 },
{ ARCADE, 75 },
{ ATARI_800, 43 },
{ ATARI_2600, 26 },
{ ATARI_5200, 40 },
{ ATARI_7800, 41 },
{ ATARI_JAGUAR, 27 },
{ ATARI_JAGUAR_CD, 171 },
{ ATARI_LYNX, 28 },
{ ATARI_ST, 42 },
{ ATARI_XE, 43 },
{ ATOMISWAVE, 53 },
{ BBC_MICRO, 37 },
{ CAVESTORY, 135 },
{ COLECOVISION, 48 },
{ COMMODORE_64, 66 },
{ COMMODORE_CDTV, 129 },
{ DAPHNE, 49 },
{ INTELLIVISION, 115 },
{ GAMEENGINE_LUTRO, 206 },
{ APPLE_MACINTOSH, 146 },
{ MICROSOFT_XBOX, 32 },
{ MICROSOFT_XBOX_360, 33 },
{ MOONLIGHT, 138 },
{ MSX, 113 },
{ MSX2, 116 },
{ MSX_TURBO_R, 118 },
{ SNK_NEO_GEO, 142 },
{ SNK_NEO_GEO_CD, 142 },
{ SNK_NEO_GEO_POCKET, 25 },
{ SNK_NEO_GEO_POCKET_COLOR, 82 },
{ NINTENDO_3DS, 17 },
{ NINTENDO_64, 14 },
{ NINTENDO_DS, 15 },
{ NINTENDO_FAMICOM, 3 },
{ NINTENDO_FAMICOM_DISK_SYSTEM, 106 },
{ NINTENDO_ENTERTAINMENT_SYSTEM, 3 },
{ FAIRCHILD_CHANNELF, 80 },
{ NINTENDO_GAME_BOY, 9 },
{ NINTENDO_GAME_BOY_ADVANCE, 12 },
{ NINTENDO_GAME_BOY_COLOR, 10 },
{ NINTENDO_GAMECUBE, 13 },
{ NINTENDO_WII, 16 },
{ NINTENDO_WII_U, 18 },
{ NINTENDO_VIRTUAL_BOY, 11 },
{ NINTENDO_GAME_AND_WATCH, 52 },
{ NINTENDO_POKEMON_MINI, 211 },
{ NINTENDO_SATELLAVIEW, 107 },
{ NINTENDO_SWITCH, 225 },
{ BANDAI_SUFAMI_TURBO, 108 },
{ DOS, 135 },
{ PC, 135 },
{ VALVE_STEAM, 135 },
{ NEC_PCFX, 72 },
{ GAMEENGINE_OPENBOR, 214 },
{ TANGERINE_ORIC, 131 },
{ GAMEENGINE_SCUMMVM, 123 },
{ SEGA_32X, 19 },
{ SEGA_CD, 20 },
{ SEGA_DREAMCAST, 23 },
{ SEGA_GAME_GEAR, 21 },
{ SEGA_GENESIS, 1 },
{ SEGA_MASTER_SYSTEM, 2 },
{ SEGA_MEGA_DRIVE, 1 },
{ SEGA_SATURN, 22 },
{ SEGA_SG1000, 109 },
{ SHARP_X1, 220 },
{ SHARP_X68000, 79 },
{ GAMEENGINE_SOLARUS, 223 },
{ SONY_PLAYSTATION, 57 },
{ SONY_PLAYSTATION_2, 58 },
{ SONY_PLAYSTATION_3, 59 },
{ SONY_PLAYSTATION_VITA, 62 },
{ SONY_PLAYSTATION_PORTABLE, 61 },
{ SAMCOUPE, 213 },
{ SUPER_NINTENDO, 4 },
{ NEC_SUPERGRAFX, 105 },
{ GAMEENGINE_TIC80, 222 },
{ NEC_PC_8800, 221 },
{ NEC_PC_9800, 208 },
{ NEC_PC_ENGINE, 31 },
{ NEC_PC_ENGINE_CD, 114 },
{ BANDAI_WONDERSWAN, 45 },
{ BANDAI_WONDERSWAN_COLOR, 46 },
{ SINCLAIR_ZX_SPECTRUM, 76 },
{ SINCLAIR_ZX81_SINCLAR, 77 },
{ VIDEOPAC_ODYSSEY2, 104 },
{ VECTREX, 102 },
{ TANDY_TRS80, 144 },
{ TANDY_COLOR_COMPUTER, 144 },
{ SEGA_NAOMI, 56 },
{ THOMSON_MOTO, 141 },
{ UZEBOX, 216 },
{ SPECTRAVIDEO, 218 },
{ PALM_OS, 219 }
};
const std::map<PlatformId, unsigned short> screenscraper_platformid_map{
{THREEDO, 29},
{COMMODORE_AMIGA, 64},
{COMMODORE_AMIGA_CD32, 130},
{AMSTRAD_CPC, 65},
{AMSTRAD_GX4000, 87},
{APPLE_II, 86},
{APPLE_IIGS, 217},
{ARCADE, 75},
{ATARI_800, 43},
{ATARI_2600, 26},
{ATARI_5200, 40},
{ATARI_7800, 41},
{ATARI_JAGUAR, 27},
{ATARI_JAGUAR_CD, 171},
{ATARI_LYNX, 28},
{ATARI_ST, 42},
{ATARI_XE, 43},
{ATOMISWAVE, 53},
{BBC_MICRO, 37},
{CAVESTORY, 135},
{COLECOVISION, 48},
{COMMODORE_64, 66},
{COMMODORE_CDTV, 129},
{DAPHNE, 49},
{INTELLIVISION, 115},
{GAMEENGINE_LUTRO, 206},
{APPLE_MACINTOSH, 146},
{MICROSOFT_XBOX, 32},
{MICROSOFT_XBOX_360, 33},
{MOONLIGHT, 138},
{MSX, 113},
{MSX2, 116},
{MSX_TURBO_R, 118},
{SNK_NEO_GEO, 142},
{SNK_NEO_GEO_CD, 142},
{SNK_NEO_GEO_POCKET, 25},
{SNK_NEO_GEO_POCKET_COLOR, 82},
{NINTENDO_3DS, 17},
{NINTENDO_64, 14},
{NINTENDO_DS, 15},
{NINTENDO_FAMICOM, 3},
{NINTENDO_FAMICOM_DISK_SYSTEM, 106},
{NINTENDO_ENTERTAINMENT_SYSTEM, 3},
{FAIRCHILD_CHANNELF, 80},
{NINTENDO_GAME_BOY, 9},
{NINTENDO_GAME_BOY_ADVANCE, 12},
{NINTENDO_GAME_BOY_COLOR, 10},
{NINTENDO_GAMECUBE, 13},
{NINTENDO_WII, 16},
{NINTENDO_WII_U, 18},
{NINTENDO_VIRTUAL_BOY, 11},
{NINTENDO_GAME_AND_WATCH, 52},
{NINTENDO_POKEMON_MINI, 211},
{NINTENDO_SATELLAVIEW, 107},
{NINTENDO_SWITCH, 225},
{BANDAI_SUFAMI_TURBO, 108},
{DOS, 135},
{PC, 135},
{VALVE_STEAM, 135},
{NEC_PCFX, 72},
{GAMEENGINE_OPENBOR, 214},
{TANGERINE_ORIC, 131},
{GAMEENGINE_SCUMMVM, 123},
{SEGA_32X, 19},
{SEGA_CD, 20},
{SEGA_DREAMCAST, 23},
{SEGA_GAME_GEAR, 21},
{SEGA_GENESIS, 1},
{SEGA_MASTER_SYSTEM, 2},
{SEGA_MEGA_DRIVE, 1},
{SEGA_SATURN, 22},
{SEGA_SG1000, 109},
{SHARP_X1, 220},
{SHARP_X68000, 79},
{GAMEENGINE_SOLARUS, 223},
{SONY_PLAYSTATION, 57},
{SONY_PLAYSTATION_2, 58},
{SONY_PLAYSTATION_3, 59},
{SONY_PLAYSTATION_VITA, 62},
{SONY_PLAYSTATION_PORTABLE, 61},
{SAMCOUPE, 213},
{SUPER_NINTENDO, 4},
{NEC_SUPERGRAFX, 105},
{GAMEENGINE_TIC80, 222},
{NEC_PC_8800, 221},
{NEC_PC_9800, 208},
{NEC_PC_ENGINE, 31},
{NEC_PC_ENGINE_CD, 114},
{BANDAI_WONDERSWAN, 45},
{BANDAI_WONDERSWAN_COLOR, 46},
{SINCLAIR_ZX_SPECTRUM, 76},
{SINCLAIR_ZX81_SINCLAR, 77},
{VIDEOPAC_ODYSSEY2, 104},
{VECTREX, 102},
{TANDY_TRS80, 144},
{TANDY_COLOR_COMPUTER, 144},
{SEGA_NAOMI, 56},
{THOMSON_MOTO, 141},
{UZEBOX, 216},
{SPECTRAVIDEO, 218},
{PALM_OS, 219}};
// Helper XML parsing method, finding a node-by-name recursively.
pugi::xml_node find_node_by_name_re(const pugi::xml_node& node,
@ -333,11 +331,10 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
Utils::String::toLower(Settings::getInstance()->getString("ScraperLanguage"));
// Name fallback: US, WOR(LD). (Xpath: Data/jeu[0]/noms/nom[*]).
result.mdl.set("name",
find_child_by_attribute_list(game.child("noms"), "nom", "region",
{ region, "wor", "us", "ss", "eu", "jp" })
.text()
.get());
result.mdl.set("name", find_child_by_attribute_list(game.child("noms"), "nom", "region",
{region, "wor", "us", "ss", "eu", "jp"})
.text()
.get());
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Name: " << result.mdl.get("name");
// Validate rating.
@ -360,7 +357,7 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
// Description fallback language: EN, WOR(LD).
std::string description = find_child_by_attribute_list(game.child("synopsis"), "synopsis",
"langue", { language, "en", "wor" })
"langue", {language, "en", "wor"})
.text()
.get();
@ -373,7 +370,7 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
// Get the date proper. The API returns multiple 'date' children nodes to the 'dates'
// main child of 'jeu'. Date fallback: WOR(LD), US, SS, JP, EU.
std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region",
{ region, "wor", "us", "ss", "jp", "eu" })
{region, "wor", "us", "ss", "jp", "eu"})
.text()
.get();
@ -411,10 +408,10 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc,
}
// Genre fallback language: EN. (Xpath: Data/jeu[0]/genres/genre[*]).
std::string genre = find_child_by_attribute_list(game.child("genres"), "genre", "langue",
{ language, "en" })
.text()
.get();
std::string genre =
find_child_by_attribute_list(game.child("genres"), "genre", "langue", {language, "en"})
.text()
.get();
if (!genre.empty()) {
result.mdl.set("genre", genre);
LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: "
@ -482,8 +479,7 @@ void ScreenScraperRequest::processMedia(ScraperSearchResult& result,
}
else {
// Region fallback: WOR(LD), US, CUS(TOM?), JP, EU.
for (auto _region :
std::vector<std::string> { region, "wor", "us", "cus", "jp", "eu" }) {
for (auto _region : std::vector<std::string>{region, "wor", "us", "cus", "jp", "eu"}) {
if (art)
break;

View file

@ -46,10 +46,10 @@ public:
std::string getGameSearchUrl(const std::string gameName) const;
// Access to the API.
const std::string API_DEV_U = { 15, 21, 39, 22, 42, 40 };
const std::string API_DEV_P = { 32, 70, 46, 54, 12, 5, 13, 120, 50, 66, 25 };
const std::string API_DEV_KEY = { 67, 112, 72, 120, 121, 77, 119, 74, 84, 56,
75, 122, 78, 98, 69, 86, 56, 120, 120, 49 };
const std::string API_DEV_U = {15, 21, 39, 22, 42, 40};
const std::string API_DEV_P = {32, 70, 46, 54, 12, 5, 13, 120, 50, 66, 25};
const std::string API_DEV_KEY = {67, 112, 72, 120, 121, 77, 119, 74, 84, 56,
75, 122, 78, 98, 69, 86, 56, 120, 120, 49};
const std::string API_URL_BASE = "https://www.screenscraper.fr/api2";
const std::string API_SOFT_NAME =
"EmulationStation-DE " + static_cast<std::string>(PROGRAM_VERSION_STRING);

View file

@ -23,8 +23,8 @@
#endif
// Buffer values for scrolling velocity (left, stopped, right).
const int logoBuffersLeft[] = { -5, -2, -1 };
const int logoBuffersRight[] = { 1, 2, 5 };
const int logoBuffersLeft[] = {-5, -2, -1};
const int logoBuffersRight[] = {1, 2, 5};
SystemView::SystemView(Window* window)
: IList<SystemViewData, SystemData*>(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP)
@ -123,8 +123,8 @@ void SystemView::populate()
e.data.logo->setOrigin(0.5, 0.5);
}
Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin();
e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0);
glm::vec2 denormalized{mCarousel.logoSize * e.data.logo->getOrigin()};
e.data.logo->setPosition(denormalized.x, denormalized.y, 0.0f);
// Make background extras.
e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow);
@ -315,7 +315,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
anim = new LambdaAnimation(
[this, startExtrasFade, startPos, endPos, posMax](float t) {
t -= 1;
float f = Math::lerp(startPos, endPos, t * t * t + 1);
float f = glm::mix(startPos, endPos, t * t * t + 1);
if (f < 0)
f += posMax;
if (f >= posMax)
@ -325,11 +325,13 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
t += 1;
if (t < 0.3f)
this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade);
this->mExtrasFadeOpacity =
glm::mix(0.0f, 1.0f, glm::clamp(t / 0.2f + startExtrasFade, 0.0f, 1.0f));
else if (t < 0.7f)
this->mExtrasFadeOpacity = 1.0f;
else
this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f);
this->mExtrasFadeOpacity =
glm::mix(1.0f, 0.0f, glm::clamp((t - 0.6f) / 0.3f, 0.0f, 1.0f));
if (t > 0.5f)
this->mExtrasCamOffset = endPos;
@ -345,7 +347,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
anim = new LambdaAnimation(
[this, startPos, endPos, posMax](float t) {
t -= 1;
float f = Math::lerp(startPos, endPos, t * t * t + 1);
float f = glm::mix(startPos, endPos, t * t * t + 1);
if (f < 0)
f += posMax;
if (f >= posMax)
@ -381,7 +383,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
anim = new LambdaAnimation(
[this, startPos, endPos, posMax](float t) {
t -= 1;
float f = Math::lerp(startPos, endPos, t * t * t + 1);
float f = glm::mix(startPos, endPos, t * t * t + 1);
if (f < 0)
f += posMax;
if (f >= posMax)
@ -396,12 +398,12 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
setAnimation(anim, 0, nullptr, false, 0);
}
void SystemView::render(const Transform4x4f& parentTrans)
void SystemView::render(const glm::mat4& parentTrans)
{
if (size() == 0)
return; // Nothing to render.
Transform4x4f trans = getTransform() * parentTrans;
glm::mat4 trans{getTransform() * parentTrans};
renderExtras(trans, INT16_MIN, INT16_MAX);
@ -430,7 +432,7 @@ std::vector<HelpPrompt> SystemView::getHelpPrompts()
if (!UIModeController::getInstance()->isUIModeKid() &&
Settings::getInstance()->getBool("ScreensaverControls"))
prompts.push_back(HelpPrompt("back", "toggle screensaver"));
prompts.push_back(HelpPrompt("back", "screensaver"));
return prompts;
}
@ -449,7 +451,6 @@ void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
populate();
}
// Get the ThemeElements that make up the SystemView.
void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
{
LOG(LogDebug) << "SystemView::getViewElements()";
@ -473,83 +474,78 @@ void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
mViewNeedsReload = false;
}
// Render system carousel.
void SystemView::renderCarousel(const Transform4x4f& trans)
void SystemView::renderCarousel(const glm::mat4& trans)
{
// Background box behind logos.
Transform4x4f carouselTrans = trans;
carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0));
carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1.0f,
mCarousel.origin.y() * mCarousel.size.y() * -1.0f, 0.0f));
glm::mat4 carouselTrans{trans};
carouselTrans =
glm::translate(carouselTrans, glm::vec3{mCarousel.pos.x, mCarousel.pos.y, 0.0f});
carouselTrans = glm::translate(carouselTrans,
glm::vec3{mCarousel.origin.x * mCarousel.size.x * -1.0f,
mCarousel.origin.y * mCarousel.size.y * -1.0f, 0.0f});
Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y());
glm::vec2 clipPos{carouselTrans[3].x, carouselTrans[3].y};
Renderer::pushClipRect(
Vector2i(static_cast<int>(clipPos.x()), static_cast<int>(clipPos.y())),
Vector2i(static_cast<int>(mCarousel.size.x()), static_cast<int>(mCarousel.size.y())));
glm::ivec2{static_cast<int>(clipPos.x), static_cast<int>(clipPos.y)},
glm::ivec2{static_cast<int>(mCarousel.size.x), static_cast<int>(mCarousel.size.y)});
Renderer::setMatrix(carouselTrans);
Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), mCarousel.color,
Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x, mCarousel.size.y, mCarousel.color,
mCarousel.colorEnd, mCarousel.colorGradientHorizontal);
// Draw logos.
// Note: logoSpacing will also include the size of the logo itself.
Vector2f logoSpacing(0.0f, 0.0f);
glm::vec2 logoSpacing{};
float xOff = 0.0f;
float yOff = 0.0f;
switch (mCarousel.type) {
case VERTICAL_WHEEL: {
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f -
(mCamOffset * logoSpacing[1]);
yOff = (mCarousel.size.y - mCarousel.logoSize.y) / 2.0f - (mCamOffset * logoSpacing.y);
if (mCarousel.logoAlignment == ALIGN_LEFT)
xOff = mCarousel.logoSize.x() / 10.0f;
xOff = mCarousel.logoSize.x / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f);
xOff = mCarousel.size.x - (mCarousel.logoSize.x * 1.1f);
else
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f;
xOff = (mCarousel.size.x - mCarousel.logoSize.x) / 2.0f;
break;
}
case VERTICAL: {
logoSpacing[1] =
((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) /
(mCarousel.maxLogoCount)) +
mCarousel.logoSize.y();
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f -
(mCamOffset * logoSpacing[1]);
logoSpacing.y = ((mCarousel.size.y - (mCarousel.logoSize.y * mCarousel.maxLogoCount)) /
(mCarousel.maxLogoCount)) +
mCarousel.logoSize.y;
yOff = (mCarousel.size.y - mCarousel.logoSize.y) / 2.0f - (mCamOffset * logoSpacing.y);
if (mCarousel.logoAlignment == ALIGN_LEFT)
xOff = mCarousel.logoSize.x() / 10.0f;
xOff = mCarousel.logoSize.x / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f);
xOff = mCarousel.size.x - (mCarousel.logoSize.x * 1.1f);
else
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f;
xOff = (mCarousel.size.x - mCarousel.logoSize.x) / 2.0f;
break;
}
case HORIZONTAL_WHEEL: {
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f -
(mCamOffset * logoSpacing[1]);
xOff = (mCarousel.size.x - mCarousel.logoSize.x) / 2.0f - (mCamOffset * logoSpacing.y);
if (mCarousel.logoAlignment == ALIGN_TOP)
yOff = mCarousel.logoSize.y() / 10.0f;
yOff = mCarousel.logoSize.y / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f);
yOff = mCarousel.size.y - (mCarousel.logoSize.y * 1.1f);
else
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f;
yOff = (mCarousel.size.y - mCarousel.logoSize.y) / 2.0f;
break;
}
case HORIZONTAL: {
}
default: {
logoSpacing[0] =
((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) /
(mCarousel.maxLogoCount)) +
mCarousel.logoSize.x();
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f -
(mCamOffset * logoSpacing[0]);
logoSpacing.x = ((mCarousel.size.x - (mCarousel.logoSize.x * mCarousel.maxLogoCount)) /
(mCarousel.maxLogoCount)) +
mCarousel.logoSize.x;
xOff = (mCarousel.size.x - mCarousel.logoSize.x) / 2.0f - (mCamOffset * logoSpacing.x);
if (mCarousel.logoAlignment == ALIGN_TOP)
yOff = mCarousel.logoSize.y() / 10.0f;
yOff = mCarousel.logoSize.y / 10.0f;
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f);
yOff = mCarousel.size.y - (mCarousel.logoSize.y * 1.1f);
else
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f;
yOff = (mCarousel.size.y - mCarousel.logoSize.y) / 2.0f;
break;
}
}
@ -575,8 +571,9 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
while (index >= static_cast<int>(mEntries.size()))
index -= static_cast<int>(mEntries.size());
Transform4x4f logoTrans = carouselTrans;
logoTrans.translate(Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing[1] + yOff, 0));
glm::mat4 logoTrans{carouselTrans};
logoTrans = glm::translate(
logoTrans, glm::vec3{i * logoSpacing.x + xOff, i * logoSpacing.y + yOff, 0.0f});
float distance = i - mCamOffset;
@ -600,16 +597,15 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
Renderer::popClipRect();
}
// Draw background extras.
void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upper)
void SystemView::renderExtras(const glm::mat4& trans, float lower, float upper)
{
int extrasCenter = static_cast<int>(mExtrasCamOffset);
// Adding texture loading buffers depending on scrolling speed and status.
int bufferIndex = getScrollingVelocity() + 1;
int bufferIndex{getScrollingVelocity() + 1};
Renderer::pushClipRect(Vector2i::Zero(),
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
Renderer::pushClipRect(glm::ivec2{},
glm::ivec2{static_cast<int>(mSize.x), static_cast<int>(mSize.y)});
for (int i = extrasCenter + logoBuffersLeft[bufferIndex];
i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) {
@ -621,16 +617,17 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
// Only render selected system when not showing.
if (mShowing || index == mCursor) {
Transform4x4f extrasTrans = trans;
glm::mat4 extrasTrans{trans};
if (mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL)
extrasTrans.translate(Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0));
extrasTrans = glm::translate(
extrasTrans, glm::vec3{(i - mExtrasCamOffset) * mSize.x, 0.0f, 0.0f});
else
extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0));
extrasTrans = glm::translate(
extrasTrans, glm::vec3{0.0f, (i - mExtrasCamOffset) * mSize.y, 0.0f});
Renderer::pushClipRect(
Vector2i(static_cast<int>(extrasTrans.translation()[0]),
static_cast<int>(extrasTrans.translation()[1])),
Vector2i(static_cast<int>(mSize.x()), static_cast<int>(mSize.y())));
glm::ivec2{static_cast<int>(extrasTrans[3].x), static_cast<int>(extrasTrans[3].y)},
glm::ivec2{static_cast<int>(mSize.x), static_cast<int>(mSize.y)});
SystemViewData data = mEntries.at(index).data;
for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) {
GuiComponent* extra = data.backgroundExtras[j];
@ -644,46 +641,45 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
Renderer::popClipRect();
}
void SystemView::renderFade(const Transform4x4f& trans)
void SystemView::renderFade(const glm::mat4& trans)
{
unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mExtrasFadeOpacity * 255.0f);
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), fadeColor, fadeColor);
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, fadeColor, fadeColor);
}
// Populate the system carousel with the legacy values.
void SystemView::getDefaultElements(void)
{
// Carousel.
mCarousel.type = HORIZONTAL;
mCarousel.logoAlignment = ALIGN_CENTER;
mCarousel.size.x() = mSize.x();
mCarousel.size.y() = floorf(0.2325f * mSize.y());
mCarousel.pos.x() = 0.0f;
mCarousel.pos.y() = floorf(0.5f * (mSize.y() - mCarousel.size.y()));
mCarousel.origin.x() = 0.0f;
mCarousel.origin.y() = 0.0f;
mCarousel.size.x = mSize.x;
mCarousel.size.y = floorf(0.2325f * mSize.y);
mCarousel.pos.x = 0.0f;
mCarousel.pos.y = floorf(0.5f * (mSize.y - mCarousel.size.y));
mCarousel.origin.x = 0.0f;
mCarousel.origin.y = 0.0f;
mCarousel.color = 0xFFFFFFD8;
mCarousel.colorEnd = 0xFFFFFFD8;
mCarousel.colorGradientHorizontal = true;
mCarousel.logoScale = 1.2f;
mCarousel.logoRotation = 7.5f;
mCarousel.logoRotationOrigin.x() = -5.0f;
mCarousel.logoRotationOrigin.y() = 0.5f;
mCarousel.logoSize.x() = 0.25f * mSize.x();
mCarousel.logoSize.y() = 0.155f * mSize.y();
mCarousel.logoRotationOrigin.x = -5.0f;
mCarousel.logoRotationOrigin.y = 0.5f;
mCarousel.logoSize.x = 0.25f * mSize.x;
mCarousel.logoSize.y = 0.155f * mSize.y;
mCarousel.maxLogoCount = 3;
mCarousel.zIndex = 40.0f;
// System info bar.
mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f);
mSystemInfo.setPosition(0, mCarousel.pos.y() + mCarousel.size.y());
mSystemInfo.setSize(mSize.x, mSystemInfo.getFont()->getLetterHeight() * 2.2f);
mSystemInfo.setPosition(0.0f, mCarousel.pos.y + mCarousel.size.y);
mSystemInfo.setBackgroundColor(0xDDDDDDD8);
mSystemInfo.setRenderBackground(true);
mSystemInfo.setFont(Font::get(static_cast<int>(0.035f * mSize.y()), Font::getDefaultPath()));
mSystemInfo.setFont(Font::get(static_cast<int>(0.035f * mSize.y), Font::getDefaultPath()));
mSystemInfo.setColor(0x000000FF);
mSystemInfo.setZIndex(50);
mSystemInfo.setDefaultZIndex(50);
mSystemInfo.setZIndex(50.0f);
mSystemInfo.setDefaultZIndex(50.0f);
}
void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
@ -699,11 +695,11 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
mCarousel.type = HORIZONTAL;
}
if (elem->has("size"))
mCarousel.size = elem->get<Vector2f>("size") * mSize;
mCarousel.size = elem->get<glm::vec2>("size") * mSize;
if (elem->has("pos"))
mCarousel.pos = elem->get<Vector2f>("pos") * mSize;
mCarousel.pos = elem->get<glm::vec2>("pos") * mSize;
if (elem->has("origin"))
mCarousel.origin = elem->get<Vector2f>("origin");
mCarousel.origin = elem->get<glm::vec2>("origin");
if (elem->has("color")) {
mCarousel.color = elem->get<unsigned int>("color");
mCarousel.colorEnd = mCarousel.color;
@ -716,7 +712,7 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
if (elem->has("logoScale"))
mCarousel.logoScale = elem->get<float>("logoScale");
if (elem->has("logoSize"))
mCarousel.logoSize = elem->get<Vector2f>("logoSize") * mSize;
mCarousel.logoSize = elem->get<glm::vec2>("logoSize") * mSize;
if (elem->has("maxLogoCount"))
mCarousel.maxLogoCount = static_cast<int>(std::round(elem->get<float>("maxLogoCount")));
if (elem->has("zIndex"))
@ -724,7 +720,7 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
if (elem->has("logoRotation"))
mCarousel.logoRotation = elem->get<float>("logoRotation");
if (elem->has("logoRotationOrigin"))
mCarousel.logoRotationOrigin = elem->get<Vector2f>("logoRotationOrigin");
mCarousel.logoRotationOrigin = elem->get<glm::vec2>("logoRotationOrigin");
if (elem->has("logoAlignment")) {
if (!(elem->get<std::string>("logoAlignment").compare("left")))
mCarousel.logoAlignment = ALIGN_LEFT;

View file

@ -34,18 +34,18 @@ struct SystemViewData {
struct SystemViewCarousel {
CarouselType type;
Vector2f pos;
Vector2f size;
Vector2f origin;
glm::vec2 pos;
glm::vec2 size;
glm::vec2 origin;
float logoScale;
float logoRotation;
Vector2f logoRotationOrigin;
glm::vec2 logoRotationOrigin;
Alignment logoAlignment;
unsigned int color;
unsigned int colorEnd;
bool colorGradientHorizontal;
int maxLogoCount; // Number of logos shown on the carousel.
Vector2f logoSize;
glm::vec2 logoSize;
float zIndex;
};
@ -62,7 +62,7 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void onThemeChanged(const std::shared_ptr<ThemeData>& theme);
@ -81,13 +81,17 @@ protected:
private:
void populate();
void updateGameCount();
// Get the ThemeElements that make up the SystemView.
void getViewElements(const std::shared_ptr<ThemeData>& theme);
// Populate the system carousel with the legacy values.
void getDefaultElements(void);
void getCarouselFromTheme(const ThemeData::ThemeElement* elem);
void renderCarousel(const Transform4x4f& parentTrans);
void renderExtras(const Transform4x4f& parentTrans, float lower, float upper);
void renderFade(const Transform4x4f& trans);
// Render system carousel.
void renderCarousel(const glm::mat4& parentTrans);
// Draw background extras.
void renderExtras(const glm::mat4& parentTrans, float lower, float upper);
void renderFade(const glm::mat4& trans);
SystemViewCarousel mCarousel;
TextComponent mSystemInfo;

View file

@ -58,9 +58,7 @@ private:
int mPassKeyCounter;
// These are Xbox button names, so they may be different in pracise on non-Xbox controllers.
const std::vector<std::string> mInputVals = {
"up", "down", "left", "right", "a", "b", "x", "y"
};
const std::vector<std::string> mInputVals = {"up", "down", "left", "right", "a", "b", "x", "y"};
};
#endif // ES_APP_VIEWS_UI_MODE_CONTROLLER_H

View file

@ -41,12 +41,14 @@ const std::string ViewController::FOLDER_CHAR = Utils::String::wideStringToStrin
const std::string ViewController::TICKMARK_CHAR = Utils::String::wideStringToString(L"\uF14A");
const std::string ViewController::CONTROLLER_CHAR = Utils::String::wideStringToString(L"\uF11b");
const std::string ViewController::FILTER_CHAR = Utils::String::wideStringToString(L"\uF0b0");
const std::string ViewController::GEAR_CHAR = Utils::String::wideStringToString(L"\uF013");
#else
const std::string ViewController::FAVORITE_CHAR = "\uF005";
const std::string ViewController::FOLDER_CHAR = "\uF07C";
const std::string ViewController::TICKMARK_CHAR = "\uF14A";
const std::string ViewController::CONTROLLER_CHAR = "\uF11b";
const std::string ViewController::FILTER_CHAR = "\uF0b0";
const std::string ViewController::GEAR_CHAR = "\uF013";
#endif
ViewController* ViewController::get()
@ -66,7 +68,7 @@ ViewController::ViewController(Window* window)
, mCurrentView(nullptr)
, mPreviousView(nullptr)
, mSkipView(nullptr)
, mCamera(Transform4x4f::Identity())
, mCamera(Renderer::getIdentity())
, mSystemViewTransition(false)
, mWrappedViews(false)
, mFadeOpacity(0)
@ -193,6 +195,17 @@ void ViewController::noGamesDialog()
mWindow->pushGui(mNoGamesMessageBox);
}
void ViewController::invalidAlternativeEmulatorDialog()
{
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
"AT LEAST ONE OF YOUR SYSTEMS HAS AN\n"
"INVALID ALTERNATIVE EMULATOR CONFIGURED\n"
"WITH NO MATCHING ENTRY IN THE SYSTEMS\n"
"CONFIGURATION FILE, PLEASE REVIEW YOUR\n"
"SETUP USING THE 'ALTERNATIVE EMULATORS'\n"
"ENTRY UNDER THE 'OTHER SETTINGS' MENU"));
}
void ViewController::goToStart()
{
// If the system view does not exist, then create it. We do this here as it would
@ -234,8 +247,8 @@ void ViewController::ReloadAndGoToStart()
bool ViewController::isCameraMoving()
{
if (mCurrentView) {
if (mCamera.r3().x() - -mCurrentView->getPosition().x() != 0 ||
mCamera.r3().y() - -mCurrentView->getPosition().y() != 0)
if (mCamera[3].x - -mCurrentView->getPosition().x != 0.0f ||
mCamera[3].y - -mCurrentView->getPosition().y != 0.0f)
return true;
}
return false;
@ -245,8 +258,8 @@ void ViewController::cancelViewTransitions()
{
if (Settings::getInstance()->getString("TransitionStyle") == "slide") {
if (isCameraMoving()) {
mCamera.r3().x() = -mCurrentView->getPosition().x();
mCamera.r3().y() = -mCurrentView->getPosition().y();
mCamera[3].x = -mCurrentView->getPosition().x;
mCamera[3].y = -mCurrentView->getPosition().y;
stopAllAnimations();
}
// mSkipView is used when skipping through the gamelists in quick succession.
@ -285,8 +298,8 @@ int ViewController::getSystemId(SystemData* system)
void ViewController::restoreViewPosition()
{
if (mPreviousView) {
Vector3f restorePosition = mPreviousView->getPosition();
restorePosition.x() = mWrapPreviousPositionX;
glm::vec3 restorePosition{mPreviousView->getPosition()};
restorePosition.x = mWrapPreviousPositionX;
mPreviousView->setPosition(restorePosition);
mWrapPreviousPositionX = 0;
mWrappedViews = false;
@ -320,7 +333,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
auto systemList = getSystemListView();
systemList->setPosition(getSystemId(system) * static_cast<float>(Renderer::getScreenWidth()),
systemList->getPosition().y());
systemList->getPosition().y);
systemList->goToSystem(system, false);
mCurrentView = systemList;
@ -328,21 +341,21 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition)
// Application startup animation.
if (applicationStartup) {
mCamera.translation() = -mCurrentView->getPosition();
mCamera = glm::translate(mCamera, -mCurrentView->getPosition());
if (Settings::getInstance()->getString("TransitionStyle") == "slide") {
if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL ||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
mCamera.translation().y() += Renderer::getScreenHeight();
mCamera[3].y += static_cast<float>(Renderer::getScreenHeight());
else
mCamera.translation().x() -= Renderer::getScreenWidth();
mCamera[3].x -= static_cast<float>(Renderer::getScreenWidth());
updateHelpPrompts();
}
else if (Settings::getInstance()->getString("TransitionStyle") == "fade") {
if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL ||
getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL)
mCamera.translation().y() += Renderer::getScreenHeight();
mCamera[3].y += static_cast<float>(Renderer::getScreenHeight());
else
mCamera.translation().x() += Renderer::getScreenWidth();
mCamera[3].x += static_cast<float>(Renderer::getScreenWidth());
}
else {
updateHelpPrompts();
@ -444,13 +457,13 @@ void ViewController::goToGameList(SystemData* system)
if (mState.viewing == SYSTEM_SELECT) {
// Move the system list.
auto sysList = getSystemListView();
float offsetX = sysList->getPosition().x();
float offsetX = sysList->getPosition().x;
int sysId = getSystemId(system);
sysList->setPosition(sysId * static_cast<float>(Renderer::getScreenWidth()),
sysList->getPosition().y());
offsetX = sysList->getPosition().x() - offsetX;
mCamera.translation().x() -= offsetX;
sysList->getPosition().y);
offsetX = sysList->getPosition().x - offsetX;
mCamera[3].x -= offsetX;
}
// If we are wrapping around, either from the first to last system, or the other way
@ -458,30 +471,30 @@ void ViewController::goToGameList(SystemData* system)
// movements will be correct. This is accomplished by simply offsetting the X position
// with the position of the first or last system plus the screen width.
if (wrapFirstToLast) {
Vector3f currentPosition = mCurrentView->getPosition();
mWrapPreviousPositionX = currentPosition.x();
float offsetX = getGameListView(system)->getPosition().x();
glm::vec3 currentPosition{mCurrentView->getPosition()};
mWrapPreviousPositionX = currentPosition.x;
float offsetX{getGameListView(system)->getPosition().x};
// This is needed to move the camera in the correct direction if there are only two systems.
if (SystemData::sSystemVector.size() == 2 && mNextSystem)
offsetX -= Renderer::getScreenWidth();
else
offsetX += Renderer::getScreenWidth();
currentPosition.x() = offsetX;
currentPosition.x = offsetX;
mCurrentView->setPosition(currentPosition);
mCamera.translation().x() -= offsetX;
mCamera[3].x -= offsetX;
mWrappedViews = true;
}
else if (wrapLastToFirst) {
Vector3f currentPosition = mCurrentView->getPosition();
mWrapPreviousPositionX = currentPosition.x();
float offsetX = getGameListView(system)->getPosition().x();
glm::vec3 currentPosition{mCurrentView->getPosition()};
mWrapPreviousPositionX = currentPosition.x;
float offsetX{getGameListView(system)->getPosition().x};
if (SystemData::sSystemVector.size() == 2 && !mNextSystem)
offsetX += Renderer::getScreenWidth();
else
offsetX -= Renderer::getScreenWidth();
currentPosition.x() = offsetX;
currentPosition.x = offsetX;
mCurrentView->setPosition(currentPosition);
mCamera.translation().x() = -offsetX;
mCamera[3].x = -offsetX;
mWrappedViews = true;
}
@ -489,13 +502,13 @@ void ViewController::goToGameList(SystemData* system)
// Application startup animation, if starting in a gamelist rather than in the system view.
if (mState.viewing == NOTHING) {
mCamera.translation() = -mCurrentView->getPosition();
mCamera = glm::translate(mCamera, -mCurrentView->getPosition());
if (Settings::getInstance()->getString("TransitionStyle") == "slide") {
mCamera.translation().y() -= Renderer::getScreenHeight();
mCamera[3].y -= static_cast<float>(Renderer::getScreenHeight());
updateHelpPrompts();
}
else if (Settings::getInstance()->getString("TransitionStyle") == "fade") {
mCamera.translation().y() += Renderer::getScreenHeight() * 2;
mCamera[3].y += static_cast<float>(Renderer::getScreenHeight() * 2);
}
else {
updateHelpPrompts();
@ -528,21 +541,23 @@ void ViewController::playViewTransition(bool instant)
{
mCancelledTransition = false;
Vector3f target(Vector3f::Zero());
glm::vec3 target{};
if (mCurrentView)
target = mCurrentView->getPosition();
// No need to animate, we're not going anywhere (probably due to goToNextGamelist()
// or goToPrevGamelist() being called when there's only 1 system).
if (target == -mCamera.translation() && !isAnimationPlaying(0))
if (target == static_cast<glm::vec3>(-mCamera[3]) && !isAnimationPlaying(0))
return;
std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
std::string transition_style{Settings::getInstance()->getString("TransitionStyle")};
if (instant || transition_style == "instant") {
setAnimation(new LambdaAnimation(
[this, target](float /*t*/) {
this->mCamera.translation() = -target;
this->mCamera[3].x = -target.x;
this->mCamera[3].y = -target.y;
this->mCamera[3].z = -target.z;
if (mPreviousView)
mPreviousView->onHide();
},
@ -559,7 +574,7 @@ void ViewController::playViewTransition(bool instant)
// Without this, a (much shorter) fade transition would still play as
// finishedCallback is calling this function.
if (!mCancelledTransition)
mFadeOpacity = Math::lerp(0.0f, 1.0f, t);
mFadeOpacity = glm::mix(0.0f, 1.0f, t);
};
auto fadeCallback = [this]() {
@ -571,14 +586,16 @@ void ViewController::playViewTransition(bool instant)
const static int FADE_WAIT = 200; // Time to wait between in/out.
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0,
[this, fadeFunc, fadeCallback, target] {
this->mCamera.translation() = -target;
this->mCamera[3].x = -target.x;
this->mCamera[3].y = -target.y;
this->mCamera[3].z = -target.z;
updateHelpPrompts();
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT,
fadeCallback, true);
});
// Fast-forward animation if we're partway faded.
if (target == -mCamera.translation()) {
if (target == static_cast<glm::vec3>(-mCamera[3])) {
// Not changing screens, so cancel the first half entirely.
advanceAnimation(0, FADE_DURATION);
advanceAnimation(0, FADE_WAIT);
@ -848,16 +865,16 @@ void ViewController::update(int deltaTime)
}
}
void ViewController::render(const Transform4x4f& parentTrans)
void ViewController::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = mCamera * parentTrans;
Transform4x4f transInverse;
transInverse.invert(trans);
glm::mat4 trans{mCamera * parentTrans};
glm::mat4 transInverse{glm::inverse(trans)};
// Camera position, position + size.
Vector3f viewStart = transInverse.translation();
Vector3f viewEnd = transInverse * Vector3f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight(), 0));
glm::vec3 viewStart{transInverse[3]};
glm::vec3 viewEnd{std::fabs(trans[3].x) + static_cast<float>(Renderer::getScreenWidth()),
std::fabs(trans[3].y) + static_cast<float>(Renderer::getScreenHeight()),
0.0f};
// Keep track of UI mode changes.
UIModeController::getInstance()->monitorUIMode();
@ -872,12 +889,12 @@ void ViewController::render(const Transform4x4f& parentTrans)
// Same thing as for the system view, limit the rendering only to what needs to be drawn.
if (it->second == mCurrentView || (it->second == mPreviousView && isCameraMoving())) {
// Clipping.
Vector3f guiStart = it->second->getPosition();
Vector3f guiEnd = it->second->getPosition() +
Vector3f(it->second->getSize().x(), it->second->getSize().y(), 0);
glm::vec3 guiStart{it->second->getPosition()};
glm::vec3 guiEnd{it->second->getPosition() +
glm::vec3{it->second->getSize().x, it->second->getSize().y, 0.0f}};
if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() &&
guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y())
if (guiEnd.x >= viewStart.x && guiEnd.y >= viewStart.y && guiStart.x <= viewEnd.x &&
guiStart.y <= viewEnd.y)
it->second->render(trans);
}
}
@ -1000,7 +1017,7 @@ void ViewController::reloadAll()
SystemData* system = mState.getSystem();
mSystemListView->goToSystem(system, false);
mCurrentView = mSystemListView;
mCamera.r3().x() = 0;
mCamera[3].x = 0.0f;
}
else {
goToSystemView(SystemData::sSystemVector.front(), false);

View file

@ -38,6 +38,7 @@ public:
// These functions are called from main().
void invalidSystemsFileDialog();
void noGamesDialog();
void invalidAlternativeEmulatorDialog();
// Try to completely populate the GameListView map.
// Caches things so there's no pauses during transitions.
@ -78,7 +79,7 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
enum ViewMode {
NOTHING, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
@ -127,6 +128,7 @@ public:
static const std::string TICKMARK_CHAR;
static const std::string CONTROLLER_CHAR;
static const std::string FILTER_CHAR;
static const std::string GEAR_CHAR;
private:
ViewController(Window* window);
@ -152,7 +154,7 @@ private:
FileData* mGameToLaunch;
State mState;
Transform4x4f mCamera;
glm::mat4 mCamera;
bool mSystemViewTransition;
bool mWrappedViews;
float mWrapPreviousPositionX;

View file

@ -19,9 +19,9 @@ BasicGameListView::BasicGameListView(Window* window, FileData* root)
: ISimpleGameListView(window, root)
, mList(window)
{
mList.setSize(mSize.x(), mSize.y() * 0.8f);
mList.setPosition(0, mSize.y() * 0.2f);
mList.setDefaultZIndex(20);
mList.setSize(mSize.x, mSize.y * 0.8f);
mList.setPosition(0.0f, mSize.y * 0.2f);
mList.setDefaultZIndex(20.0f);
addChild(&mList);
populateList(root->getChildrenListToDisplay(), root);
@ -287,8 +287,6 @@ std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
SystemData::sSystemVector.size() > 1)
prompts.push_back(HelpPrompt("left/right", "system"));
prompts.push_back(HelpPrompt("up/down", "choose"));
if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() &&
ViewController::get()->getState().viewing == ViewController::GAME_LIST)
prompts.push_back(HelpPrompt("a", "enter"));

View file

@ -45,8 +45,8 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
{
const float padding = 0.01f;
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y());
mList.setPosition(mSize.x * (0.50f + padding), mList.getPosition().y);
mList.setSize(mSize.x * (0.50f - padding), mList.getSize().y);
mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
@ -54,8 +54,8 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
mThumbnail.setOrigin(0.5f, 0.5f);
mThumbnail.setPosition(2.0f, 2.0f);
mThumbnail.setVisible(false);
mThumbnail.setMaxSize(mSize.x() * (0.25f - 2 * padding), mSize.y() * 0.10f);
mThumbnail.setDefaultZIndex(25);
mThumbnail.setMaxSize(mSize.x * (0.25f - 2.0f * padding), mSize.y * 0.10f);
mThumbnail.setDefaultZIndex(25.0f);
addChild(&mThumbnail);
// Marquee.
@ -63,15 +63,15 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
// Default to off the screen.
mMarquee.setPosition(2.0f, 2.0f);
mMarquee.setVisible(false);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35);
mMarquee.setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f);
mMarquee.setDefaultZIndex(35.0f);
addChild(&mMarquee);
// Image.
mImage.setOrigin(0.5f, 0.5f);
mImage.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f);
mImage.setMaxSize(mSize.x() * (0.50f - 2 * padding), mSize.y() * 0.4f);
mImage.setDefaultZIndex(30);
mImage.setPosition(mSize.x * 0.25f, mList.getPosition().y + mSize.y * 0.2125f);
mImage.setMaxSize(mSize.x * (0.50f - 2.0f * padding), mSize.y * 0.4f);
mImage.setDefaultZIndex(30.0f);
addChild(&mImage);
// Metadata labels + values.
@ -101,27 +101,27 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
addChild(&mLblPlayCount);
addChild(&mPlayCount);
mName.setPosition(mSize.x(), mSize.y());
mName.setDefaultZIndex(40);
mName.setPosition(mSize.x, mSize.y);
mName.setDefaultZIndex(40.0f);
mName.setColor(0xAAAAAAFF);
mName.setFont(Font::get(FONT_SIZE_MEDIUM));
mName.setHorizontalAlignment(ALIGN_CENTER);
addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setPosition(mSize.x * padding, mSize.y * 0.65f);
mDescContainer.setSize(mSize.x * (0.50f - 2.0f * padding),
mSize.y - mDescContainer.getPosition().y);
mDescContainer.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40);
mDescContainer.setDefaultZIndex(40.0f);
addChild(&mDescContainer);
mDescription.setFont(Font::get(FONT_SIZE_SMALL));
mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.setSize(mDescContainer.getSize().x, 0.0f);
mDescContainer.addChild(&mDescription);
mGamelistInfo.setOrigin(0.5f, 0.5f);
mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL));
mGamelistInfo.setDefaultZIndex(50);
mGamelistInfo.setDefaultZIndex(50.0f);
mGamelistInfo.setVisible(true);
addChild(&mGamelistInfo);
@ -145,10 +145,9 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
initMDLabels();
std::vector<TextComponent*> labels = getMDLabels();
assert(labels.size() == 8);
std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
"md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players",
"md_lbl_lastplayed", "md_lbl_playcount" };
std::vector<std::string> lblElements = {
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"};
for (unsigned int i = 0; i < labels.size(); i++)
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
@ -156,23 +155,23 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
initMDValues();
std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 8);
std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount" };
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount"};
for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
mDescContainer.applyTheme(theme, getName(), "md_description",
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.setSize(mDescContainer.getSize().x, 0.0f);
mDescription.applyTheme(
theme, getName(), "md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
// If there is no position defined in the theme for gamelistInfo, then hide it.
if (mGamelistInfo.getPosition() == 0)
if (mGamelistInfo.getPosition() == glm::vec3{})
mGamelistInfo.setVisible(false);
else
mGamelistInfo.setVisible(true);
@ -187,26 +186,26 @@ void DetailedGameListView::initMDLabels()
const unsigned int colCount = 2;
const unsigned int rowCount = static_cast<int>(components.size() / 2);
Vector3f start(mSize.x() * 0.01f, mSize.y() * 0.625f, 0.0f);
glm::vec3 start{mSize.x * 0.01f, mSize.y * 0.625f, 0.0f};
const float colSize = (mSize.x() * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y();
const float colSize = (mSize.x * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y;
for (unsigned int i = 0; i < components.size(); i++) {
const unsigned int row = i % rowCount;
Vector3f pos(0.0f, 0.0f, 0.0f);
glm::vec3 pos{};
if (row == 0) {
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
pos = start + glm::vec3{colSize * (i / rowCount), 0.0f, 0.0f};
}
else {
// Work from the last component.
GuiComponent* lc = components[i - 1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
pos = lc->getPosition() + glm::vec3{0.0f, lc->getSize().y + rowPadding, 0.0f};
}
components[i]->setFont(Font::get(FONT_SIZE_SMALL));
components[i]->setPosition(pos);
components[i]->setDefaultZIndex(40);
components[i]->setDefaultZIndex(40.0f);
}
}
@ -227,23 +226,22 @@ void DetailedGameListView::initMDValues()
float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2.0f;
const float colSize = (mSize.x * 0.48f) / 2.0f;
for (unsigned int i = 0; i < labels.size(); i++) {
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f;
const float heightDiff = (labels[i]->getSize().y - values[i]->getSize().y) / 2.0f;
values[i]->setPosition(labels[i]->getPosition() +
Vector3f(labels[i]->getSize().x(), heightDiff, 0));
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
glm::vec3{labels[i]->getSize().x, heightDiff, 0.0f});
values[i]->setSize(colSize - labels[i]->getSize().x, values[i]->getSize().y);
values[i]->setDefaultZIndex(40.0f);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
float testBot = values[i]->getPosition().y + values[i]->getSize().y;
if (testBot > bottom)
bottom = testBot;
}
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x(),
mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setPosition(mDescContainer.getPosition().x, bottom + mSize.y * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x, mSize.y - mDescContainer.getPosition().y);
}
void DetailedGameListView::updateInfoPanel()
@ -333,7 +331,7 @@ void DetailedGameListView::updateInfoPanel()
CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem());
if (mRandomGame) {
mThumbnail.setImage(mRandomGame->getThumbnailPath());
mMarquee.setImage(mRandomGame->getMarqueePath());
mMarquee.setImage(mRandomGame->getMarqueePath(), false, true);
mImage.setImage(mRandomGame->getImagePath());
}
else {
@ -344,7 +342,7 @@ void DetailedGameListView::updateInfoPanel()
}
else {
mThumbnail.setImage(file->getThumbnailPath());
mMarquee.setImage(file->getMarqueePath());
mMarquee.setImage(file->getMarqueePath(), false, true);
mImage.setImage(file->getImagePath());
}
@ -386,7 +384,7 @@ void DetailedGameListView::updateInfoPanel()
// Fade in the game image.
auto func = [this](float t) {
mImage.setOpacity(static_cast<unsigned char>(
Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
glm::mix(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
};
mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
@ -434,7 +432,7 @@ void DetailedGameListView::updateInfoPanel()
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
auto func = [comp](float t) {
comp->setOpacity(static_cast<unsigned char>(Math::lerp(0.0f, 1.0f, t) * 255));
comp->setOpacity(static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
}

View file

@ -47,8 +47,8 @@ GridGameListView::GridGameListView(Window* window, FileData* root)
{
const float padding = 0.01f;
mGrid.setPosition(mSize.x() * 0.1f, mSize.y() * 0.1f);
mGrid.setDefaultZIndex(20);
mGrid.setPosition(mSize.x * 0.1f, mSize.y * 0.1f);
mGrid.setDefaultZIndex(20.0f);
mGrid.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
addChild(&mGrid);
@ -81,41 +81,41 @@ GridGameListView::GridGameListView(Window* window, FileData* root)
addChild(&mLblPlayCount);
addChild(&mPlayCount);
mName.setPosition(mSize.x(), mSize.y());
mName.setDefaultZIndex(40);
mName.setPosition(mSize.x, mSize.y);
mName.setDefaultZIndex(40.0f);
mName.setColor(0xAAAAAAFF);
mName.setFont(Font::get(FONT_SIZE_MEDIUM));
mName.setHorizontalAlignment(ALIGN_CENTER);
addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setPosition(mSize.x * padding, mSize.y * 0.65f);
mDescContainer.setSize(mSize.x * (0.50f - 2.0f * padding),
mSize.y - mDescContainer.getPosition().y);
mDescContainer.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40);
mDescContainer.setDefaultZIndex(40.0f);
addChild(&mDescContainer);
mDescription.setFont(Font::get(FONT_SIZE_SMALL));
mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.setSize(mDescContainer.getSize().x, 0.0f);
mDescContainer.addChild(&mDescription);
mMarquee.setOrigin(0.5f, 0.5f);
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35);
mMarquee.setPosition(mSize.x * 0.25f, mSize.y * 0.10f);
mMarquee.setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f);
mMarquee.setDefaultZIndex(35.0f);
mMarquee.setVisible(false);
addChild(&mMarquee);
mImage.setOrigin(0.5f, 0.5f);
mImage.setPosition(2.0f, 2.0f);
mImage.setMaxSize(mSize.x() * (0.50f - 2 * padding), mSize.y() * 0.4f);
mImage.setDefaultZIndex(10);
mImage.setMaxSize(mSize.x * (0.50f - 2.0f * padding), mSize.y * 0.4f);
mImage.setDefaultZIndex(10.0f);
mImage.setVisible(false);
addChild(&mImage);
mGamelistInfo.setOrigin(0.5f, 0.5f);
mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL));
mGamelistInfo.setDefaultZIndex(50);
mGamelistInfo.setDefaultZIndex(50.0f);
mGamelistInfo.setVisible(true);
addChild(&mGamelistInfo);
@ -244,10 +244,9 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDLabels();
std::vector<TextComponent*> labels = getMDLabels();
assert(labels.size() == 8);
std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
"md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players",
"md_lbl_lastplayed", "md_lbl_playcount" };
std::vector<std::string> lblElements = {
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"};
for (unsigned int i = 0; i < labels.size(); i++)
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
@ -255,16 +254,16 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDValues();
std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 8);
std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount" };
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount"};
for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
mDescContainer.applyTheme(theme, getName(), "md_description",
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.setSize(mDescContainer.getSize().x, 0.0f);
mDescription.applyTheme(
theme, getName(), "md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
@ -277,7 +276,7 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
// If there is no position defined in the theme for gamelistInfo, then hide it.
if (mGamelistInfo.getPosition() == 0)
if (mGamelistInfo.getPosition() == glm::vec3{})
mGamelistInfo.setVisible(false);
else
mGamelistInfo.setVisible(true);
@ -298,26 +297,26 @@ void GridGameListView::initMDLabels()
const unsigned int colCount = 2;
const unsigned int rowCount = static_cast<int>(components.size() / 2);
Vector3f start(mSize.x() * 0.01f, mSize.y() * 0.625f, 0.0f);
glm::vec3 start{mSize.x * 0.01f, mSize.y * 0.625f, 0.0f};
const float colSize = (mSize.x() * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y();
const float colSize = (mSize.x * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y;
for (unsigned int i = 0; i < components.size(); i++) {
const unsigned int row = i % rowCount;
Vector3f pos(0.0f, 0.0f, 0.0f);
glm::vec3 pos{};
if (row == 0) {
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
pos = start + glm::vec3{colSize * (i / rowCount), 0.0f, 0.0f};
}
else {
// Work from the last component.
GuiComponent* lc = components[i - 1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
pos = lc->getPosition() + glm::vec3{0.0f, lc->getSize().y + rowPadding, 0.0f};
}
components[i]->setFont(Font::get(FONT_SIZE_SMALL));
components[i]->setPosition(pos);
components[i]->setDefaultZIndex(40);
components[i]->setDefaultZIndex(40.0f);
}
}
@ -338,22 +337,21 @@ void GridGameListView::initMDValues()
float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2.0f;
const float colSize = (mSize.x * 0.48f) / 2.0f;
for (unsigned int i = 0; i < labels.size(); i++) {
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f;
const float heightDiff = (labels[i]->getSize().y - values[i]->getSize().y) / 2.0f;
values[i]->setPosition(labels[i]->getPosition() +
Vector3f(labels[i]->getSize().x(), heightDiff, 0));
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40);
glm::vec3{labels[i]->getSize().x, heightDiff, 0.0f});
values[i]->setSize(colSize - labels[i]->getSize().x, values[i]->getSize().y);
values[i]->setDefaultZIndex(40.0f);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
float testBot = values[i]->getPosition().y + values[i]->getSize().y;
if (testBot > bottom)
bottom = testBot;
}
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x(),
mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setPosition(mDescContainer.getPosition().x, bottom + mSize.y * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x, mSize.y - mDescContainer.getPosition().y);
}
void GridGameListView::updateInfoPanel()
@ -406,7 +404,7 @@ void GridGameListView::updateInfoPanel()
fadingOut = true;
}
else {
mMarquee.setImage(file->getMarqueePath());
mMarquee.setImage(file->getMarqueePath(), false, true);
// Populate the gamelistInfo field which shows an icon if a folder has been entered
// as well as the game count for the entire system (total and favorites separately).
@ -446,7 +444,7 @@ void GridGameListView::updateInfoPanel()
// Fade in the game image.
auto func = [this](float t) {
mImage.setOpacity(static_cast<unsigned char>(
Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
glm::mix(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
};
mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
@ -494,7 +492,7 @@ void GridGameListView::updateInfoPanel()
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
auto func = [comp](float t) {
// TEMPORARY - This does not seem to work, needs to be reviewed later.
// comp->setOpacity(static_cast<unsigned char>(Math::lerp(0.0f, 1.0f, t) * 255));
// comp->setOpacity(static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
}

View file

@ -60,17 +60,17 @@ HelpStyle IGameListView::getHelpStyle()
return style;
}
void IGameListView::render(const Transform4x4f& parentTrans)
void IGameListView::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
float scaleX = trans.r0().x();
float scaleY = trans.r1().y();
float scaleX = trans[0].x;
float scaleY = trans[1].y;
Vector2i pos(static_cast<int>(std::round(trans.translation()[0])),
static_cast<int>(std::round(trans.translation()[1])));
Vector2i size(static_cast<int>(std::round(mSize.x() * scaleX)),
static_cast<int>(std::round(mSize.y() * scaleY)));
glm::ivec2 pos{static_cast<int>(std::round(trans[3].x)),
static_cast<int>(std::round(trans[3].y))};
glm::ivec2 size{static_cast<int>(std::round(mSize.x * scaleX)),
static_cast<int>(std::round(mSize.y * scaleY))};
Renderer::pushClipRect(pos, size);
renderChildren(trans);

View file

@ -54,7 +54,7 @@ public:
virtual HelpStyle getHelpStyle() override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
protected:
FileData* mRoot;

View file

@ -28,18 +28,18 @@ ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root)
, mRandomGame(nullptr)
{
mHeaderText.setText("Logo Text");
mHeaderText.setSize(mSize.x(), 0);
mHeaderText.setPosition(0, 0);
mHeaderText.setSize(mSize.x, 0.0f);
mHeaderText.setPosition(0.0f, 0.0f);
mHeaderText.setHorizontalAlignment(ALIGN_CENTER);
mHeaderText.setDefaultZIndex(50);
mHeaderText.setDefaultZIndex(50.0f);
mHeaderImage.setResize(0, mSize.y() * 0.185f);
mHeaderImage.setResize(0.0f, mSize.y * 0.185f);
mHeaderImage.setOrigin(0.5f, 0.0f);
mHeaderImage.setPosition(mSize.x() / 2, 0);
mHeaderImage.setDefaultZIndex(50);
mHeaderImage.setPosition(mSize.x / 2.0f, 0.0f);
mHeaderImage.setDefaultZIndex(50.0f);
mBackground.setResize(mSize.x(), mSize.y());
mBackground.setDefaultZIndex(0);
mBackground.setResize(mSize.x, mSize.y);
mBackground.setDefaultZIndex(0.0f);
addChild(&mHeaderText);
addChild(&mBackground);

View file

@ -64,8 +64,8 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root)
mVideo = new VideoFFmpegComponent(window);
#endif
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y());
mList.setPosition(mSize.x * (0.50f + padding), mList.getPosition().y);
mList.setSize(mSize.x * (0.50f - padding), mList.getSize().y);
mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
@ -73,22 +73,22 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root)
mThumbnail.setOrigin(0.5f, 0.5f);
mThumbnail.setPosition(2.0f, 2.0f);
mThumbnail.setVisible(false);
mThumbnail.setMaxSize(mSize.x() * (0.25f - 2.0f * padding), mSize.y() * 0.10f);
mThumbnail.setDefaultZIndex(35);
mThumbnail.setMaxSize(mSize.x * (0.25f - 2.0f * padding), mSize.y * 0.10f);
mThumbnail.setDefaultZIndex(35.0f);
addChild(&mThumbnail);
// Marquee.
mMarquee.setOrigin(0.5f, 0.5f);
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35);
mMarquee.setPosition(mSize.x * 0.25f, mSize.y * 0.10f);
mMarquee.setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f);
mMarquee.setDefaultZIndex(35.0f);
addChild(&mMarquee);
// Video.
mVideo->setOrigin(0.5f, 0.5f);
mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f);
mVideo->setSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.4f);
mVideo->setDefaultZIndex(30);
mVideo->setPosition(mSize.x * 0.25f, mSize.y * 0.4f);
mVideo->setSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.4f);
mVideo->setDefaultZIndex(30.0f);
addChild(mVideo);
// Metadata labels + values.
@ -118,27 +118,27 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root)
addChild(&mLblPlayCount);
addChild(&mPlayCount);
mName.setPosition(mSize.x(), mSize.y());
mName.setDefaultZIndex(40);
mName.setPosition(mSize.x, mSize.y);
mName.setDefaultZIndex(40.0f);
mName.setColor(0xAAAAAAFF);
mName.setFont(Font::get(FONT_SIZE_MEDIUM));
mName.setHorizontalAlignment(ALIGN_CENTER);
addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding),
mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setPosition(mSize.x * padding, mSize.y * 0.65f);
mDescContainer.setSize(mSize.x * (0.50f - 2.0f * padding),
mSize.y - mDescContainer.getPosition().y);
mDescContainer.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40);
mDescContainer.setDefaultZIndex(40.0f);
addChild(&mDescContainer);
mDescription.setFont(Font::get(FONT_SIZE_SMALL));
mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.setSize(mDescContainer.getSize().x, 0.0f);
mDescContainer.addChild(&mDescription);
mGamelistInfo.setOrigin(0.5f, 0.5f);
mGamelistInfo.setFont(Font::get(FONT_SIZE_SMALL));
mGamelistInfo.setDefaultZIndex(50);
mGamelistInfo.setDefaultZIndex(50.0f);
mGamelistInfo.setVisible(true);
addChild(&mGamelistInfo);
@ -167,10 +167,9 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDLabels();
std::vector<TextComponent*> labels = getMDLabels();
assert(labels.size() == 8);
std::vector<std::string> lblElements = { "md_lbl_rating", "md_lbl_releasedate",
"md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players",
"md_lbl_lastplayed", "md_lbl_playcount" };
std::vector<std::string> lblElements = {
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"};
for (unsigned int i = 0; i < labels.size(); i++)
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
@ -178,23 +177,23 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDValues();
std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 8);
std::vector<std::string> valElements = { "md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount" };
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount"};
for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
mDescContainer.applyTheme(theme, getName(), "md_description",
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescription.setSize(mDescContainer.getSize().x, 0.0f);
mDescription.applyTheme(
theme, getName(), "md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT);
// If there is no position defined in the theme for gamelistInfo, then hide it.
if (mGamelistInfo.getPosition() == 0)
if (mGamelistInfo.getPosition() == glm::vec3{})
mGamelistInfo.setVisible(false);
else
mGamelistInfo.setVisible(true);
@ -209,26 +208,26 @@ void VideoGameListView::initMDLabels()
const unsigned int colCount = 2;
const unsigned int rowCount = static_cast<int>(components.size() / 2);
Vector3f start(mSize.x() * 0.01f, mSize.y() * 0.625f, 0.0f);
glm::vec3 start{mSize.x * 0.01f, mSize.y * 0.625f, 0.0f};
const float colSize = (mSize.x() * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y();
const float colSize = (mSize.x * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y;
for (unsigned int i = 0; i < components.size(); i++) {
const unsigned int row = i % rowCount;
Vector3f pos(0.0f, 0.0f, 0.0f);
glm::vec3 pos{};
if (row == 0) {
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
pos = start + glm::vec3{colSize * (i / rowCount), 0.0f, 0.0f};
}
else {
// Work from the last component.
GuiComponent* lc = components[i - 1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
pos = lc->getPosition() + glm::vec3{0.0f, lc->getSize().y + rowPadding, 0.0f};
}
components[i]->setFont(Font::get(FONT_SIZE_SMALL));
components[i]->setPosition(pos);
components[i]->setDefaultZIndex(40);
components[i]->setDefaultZIndex(40.0f);
}
}
@ -249,23 +248,22 @@ void VideoGameListView::initMDValues()
float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2.0f;
const float colSize = (mSize.x * 0.48f) / 2.0f;
for (unsigned int i = 0; i < labels.size(); i++) {
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f;
const float heightDiff = (labels[i]->getSize().y - values[i]->getSize().y) / 2.0f;
values[i]->setPosition(labels[i]->getPosition() +
Vector3f(labels[i]->getSize().x(), heightDiff, 0));
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40);
glm::vec3{labels[i]->getSize().x, heightDiff, 0.0f});
values[i]->setSize(colSize - labels[i]->getSize().x, values[i]->getSize().y);
values[i]->setDefaultZIndex(40.0f);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
float testBot = values[i]->getPosition().y + values[i]->getSize().y;
if (testBot > bottom)
bottom = testBot;
}
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x(),
mSize.y() - mDescContainer.getPosition().y());
mDescContainer.setPosition(mDescContainer.getPosition().x, bottom + mSize.y * 0.01f);
mDescContainer.setSize(mDescContainer.getSize().x, mSize.y - mDescContainer.getPosition().y);
}
void VideoGameListView::updateInfoPanel()
@ -356,7 +354,7 @@ void VideoGameListView::updateInfoPanel()
CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem());
if (mRandomGame) {
mThumbnail.setImage(mRandomGame->getThumbnailPath());
mMarquee.setImage(mRandomGame->getMarqueePath());
mMarquee.setImage(mRandomGame->getMarqueePath(), false, true);
mVideo->setImage(mRandomGame->getImagePath());
// Always stop the video before setting a new video as it will otherwise continue
// to play if it has the same path (i.e. it is the same physical video file) as
@ -378,7 +376,7 @@ void VideoGameListView::updateInfoPanel()
}
else {
mThumbnail.setImage(file->getThumbnailPath());
mMarquee.setImage(file->getMarqueePath());
mMarquee.setImage(file->getMarqueePath(), false, true);
mVideo->setImage(file->getImagePath());
mVideo->onHide();
@ -426,7 +424,7 @@ void VideoGameListView::updateInfoPanel()
// Fade in the game image.
auto func = [this](float t) {
mVideo->setOpacity(static_cast<unsigned char>(
Math::lerp(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
glm::mix(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
};
mVideo->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
@ -474,7 +472,7 @@ void VideoGameListView::updateInfoPanel()
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
auto func = [comp](float t) {
comp->setOpacity(static_cast<unsigned char>(Math::lerp(0.0f, 1.0f, t) * 255));
comp->setOpacity(static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
comp->setAnimation(new LambdaAnimation(func, 200), 0, nullptr, fadingOut);
}

View file

@ -22,6 +22,7 @@ set(CORE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/animations/Animation.h
${CMAKE_CURRENT_SOURCE_DIR}/src/animations/AnimationController.h
${CMAKE_CURRENT_SOURCE_DIR}/src/animations/LambdaAnimation.h
${CMAKE_CURRENT_SOURCE_DIR}/src/animations/MoveCameraAnimation.h
# GUI components
${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.h
@ -57,14 +58,6 @@ set(CORE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditPopup.h
# Math
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Misc.h
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Transform4x4f.h
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector2f.h
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector2i.h
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector3f.h
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector4f.h
# Renderers
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.h
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Shader_GL21.h
@ -79,6 +72,7 @@ set(CORE_HEADERS
# Utils
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/CImgUtil.h
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/FileSystemUtil.h
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/MathUtil.h
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/StringUtil.h
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/TimeUtil.h
)
@ -134,14 +128,6 @@ set(CORE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditPopup.cpp
# Math
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Misc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Transform4x4f.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector2f.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector2i.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector3f.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector4f.cpp
# Renderer
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/renderers/Renderer_GL21.cpp
@ -158,6 +144,7 @@ set(CORE_SOURCES
# Utils
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/CImgUtil.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/FileSystemUtil.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/MathUtil.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/StringUtil.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/TimeUtil.cpp
)

View file

@ -24,11 +24,11 @@ GuiComponent::GuiComponent(Window* window)
, mColorShiftEnd(0)
, mOpacity(255)
, mSaturation(1.0f)
, mPosition(Vector3f::Zero())
, mOrigin(Vector2f::Zero())
, mPosition({})
, mOrigin({})
, mRotationOrigin(0.5f, 0.5f)
, mSize(Vector2f::Zero())
, mTransform(Transform4x4f::Identity())
, mSize({})
, mTransform(Renderer::getIdentity())
, mIsProcessing(false)
, mVisible(true)
, mEnabled(true)
@ -78,16 +78,16 @@ void GuiComponent::update(int deltaTime)
updateChildren(deltaTime);
}
void GuiComponent::render(const Transform4x4f& parentTrans)
void GuiComponent::render(const glm::mat4& parentTrans)
{
if (!isVisible())
return;
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
renderChildren(trans);
}
void GuiComponent::renderChildren(const Transform4x4f& transform) const
void GuiComponent::renderChildren(const glm::mat4& transform) const
{
for (unsigned int i = 0; i < getChildCount(); i++)
getChild(i)->render(transform);
@ -95,26 +95,26 @@ void GuiComponent::renderChildren(const Transform4x4f& transform) const
void GuiComponent::setPosition(float x, float y, float z)
{
mPosition = Vector3f(x, y, z);
mPosition = glm::vec3{x, y, z};
onPositionChanged();
}
void GuiComponent::setOrigin(float x, float y)
{
mOrigin = Vector2f(x, y);
mOrigin = glm::vec2{x, y};
onOriginChanged();
}
void GuiComponent::setSize(float w, float h)
{
mSize = Vector2f(w, h);
mSize = glm::vec2{w, h};
onSizeChanged();
}
Vector2f GuiComponent::getCenter() const
glm::vec2 GuiComponent::getCenter() const
{
return Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2.0f,
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2.0f);
return glm::vec2{mPosition.x - (getSize().x * mOrigin.x) + getSize().x / 2.0f,
mPosition.y - (getSize().y * mOrigin.y) + getSize().y / 2.0f};
}
void GuiComponent::addChild(GuiComponent* cmp)
@ -171,33 +171,34 @@ void GuiComponent::setOpacity(unsigned char opacity)
(*it)->setOpacity(opacity);
}
const Transform4x4f& GuiComponent::getTransform()
const glm::mat4& GuiComponent::getTransform()
{
mTransform = Transform4x4f::Identity();
mTransform.translate(mPosition);
mTransform = Renderer::getIdentity();
mTransform = glm::translate(mTransform, mPosition);
if (mScale != 1.0f)
mTransform.scale(mScale);
mTransform = glm::scale(mTransform, glm::vec3{mScale});
if (mRotation != 0.0f) {
// Calculate offset as difference between origin and rotation origin.
Vector2f rotationSize = getRotationSize();
float xOff = (mOrigin.x() - mRotationOrigin.x()) * rotationSize.x();
float yOff = (mOrigin.y() - mRotationOrigin.y()) * rotationSize.y();
glm::vec2 rotationSize{getRotationSize()};
float xOff{(mOrigin.x - mRotationOrigin.x) * rotationSize.x};
float yOff{(mOrigin.y - mRotationOrigin.y) * rotationSize.y};
// Transform to offset point.
if (xOff != 0.0f || yOff != 0.0f)
mTransform.translate(Vector3f(xOff * -1.0f, yOff * -1.0f, 0.0f));
mTransform = glm::translate(mTransform, glm::vec3{xOff * -1.0f, yOff * -1.0f, 0.0f});
// Apply rotation transform.
mTransform.rotateZ(mRotation);
mTransform = glm::rotate(mTransform, mRotation, glm::vec3{0.0f, 0.0f, 1.0f});
// Transform back to original point.
if (xOff != 0.0f || yOff != 0.0f)
mTransform.translate(Vector3f(xOff, yOff, 0.0f));
mTransform = glm::translate(mTransform, glm::vec3{xOff, yOff, 0.0f});
}
mTransform.translate(
Vector3f(mOrigin.x() * mSize.x() * -1.0f, mOrigin.y() * mSize.y() * -1.0f, 0.0f));
mTransform = glm::translate(
mTransform, glm::vec3{mOrigin.x * mSize.x * -1.0f, mOrigin.y * mSize.y * -1.0f, 0.0f});
return mTransform;
}
@ -301,9 +302,9 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& element,
unsigned int properties)
{
Vector2f scale = getParent() ? getParent()->getSize() :
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
glm::vec2 scale{getParent() ? getParent()->getSize() :
glm::vec2{static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())}};
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "");
if (!elem)
@ -311,24 +312,24 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
using namespace ThemeFlags;
if (properties & POSITION && elem->has("pos")) {
Vector2f denormalized = elem->get<Vector2f>("pos") * scale;
setPosition(Vector3f(denormalized.x(), denormalized.y(), 0));
glm::vec2 denormalized{elem->get<glm::vec2>("pos") * scale};
setPosition(glm::vec3{denormalized.x, denormalized.y, 0.0f});
}
if (properties & ThemeFlags::SIZE && elem->has("size"))
setSize(elem->get<Vector2f>("size") * scale);
setSize(elem->get<glm::vec2>("size") * scale);
// Position + size also implies origin.
if ((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) &&
elem->has("origin")) {
setOrigin(elem->get<Vector2f>("origin"));
setOrigin(elem->get<glm::vec2>("origin"));
}
if (properties & ThemeFlags::ROTATION) {
if (elem->has("rotation"))
setRotationDegrees(elem->get<float>("rotation"));
if (elem->has("rotationOrigin"))
setRotationOrigin(elem->get<Vector2f>("rotationOrigin"));
setRotationOrigin(elem->get<glm::vec2>("rotationOrigin"));
}
if (properties & ThemeFlags::Z_INDEX && elem->has("zIndex"))

View file

@ -13,8 +13,6 @@
#include "HelpStyle.h"
#include "InputConfig.h"
#include "animations/AnimationController.h"
#include "math/Misc.h"
#include "math/Transform4x4f.h"
#include <functional>
#include <memory>
@ -45,8 +43,8 @@ public:
virtual void textInput(const std::string& text);
// Called when input is received.
// Return true if the input is consumed, false if
// it should continue to be passed to other children.
// Return true if the input is consumed, false if it should continue to be passed
// to other children.
virtual bool input(InputConfig* config, Input input);
// Called when time passes.
@ -56,49 +54,50 @@ public:
virtual void update(int deltaTime);
// Called when it's time to render.
// By default, just calls renderChildren(parentTrans * getTransform()).
// You probably want to override this like so:
// 1. Calculate the new transform that your control will draw at with
// Transform4x4f t = parentTrans * getTransform().
// By default, just calls renderChildren(parentTrans * getTransform())
// Normally the following steps are required:
// 1. Calculate the new transform that your component will draw at
// glm::mat4 trans{parentTrans * getTransform()};
// 2. Set the renderer to use that new transform as the model matrix
// Renderer::setMatrix(t);
// 3. Draw your component.
// 4. Tell your children to render, based on your component's transform - renderChildren(t).
virtual void render(const Transform4x4f& parentTrans);
// Renderer::setMatrix(trans);
// 3. Draw your component
// 4. Tell your children to render, based on your component's transform
// renderChildren(trans);
virtual void render(const glm::mat4& parentTrans);
Vector3f getPosition() const { return mPosition; }
void setPosition(const Vector3f& offset) { setPosition(offset.x(), offset.y(), offset.z()); }
glm::vec3 getPosition() const { return mPosition; }
void setPosition(const glm::vec3& offset) { setPosition(offset.x, offset.y, offset.z); }
void setPosition(float x, float y, float z = 0.0f);
virtual void onPositionChanged() {}
Vector2f getOrigin() const { return mOrigin; }
glm::vec2 getOrigin() const { return mOrigin; }
// Sets the origin as a percentage of this image.
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center.)
void setOrigin(float originX, float originY);
void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); }
void setOrigin(glm::vec2 origin) { setOrigin(origin.x, origin.y); }
virtual void onOriginChanged() {}
Vector2f getRotationOrigin() const { return mRotationOrigin; }
glm::vec2 getRotationOrigin() const { return mRotationOrigin; }
// Sets the rotation origin as a percentage of this image.
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center.)
void setRotationOrigin(float originX, float originY)
{
mRotationOrigin = Vector2f(originX, originY);
mRotationOrigin = glm::vec2{originX, originY};
}
void setRotationOrigin(Vector2f origin) { setRotationOrigin(origin.x(), origin.y()); }
void setRotationOrigin(glm::vec2 origin) { setRotationOrigin(origin.x, origin.y); }
virtual Vector2f getSize() const { return mSize; }
void setSize(const Vector2f& size) { setSize(size.x(), size.y()); }
virtual glm::vec2 getSize() const { return mSize; }
void setSize(const glm::vec2& size) { setSize(size.x, size.y); }
void setSize(float w, float h);
virtual void setResize(float width, float height) {}
virtual void onSizeChanged() {}
virtual Vector2f getRotationSize() const { return getSize(); }
virtual glm::vec2 getRotationSize() const { return getSize(); }
float getRotation() const { return mRotation; }
void setRotation(float rotation) { mRotation = rotation; }
void setRotationDegrees(float rotation)
{
setRotation(static_cast<float>(ES_DEG_TO_RAD(rotation)));
setRotation(static_cast<float>(glm::radians(rotation)));
}
float getScale() const { return mScale; }
@ -114,7 +113,7 @@ public:
void setVisible(bool visible) { mVisible = visible; }
// Returns the center point of the image (takes origin into account).
Vector2f getCenter() const;
glm::vec2 getCenter() const;
void setParent(GuiComponent* parent) { mParent = parent; }
GuiComponent* getParent() const { return mParent; }
@ -183,7 +182,7 @@ public:
virtual std::shared_ptr<Font> getFont() const { return nullptr; }
const Transform4x4f& getTransform();
const glm::mat4& getTransform();
virtual std::string getValue() const { return ""; }
virtual void setValue(const std::string& value) {}
@ -231,7 +230,7 @@ public:
const static unsigned char MAX_ANIMATIONS = 4;
protected:
void renderChildren(const Transform4x4f& transform) const;
void renderChildren(const glm::mat4& transform) const;
void updateSelf(int deltaTime); // Updates animations.
void updateChildren(int deltaTime); // Updates animations.
@ -249,10 +248,10 @@ protected:
GuiComponent* mParent;
std::vector<GuiComponent*> mChildren;
Vector3f mPosition;
Vector2f mOrigin;
Vector2f mRotationOrigin;
Vector2f mSize;
glm::vec3 mPosition;
glm::vec2 mOrigin;
glm::vec2 mRotationOrigin;
glm::vec2 mSize;
float mRotation = 0.0;
float mScale = 1.0;
@ -265,8 +264,8 @@ protected:
bool mEnabled;
private:
// Don't access this directly! Use getTransform()!
Transform4x4f mTransform;
// Don't access this directly, instead use getTransform().
glm::mat4 mTransform;
AnimationController* mAnimationMap[MAX_ANIMATIONS];
};

View file

@ -13,10 +13,16 @@
HelpStyle::HelpStyle()
{
position = Vector2f(Renderer::getScreenWidth() * 0.012f, Renderer::getScreenHeight() * 0.9515f);
origin = Vector2f(0.0f, 0.0f);
iconColor = 0x777777FF;
position =
glm::vec2{Renderer::getScreenWidth() * 0.012f, Renderer::getScreenHeight() * 0.9515f};
origin = glm::vec2{};
textColor = 0x777777FF;
textColorDimmed = 0x777777FF;
iconColor = 0x777777FF;
iconColorDimmed = 0x777777FF;
entrySpacing = 16.0f;
iconTextSpacing = 8.0f;
textStyle = "uppercase";
if (FONT_SIZE_SMALL != 0)
font = Font::get(FONT_SIZE_SMALL);
@ -31,19 +37,102 @@ void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::s
return;
if (elem->has("pos"))
position =
elem->get<Vector2f>("pos") * Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
position = elem->get<glm::vec2>("pos") *
glm::vec2{static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())};
if (elem->has("origin"))
origin = elem->get<Vector2f>("origin");
origin = elem->get<glm::vec2>("origin");
if (elem->has("textColor"))
textColor = elem->get<unsigned int>("textColor");
if (elem->has("textColorDimmed"))
textColorDimmed = elem->get<unsigned int>("textColorDimmed");
if (elem->has("iconColor"))
iconColor = elem->get<unsigned int>("iconColor");
if (elem->has("iconColorDimmed"))
iconColorDimmed = elem->get<unsigned int>("iconColorDimmed");
if (elem->has("fontPath") || elem->has("fontSize"))
font = Font::getFromTheme(elem, ThemeFlags::ALL, font);
if (elem->has("entrySpacing"))
entrySpacing = elem->get<float>("entrySpacing");
if (elem->has("iconTextSpacing"))
iconTextSpacing = elem->get<float>("iconTextSpacing");
if (elem->has("textStyle"))
textStyle = elem->get<std::string>("textStyle");
// Load custom button icons.
// General.
if (elem->has("dpad_updown"))
mCustomButtons.dpad_updown = elem->get<std::string>("dpad_updown");
if (elem->has("dpad_leftright"))
mCustomButtons.dpad_leftright = elem->get<std::string>("dpad_leftright");
if (elem->has("dpad_all"))
mCustomButtons.dpad_all = elem->get<std::string>("dpad_all");
if (elem->has("thumbstick_click"))
mCustomButtons.thumbstick_click = elem->get<std::string>("thumbstick_click");
if (elem->has("button_l"))
mCustomButtons.button_l = elem->get<std::string>("button_l");
if (elem->has("button_r"))
mCustomButtons.button_r = elem->get<std::string>("button_r");
if (elem->has("button_lr"))
mCustomButtons.button_lr = elem->get<std::string>("button_lr");
// SNES.
if (elem->has("button_a_SNES"))
mCustomButtons.button_a_SNES = elem->get<std::string>("button_a_SNES");
if (elem->has("button_b_SNES"))
mCustomButtons.button_b_SNES = elem->get<std::string>("button_b_SNES");
if (elem->has("button_x_SNES"))
mCustomButtons.button_x_SNES = elem->get<std::string>("button_x_SNES");
if (elem->has("button_y_SNES"))
mCustomButtons.button_y_SNES = elem->get<std::string>("button_y_SNES");
if (elem->has("button_start_SNES"))
mCustomButtons.button_start_SNES = elem->get<std::string>("button_start_SNES");
if (elem->has("button_back_SNES"))
mCustomButtons.button_back_SNES = elem->get<std::string>("button_back_SNES");
// PS.
if (elem->has("button_a_PS"))
mCustomButtons.button_a_PS = elem->get<std::string>("button_a_PS");
if (elem->has("button_b_PS"))
mCustomButtons.button_b_PS = elem->get<std::string>("button_b_PS");
if (elem->has("button_x_PS"))
mCustomButtons.button_x_PS = elem->get<std::string>("button_x_PS");
if (elem->has("button_y_PS"))
mCustomButtons.button_y_PS = elem->get<std::string>("button_y_PS");
if (elem->has("button_start_PS4"))
mCustomButtons.button_start_PS4 = elem->get<std::string>("button_start_PS4");
if (elem->has("button_back_PS4"))
mCustomButtons.button_back_PS4 = elem->get<std::string>("button_back_PS4");
if (elem->has("button_start_PS5"))
mCustomButtons.button_start_PS5 = elem->get<std::string>("button_start_PS5");
if (elem->has("button_back_PS5"))
mCustomButtons.button_back_PS5 = elem->get<std::string>("button_back_PS5");
// XBOX.
if (elem->has("button_a_XBOX"))
mCustomButtons.button_a_XBOX = elem->get<std::string>("button_a_XBOX");
if (elem->has("button_b_XBOX"))
mCustomButtons.button_b_XBOX = elem->get<std::string>("button_b_XBOX");
if (elem->has("button_x_XBOX"))
mCustomButtons.button_x_XBOX = elem->get<std::string>("button_x_XBOX");
if (elem->has("button_y_XBOX"))
mCustomButtons.button_y_XBOX = elem->get<std::string>("button_y_XBOX");
if (elem->has("button_start_XBOX"))
mCustomButtons.button_start_XBOX = elem->get<std::string>("button_start_XBOX");
if (elem->has("button_back_XBOX"))
mCustomButtons.button_back_XBOX = elem->get<std::string>("button_back_XBOX");
if (elem->has("button_start_XBOX360"))
mCustomButtons.button_start_XBOX360 = elem->get<std::string>("button_start_XBOX360");
if (elem->has("button_back_XBOX360"))
mCustomButtons.button_back_XBOX360 = elem->get<std::string>("button_back_XBOX360");
}

View file

@ -10,7 +10,7 @@
#ifndef ES_CORE_HELP_STYLE_H
#define ES_CORE_HELP_STYLE_H
#include "math/Vector2f.h"
#include "utils/MathUtil.h"
#include <memory>
#include <string>
@ -19,11 +19,58 @@ class Font;
class ThemeData;
struct HelpStyle {
Vector2f position;
Vector2f origin;
unsigned int iconColor;
glm::vec2 position;
glm::vec2 origin;
unsigned int textColor;
unsigned int textColorDimmed;
unsigned int iconColor;
unsigned int iconColorDimmed;
std::shared_ptr<Font> font;
float entrySpacing;
float iconTextSpacing;
std::string textStyle;
struct CustomButtonIcons {
// General.
std::string dpad_updown;
std::string dpad_leftright;
std::string dpad_all;
std::string thumbstick_click;
std::string button_l;
std::string button_r;
std::string button_lr;
// SNES.
std::string button_a_SNES;
std::string button_b_SNES;
std::string button_x_SNES;
std::string button_y_SNES;
std::string button_start_SNES;
std::string button_back_SNES;
// PS.
std::string button_a_PS;
std::string button_b_PS;
std::string button_x_PS;
std::string button_y_PS;
std::string button_start_PS4;
std::string button_back_PS4;
std::string button_start_PS5;
std::string button_back_PS5;
// XBOX.
std::string button_a_XBOX;
std::string button_b_XBOX;
std::string button_x_XBOX;
std::string button_y_XBOX;
std::string button_start_XBOX;
std::string button_back_XBOX;
std::string button_start_XBOX360;
std::string button_back_XBOX360;
};
CustomButtonIcons mCustomButtons;
HelpStyle(); // Default values.
void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view);

View file

@ -49,12 +49,11 @@ protected:
std::ostringstream os;
private:
std::map<LogLevel, std::string> logLevelMap { // Log level indicators.
{ LogError, "Error" },
{ LogWarning, "Warn" },
{ LogInfo, "Info" },
{ LogDebug, "Debug" }
};
std::map<LogLevel, std::string> logLevelMap{// Log level indicators.
{LogError, "Error"},
{LogWarning, "Warn"},
{LogInfo, "Info"},
{LogDebug, "Debug"}};
static LogLevel reportingLevel;
LogLevel messageLevel;

View file

@ -68,8 +68,8 @@ MameNames::MameNames()
for (pugi::xml_node gameNode = doc.child("game"); gameNode;
gameNode = gameNode.next_sibling("game")) {
NamePair namePair = { gameNode.child("mamename").text().get(),
gameNode.child("realname").text().get() };
NamePair namePair = {gameNode.child("mamename").text().get(),
gameNode.child("realname").text().get()};
mNamePairs.push_back(namePair);
}

View file

@ -139,7 +139,7 @@ int launchGameUnix(const std::string& cmd_utf8, bool runInBackground)
int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground, bool hideWindow)
{
#if defined(_WIN64)
STARTUPINFOW si {};
STARTUPINFOW si{};
PROCESS_INFORMATION pi;
si.cb = sizeof(si);

View file

@ -97,161 +97,162 @@ void Settings::setDefaults()
//
// Scraper.
mStringMap["Scraper"] = { "screenscraper", "screenscraper" };
mBoolMap["ScraperUseAccountScreenScraper"] = { false, false };
mStringMap["ScraperUsernameScreenScraper"] = { "", "" };
mStringMap["ScraperPasswordScreenScraper"] = { "", "" };
mStringMap["Scraper"] = {"screenscraper", "screenscraper"};
mBoolMap["ScraperUseAccountScreenScraper"] = {false, false};
mStringMap["ScraperUsernameScreenScraper"] = {"", ""};
mStringMap["ScraperPasswordScreenScraper"] = {"", ""};
mBoolMap["ScrapeGameNames"] = { true, true };
mBoolMap["ScrapeRatings"] = { true, true };
mBoolMap["ScrapeMetadata"] = { true, true };
mBoolMap["ScrapeVideos"] = { true, true };
mBoolMap["ScrapeScreenshots"] = { true, true };
mBoolMap["ScrapeCovers"] = { true, true };
mBoolMap["ScrapeMarquees"] = { true, true };
mBoolMap["Scrape3DBoxes"] = { true, true };
mBoolMap["ScrapeGameNames"] = {true, true};
mBoolMap["ScrapeRatings"] = {true, true};
mBoolMap["ScrapeMetadata"] = {true, true};
mBoolMap["ScrapeVideos"] = {true, true};
mBoolMap["ScrapeScreenshots"] = {true, true};
mBoolMap["ScrapeCovers"] = {true, true};
mBoolMap["ScrapeMarquees"] = {true, true};
mBoolMap["Scrape3DBoxes"] = {true, true};
mStringMap["MiximageResolution"] = { "1280x960", "1280x960" };
mStringMap["MiximageScreenshotScaling"] = { "sharp", "sharp" };
mBoolMap["MiximageGenerate"] = { true, true };
mBoolMap["MiximageOverwrite"] = { true, true };
mBoolMap["MiximageRemoveLetterboxes"] = { true, true };
mBoolMap["MiximageRemovePillarboxes"] = { true, true };
mBoolMap["MiximageIncludeMarquee"] = { true, true };
mBoolMap["MiximageIncludeBox"] = { true, true };
mBoolMap["MiximageCoverFallback"] = { true, true };
mStringMap["MiximageResolution"] = {"1280x960", "1280x960"};
mStringMap["MiximageScreenshotScaling"] = {"sharp", "sharp"};
mBoolMap["MiximageGenerate"] = {true, true};
mBoolMap["MiximageOverwrite"] = {true, true};
mBoolMap["MiximageRemoveLetterboxes"] = {true, true};
mBoolMap["MiximageRemovePillarboxes"] = {true, true};
mBoolMap["MiximageIncludeMarquee"] = {true, true};
mBoolMap["MiximageIncludeBox"] = {true, true};
mBoolMap["MiximageCoverFallback"] = {true, true};
mStringMap["ScraperRegion"] = { "eu", "eu" };
mStringMap["ScraperLanguage"] = { "en", "en" };
mBoolMap["ScraperOverwriteData"] = { true, true };
mBoolMap["ScraperHaltOnInvalidMedia"] = { true, true };
mBoolMap["ScraperSearchMetadataName"] = { true, true };
mBoolMap["ScraperInteractive"] = { true, true };
mBoolMap["ScraperSemiautomatic"] = { true, true };
mBoolMap["ScraperRespectExclusions"] = { true, true };
mBoolMap["ScraperExcludeRecursively"] = { true, true };
mBoolMap["ScraperIncludeFolders"] = { false, false };
mBoolMap["ScraperRetryPeerVerification"] = { false, false };
mStringMap["ScraperRegion"] = {"eu", "eu"};
mStringMap["ScraperLanguage"] = {"en", "en"};
mBoolMap["ScraperOverwriteData"] = {true, true};
mBoolMap["ScraperHaltOnInvalidMedia"] = {true, true};
mBoolMap["ScraperSearchMetadataName"] = {true, true};
mBoolMap["ScraperInteractive"] = {true, true};
mBoolMap["ScraperSemiautomatic"] = {true, true};
mBoolMap["ScraperRespectExclusions"] = {true, true};
mBoolMap["ScraperExcludeRecursively"] = {true, true};
mBoolMap["ScraperIncludeFolders"] = {false, false};
mBoolMap["ScraperRetryPeerVerification"] = {false, false};
// UI settings.
mStringMap["StartupSystem"] = { "", "" };
mStringMap["GamelistViewStyle"] = { "automatic", "automatic" };
mStringMap["TransitionStyle"] = { "slide", "slide" };
mStringMap["ThemeSet"] = { "rbsimple-DE", "rbsimple-DE" };
mStringMap["UIMode"] = { "full", "full" };
mStringMap["DefaultSortOrder"] = { "filename, ascending", "filename, ascending" };
mStringMap["MenuOpeningEffect"] = { "scale-up", "scale-up" };
mStringMap["LaunchScreenDuration"] = { "normal", "normal" };
mBoolMap["MenuBlurBackground"] = { true, true };
mBoolMap["GamelistVideoPillarbox"] = { true, true };
mBoolMap["GamelistVideoScanlines"] = { false, false };
mBoolMap["FoldersOnTop"] = { true, true };
mBoolMap["FavoritesFirst"] = { true, true };
mBoolMap["FavoritesStar"] = { true, true };
mBoolMap["SpecialCharsASCII"] = { false, false };
mBoolMap["ListScrollOverlay"] = { false, false };
mBoolMap["FavoritesAddButton"] = { true, true };
mBoolMap["RandomAddButton"] = { false, false };
mBoolMap["GamelistFilters"] = { true, true };
mBoolMap["QuickSystemSelect"] = { true, true };
mBoolMap["ShowHelpPrompts"] = { true, true };
mBoolMap["PlayVideosImmediately"] = { false, false };
mBoolMap["EnableMenuKidMode"] = { false, false };
mStringMap["StartupSystem"] = {"", ""};
mStringMap["GamelistViewStyle"] = {"automatic", "automatic"};
mStringMap["TransitionStyle"] = {"slide", "slide"};
mStringMap["ThemeSet"] = {"rbsimple-DE", "rbsimple-DE"};
mStringMap["UIMode"] = {"full", "full"};
mStringMap["DefaultSortOrder"] = {"filename, ascending", "filename, ascending"};
mStringMap["MenuOpeningEffect"] = {"scale-up", "scale-up"};
mStringMap["LaunchScreenDuration"] = {"normal", "normal"};
// UI settings -> media viewer settings.
mBoolMap["MediaViewerKeepVideoRunning"] = { true, true };
mBoolMap["MediaViewerStretchVideos"] = { false, false };
mBoolMap["MediaViewerVideoScanlines"] = { true, true };
mBoolMap["MediaViewerVideoBlur"] = { false, false };
mBoolMap["MediaViewerScreenshotScanlines"] = { true, true };
mBoolMap["MediaViewerKeepVideoRunning"] = {true, true};
mBoolMap["MediaViewerStretchVideos"] = {false, false};
mBoolMap["MediaViewerVideoScanlines"] = {true, true};
mBoolMap["MediaViewerVideoBlur"] = {false, false};
mBoolMap["MediaViewerScreenshotScanlines"] = {true, true};
// UI settings -> screensaver settings.
mIntMap["ScreensaverTimer"] = { 5 * 60 * 1000, 5 * 60 * 1000 }; // 5 minutes.
mStringMap["ScreensaverType"] = { "video", "video" };
mBoolMap["ScreensaverControls"] = { true, true };
mIntMap["ScreensaverTimer"] = {5 * 60 * 1000, 5 * 60 * 1000}; // 5 minutes.
mStringMap["ScreensaverType"] = {"video", "video"};
mBoolMap["ScreensaverControls"] = {true, true};
// UI settings -> screensaver settings -> slideshow screensaver settings.
mIntMap["ScreensaverSwapImageTimeout"] = { 10000, 10000 };
mBoolMap["ScreensaverStretchImages"] = { false, false };
mBoolMap["ScreensaverSlideshowGameInfo"] = { true, true };
mBoolMap["ScreensaverSlideshowScanlines"] = { true, true };
mBoolMap["ScreensaverSlideshowCustomImages"] = { false, false };
mBoolMap["ScreensaverSlideshowRecurse"] = { false, false };
mStringMap["ScreensaverSlideshowImageDir"] = { "~/.emulationstation/slideshow/custom_images",
"~/.emulationstation/slideshow/custom_images" };
mIntMap["ScreensaverSwapImageTimeout"] = {10000, 10000};
mBoolMap["ScreensaverStretchImages"] = {false, false};
mBoolMap["ScreensaverSlideshowGameInfo"] = {true, true};
mBoolMap["ScreensaverSlideshowScanlines"] = {true, true};
mBoolMap["ScreensaverSlideshowCustomImages"] = {false, false};
mBoolMap["ScreensaverSlideshowRecurse"] = {false, false};
mStringMap["ScreensaverSlideshowImageDir"] = {"~/.emulationstation/slideshow/custom_images",
"~/.emulationstation/slideshow/custom_images"};
// UI settings -> screensaver settings -> video screensaver settings.
mIntMap["ScreensaverSwapVideoTimeout"] = { 0, 0 };
mBoolMap["ScreensaverStretchVideos"] = { false, false };
mBoolMap["ScreensaverVideoGameInfo"] = { true, true };
mBoolMap["ScreensaverVideoScanlines"] = { true, true };
mBoolMap["ScreensaverVideoBlur"] = { false, false };
mIntMap["ScreensaverSwapVideoTimeout"] = {0, 0};
mBoolMap["ScreensaverStretchVideos"] = {false, false};
mBoolMap["ScreensaverVideoGameInfo"] = {true, true};
mBoolMap["ScreensaverVideoScanlines"] = {true, true};
mBoolMap["ScreensaverVideoBlur"] = {false, false};
mBoolMap["MenuBlurBackground"] = {true, true};
mBoolMap["GamelistVideoPillarbox"] = {true, true};
mBoolMap["GamelistVideoScanlines"] = {false, false};
mBoolMap["FoldersOnTop"] = {true, true};
mBoolMap["FavoritesFirst"] = {true, true};
mBoolMap["FavoritesStar"] = {true, true};
mBoolMap["SpecialCharsASCII"] = {false, false};
mBoolMap["ListScrollOverlay"] = {false, false};
mBoolMap["FavoritesAddButton"] = {true, true};
mBoolMap["RandomAddButton"] = {false, false};
mBoolMap["GamelistFilters"] = {true, true};
mBoolMap["QuickSystemSelect"] = {true, true};
mBoolMap["ShowHelpPrompts"] = {true, true};
mBoolMap["PlayVideosImmediately"] = {false, false};
mBoolMap["EnableMenuKidMode"] = {false, false};
// Sound settings.
mIntMap["SoundVolumeNavigation"] = { 80, 80 };
mIntMap["SoundVolumeVideos"] = { 100, 100 };
mBoolMap["GamelistVideoAudio"] = { true, true };
mBoolMap["MediaViewerVideoAudio"] = { true, true };
mBoolMap["ScreensaverVideoAudio"] = { false, false };
mBoolMap["NavigationSounds"] = { true, true };
mIntMap["SoundVolumeNavigation"] = {80, 80};
mIntMap["SoundVolumeVideos"] = {100, 100};
mBoolMap["GamelistVideoAudio"] = {true, true};
mBoolMap["MediaViewerVideoAudio"] = {true, true};
mBoolMap["ScreensaverVideoAudio"] = {false, false};
mBoolMap["NavigationSounds"] = {true, true};
// Input device settings.
mStringMap["InputControllerType"] = { "xbox", "xbox" };
mBoolMap["InputOnlyFirstController"] = { false, false };
mStringMap["InputControllerType"] = {"xbox", "xbox"};
mBoolMap["InputOnlyFirstController"] = {false, false};
// Game collection settings.
mStringMap["CollectionSystemsAuto"] = { "", "" };
mStringMap["CollectionSystemsCustom"] = { "", "" };
mBoolMap["FavFirstCustom"] = { false, false };
mBoolMap["FavStarCustom"] = { false, false };
mBoolMap["UseCustomCollectionsSystem"] = { true, true };
mBoolMap["CollectionShowSystemInfo"] = { true, true };
mStringMap["CollectionSystemsAuto"] = {"", ""};
mStringMap["CollectionSystemsCustom"] = {"", ""};
mBoolMap["FavFirstCustom"] = {false, false};
mBoolMap["FavStarCustom"] = {false, false};
mBoolMap["UseCustomCollectionsSystem"] = {true, true};
mBoolMap["CollectionShowSystemInfo"] = {true, true};
// Other settings.
// Other settings.
mStringMap["MediaDirectory"] = {"", ""};
#if defined(_RPI_)
mIntMap["MaxVRAM"] = { 80, 80 };
mIntMap["MaxVRAM"] = {80, 80};
#else
mIntMap["MaxVRAM"] = { 256, 256 };
mIntMap["MaxVRAM"] = {256, 256};
#endif
mIntMap["DisplayIndex"] = { 1, 1 };
mIntMap["DisplayIndex"] = {1, 1};
#if defined(__unix__)
mStringMap["FullscreenMode"] = { "normal", "normal" };
mStringMap["FullscreenMode"] = {"normal", "normal"};
#endif
#if defined(BUILD_VLC_PLAYER)
#if defined(_RPI_)
// As the FFmpeg video player is not HW accelerated, use VLC as default on this weak device.
mStringMap["VideoPlayer"] = { "vlc", "vlc" };
mStringMap["VideoPlayer"] = {"vlc", "vlc"};
#else
mStringMap["VideoPlayer"] = { "ffmpeg", "ffmpeg" };
mStringMap["VideoPlayer"] = {"ffmpeg", "ffmpeg"};
#endif
#endif
mStringMap["ExitButtonCombo"] = { "F4", "F4" };
mStringMap["SaveGamelistsMode"] = { "always", "always" };
mStringMap["ExitButtonCombo"] = {"F4", "F4"};
mStringMap["SaveGamelistsMode"] = {"always", "always"};
#if defined(_WIN64)
mBoolMap["HideTaskbar"] = { false, false };
mBoolMap["HideTaskbar"] = {false, false};
#endif
mBoolMap["RunInBackground"] = { false, false };
mBoolMap["RunInBackground"] = {false, false};
#if defined(_WIN64)
mBoolMap["LaunchWorkaround"] = { true, true };
mBoolMap["LaunchWorkaround"] = {true, true};
#endif
mStringMap["MediaDirectory"] = { "", "" };
#if !defined(_RPI_)
mBoolMap["VideoHardwareDecoding"] = { false, false };
mBoolMap["VideoHardwareDecoding"] = {false, false};
#endif
mBoolMap["VideoUpscaleFrameRate"] = { false, false };
mBoolMap["LaunchCommandOverride"] = { true, true };
mBoolMap["ShowHiddenFiles"] = { true, true };
mBoolMap["ShowHiddenGames"] = { true, true };
mBoolMap["CustomEventScripts"] = { false, false };
mBoolMap["ParseGamelistOnly"] = { false, false };
mBoolMap["VideoUpscaleFrameRate"] = {false, false};
mBoolMap["LaunchCommandOverride"] = {true, true};
mBoolMap["ShowHiddenFiles"] = {true, true};
mBoolMap["ShowHiddenGames"] = {true, true};
mBoolMap["CustomEventScripts"] = {false, false};
mBoolMap["ParseGamelistOnly"] = {false, false};
#if defined(__unix__)
mBoolMap["DisableComposition"] = { true, true };
mBoolMap["DisableComposition"] = {true, true};
#endif
mBoolMap["DisplayGPUStatistics"] = { false, false };
mBoolMap["DisplayGPUStatistics"] = {false, false};
// macOS requires root privileges to reboot and power off so it doesn't make much
// sense to enable this setting and menu entry for that operating system.
#if !defined(__APPLE__)
mBoolMap["ShowQuitMenu"] = { false, false };
mBoolMap["ShowQuitMenu"] = {false, false};
#endif
//
@ -259,45 +260,45 @@ void Settings::setDefaults()
//
// Options listed using --help
mBoolMap["Debug"] = { false, false };
mBoolMap["ForceFull"] = { false, false };
mBoolMap["ForceKid"] = { false, false };
mBoolMap["ForceKiosk"] = { false, false };
mBoolMap["IgnoreGamelist"] = { false, false };
mBoolMap["SplashScreen"] = { true, true };
mBoolMap["VSync"] = { true, true };
mBoolMap["Debug"] = {false, false};
mBoolMap["ForceFull"] = {false, false};
mBoolMap["ForceKid"] = {false, false};
mBoolMap["ForceKiosk"] = {false, false};
mBoolMap["IgnoreGamelist"] = {false, false};
mBoolMap["SplashScreen"] = {true, true};
mBoolMap["VSync"] = {true, true};
#if !defined(_WIN64)
mBoolMap["Windowed"] = { false, false };
mBoolMap["Windowed"] = {false, false};
#endif
mIntMap["WindowWidth"] = { 0, 0 };
mIntMap["WindowHeight"] = { 0, 0 };
mIntMap["ScreenWidth"] = { 0, 0 };
mIntMap["WindowWidth"] = {0, 0};
mIntMap["WindowHeight"] = {0, 0};
mIntMap["ScreenWidth"] = {0, 0};
// Undocumented options.
mIntMap["ScreenHeight"] = { 0, 0 };
mIntMap["ScreenOffsetX"] = { 0, 0 };
mIntMap["ScreenOffsetY"] = { 0, 0 };
mIntMap["ScreenRotate"] = { 0, 0 };
mIntMap["ScreenHeight"] = {0, 0};
mIntMap["ScreenOffsetX"] = {0, 0};
mIntMap["ScreenOffsetY"] = {0, 0};
mIntMap["ScreenRotate"] = {0, 0};
//
// Settings that can be changed in es_settings.xml
// but that are not configurable via the GUI.
//
mBoolMap["DebugSkipInputLogging"] = { false, false };
mStringMap["ROMDirectory"] = { "", "" };
mStringMap["UIMode_passkey"] = { "uuddlrlrba", "uuddlrlrba" };
mBoolMap["DebugSkipInputLogging"] = {false, false};
mStringMap["ROMDirectory"] = {"", ""};
mStringMap["UIMode_passkey"] = {"uuddlrlrba", "uuddlrlrba"};
//
// Hardcoded or program-internal settings.
//
mStringMap["ApplicationVersion"] = { "", "" };
mBoolMap["DebugGrid"] = { false, false };
mBoolMap["DebugText"] = { false, false };
mBoolMap["DebugImage"] = { false, false };
mBoolMap["SplashScreenProgress"] = { true, true };
mIntMap["ScraperFilter"] = { 0, 0 };
mStringMap["ApplicationVersion"] = {"", ""};
mBoolMap["DebugGrid"] = {false, false};
mBoolMap["DebugText"] = {false, false};
mBoolMap["DebugImage"] = {false, false};
mBoolMap["SplashScreenProgress"] = {true, true};
mIntMap["ScraperFilter"] = {0, 0};
}
template <typename K, typename V>

View file

@ -21,181 +21,184 @@
#include <algorithm>
#include <pugixml.hpp>
std::vector<std::string> ThemeData::sSupportedViews { { "all" }, { "system" }, { "basic" },
{ "detailed" }, { "grid" }, { "video" } };
std::vector<std::string> ThemeData::sSupportedFeatures {
{ "navigationsounds" }, { "video" }, { "carousel" }, { "z-index" }, { "visible" }
};
std::vector<std::string> ThemeData::sSupportedViews{{"all"}, {"system"}, {"basic"},
{"detailed"}, {"grid"}, {"video"}};
std::vector<std::string> ThemeData::sSupportedFeatures{
{"navigationsounds"}, {"video"}, {"carousel"}, {"z-index"}, {"visible"}};
std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
ThemeData::sElementMap {
{ "image",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "maxSize", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "rotation", FLOAT },
{ "rotationOrigin", NORMALIZED_PAIR },
{ "path", PATH },
{ "default", PATH },
{ "tile", BOOLEAN },
{ "color", COLOR },
{ "colorEnd", COLOR },
{ "gradientType", STRING },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "imagegrid",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "margin", NORMALIZED_PAIR },
{ "padding", NORMALIZED_RECT },
{ "autoLayout", NORMALIZED_PAIR },
{ "autoLayoutSelectedZoom", FLOAT },
{ "gameImage", PATH },
{ "folderImage", PATH },
{ "imageSource", STRING },
{ "scrollDirection", STRING },
{ "centerSelection", BOOLEAN },
{ "scrollLoop", BOOLEAN },
{ "animate", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "gridtile",
{ { "size", NORMALIZED_PAIR },
{ "padding", NORMALIZED_PAIR },
{ "imageColor", COLOR },
{ "backgroundImage", PATH },
{ "backgroundCornerSize", NORMALIZED_PAIR },
{ "backgroundColor", COLOR },
{ "backgroundCenterColor", COLOR },
{ "backgroundEdgeColor", COLOR } } },
{ "text",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "rotation", FLOAT },
{ "rotationOrigin", NORMALIZED_PAIR },
{ "text", STRING },
{ "backgroundColor", COLOR },
{ "fontPath", PATH },
{ "fontSize", FLOAT },
{ "color", COLOR },
{ "alignment", STRING },
{ "forceUppercase", BOOLEAN },
{ "lineSpacing", FLOAT },
{ "value", STRING },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "textlist",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "selectorHeight", FLOAT },
{ "selectorOffsetY", FLOAT },
{ "selectorColor", COLOR },
{ "selectorColorEnd", COLOR },
{ "selectorGradientType", STRING },
{ "selectorImagePath", PATH },
{ "selectorImageTile", BOOLEAN },
{ "selectedColor", COLOR },
{ "primaryColor", COLOR },
{ "secondaryColor", COLOR },
{ "fontPath", PATH },
{ "fontSize", FLOAT },
{ "scrollSound", PATH }, // For backward compatibility with old themes.
{ "alignment", STRING },
{ "horizontalMargin", FLOAT },
{ "forceUppercase", BOOLEAN },
{ "lineSpacing", FLOAT },
{ "zIndex", FLOAT } } },
{ "container",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "ninepatch",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "path", PATH },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "datetime",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "rotation", FLOAT },
{ "rotationOrigin", NORMALIZED_PAIR },
{ "backgroundColor", COLOR },
{ "fontPath", PATH },
{ "fontSize", FLOAT },
{ "color", COLOR },
{ "alignment", STRING },
{ "forceUppercase", BOOLEAN },
{ "lineSpacing", FLOAT },
{ "value", STRING },
{ "format", STRING },
{ "displayRelative", BOOLEAN },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "rating",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "rotation", FLOAT },
{ "rotationOrigin", NORMALIZED_PAIR },
{ "color", COLOR },
{ "filledPath", PATH },
{ "unfilledPath", PATH },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT } } },
{ "sound", { { "path", PATH } } },
{ "helpsystem",
{ { "pos", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "textColor", COLOR },
{ "iconColor", COLOR },
{ "fontPath", PATH },
{ "fontSize", FLOAT } } },
{ "navigationsounds",
{ { "systembrowseSound", PATH },
{ "quicksysselectSound", PATH },
{ "selectSound", PATH },
{ "backSound", PATH },
{ "scrollSound", PATH },
{ "favoriteSound", PATH },
{ "launchSound", PATH } } },
{ "video",
{ { "pos", NORMALIZED_PAIR },
{ "size", NORMALIZED_PAIR },
{ "maxSize", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "rotation", FLOAT },
{ "rotationOrigin", NORMALIZED_PAIR },
{ "default", PATH },
{ "delay", FLOAT },
{ "visible", BOOLEAN },
{ "zIndex", FLOAT },
{ "showSnapshotNoVideo", BOOLEAN },
{ "showSnapshotDelay", BOOLEAN } } },
{ "carousel",
{ { "type", STRING },
{ "size", NORMALIZED_PAIR },
{ "pos", NORMALIZED_PAIR },
{ "origin", NORMALIZED_PAIR },
{ "color", COLOR },
{ "colorEnd", COLOR },
{ "gradientType", STRING },
{ "logoScale", FLOAT },
{ "logoRotation", FLOAT },
{ "logoRotationOrigin", NORMALIZED_PAIR },
{ "logoSize", NORMALIZED_PAIR },
{ "logoAlignment", STRING },
{ "maxLogoCount", FLOAT },
{ "zIndex", FLOAT } } }
};
std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>> ThemeData::sElementMap{
{"image",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"maxSize", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"rotation", FLOAT},
{"rotationOrigin", NORMALIZED_PAIR},
{"path", PATH},
{"default", PATH},
{"tile", BOOLEAN},
{"color", COLOR},
{"colorEnd", COLOR},
{"gradientType", STRING},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"imagegrid",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"margin", NORMALIZED_PAIR},
{"padding", NORMALIZED_RECT},
{"autoLayout", NORMALIZED_PAIR},
{"autoLayoutSelectedZoom", FLOAT},
{"gameImage", PATH},
{"folderImage", PATH},
{"imageSource", STRING},
{"scrollDirection", STRING},
{"centerSelection", BOOLEAN},
{"scrollLoop", BOOLEAN},
{"animate", BOOLEAN},
{"zIndex", FLOAT}}},
{"gridtile",
{{"size", NORMALIZED_PAIR},
{"padding", NORMALIZED_PAIR},
{"imageColor", COLOR},
{"backgroundImage", PATH},
{"backgroundCornerSize", NORMALIZED_PAIR},
{"backgroundColor", COLOR},
{"backgroundCenterColor", COLOR},
{"backgroundEdgeColor", COLOR}}},
{"text",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"rotation", FLOAT},
{"rotationOrigin", NORMALIZED_PAIR},
{"text", STRING},
{"backgroundColor", COLOR},
{"fontPath", PATH},
{"fontSize", FLOAT},
{"color", COLOR},
{"alignment", STRING},
{"forceUppercase", BOOLEAN},
{"lineSpacing", FLOAT},
{"value", STRING},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"textlist",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"selectorHeight", FLOAT},
{"selectorOffsetY", FLOAT},
{"selectorColor", COLOR},
{"selectorColorEnd", COLOR},
{"selectorGradientType", STRING},
{"selectorImagePath", PATH},
{"selectorImageTile", BOOLEAN},
{"selectedColor", COLOR},
{"primaryColor", COLOR},
{"secondaryColor", COLOR},
{"fontPath", PATH},
{"fontSize", FLOAT},
{"scrollSound", PATH}, // For backward compatibility with old themes.
{"alignment", STRING},
{"horizontalMargin", FLOAT},
{"forceUppercase", BOOLEAN},
{"lineSpacing", FLOAT},
{"zIndex", FLOAT}}},
{"container",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"ninepatch",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"path", PATH},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"datetime",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"rotation", FLOAT},
{"rotationOrigin", NORMALIZED_PAIR},
{"backgroundColor", COLOR},
{"fontPath", PATH},
{"fontSize", FLOAT},
{"color", COLOR},
{"alignment", STRING},
{"forceUppercase", BOOLEAN},
{"lineSpacing", FLOAT},
{"value", STRING},
{"format", STRING},
{"displayRelative", BOOLEAN},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"rating",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"rotation", FLOAT},
{"rotationOrigin", NORMALIZED_PAIR},
{"color", COLOR},
{"filledPath", PATH},
{"unfilledPath", PATH},
{"visible", BOOLEAN},
{"zIndex", FLOAT}}},
{"sound", {{"path", PATH}}},
{"helpsystem",
{{"pos", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"textColor", COLOR},
{"textColorDimmed", COLOR},
{"iconColor", COLOR},
{"iconColorDimmed", COLOR},
{"fontPath", PATH},
{"fontSize", FLOAT},
{"entrySpacing", FLOAT},
{"iconTextSpacing", FLOAT},
{"textStyle", STRING},
{"customButtonIcon", PATH}}},
{"navigationsounds",
{{"systembrowseSound", PATH},
{"quicksysselectSound", PATH},
{"selectSound", PATH},
{"backSound", PATH},
{"scrollSound", PATH},
{"favoriteSound", PATH},
{"launchSound", PATH}}},
{"video",
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"maxSize", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"rotation", FLOAT},
{"rotationOrigin", NORMALIZED_PAIR},
{"default", PATH},
{"delay", FLOAT},
{"visible", BOOLEAN},
{"zIndex", FLOAT},
{"showSnapshotNoVideo", BOOLEAN},
{"showSnapshotDelay", BOOLEAN}}},
{"carousel",
{{"type", STRING},
{"size", NORMALIZED_PAIR},
{"pos", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"color", COLOR},
{"colorEnd", COLOR},
{"gradientType", STRING},
{"logoScale", FLOAT},
{"logoRotation", FLOAT},
{"logoRotationOrigin", NORMALIZED_PAIR},
{"logoSize", NORMALIZED_PAIR},
{"logoAlignment", STRING},
{"maxLogoCount", FLOAT},
{"zIndex", FLOAT}}}};
#define MINIMUM_THEME_FORMAT_VERSION 3
#define CURRENT_THEME_FORMAT_VERSION 6
#define CURRENT_THEME_FORMAT_VERSION 7
// Helper.
unsigned int getHexColor(const std::string& str)
@ -450,20 +453,20 @@ void ThemeData::parseElement(const pugi::xml_node& root,
switch (typeIt->second) {
case NORMALIZED_RECT: {
Vector4f val;
glm::vec4 val;
auto splits = Utils::String::delimitedStringToVector(str, " ");
if (splits.size() == 2) {
val = Vector4f(static_cast<float>(atof(splits.at(0).c_str())),
static_cast<float>(atof(splits.at(1).c_str())),
static_cast<float>(atof(splits.at(0).c_str())),
static_cast<float>(atof(splits.at(1).c_str())));
val = glm::vec4{static_cast<float>(atof(splits.at(0).c_str())),
static_cast<float>(atof(splits.at(1).c_str())),
static_cast<float>(atof(splits.at(0).c_str())),
static_cast<float>(atof(splits.at(1).c_str()))};
}
else if (splits.size() == 4) {
val = Vector4f(static_cast<float>(atof(splits.at(0).c_str())),
static_cast<float>(atof(splits.at(1).c_str())),
static_cast<float>(atof(splits.at(2).c_str())),
static_cast<float>(atof(splits.at(3).c_str())));
val = glm::vec4{static_cast<float>(atof(splits.at(0).c_str())),
static_cast<float>(atof(splits.at(1).c_str())),
static_cast<float>(atof(splits.at(2).c_str())),
static_cast<float>(atof(splits.at(3).c_str()))};
}
element.properties[node.name()] = val;
@ -475,11 +478,11 @@ void ThemeData::parseElement(const pugi::xml_node& root,
throw error << "invalid normalized pair (property \"" << node.name()
<< "\", value \"" << str.c_str() << "\")";
std::string first = str.substr(0, divider);
std::string second = str.substr(divider, std::string::npos);
std::string first{str.substr(0, divider)};
std::string second{str.substr(divider, std::string::npos)};
Vector2f val(static_cast<float>(atof(first.c_str())),
static_cast<float>(atof(second.c_str())));
glm::vec2 val{static_cast<float>(atof(first.c_str())),
static_cast<float>(atof(second.c_str()))};
element.properties[node.name()] = val;
break;
@ -499,7 +502,20 @@ void ThemeData::parseElement(const pugi::xml_node& root,
<< ((node.text().get() != path) ? "which resolves to \"" + path + "\"" :
"");
}
element.properties[node.name()] = path;
// Special parsing instruction for customButtonIcon -> save node as it's button
// attribute to prevent nodes overwriting each other.
if (strcmp(node.name(), "customButtonIcon") == 0) {
const auto btn = node.attribute("button").as_string("");
if (strcmp(btn, "") == 0)
LOG(LogError)
<< "<customButtonIcon> element requires the `button` property.";
else
element.properties[btn] = path;
}
else
element.properties[node.name()] = path;
break;
}
case COLOR: {
@ -602,7 +618,7 @@ std::vector<GuiComponent*> ThemeData::makeExtras(const std::shared_ptr<ThemeData
comp = new TextComponent(window);
if (comp) {
comp->setDefaultZIndex(10);
comp->setDefaultZIndex(10.0f);
comp->applyTheme(theme, view, *it, ThemeFlags::ALL);
comps.push_back(comp);
}
@ -643,7 +659,7 @@ std::map<std::string, ThemeSet> ThemeData::getThemeSets()
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
it != dirContent.cend(); it++) {
if (Utils::FileSystem::isDirectory(*it)) {
ThemeSet set = { *it };
ThemeSet set = {*it};
sets[set.getName()] = set;
}
}

View file

@ -11,9 +11,8 @@
#ifndef ES_CORE_THEME_DATA_H
#define ES_CORE_THEME_DATA_H
#include "math/Vector2f.h"
#include "math/Vector4f.h"
#include "utils/FileSystemUtil.h"
#include "utils/MathUtil.h"
#include <deque>
#include <map>
@ -103,20 +102,20 @@ public:
std::string type;
struct Property {
void operator=(const Vector4f& value)
void operator=(const glm::vec4& value)
{
r = value;
const Vector4f initVector = value;
v = Vector2f(initVector.x(), initVector.y());
const glm::vec4 initVector{value};
v = glm::vec2{initVector.x, initVector.y};
}
void operator=(const Vector2f& value) { v = value; }
void operator=(const glm::vec2& value) { v = value; }
void operator=(const std::string& value) { s = value; }
void operator=(const unsigned int& value) { i = value; }
void operator=(const float& value) { f = value; }
void operator=(const bool& value) { b = value; }
Vector4f r;
Vector2f v;
glm::vec4 r;
glm::vec2 v;
std::string s;
unsigned int i;
float f;
@ -127,7 +126,7 @@ public:
template <typename T> const T get(const std::string& prop) const
{
if (std::is_same<T, Vector2f>::value)
if (std::is_same<T, glm::vec2>::value)
return *(const T*)&properties.at(prop).v;
else if (std::is_same<T, std::string>::value)
return *(const T*)&properties.at(prop).s;
@ -137,7 +136,7 @@ public:
return *(const T*)&properties.at(prop).f;
else if (std::is_same<T, bool>::value)
return *(const T*)&properties.at(prop).b;
else if (std::is_same<T, Vector4f>::value)
else if (std::is_same<T, glm::vec4>::value)
return *(const T*)&properties.at(prop).r;
return T();
}

View file

@ -45,7 +45,6 @@ Window::Window()
, mCachedBackground(false)
, mInvalidatedCachedBackground(false)
, mVideoPlayerCount(0)
, mTopOpacity(0)
, mTopScale(0.5)
, mListScrollOpacity(0)
, mChangedThemeSet(false)
@ -231,6 +230,20 @@ void Window::input(InputConfig* config, Input input)
return;
}
if (config->isMappedTo("a", input) && input.value != 0 &&
Settings::getInstance()->getString("MenuOpeningEffect") == "scale-up" && mTopScale < 1.0f &&
mGuiStack.size() == 2) {
// The user has entered a submenu when the initial menu screen has not finished scaling
// up. So scale it to full size so it won't be stuck at a smaller size when returning
// from the submenu.
mTopScale = 1.0f;
GuiComponent* menu = mGuiStack.back();
glm::vec2 menuCenter{menu->getCenter()};
menu->setOrigin(0.5f, 0.5f);
menu->setPosition(menuCenter.x, menuCenter.y, 0.0f);
menu->setScale(1.0f);
}
if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_g &&
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
// Toggle debug grid with Ctrl-G.
@ -346,14 +359,19 @@ void Window::update(int deltaTime)
mScreensaver->update(deltaTime);
}
bool Window::isBackgroundDimmed()
{
return !mGuiStack.empty() && (mGuiStack.front() != mGuiStack.back() || mRenderLaunchScreen);
}
void Window::render()
{
Transform4x4f transform = Transform4x4f::Identity();
glm::mat4 trans{Renderer::getIdentity()};
mRenderedHelpPrompts = false;
// Draw only bottom and top of GuiStack (if they are different).
if (mGuiStack.size()) {
if (!mGuiStack.empty()) {
auto& bottom = mGuiStack.front();
auto& top = mGuiStack.back();
@ -378,7 +396,7 @@ void Window::render()
renderBottom = false;
if (renderBottom)
bottom->render(transform);
bottom->render(trans);
if (bottom != top || mRenderLaunchScreen) {
#if defined(USE_OPENGL_21)
@ -395,7 +413,7 @@ void Window::render()
unsigned char* processedTexture =
new unsigned char[Renderer::getScreenWidth() * Renderer::getScreenHeight() * 4];
// Defocus the background using multiple passes of gaussian blur, with the number
// De-focus the background using multiple passes of gaussian blur, with the number
// of iterations relative to the screen resolution.
Renderer::shaderParameters backgroundParameters;
@ -464,52 +482,51 @@ void Window::render()
if (Settings::getInstance()->getString("MenuOpeningEffect") == "scale-up") {
mBackgroundOverlay->setOpacity(mBackgroundOverlayOpacity);
if (mBackgroundOverlayOpacity < 255)
mBackgroundOverlayOpacity = Math::clamp(mBackgroundOverlayOpacity + 30, 0, 255);
mBackgroundOverlayOpacity = glm::clamp(mBackgroundOverlayOpacity + 30, 0, 255);
}
#endif // USE_OPENGL_21
mBackgroundOverlay->render(transform);
mBackgroundOverlay->render(trans);
// Scale-up menu opening effect.
if (Settings::getInstance()->getString("MenuOpeningEffect") == "scale-up") {
if (mTopScale < 1.0f) {
mTopScale = Math::clamp(mTopScale + 0.07f, 0.0f, 1.0f);
Vector2f topCenter = top->getCenter();
top->setOrigin({ 0.5f, 0.5f });
top->setPosition({ topCenter.x(), topCenter.y(), 0.0f });
mTopScale = glm::clamp(mTopScale + 0.07f, 0.0f, 1.0f);
glm::vec2 topCenter{top->getCenter()};
top->setOrigin(0.5f, 0.5f);
top->setPosition(topCenter.x, topCenter.y, 0.0f);
top->setScale(mTopScale);
}
}
if (!mRenderLaunchScreen)
top->render(transform);
top->render(trans);
}
else {
mCachedBackground = false;
mTopOpacity = 0;
mTopScale = 0.5f;
}
}
// Render the quick list scrolling overlay, which is triggered in IList.
if (mListScrollOpacity != 0) {
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::setMatrix(Renderer::getIdentity());
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()),
0x00000000 | mListScrollOpacity, 0x00000000 | mListScrollOpacity);
Vector2f offset = mListScrollFont->sizeText(mListScrollText);
offset[0] = (Renderer::getScreenWidth() - offset.x()) * 0.5f;
offset[1] = (Renderer::getScreenHeight() - offset.y()) * 0.5f;
glm::vec2 offset{mListScrollFont->sizeText(mListScrollText)};
offset.x = (Renderer::getScreenWidth() - offset.x) * 0.5f;
offset.y = (Renderer::getScreenHeight() - offset.y) * 0.5f;
TextCache* cache = mListScrollFont->buildTextCache(mListScrollText, offset.x(), offset.y(),
TextCache* cache = mListScrollFont->buildTextCache(mListScrollText, offset.x, offset.y,
0xFFFFFF00 | mListScrollOpacity);
mListScrollFont->renderTextCache(cache);
delete cache;
}
if (!mRenderedHelpPrompts)
mHelp->render(transform);
mHelp->render(trans);
unsigned int screensaverTimer =
static_cast<unsigned int>(Settings::getInstance()->getInt("ScreensaverTimer"));
@ -531,7 +548,7 @@ void Window::render()
renderScreensaver();
if (!mRenderScreensaver && mInfoPopup)
mInfoPopup->render(transform);
mInfoPopup->render(trans);
if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) {
if (!isProcessing() && mAllowSleep && (!mScreensaver)) {
@ -550,14 +567,14 @@ void Window::render()
mLaunchScreen->render();
if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) {
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::setMatrix(Renderer::getIdentity());
mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get());
}
}
void Window::renderLoadingScreen(std::string text)
{
Transform4x4f trans = Transform4x4f::Identity();
glm::mat4 trans{Renderer::getIdentity()};
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
@ -565,16 +582,16 @@ void Window::renderLoadingScreen(std::string text)
ImageComponent splash(this, true);
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
splash.setImage(":/graphics/splash.svg");
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2.0f,
(Renderer::getScreenHeight() - splash.getSize().y()) / 2.0f * 0.6f);
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x) / 2.0f,
(Renderer::getScreenHeight() - splash.getSize().y) / 2.0f * 0.6f);
splash.render(trans);
auto& font = mDefaultFonts.at(1);
TextCache* cache = font->buildTextCache(text, 0.0f, 0.0f, 0x656565FF);
float x = std::round((Renderer::getScreenWidth() - cache->metrics.size.x()) / 2.0f);
float x = std::round((Renderer::getScreenWidth() - cache->metrics.size.x) / 2.0f);
float y = std::round(Renderer::getScreenHeight() * 0.835f);
trans = trans.translate(Vector3f(x, y, 0.0f));
trans = glm::translate(trans, glm::vec3{x, y, 0.0f});
Renderer::setMatrix(trans);
font->renderTextCache(cache);
delete cache;
@ -590,7 +607,7 @@ void Window::renderListScrollOverlay(unsigned char opacity, const std::string& t
void Window::renderHelpPromptsEarly()
{
mHelp->render(Transform4x4f::Identity());
mHelp->render(Renderer::getIdentity());
mRenderedHelpPrompts = true;
}
@ -633,17 +650,17 @@ void Window::setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpSt
// Sort prompts so it goes [dpad_all] [dpad_u/d] [dpad_l/r] [a/b/x/y/l/r] [start/back].
std::sort(addPrompts.begin(), addPrompts.end(),
[](const HelpPrompt& a, const HelpPrompt& b) -> bool {
static const std::vector<std::string> map = { "up/down/left/right",
"up/down",
"left/right",
"a",
"b",
"x",
"y",
"l",
"r",
"start",
"back" };
static const std::vector<std::string> map = {"up/down/left/right",
"up/down",
"left/right",
"a",
"b",
"x",
"y",
"l",
"r",
"start",
"back"};
int i = 0;
int aVal = 0;
int bVal = 0;

View file

@ -26,7 +26,6 @@ class HelpComponent;
class ImageComponent;
class InputConfig;
class TextCache;
class Transform4x4f;
struct HelpStyle;
class Window
@ -77,7 +76,7 @@ public:
class InfoPopup
{
public:
virtual void render(const Transform4x4f& parentTrans) = 0;
virtual void render(const glm::mat4& parentTrans) = 0;
virtual void stop() = 0;
virtual ~InfoPopup() {}
};
@ -89,6 +88,7 @@ public:
void removeGui(GuiComponent* gui);
GuiComponent* peekGui();
int getGuiStackSize() { return static_cast<int>(mGuiStack.size()); }
bool isBackgroundDimmed();
bool init();
void deinit();
@ -191,7 +191,6 @@ private:
int mVideoPlayerCount;
std::mutex mVideoCountMutex;
unsigned char mTopOpacity;
float mTopScale;
bool mRenderedHelpPrompts;
bool mChangedThemeSet;

View file

@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// MoveCameraAnimation.h
//
// Animation to play when moving the camera, used by the slide transition style.
//
#ifndef ES_CORE_ANIMATIONS_MOVE_CAMERA_ANIMATION_H
#define ES_CORE_ANIMATIONS_MOVE_CAMERA_ANIMATION_H
#include "animations/Animation.h"
#include "utils/MathUtil.h"
class MoveCameraAnimation : public Animation
{
public:
MoveCameraAnimation(glm::mat4& camera, const glm::vec3& target)
: mCameraStart(camera)
, mTarget(target)
, cameraPosition(camera)
{
}
int getDuration() const override { return 400; }
void apply(float t) override
{
t -= 1;
cameraPosition[3].x = -glm::mix(-mCameraStart[3].x, mTarget.x, t * t * t + 1);
cameraPosition[3].y = -glm::mix(-mCameraStart[3].y, mTarget.y, t * t * t + 1);
cameraPosition[3].z = -glm::mix(-mCameraStart[3].z, mTarget.z, t * t * t + 1);
}
private:
glm::mat4 mCameraStart;
glm::mat4& cameraPosition;
glm::vec3 mTarget;
};
#endif // ES_CORE_ANIMATIONS_MOVE_CAMERA_ANIMATION_H

View file

@ -33,7 +33,7 @@ void AnimatedImageComponent::load(const AnimationDef* def)
}
auto img = std::unique_ptr<ImageComponent>(new ImageComponent(mWindow));
img->setResize(mSize.x(), mSize.y());
img->setResize(mSize.x, mSize.y);
img->setImage(std::string(def->frames[i].path), false);
mFrames.push_back(ImageFrame(std::move(img), def->frames[i].time));
@ -55,7 +55,7 @@ void AnimatedImageComponent::reset()
void AnimatedImageComponent::onSizeChanged()
{
for (auto it = mFrames.cbegin(); it != mFrames.cend(); it++) {
it->first->setResize(mSize.x(), mSize.y());
it->first->setResize(mSize.x, mSize.y);
}
}
@ -86,7 +86,7 @@ void AnimatedImageComponent::update(int deltaTime)
}
}
void AnimatedImageComponent::render(const Transform4x4f& trans)
void AnimatedImageComponent::render(const glm::mat4& trans)
{
if (mFrames.size())
mFrames.at(mCurrentFrame).first->render(getTransform() * trans);

View file

@ -34,7 +34,7 @@ public:
void reset(); // Set to frame 0.
void update(int deltaTime) override;
void render(const Transform4x4f& trans) override;
void render(const glm::mat4& trans) override;
void onSizeChanged() override;

View file

@ -14,18 +14,18 @@
// Animation definition.
AnimationFrame BUSY_ANIMATION_FRAMES[] = {
{ ":/graphics/busy_0.svg", 300 },
{ ":/graphics/busy_1.svg", 300 },
{ ":/graphics/busy_2.svg", 300 },
{ ":/graphics/busy_3.svg", 300 },
{":/graphics/busy_0.svg", 300},
{":/graphics/busy_1.svg", 300},
{":/graphics/busy_2.svg", 300},
{":/graphics/busy_3.svg", 300},
};
const AnimationDef BUSY_ANIMATION_DEF = { BUSY_ANIMATION_FRAMES, 4, true };
const AnimationDef BUSY_ANIMATION_DEF = {BUSY_ANIMATION_FRAMES, 4, true};
BusyComponent::BusyComponent(Window* window)
: GuiComponent(window)
, mBackground(window, ":/graphics/frame.png")
, mGrid(window, Vector2i(5, 3))
, mGrid(window, glm::ivec2{5, 3})
{
mAnimation = std::make_shared<AnimatedImageComponent>(mWindow);
mAnimation->load(&BUSY_ANIMATION_DEF);
@ -33,8 +33,8 @@ BusyComponent::BusyComponent(Window* window)
0x777777FF);
// Col 0 = animation, col 1 = spacer, col 2 = text.
mGrid.setEntry(mAnimation, Vector2i(1, 1), false, true);
mGrid.setEntry(mText, Vector2i(3, 1), false, true);
mGrid.setEntry(mAnimation, glm::ivec2{1, 1}, false, true);
mGrid.setEntry(mText, glm::ivec2{3, 1}, false, true);
addChild(&mBackground);
addChild(&mGrid);
@ -44,25 +44,25 @@ void BusyComponent::onSizeChanged()
{
mGrid.setSize(mSize);
if (mSize.x() == 0 || mSize.y() == 0)
if (mSize.x == 0.0f || mSize.y == 0.0f)
return;
const float middleSpacerWidth = 0.01f * Renderer::getScreenWidth();
const float textHeight = mText->getFont()->getLetterHeight();
mText->setSize(0, textHeight);
const float textWidth = mText->getSize().x() + (4 * Renderer::getScreenWidthModifier());
const float textWidth = mText->getSize().x + (4.0f * Renderer::getScreenWidthModifier());
mGrid.setColWidthPerc(1, textHeight / mSize.x()); // Animation is square.
mGrid.setColWidthPerc(2, middleSpacerWidth / mSize.x());
mGrid.setColWidthPerc(3, textWidth / mSize.x());
mGrid.setColWidthPerc(1, textHeight / mSize.x); // Animation is square.
mGrid.setColWidthPerc(2, middleSpacerWidth / mSize.x);
mGrid.setColWidthPerc(3, textWidth / mSize.x);
mGrid.setRowHeightPerc(1, textHeight / mSize.y());
mGrid.setRowHeightPerc(1, textHeight / mSize.y);
mBackground.setCornerSize({ 16.0f * Renderer::getScreenWidthModifier(),
16.0f * Renderer::getScreenHeightModifier() });
mBackground.fitTo(Vector2f(mGrid.getColWidth(1) + mGrid.getColWidth(2) + mGrid.getColWidth(3),
textHeight + (2.0f * Renderer::getScreenHeightModifier())),
mAnimation->getPosition(), Vector2f(0, 0));
mBackground.setCornerSize(
{16.0f * Renderer::getScreenWidthModifier(), 16.0f * Renderer::getScreenHeightModifier()});
mBackground.fitTo(glm::vec2{mGrid.getColWidth(1) + mGrid.getColWidth(2) + mGrid.getColWidth(3),
textHeight + (2.0f * Renderer::getScreenHeightModifier())},
mAnimation->getPosition(), glm::vec2{});
}
void BusyComponent::reset()

View file

@ -32,7 +32,7 @@ ButtonComponent::ButtonComponent(Window* window,
void ButtonComponent::onSizeChanged()
{
// Fit to mBox.
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBox.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
}
bool ButtonComponent::input(InputConfig* config, Input input)
@ -53,10 +53,10 @@ void ButtonComponent::setText(const std::string& text, const std::string& helpTe
mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache(mText, 0, 0, getCurTextColor()));
float minWidth = mFont->sizeText("DELETE").x() + (12.0f * Renderer::getScreenWidthModifier());
setSize(std::max(mTextCache->metrics.size.x() + (12.0f * Renderer::getScreenWidthModifier()),
float minWidth = mFont->sizeText("DELETE").x + (12.0f * Renderer::getScreenWidthModifier());
setSize(std::max(mTextCache->metrics.size.x + (12.0f * Renderer::getScreenWidthModifier()),
minWidth),
mTextCache->metrics.size.y());
mTextCache->metrics.size.y);
updateHelpPrompts();
}
@ -93,29 +93,29 @@ void ButtonComponent::updateImage()
mBox.setImagePath(mFocused ? ":/graphics/button_filled.svg" : ":/graphics/button.svg");
}
void ButtonComponent::render(const Transform4x4f& parentTrans)
void ButtonComponent::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
mBox.render(trans);
if (mTextCache) {
Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2.0f,
(mSize.y() - mTextCache->metrics.size.y()) / 2.0f, 0);
trans = trans.translate(centerOffset);
glm::vec3 centerOffset{(mSize.x - mTextCache->metrics.size.x) / 2.0f,
(mSize.y - mTextCache->metrics.size.y) / 2.0f, 0.0f};
trans = glm::translate(trans, centerOffset);
if (Settings::getInstance()->getBool("DebugText")) {
Renderer::drawRect(centerOffset.x(), 0.0f, mTextCache->metrics.size.x(), mSize.y(),
Renderer::drawRect(centerOffset.x, 0.0f, mTextCache->metrics.size.x, mSize.y,
0x00000033, 0x00000033);
Renderer::drawRect(mBox.getPosition().x(), 0.0f, mBox.getSize().x(), mSize.y(),
0x0000FF33, 0x0000FF33);
Renderer::drawRect(mBox.getPosition().x, 0.0f, mBox.getSize().x, mSize.y, 0x0000FF33,
0x0000FF33);
}
Renderer::setMatrix(trans);
mTextCache->setColor(getCurTextColor());
mFont->renderTextCache(mTextCache.get());
trans = trans.translate(-centerOffset);
trans = glm::translate(trans, -centerOffset);
}
renderChildren(trans);

View file

@ -26,7 +26,7 @@ public:
void setEnabled(bool state) override;
bool input(InputConfig* config, Input input) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void setText(const std::string& text, const std::string& helpText);

View file

@ -12,21 +12,21 @@
using namespace GridFlags;
ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions)
ComponentGrid::ComponentGrid(Window* window, const glm::ivec2& gridDimensions)
: GuiComponent(window)
, mGridSize(gridDimensions)
, mCursor(0, 0)
{
assert(gridDimensions.x() > 0 && gridDimensions.y() > 0);
assert(gridDimensions.x > 0 && gridDimensions.y > 0);
mCells.reserve(gridDimensions.x() * gridDimensions.y());
mCells.reserve(gridDimensions.x * gridDimensions.y);
mColWidths = new float[gridDimensions.x()];
mRowHeights = new float[gridDimensions.y()];
mColWidths = new float[gridDimensions.x];
mRowHeights = new float[gridDimensions.y];
for (int x = 0; x < gridDimensions.x(); x++)
for (int x = 0; x < gridDimensions.x; x++)
mColWidths[x] = 0;
for (int y = 0; y < gridDimensions.y(); y++)
for (int y = 0; y < gridDimensions.y; y++)
mRowHeights[y] = 0;
}
@ -39,40 +39,40 @@ ComponentGrid::~ComponentGrid()
float ComponentGrid::getColWidth(int col)
{
if (mColWidths[col] != 0)
return mColWidths[col] * mSize.x();
return mColWidths[col] * mSize.x;
// Calculate automatic width.
float freeWidthPerc = 1;
int between = 0;
for (int x = 0; x < mGridSize.x(); x++) {
for (int x = 0; x < mGridSize.x; x++) {
freeWidthPerc -= mColWidths[x]; // If it's 0 it won't do anything.
if (mColWidths[x] == 0)
between++;
}
return (freeWidthPerc * mSize.x()) / between;
return (freeWidthPerc * mSize.x) / static_cast<float>(between);
}
float ComponentGrid::getRowHeight(int row)
{
if (mRowHeights[row] != 0)
return mRowHeights[row] * mSize.y();
return mRowHeights[row] * mSize.y;
// Calculate automatic height.
float freeHeightPerc = 1;
int between = 0;
for (int y = 0; y < mGridSize.y(); y++) {
for (int y = 0; y < mGridSize.y; y++) {
freeHeightPerc -= mRowHeights[y]; // If it's 0 it won't do anything.
if (mRowHeights[y] == 0)
between++;
}
return (freeHeightPerc * mSize.y()) / between;
return (freeHeightPerc * mSize.y) / static_cast<float>(between);
}
void ComponentGrid::setColWidthPerc(int col, float width, bool update)
{
assert(col >= 0 && col < mGridSize.x());
assert(col >= 0 && col < mGridSize.x);
mColWidths[col] = width;
if (update)
@ -82,7 +82,7 @@ void ComponentGrid::setColWidthPerc(int col, float width, bool update)
void ComponentGrid::setRowHeightPerc(int row, float height, bool update)
{
assert(height >= 0 && height <= 1);
assert(row >= 0 && row < mGridSize.y());
assert(row >= 0 && row < mGridSize.y);
mRowHeights[row] = height;
if (update)
@ -90,14 +90,14 @@ void ComponentGrid::setRowHeightPerc(int row, float height, bool update)
}
void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp,
const Vector2i& pos,
const glm::ivec2& pos,
bool canFocus,
bool resize,
const Vector2i& size,
const glm::ivec2& size,
unsigned int border,
GridFlags::UpdateType updateType)
{
assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
assert(pos.x >= 0 && pos.x < mGridSize.x && pos.y >= 0 && pos.y < mGridSize.y);
assert(comp != nullptr);
assert(comp->getParent() == nullptr);
@ -132,26 +132,25 @@ bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
void ComponentGrid::updateCellComponent(const GridEntry& cell)
{
// Size.
Vector2f size(0, 0);
for (int x = cell.pos.x(); x < cell.pos.x() + cell.dim.x(); x++)
size[0] += getColWidth(x);
for (int y = cell.pos.y(); y < cell.pos.y() + cell.dim.y(); y++)
size[1] += getRowHeight(y);
glm::vec2 size{0.0f};
for (int x = cell.pos.x; x < cell.pos.x + cell.dim.x; x++)
size.x += getColWidth(x);
for (int y = cell.pos.y; y < cell.pos.y + cell.dim.y; y++)
size.y += getRowHeight(y);
if (cell.resize)
cell.component->setSize(size);
// Position.
// Find top left corner.
Vector3f pos(0, 0, 0);
for (int x = 0; x < cell.pos.x(); x++)
pos[0] += getColWidth(x);
for (int y = 0; y < cell.pos.y(); y++)
pos[1] += getRowHeight(y);
glm::vec3 pos{};
for (int x = 0; x < cell.pos.x; x++)
pos.x += getColWidth(x);
for (int y = 0; y < cell.pos.y; y++)
pos.y += getRowHeight(y);
// Center component.
pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2;
pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2;
pos.x = pos.x + (size.x - cell.component->getSize().x) / 2.0f;
pos.y = pos.y + (size.y - cell.component->getSize().y) / 2.0f;
cell.component->setPosition(pos);
}
@ -162,58 +161,58 @@ void ComponentGrid::updateSeparators()
bool drawAll = Settings::getInstance()->getBool("DebugGrid");
Vector2f pos;
Vector2f size;
glm::vec2 pos;
glm::vec2 size;
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (!it->border && !drawAll)
continue;
// Find component position + size.
pos = Vector2f(0, 0);
size = Vector2f(0, 0);
for (int x = 0; x < it->pos.x(); x++)
pos = glm::vec2{};
size = glm::vec2{};
for (int x = 0; x < it->pos.x; x++)
pos[0] += getColWidth(x);
for (int y = 0; y < it->pos.y(); y++)
for (int y = 0; y < it->pos.y; y++)
pos[1] += getRowHeight(y);
for (int x = it->pos.x(); x < it->pos.x() + it->dim.x(); x++)
for (int x = it->pos.x; x < it->pos.x + it->dim.x; x++)
size[0] += getColWidth(x);
for (int y = it->pos.y(); y < it->pos.y() + it->dim.y(); y++)
for (int y = it->pos.y; y < it->pos.y + it->dim.y; y++)
size[1] += getRowHeight(y);
if (size == 0)
if (size == glm::vec2{})
return;
if (it->border & BORDER_TOP || drawAll) {
std::vector<float> coordVector;
coordVector.push_back(pos.x());
coordVector.push_back(pos.y());
coordVector.push_back(size.x());
coordVector.push_back(pos.x);
coordVector.push_back(pos.y);
coordVector.push_back(size.x);
coordVector.push_back(1.0f * Renderer::getScreenHeightModifier());
mSeparators.push_back(coordVector);
}
if (it->border & BORDER_BOTTOM || drawAll) {
std::vector<float> coordVector;
coordVector.push_back(pos.x());
coordVector.push_back(pos.y() + size.y());
coordVector.push_back(size.x());
coordVector.push_back(pos.x);
coordVector.push_back(pos.y + size.y);
coordVector.push_back(size.x);
coordVector.push_back(1.0f * Renderer::getScreenHeightModifier());
mSeparators.push_back(coordVector);
}
if (it->border & BORDER_LEFT || drawAll) {
std::vector<float> coordVector;
coordVector.push_back(pos.x());
coordVector.push_back(pos.y());
coordVector.push_back(pos.x);
coordVector.push_back(pos.y);
coordVector.push_back(1.0f * Renderer::getScreenWidthModifier());
coordVector.push_back(size.y());
coordVector.push_back(size.y);
mSeparators.push_back(coordVector);
}
if (it->border & BORDER_RIGHT || drawAll) {
std::vector<float> coordVector;
coordVector.push_back(pos.x() + size.x());
coordVector.push_back(pos.y());
coordVector.push_back(pos.x + size.x);
coordVector.push_back(pos.y);
coordVector.push_back(1.0f * Renderer::getScreenWidthModifier());
coordVector.push_back(size.y());
coordVector.push_back(size.y);
mSeparators.push_back(coordVector);
}
}
@ -229,13 +228,13 @@ void ComponentGrid::onSizeChanged()
const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
{
assert(x >= 0 && x < mGridSize.x() && y >= 0 && y < mGridSize.y());
assert(x >= 0 && x < mGridSize.x && y >= 0 && y < mGridSize.y);
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
int xmin = it->pos.x();
int xmax = xmin + it->dim.x();
int ymin = it->pos.y();
int ymax = ymin + it->dim.y();
int xmin = it->pos.x;
int xmax = xmin + it->dim.x;
int ymin = it->pos.y;
int ymax = ymin + it->dim.y;
if (x >= xmin && y >= ymin && x < xmax && y < ymax)
return &(*it);
@ -254,16 +253,16 @@ bool ComponentGrid::input(InputConfig* config, Input input)
return false;
if (config->isMappedLike("down", input))
return moveCursor(Vector2i(0, 1));
return moveCursor(glm::ivec2{0, 1});
if (config->isMappedLike("up", input))
return moveCursor(Vector2i(0, -1));
return moveCursor(glm::ivec2{0, -1});
if (config->isMappedLike("left", input))
return moveCursor(Vector2i(-1, 0));
return moveCursor(glm::ivec2{-1, 0});
if (config->isMappedLike("right", input))
return moveCursor(Vector2i(1, 0));
return moveCursor(glm::ivec2{1, 0});
return false;
}
@ -275,7 +274,7 @@ void ComponentGrid::resetCursor()
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (it->canFocus) {
Vector2i origCursor = mCursor;
glm::ivec2 origCursor = mCursor;
mCursor = it->pos;
onCursorMoved(origCursor, mCursor);
break;
@ -283,23 +282,22 @@ void ComponentGrid::resetCursor()
}
}
bool ComponentGrid::moveCursor(Vector2i dir)
bool ComponentGrid::moveCursor(glm::ivec2 dir)
{
assert(dir.x() || dir.y());
assert(dir.x || dir.y);
const Vector2i origCursor = mCursor;
const glm::ivec2 origCursor{mCursor};
const GridEntry* currentCursorEntry = getCellAt(mCursor);
Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
glm::ivec2 searchAxis(dir.x == 0, dir.y == 0);
while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
mCursor.y() < mGridSize.y()) {
while (mCursor.x >= 0 && mCursor.y >= 0 && mCursor.x < mGridSize.x && mCursor.y < mGridSize.y) {
mCursor = mCursor + dir;
Vector2i curDirPos = mCursor;
glm::ivec2 curDirPos{mCursor};
const GridEntry* cursorEntry;
// Spread out on search axis+
while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y() && mCursor.x() >= 0 &&
mCursor.y() >= 0) {
while (mCursor.x < mGridSize.x && mCursor.y < mGridSize.y && mCursor.x >= 0 &&
mCursor.y >= 0) {
cursorEntry = getCellAt(mCursor);
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
onCursorMoved(origCursor, mCursor);
@ -310,8 +308,8 @@ bool ComponentGrid::moveCursor(Vector2i dir)
// Now again on search axis-
mCursor = curDirPos;
while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
mCursor.y() < mGridSize.y()) {
while (mCursor.x >= 0 && mCursor.y >= 0 && mCursor.x < mGridSize.x &&
mCursor.y < mGridSize.y) {
cursorEntry = getCellAt(mCursor);
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
@ -360,9 +358,9 @@ void ComponentGrid::update(int deltaTime)
}
}
void ComponentGrid::render(const Transform4x4f& parentTrans)
void ComponentGrid::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
renderChildren(trans);
@ -381,7 +379,7 @@ void ComponentGrid::textInput(const std::string& text)
selectedEntry->component->textInput(text);
}
void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to)
void ComponentGrid::onCursorMoved(glm::ivec2 from, glm::ivec2 to)
{
const GridEntry* cell = getCellAt(from);
if (cell)
@ -398,7 +396,7 @@ void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp)
{
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (it->component == comp) {
Vector2i oldCursor = mCursor;
glm::ivec2 oldCursor{mCursor};
mCursor = it->pos;
onCursorMoved(oldCursor, mCursor);
return;
@ -416,8 +414,8 @@ std::vector<HelpPrompt> ComponentGrid::getHelpPrompts()
if (e)
prompts = e->component->getHelpPrompts();
bool canScrollVert = mGridSize.y() > 1;
bool canScrollHoriz = mGridSize.x() > 1;
bool canScrollVert = mGridSize.y > 1;
bool canScrollHoriz = mGridSize.x > 1;
for (auto it = prompts.cbegin(); it != prompts.cend(); it++) {
if (it->first == "up/down/left/right") {
canScrollHoriz = false;

View file

@ -10,7 +10,6 @@
#define ES_CORE_COMPONENTS_COMPONENT_GRID_H
#include "GuiComponent.h"
#include "math/Vector2i.h"
#include "renderers/Renderer.h"
namespace GridFlags
@ -34,23 +33,23 @@ namespace GridFlags
class ComponentGrid : public GuiComponent
{
public:
ComponentGrid(Window* window, const Vector2i& gridDimensions);
ComponentGrid(Window* window, const glm::ivec2& gridDimensions);
virtual ~ComponentGrid();
bool removeEntry(const std::shared_ptr<GuiComponent>& comp);
void setEntry(const std::shared_ptr<GuiComponent>& comp,
const Vector2i& pos,
const glm::ivec2& pos,
bool canFocus,
bool resize = true,
const Vector2i& size = Vector2i(1, 1),
const glm::ivec2& size = glm::ivec2{1, 1},
unsigned int border = GridFlags::BORDER_NONE,
GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS);
void textInput(const std::string& text) override;
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void onSizeChanged() override;
void resetCursor();
@ -65,7 +64,7 @@ public:
// Dito.
void setRowHeightPerc(int row, float height, bool update = true);
bool moveCursor(Vector2i dir);
bool moveCursor(glm::ivec2 dir);
void setCursorTo(const std::shared_ptr<GuiComponent>& comp);
std::shared_ptr<GuiComponent> getSelectedComponent()
@ -86,16 +85,16 @@ private:
class GridEntry
{
public:
Vector2i pos;
Vector2i dim;
glm::ivec2 pos;
glm::ivec2 dim;
std::shared_ptr<GuiComponent> component;
bool canFocus;
bool resize;
GridFlags::UpdateType updateType;
unsigned int border;
GridEntry(const Vector2i& p = Vector2i::Zero(),
const Vector2i& d = Vector2i::Zero(),
GridEntry(const glm::ivec2& p = glm::ivec2{},
const glm::ivec2& d = glm::ivec2{},
const std::shared_ptr<GuiComponent>& cmp = nullptr,
bool f = false,
bool r = true,
@ -118,14 +117,14 @@ private:
void updateCellComponent(const GridEntry& cell);
void updateSeparators();
void onCursorMoved(Vector2i from, Vector2i to);
void onCursorMoved(glm::ivec2 from, glm::ivec2 to);
const GridEntry* getCellAt(int x, int y) const;
const GridEntry* getCellAt(const Vector2i& pos) const { return getCellAt(pos.x(), pos.y()); }
const GridEntry* getCellAt(const glm::ivec2& pos) const { return getCellAt(pos.x, pos.y); }
std::vector<std::vector<float>> mSeparators;
Vector2i mGridSize;
glm::ivec2 mGridSize;
std::vector<GridEntry> mCells;
Vector2i mCursor;
glm::ivec2 mCursor;
float* mRowHeights;
float* mColWidths;

View file

@ -145,45 +145,47 @@ void ComponentList::updateCameraOffset()
{
// Move the camera to scroll.
const float totalHeight = getTotalRowHeight();
if (totalHeight > mSize.y()) {
float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2.0f -
(mSize.y() / 2.0f);
if (totalHeight > mSize.y) {
float target =
mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2.0f - (mSize.y / 2.0f);
// Clamp the camera to prevent a fraction of a row from being displayed.
mCameraOffset = 0.0f;
unsigned int i = 0;
while (mCameraOffset < target && i < mEntries.size()) {
mCameraOffset += getRowHeight(mEntries.at(i).data);
if (mCameraOffset > totalHeight - mSize.y())
if (mCameraOffset > totalHeight - mSize.y)
break;
i++;
}
if (mCameraOffset < 0)
mCameraOffset = 0;
if (mCameraOffset < 0.0f)
mCameraOffset = 0.0f;
}
else {
mCameraOffset = 0;
mCameraOffset = 0.0f;
}
}
void ComponentList::render(const Transform4x4f& parentTrans)
void ComponentList::render(const glm::mat4& parentTrans)
{
if (!size())
return;
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
// Clip everything to be inside our bounds.
Vector3f dim(mSize.x(), mSize.y(), 0.0f);
dim = trans * dim - trans.translation();
glm::vec3 dim{mSize.x, mSize.y, 0.0f};
dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x;
dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y;
Renderer::pushClipRect(
Vector2i(static_cast<int>(std::round(trans.translation().x())),
static_cast<int>(std::round(trans.translation().y()))),
Vector2i(static_cast<int>(std::round(dim.x())), static_cast<int>(std::round(dim.y()))));
glm::ivec2{static_cast<int>(std::round(trans[3].x)),
static_cast<int>(std::round(trans[3].y))},
glm::ivec2{static_cast<int>(std::round(dim.x)), static_cast<int>(std::round(dim.y))});
// Scroll the camera.
trans.translate(Vector3f(0.0f, -std::round(mCameraOffset), 0.0f));
trans = glm::translate(trans, glm::vec3{0.0f, -mCameraOffset, 0.0f});
// Draw our entries.
std::vector<GuiComponent*> drawAfterCursor;
@ -243,19 +245,14 @@ void ComponentList::render(const Transform4x4f& parentTrans)
// Draw selector bar.
if (mFocused) {
// Inversion: src * (1 - dst) + dst * 0 = where src = 1
// Need a function that goes roughly 0x777777 -> 0xFFFFFF
// and 0xFFFFFF -> 0x777777
// (1 - dst) + 0x77
const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data);
if (opacity == 1) {
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0xFFFFFFFF,
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x, selectedRowHeight, 0xFFFFFFFF,
0xFFFFFFFF, false, opacity, trans,
Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO);
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0x777777FF,
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x, selectedRowHeight, 0x777777FF,
0x777777FF, false, opacity, trans, Renderer::Blend::ONE,
Renderer::Blend::ONE);
}
@ -271,12 +268,12 @@ void ComponentList::render(const Transform4x4f& parentTrans)
// Draw separators.
float y = 0;
for (unsigned int i = 0; i < mEntries.size(); i++) {
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(),
0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans);
Renderer::drawRect(0.0f, y, mSize.x, 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF,
0xC6C7C6FF, false, opacity, trans);
y += getRowHeight(mEntries.at(i).data);
}
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF,
Renderer::drawRect(0.0f, y, mSize.x, 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF,
0xC6C7C6FF, false, opacity, trans);
Renderer::popClipRect();
}
@ -286,11 +283,11 @@ float ComponentList::getRowHeight(const ComponentListRow& row) const
// Returns the highest component height found in the row.
float height = 0;
for (unsigned int i = 0; i < row.elements.size(); i++) {
if (row.elements.at(i).component->getSize().y() > height)
height = row.elements.at(i).component->getSize().y();
if (row.elements.at(i).component->getSize().y > height)
height = row.elements.at(i).component->getSize().y;
}
return height;
return std::floor(height);
}
float ComponentList::getTotalRowHeight() const
@ -316,27 +313,27 @@ void ComponentList::updateElementPosition(const ComponentListRow& row)
const auto comp = row.elements.at(i).component;
// Center vertically.
comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2.0f + yOffset);
x += comp->getSize().x();
comp->setPosition(x, (rowHeight - comp->getSize().y) / 2.0f + yOffset);
x += comp->getSize().x;
}
}
void ComponentList::updateElementSize(const ComponentListRow& row)
{
float width = mSize.x() - mHorizontalPadding;
float width = mSize.x - mHorizontalPadding;
std::vector<std::shared_ptr<GuiComponent>> resizeVec;
for (auto it = row.elements.cbegin(); it != row.elements.cend(); it++) {
if (it->resize_width)
resizeVec.push_back(it->component);
else
width -= it->component->getSize().x();
width -= it->component->getSize().x;
}
// Redistribute the "unused" width equally among the components with resize_width set to true.
width = width / resizeVec.size();
for (auto it = resizeVec.cbegin(); it != resizeVec.cend(); it++)
(*it)->setSize(width, (*it)->getSize().y());
(*it)->setSize(width, (*it)->getSize().y);
}
void ComponentList::textInput(const std::string& text)

View file

@ -66,7 +66,7 @@ public:
void textInput(const std::string& text) override;
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
virtual std::vector<HelpPrompt> getHelpPrompts() override;
void onSizeChanged() override;

View file

@ -27,8 +27,8 @@ DateTimeComponent::DateTimeComponent(Window* window,
const std::shared_ptr<Font>& font,
unsigned int color,
Alignment align,
Vector3f pos,
Vector2f size,
glm::vec3 pos,
glm::vec2 size,
unsigned int bgcolor)
: TextComponent(window, text, font, color, align, pos, size, bgcolor)
, mDisplayRelative(false)
@ -101,8 +101,9 @@ std::string DateTimeComponent::getDisplayString() const
return Utils::Time::timeToString(mTime.getTime(), mFormat);
}
void DateTimeComponent::render(const Transform4x4f& parentTrans)
void DateTimeComponent::render(const glm::mat4& parentTrans)
{
// Render the component.
TextComponent::render(parentTrans);
}

View file

@ -26,11 +26,11 @@ public:
const std::shared_ptr<Font>& font,
unsigned int color = 0x000000FF,
Alignment align = ALIGN_LEFT,
Vector3f pos = Vector3f::Zero(),
Vector2f size = Vector2f::Zero(),
glm::vec3 pos = {},
glm::vec2 size = {},
unsigned int bgcolor = 0x00000000);
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void setValue(const std::string& val) override;
std::string getValue() const override;

View file

@ -154,9 +154,9 @@ void DateTimeEditComponent::update(int deltaTime)
GuiComponent::update(deltaTime);
}
void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
void DateTimeEditComponent::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
if (mTextCache) {
std::shared_ptr<Font> font = getFont();
@ -164,28 +164,28 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
if (mAlignRight) {
if (mTime != 0)
referenceSize = font->sizeText("ABCDEFG").x();
referenceSize = font->sizeText("ABCDEFG").x;
else
referenceSize = font->sizeText("ABCDEIJ").x();
referenceSize = font->sizeText("ABCDEIJ").x;
}
// Vertically center.
Vector3f off(0, (mSize.y() - mTextCache->metrics.size.y()) / 2.0f, 0.0f);
glm::vec3 off{0.0f, (mSize.y - mTextCache->metrics.size.y) / 2.0f, 0.0f};
if (mAlignRight)
off.x() += referenceSize - mTextCache->metrics.size.x();
trans.translate(off);
off.x += referenceSize - mTextCache->metrics.size.x;
trans = glm::translate(trans, off);
Renderer::setMatrix(trans);
if (Settings::getInstance()->getBool("DebugText")) {
Renderer::setMatrix(trans);
if (mTextCache->metrics.size.x() > 0) {
Renderer::drawRect(0.0f, 0.0f - off.y(), mSize.x() - off.x(), mSize.y(), 0x0000FF33,
if (mTextCache->metrics.size.x > 0.0f) {
Renderer::drawRect(0.0f, 0.0f - off.y, mSize.x - off.x, mSize.y, 0x0000FF33,
0x0000FF33);
}
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y,
0x00000033, 0x00000033);
}
mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity());
@ -300,22 +300,22 @@ void DateTimeEditComponent::updateTextCache()
return;
// Year.
Vector2f start(0, 0);
Vector2f end = font->sizeText(dispString.substr(0, 4));
Vector2f diff = end - start;
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
glm::vec2 start{};
glm::vec2 end{font->sizeText(dispString.substr(0, 4))};
glm::vec2 diff{end - start};
mCursorBoxes.push_back(glm::vec4{start[0], start[1], diff[0], diff[1]});
// Month.
start[0] = font->sizeText(dispString.substr(0, 5)).x();
start[0] = font->sizeText(dispString.substr(0, 5)).x;
end = font->sizeText(dispString.substr(0, 7));
diff = end - start;
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
mCursorBoxes.push_back(glm::vec4{start[0], start[1], diff[0], diff[1]});
// Day.
start[0] = font->sizeText(dispString.substr(0, 8)).x();
start[0] = font->sizeText(dispString.substr(0, 8)).x;
end = font->sizeText(dispString.substr(0, 10));
diff = end - start;
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
mCursorBoxes.push_back(glm::vec4{start[0], start[1], diff[0], diff[1]});
// The logic for handling time for 'mode = DISP_DATE_TIME' is missing, but
// nobody will use it anyway so it's not worthwhile implementing.

View file

@ -34,7 +34,7 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
unsigned int getColor() const override { return mColor; }
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void onSizeChanged() override;
// Set how the point in time will be displayed:
@ -82,7 +82,7 @@ private:
int mRelativeUpdateAccumulator;
std::unique_ptr<TextCache> mTextCache;
std::vector<Vector4f> mCursorBoxes;
std::vector<glm::vec4> mCursorBoxes;
unsigned int mColor;
Utils::Time::DateTime mOriginalValue;

View file

@ -17,12 +17,12 @@ GridTileComponent::GridTileComponent(Window* window)
, mBackground(window, ":/graphics/frame.png")
{
mDefaultProperties.mSize = getDefaultTileSize();
mDefaultProperties.mPadding = Vector2f(16.0f * Renderer::getScreenWidthModifier(),
16.0f * Renderer::getScreenHeightModifier());
mDefaultProperties.mPadding = glm::vec2{16.0f * Renderer::getScreenWidthModifier(),
16.0f * Renderer::getScreenHeightModifier()};
mDefaultProperties.mImageColor = 0xAAAAAABB;
// Attempting to use frame.svg instead causes quite severe performance problems.
mDefaultProperties.mBackgroundImage = ":/graphics/frame.png";
mDefaultProperties.mBackgroundCornerSize = Vector2f(16.0f, 16.0f);
mDefaultProperties.mBackgroundCornerSize = glm::vec2{16.0f, 16.0f};
mDefaultProperties.mBackgroundCenterColor = 0xAAAAEEFF;
mDefaultProperties.mBackgroundEdgeColor = 0xAAAAEEFF;
@ -50,9 +50,9 @@ GridTileComponent::GridTileComponent(Window* window)
setVisible(true);
}
void GridTileComponent::render(const Transform4x4f& parentTrans)
void GridTileComponent::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = getTransform() * parentTrans;
glm::mat4 trans{getTransform() * parentTrans};
if (mVisible)
renderChildren(trans);
@ -76,14 +76,14 @@ void GridTileComponent::update(int deltaTime)
void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties)
{
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
glm::vec2 screen{static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())};
if (elem->has("size"))
properties->mSize = elem->get<Vector2f>("size") * screen;
properties->mSize = elem->get<glm::vec2>("size") * screen;
if (elem->has("padding"))
properties->mPadding = elem->get<Vector2f>("padding");
properties->mPadding = elem->get<glm::vec2>("padding");
if (elem->has("imageColor"))
properties->mImageColor = elem->get<unsigned int>("imageColor");
@ -92,7 +92,7 @@ void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTilePropert
properties->mBackgroundImage = elem->get<std::string>("backgroundImage");
if (elem->has("backgroundCornerSize"))
properties->mBackgroundCornerSize = elem->get<Vector2f>("backgroundCornerSize");
properties->mBackgroundCornerSize = elem->get<glm::vec2>("backgroundCornerSize");
if (elem->has("backgroundColor")) {
properties->mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
@ -111,9 +111,6 @@ void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& /*element*/,
unsigned int /*properties*/)
{
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
// Apply theme to the default gridtile.
const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile");
if (elem)
@ -133,15 +130,15 @@ void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
applyThemeToProperties(elem, &mSelectedProperties);
}
Vector2f GridTileComponent::getDefaultTileSize()
glm::vec2 GridTileComponent::getDefaultTileSize()
{
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
glm::vec2 screen{glm::vec2(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()))};
return screen * 0.22f;
}
Vector2f GridTileComponent::getSelectedTileSize() const
glm::vec2 GridTileComponent::getSelectedTileSize() const
{
// Return the tile size.
return mDefaultProperties.mSize * 1.2f;
@ -171,7 +168,7 @@ void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture
void GridTileComponent::setSelected(bool selected,
bool allowAnimation,
Vector3f* pPosition,
glm::vec3* pPosition,
bool force)
{
if (mSelected == selected && !force)
@ -184,16 +181,16 @@ void GridTileComponent::setSelected(bool selected,
cancelAnimation(3);
this->setSelectedZoom(1);
mAnimPosition = Vector3f(0, 0, 0);
mAnimPosition = {};
resize();
}
else {
mAnimPosition = Vector3f(pPosition->x(), pPosition->y(), pPosition->z());
mAnimPosition = glm::vec3{pPosition->x, pPosition->y, pPosition->z};
auto func = [this](float t) {
t -= 1; // Cubic ease out.
float pct = Math::lerp(0, 1, t * t * t + 1);
t -= 1;
float pct = glm::mix(0.0f, 1.0f, t * t * t + 1.0f);
this->setSelectedZoom(pct);
};
@ -202,7 +199,7 @@ void GridTileComponent::setSelected(bool selected,
new LambdaAnimation(func, 250), 0,
[this] {
this->setSelectedZoom(1);
mAnimPosition = Vector3f(0, 0, 0);
mAnimPosition = {};
},
false, 3);
}
@ -218,8 +215,8 @@ void GridTileComponent::setSelected(bool selected,
this->setSelectedZoom(1);
auto func = [this](float t) {
t -= 1.0f; // Cubic ease out.
float pct = Math::lerp(0, 1, t * t * t + 1.0f);
t -= 1.0f;
float pct = glm::mix(0.0f, 1.0f, t * t * t + 1.0f);
this->setSelectedZoom(1.0f - pct);
};
@ -307,7 +304,7 @@ void GridTileComponent::calcCurrentProperties()
}
}
Vector3f GridTileComponent::getBackgroundPosition()
glm::vec3 GridTileComponent::getBackgroundPosition()
{
return mBackground.getPosition() + mPosition;
}
@ -320,7 +317,7 @@ std::shared_ptr<TextureResource> GridTileComponent::getTexture()
return nullptr;
};
void GridTileComponent::forceSize(Vector2f size, float selectedZoom)
void GridTileComponent::forceSize(glm::vec2 size, float selectedZoom)
{
mDefaultProperties.mSize = size;
mSelectedProperties.mSize = size * selectedZoom;

View file

@ -13,11 +13,11 @@
#include "NinePatchComponent.h"
struct GridTileProperties {
Vector2f mSize;
Vector2f mPadding;
glm::vec2 mSize;
glm::vec2 mPadding;
unsigned int mImageColor;
std::string mBackgroundImage;
Vector2f mBackgroundCornerSize;
glm::vec2 mBackgroundCornerSize;
unsigned int mBackgroundCenterColor;
unsigned int mBackgroundEdgeColor;
};
@ -27,7 +27,7 @@ class GridTileComponent : public GuiComponent
public:
GridTileComponent(Window* window);
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
@ -35,8 +35,8 @@ public:
// Made this a static function because the ImageGridComponent needs to know the default tile
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
static Vector2f getDefaultTileSize();
Vector2f getSelectedTileSize() const;
static glm::vec2 getDefaultTileSize();
glm::vec2 getSelectedTileSize() const;
bool isSelected() const;
void reset() { setImage(""); }
@ -45,13 +45,13 @@ public:
void setImage(const std::shared_ptr<TextureResource>& texture);
void setSelected(bool selected,
bool allowAnimation = true,
Vector3f* pPosition = nullptr,
glm::vec3* pPosition = nullptr,
bool force = false);
void setVisible(bool visible);
void forceSize(Vector2f size, float selectedZoom);
void forceSize(glm::vec2 size, float selectedZoom);
Vector3f getBackgroundPosition();
glm::vec3 getBackgroundPosition();
virtual void update(int deltaTime) override;
@ -73,7 +73,7 @@ private:
bool mSelected;
bool mVisible;
Vector3f mAnimPosition;
glm::vec3 mAnimPosition;
};
#endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H

View file

@ -10,16 +10,14 @@
#include "Log.h"
#include "Settings.h"
#include "Window.h"
#include "components/ComponentGrid.h"
#include "components/ImageComponent.h"
#include "components/TextComponent.h"
#include "resources/TextureResource.h"
#include "utils/StringUtil.h"
#define ICON_TEXT_SPACING 8 // Space between [icon] and [text] (px).
#define ENTRY_SPACING 16 // Space between [text] and next [icon] (px).
static std::map<std::string, std::string> sIconPathMap {};
static std::map<std::string, std::string> sIconPathMap{};
HelpComponent::HelpComponent(Window* window)
: GuiComponent(window)
@ -31,58 +29,144 @@ void HelpComponent::assignIcons()
{
std::string controllerType = Settings::getInstance()->getString("InputControllerType");
std::map<std::string, std::string> sIconPathMapOld(sIconPathMap);
sIconPathMap.clear();
// These graphics files are common between all controller types.
sIconPathMap["up/down"] = ":/help/dpad_updown.svg";
sIconPathMap["left/right"] = ":/help/dpad_leftright.svg";
sIconPathMap["up/down/left/right"] = ":/help/dpad_all.svg";
sIconPathMap["thumbstickclick"] = ":/help/thumbstick_click.svg";
sIconPathMap["l"] = ":/help/button_l.svg";
sIconPathMap["r"] = ":/help/button_r.svg";
sIconPathMap["lr"] = ":/help/button_lr.svg";
sIconPathMap["up/down"] = mStyle.mCustomButtons.dpad_updown.empty() ?
":/help/dpad_updown.svg" :
mStyle.mCustomButtons.dpad_updown;
sIconPathMap["left/right"] = mStyle.mCustomButtons.dpad_leftright.empty() ?
":/help/dpad_leftright.svg" :
mStyle.mCustomButtons.dpad_leftright;
sIconPathMap["up/down/left/right"] = mStyle.mCustomButtons.dpad_all.empty() ?
":/help/dpad_all.svg" :
mStyle.mCustomButtons.dpad_all;
sIconPathMap["thumbstickclick"] = mStyle.mCustomButtons.thumbstick_click.empty() ?
":/help/thumbstick_click.svg" :
mStyle.mCustomButtons.thumbstick_click;
sIconPathMap["l"] = mStyle.mCustomButtons.button_l.empty() ? ":/help/button_l.svg" :
mStyle.mCustomButtons.button_l;
sIconPathMap["r"] = mStyle.mCustomButtons.button_r.empty() ? ":/help/button_r.svg" :
mStyle.mCustomButtons.button_r;
sIconPathMap["lr"] = mStyle.mCustomButtons.button_lr.empty() ? ":/help/button_lr.svg" :
mStyle.mCustomButtons.button_lr;
// These graphics files are custom per controller type.
if (controllerType == "snes") {
sIconPathMap["a"] = ":/help/button_a_SNES.svg";
sIconPathMap["b"] = ":/help/button_b_SNES.svg";
sIconPathMap["x"] = ":/help/button_x_SNES.svg";
sIconPathMap["y"] = ":/help/button_y_SNES.svg";
sIconPathMap["start"] = ":/help/button_start_SNES.svg";
sIconPathMap["back"] = ":/help/button_back_SNES.svg";
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_SNES.empty() ?
":/help/button_a_SNES.svg" :
mStyle.mCustomButtons.button_a_SNES;
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_SNES.empty() ?
":/help/button_b_SNES.svg" :
mStyle.mCustomButtons.button_b_SNES;
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_SNES.empty() ?
":/help/button_x_SNES.svg" :
mStyle.mCustomButtons.button_x_SNES;
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_SNES.empty() ?
":/help/button_y_SNES.svg" :
mStyle.mCustomButtons.button_y_SNES;
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_SNES.empty() ?
":/help/button_start_SNES.svg" :
mStyle.mCustomButtons.button_start_SNES;
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_SNES.empty() ?
":/help/button_back_SNES.svg" :
mStyle.mCustomButtons.button_back_SNES;
}
else if (controllerType == "ps4") {
sIconPathMap["a"] = ":/help/button_a_PS.svg";
sIconPathMap["b"] = ":/help/button_b_PS.svg";
sIconPathMap["x"] = ":/help/button_x_PS.svg";
sIconPathMap["y"] = ":/help/button_y_PS.svg";
sIconPathMap["start"] = ":/help/button_start_PS4.svg";
sIconPathMap["back"] = ":/help/button_back_PS4.svg";
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ?
":/help/button_a_PS.svg" :
mStyle.mCustomButtons.button_a_PS;
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ?
":/help/button_b_PS.svg" :
mStyle.mCustomButtons.button_b_PS;
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ?
":/help/button_x_PS.svg" :
mStyle.mCustomButtons.button_x_PS;
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ?
":/help/button_y_PS.svg" :
mStyle.mCustomButtons.button_y_PS;
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS4.empty() ?
":/help/button_start_PS4.svg" :
mStyle.mCustomButtons.button_start_PS4;
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS4.empty() ?
":/help/button_back_PS4.svg" :
mStyle.mCustomButtons.button_back_PS4;
}
else if (controllerType == "ps5") {
sIconPathMap["a"] = ":/help/button_a_PS.svg";
sIconPathMap["b"] = ":/help/button_b_PS.svg";
sIconPathMap["x"] = ":/help/button_x_PS.svg";
sIconPathMap["y"] = ":/help/button_y_PS.svg";
sIconPathMap["start"] = ":/help/button_start_PS5.svg";
sIconPathMap["back"] = ":/help/button_back_PS5.svg";
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ?
":/help/button_a_PS.svg" :
mStyle.mCustomButtons.button_a_PS;
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ?
":/help/button_b_PS.svg" :
mStyle.mCustomButtons.button_b_PS;
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ?
":/help/button_x_PS.svg" :
mStyle.mCustomButtons.button_x_PS;
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ?
":/help/button_y_PS.svg" :
mStyle.mCustomButtons.button_y_PS;
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS5.empty() ?
":/help/button_start_PS5.svg" :
mStyle.mCustomButtons.button_start_PS5;
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS5.empty() ?
":/help/button_back_PS5.svg" :
mStyle.mCustomButtons.button_back_PS5;
}
else if (controllerType == "xbox360") {
sIconPathMap["a"] = ":/help/button_a_XBOX.svg";
sIconPathMap["b"] = ":/help/button_b_XBOX.svg";
sIconPathMap["x"] = ":/help/button_x_XBOX.svg";
sIconPathMap["y"] = ":/help/button_y_XBOX.svg";
sIconPathMap["start"] = ":/help/button_start_XBOX360.svg";
sIconPathMap["back"] = ":/help/button_back_XBOX360.svg";
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ?
":/help/button_a_XBOX.svg" :
mStyle.mCustomButtons.button_a_XBOX;
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ?
":/help/button_b_XBOX.svg" :
mStyle.mCustomButtons.button_b_XBOX;
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ?
":/help/button_x_XBOX.svg" :
mStyle.mCustomButtons.button_x_XBOX;
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ?
":/help/button_y_XBOX.svg" :
mStyle.mCustomButtons.button_y_XBOX;
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX360.empty() ?
":/help/button_start_XBOX360.svg" :
mStyle.mCustomButtons.button_start_XBOX360;
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX360.empty() ?
":/help/button_back_XBOX360.svg" :
mStyle.mCustomButtons.button_back_XBOX360;
}
else {
// Xbox One and later.
sIconPathMap["a"] = ":/help/button_a_XBOX.svg";
sIconPathMap["b"] = ":/help/button_b_XBOX.svg";
sIconPathMap["x"] = ":/help/button_x_XBOX.svg";
sIconPathMap["y"] = ":/help/button_y_XBOX.svg";
sIconPathMap["start"] = ":/help/button_start_XBOX.svg";
sIconPathMap["back"] = ":/help/button_back_XBOX.svg";
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ?
":/help/button_a_XBOX.svg" :
mStyle.mCustomButtons.button_a_XBOX;
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ?
":/help/button_b_XBOX.svg" :
mStyle.mCustomButtons.button_b_XBOX;
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ?
":/help/button_x_XBOX.svg" :
mStyle.mCustomButtons.button_x_XBOX;
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ?
":/help/button_y_XBOX.svg" :
mStyle.mCustomButtons.button_y_XBOX;
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX.empty() ?
":/help/button_start_XBOX.svg" :
mStyle.mCustomButtons.button_start_XBOX;
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX.empty() ?
":/help/button_back_XBOX.svg" :
mStyle.mCustomButtons.button_back_XBOX;
}
// Invalidate cache for icons that have changed.
auto it = sIconPathMap.begin();
while (it != sIconPathMap.end()) {
if (sIconPathMapOld.find(it->first) != sIconPathMapOld.end()) {
if (sIconPathMapOld[it->first] != sIconPathMap[it->first]) {
if (mIconCache.find(it->first) != mIconCache.end()) {
mIconCache.erase(mIconCache.find(it->first));
}
}
}
it++;
}
}
@ -102,6 +186,7 @@ void HelpComponent::setStyle(const HelpStyle& style)
{
mStyle = style;
updateGrid();
assignIcons();
}
void HelpComponent::updateGrid()
@ -114,7 +199,7 @@ void HelpComponent::updateGrid()
std::shared_ptr<Font>& font = mStyle.font;
mGrid = std::make_shared<ComponentGrid>(mWindow,
Vector2i(static_cast<int>(mPrompts.size()) * 4, 1));
glm::ivec2{static_cast<int>(mPrompts.size()) * 4, 1});
// [icon] [spacer1] [text] [spacer2]
@ -124,35 +209,47 @@ void HelpComponent::updateGrid()
float width = 0;
const float height = std::round(font->getLetterHeight() * 1.25f);
// State variable indicating whether gui is dimmed.
bool isDimmed = mWindow->isBackgroundDimmed();
for (auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++) {
auto icon = std::make_shared<ImageComponent>(mWindow);
icon->setImage(getIconTexture(it->first.c_str()));
icon->setColorShift(mStyle.iconColor);
icon->setColorShift(isDimmed ? mStyle.iconColorDimmed : mStyle.iconColor);
icon->setResize(0, height);
icons.push_back(icon);
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(it->second),
font, mStyle.textColor);
// Apply text style and color from the theme to the label and add it to the label list.
std::string lblInput = it->second;
if (mStyle.textStyle == "lowercase")
lblInput = Utils::String::toLower(lblInput);
else if (mStyle.textStyle == "camelcase")
lblInput = Utils::String::toCamelCase(lblInput);
else
lblInput = Utils::String::toUpper(lblInput);
auto lbl = std::make_shared<TextComponent>(
mWindow, lblInput, font, isDimmed ? mStyle.textColorDimmed : mStyle.textColor);
labels.push_back(lbl);
width += icon->getSize().x() + lbl->getSize().x() +
((ICON_TEXT_SPACING + ENTRY_SPACING) * Renderer::getScreenWidthModifier());
width +=
icon->getSize().x + lbl->getSize().x +
((mStyle.iconTextSpacing + mStyle.entrySpacing) * Renderer::getScreenWidthModifier());
}
mGrid->setSize(width, height);
for (unsigned int i = 0; i < icons.size(); i++) {
const int col = i * 4;
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width);
mGrid->setColWidthPerc(col + 1,
(ICON_TEXT_SPACING * Renderer::getScreenWidthModifier()) / width);
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width);
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x / width);
mGrid->setColWidthPerc(
col + 1, (mStyle.iconTextSpacing * Renderer::getScreenWidthModifier()) / width);
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x / width);
mGrid->setEntry(icons.at(i), Vector2i(col, 0), false, false);
mGrid->setEntry(labels.at(i), Vector2i(col + 2, 0), false, false);
mGrid->setEntry(icons.at(i), glm::ivec2{col, 0}, false, false);
mGrid->setEntry(labels.at(i), glm::ivec2{col + 2, 0}, false, false);
}
mGrid->setPosition(Vector3f(mStyle.position.x(), mStyle.position.y(), 0.0f));
mGrid->setPosition({mStyle.position.x, mStyle.position.y, 0.0f});
mGrid->setOrigin(mStyle.origin);
}
@ -167,6 +264,7 @@ std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
LOG(LogError) << "Unknown help icon \"" << name << "\"";
return nullptr;
}
if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) {
LOG(LogError) << "Couldn't load help icon \"" << name << "\" as the file \""
<< pathLookup->second << "\" is missing";
@ -187,9 +285,9 @@ void HelpComponent::setOpacity(unsigned char opacity)
mGrid->getChild(i)->setOpacity(opacity);
}
void HelpComponent::render(const Transform4x4f& parentTrans)
void HelpComponent::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
if (mGrid)
mGrid->render(trans);

View file

@ -26,7 +26,7 @@ public:
void clearPrompts();
void setPrompts(const std::vector<HelpPrompt>& prompts);
void render(const Transform4x4f& parent) override;
void render(const glm::mat4& parent) override;
void setOpacity(unsigned char opacity) override;
void setStyle(const HelpStyle& style);

View file

@ -37,9 +37,9 @@ struct ScrollTierList {
// Default scroll tiers.
// clang-format off
const ScrollTier QUICK_SCROLL_TIERS[] = {
{ 500, 500 },
{ 1200, 114 },
{ 0, 16 }
{500, 500},
{1200, 114},
{0, 16}
};
const ScrollTierList LIST_SCROLL_STYLE_QUICK = {
3,
@ -47,8 +47,8 @@ const ScrollTierList LIST_SCROLL_STYLE_QUICK = {
};
const ScrollTier SLOW_SCROLL_TIERS[] = {
{ 500, 500 },
{ 0, 200 }
{500, 500},
{0, 200}
};
const ScrollTierList LIST_SCROLL_STYLE_SLOW = {
@ -282,7 +282,7 @@ protected:
scroll(mScrollVelocity);
}
void listRenderTitleOverlay(const Transform4x4f& /*trans*/)
void listRenderTitleOverlay(const glm::mat4& /*trans*/)
{
if (!Settings::getInstance()->getBool("ListScrollOverlay"))
return;

View file

@ -14,15 +14,15 @@
#include "resources/TextureResource.h"
#include "utils/CImgUtil.h"
Vector2i ImageComponent::getTextureSize() const
glm::ivec2 ImageComponent::getTextureSize() const
{
if (mTexture)
return mTexture->getSize();
else
return Vector2i::Zero();
return glm::ivec2{};
}
Vector2f ImageComponent::getSize() const
glm::vec2 ImageComponent::getSize() const
{
return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop);
}
@ -53,8 +53,8 @@ void ImageComponent::resize()
if (!mTexture)
return;
const Vector2f textureSize = mTexture->getSourceImageSize();
if (textureSize == Vector2f::Zero())
const glm::vec2 textureSize{mTexture->getSourceImageSize()};
if (textureSize == glm::vec2{})
return;
if (mTexture->isTiled()) {
@ -71,75 +71,74 @@ void ImageComponent::resize()
if (mTargetIsMax) {
mSize = textureSize;
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
glm::vec2 resizeScale{(mTargetSize.x / mSize.x), (mTargetSize.y / mSize.y)};
if (resizeScale.x() < resizeScale.y()) {
// This will be mTargetSize.x(). We can't exceed it, nor be lower than it.
mSize[0] *= resizeScale.x();
if (resizeScale.x < resizeScale.y) {
// This will be mTargetSize.x. We can't exceed it, nor be lower than it.
mSize.x *= resizeScale.x;
// We need to make sure we're not creating an image larger than max size.
mSize[1] = std::min(floorf(mSize[1] *= resizeScale.x()), mTargetSize.y());
mSize.y = std::min(floorf(mSize.y *= resizeScale.x), mTargetSize.y);
}
else {
// This will be mTargetSize.y(). We can't exceed it.
mSize[1] = floorf(mSize[1] * resizeScale.y());
mSize.y = floorf(mSize.y * resizeScale.y);
// For SVG rasterization, always calculate width from rounded height (see comment
// above). We need to make sure we're not creating an image larger than max size.
mSize[0] =
std::min((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
mSize.x = std::min((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x);
}
}
else if (mTargetIsMin) {
mSize = textureSize;
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
glm::vec2 resizeScale{(mTargetSize.x / mSize.x), (mTargetSize.y / mSize.y)};
if (resizeScale.x() > resizeScale.y()) {
mSize[0] *= resizeScale.x();
mSize[1] *= resizeScale.x();
if (resizeScale.x > resizeScale.y) {
mSize.x *= resizeScale.x;
mSize.y *= resizeScale.x;
float cropPercent = (mSize.y() - mTargetSize.y()) / (mSize.y() * 2);
crop(0, cropPercent, 0, cropPercent);
float cropPercent = (mSize.y - mTargetSize.y) / (mSize.y * 2.0f);
crop(0.0f, cropPercent, 0.0f, cropPercent);
}
else {
mSize[0] *= resizeScale.y();
mSize[1] *= resizeScale.y();
mSize.x *= resizeScale.y;
mSize.y *= resizeScale.y;
float cropPercent = (mSize.x() - mTargetSize.x()) / (mSize.x() * 2);
crop(cropPercent, 0, cropPercent, 0);
float cropPercent = (mSize.x - mTargetSize.x) / (mSize.x * 2.0f);
crop(cropPercent, 0.0f, cropPercent, 0.0f);
}
// For SVG rasterization, always calculate width from rounded height (see comment
// above). We need to make sure we're not creating an image smaller than min size.
mSize[1] = std::max(floorf(mSize[1]), mTargetSize.y());
mSize[0] = std::max((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
mSize.y = std::max(floorf(mSize.y), mTargetSize.y);
mSize.x = std::max((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x);
}
else {
// If both components are set, we just stretch.
// If no components are set, we don't resize at all.
mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
mSize = mTargetSize == glm::vec2{} ? textureSize : mTargetSize;
// If only one component is set, we resize in a way that maintains aspect ratio.
// For SVG rasterization, we always calculate width from rounded height (see
// comment above).
if (!mTargetSize.x() && mTargetSize.y()) {
mSize[1] = floorf(mTargetSize.y());
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
if (!mTargetSize.x && mTargetSize.y) {
mSize.y = floorf(mTargetSize.y);
mSize.x = (mSize.y / textureSize.y) * textureSize.x;
}
else if (mTargetSize.x() && !mTargetSize.y()) {
mSize[1] = floorf((mTargetSize.x() / textureSize.x()) * textureSize.y());
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
else if (mTargetSize.x && !mTargetSize.y) {
mSize.y = floorf((mTargetSize.x / textureSize.x) * textureSize.y);
mSize.x = (mSize.y / textureSize.y) * textureSize.x;
}
}
}
mSize[0] = floorf(mSize.x());
mSize[1] = floorf(mSize.y());
mSize.x = floorf(mSize.x);
mSize.y = floorf(mSize.y);
// mSize.y() should already be rounded.
mTexture->rasterizeAt(static_cast<size_t>(mSize.x()), static_cast<size_t>(mSize.y()));
mTexture->rasterizeAt(static_cast<size_t>(mSize.x), static_cast<size_t>(mSize.y));
onSizeChanged();
}
void ImageComponent::setImage(std::string path, bool tile)
void ImageComponent::setImage(std::string path, bool tile, bool linearMagnify)
{
// Always load bundled graphic resources statically, unless mForceLoad has been set.
// This eliminates annoying texture pop-in problems that would otherwise occur.
@ -151,10 +150,11 @@ void ImageComponent::setImage(std::string path, bool tile)
if (mDefaultPath.empty() || !ResourceManager::getInstance()->fileExists(mDefaultPath))
mTexture.reset();
else
mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic);
mTexture =
TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic, linearMagnify);
}
else {
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic);
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic, linearMagnify);
}
resize();
@ -178,7 +178,7 @@ void ImageComponent::setImage(const std::shared_ptr<TextureResource>& texture)
void ImageComponent::setResize(float width, float height)
{
mTargetSize = Vector2f(width, height);
mTargetSize = glm::vec2{width, height};
mTargetIsMax = false;
mTargetIsMin = false;
resize();
@ -186,7 +186,7 @@ void ImageComponent::setResize(float width, float height)
void ImageComponent::setMaxSize(float width, float height)
{
mTargetSize = Vector2f(width, height);
mTargetSize = glm::vec2{width, height};
mTargetIsMax = true;
mTargetIsMin = false;
resize();
@ -194,7 +194,7 @@ void ImageComponent::setMaxSize(float width, float height)
void ImageComponent::setMinSize(float width, float height)
{
mTargetSize = Vector2f(width, height);
mTargetSize = glm::vec2{width, height};
mTargetIsMax = false;
mTargetIsMin = true;
resize();
@ -203,25 +203,25 @@ void ImageComponent::setMinSize(float width, float height)
void ImageComponent::cropLeft(float percent)
{
assert(percent >= 0.0f && percent <= 1.0f);
mTopLeftCrop.x() = percent;
mTopLeftCrop.x = percent;
}
void ImageComponent::cropTop(float percent)
{
assert(percent >= 0.0f && percent <= 1.0f);
mTopLeftCrop.y() = percent;
mTopLeftCrop.y = percent;
}
void ImageComponent::cropRight(float percent)
{
assert(percent >= 0.0f && percent <= 1.0f);
mBottomRightCrop.x() = 1.0f - percent;
mBottomRightCrop.x = 1.0f - percent;
}
void ImageComponent::cropBot(float percent)
{
assert(percent >= 0.0f && percent <= 1.0f);
mBottomRightCrop.y() = 1.0f - percent;
mBottomRightCrop.y = 1.0f - percent;
}
void ImageComponent::crop(float left, float top, float right, float bot)
@ -235,12 +235,12 @@ void ImageComponent::crop(float left, float top, float right, float bot)
void ImageComponent::uncrop()
{
// Remove any applied crop.
crop(0, 0, 0, 0);
crop(0.0f, 0.0f, 0.0f, 0.0f);
}
void ImageComponent::cropTransparentPadding(float maxSizeX, float maxSizeY)
{
if (mSize == 0)
if (mSize == glm::vec2{})
return;
std::vector<unsigned char> imageRGBA = mTexture.get()->getRawRGBAData();
@ -248,10 +248,10 @@ void ImageComponent::cropTransparentPadding(float maxSizeX, float maxSizeY)
if (imageRGBA.size() == 0)
return;
Vector2i imageSize = mTexture.get()->getSize();
cimg_library::CImg<unsigned char> imageCImg(imageSize.x(), imageSize.y(), 1, 4, 0);
glm::ivec2 imageSize{mTexture.get()->getSize()};
cimg_library::CImg<unsigned char> imageCImg(imageSize.x, imageSize.y, 1, 4, 0);
int paddingCoords[4] = {};
int paddingCoords[4]{};
// We need to convert our RGBA data to the CImg internal format as CImg does not interleave
// the pixels (as in RGBARGBARGBA).
@ -260,36 +260,36 @@ void ImageComponent::cropTransparentPadding(float maxSizeX, float maxSizeY)
// This will give us the coordinates for the fully transparent areas.
Utils::CImg::getTransparentPaddingCoords(imageCImg, paddingCoords);
Vector2f originalSize = mSize;
glm::vec2 originalSize{mSize};
float cropLeft = static_cast<float>(paddingCoords[0]) / static_cast<float>(imageSize.x());
float cropTop = static_cast<float>(paddingCoords[1]) / static_cast<float>(imageSize.y());
float cropRight = static_cast<float>(paddingCoords[2]) / static_cast<float>(imageSize.x());
float cropBottom = static_cast<float>(paddingCoords[3]) / static_cast<float>(imageSize.y());
float cropLeft{static_cast<float>(paddingCoords[0]) / static_cast<float>(imageSize.x)};
float cropTop{static_cast<float>(paddingCoords[1]) / static_cast<float>(imageSize.y)};
float cropRight{static_cast<float>(paddingCoords[2]) / static_cast<float>(imageSize.x)};
float cropBottom{static_cast<float>(paddingCoords[3]) / static_cast<float>(imageSize.y)};
crop(cropLeft, cropTop, cropRight, cropBottom);
// Cropping the image obviously leads to a reduction in size, so we need to determine
// how much to scale up after cropping to keep within the max size restrictions that
// were passed as arguments.
mSize.x() -= mSize.x() * (cropLeft + cropRight);
mSize.y() -= mSize.y() * (cropTop + cropBottom);
mSize.x -= mSize.x * (cropLeft + cropRight);
mSize.y -= mSize.y * (cropTop + cropBottom);
float scaleFactor = originalSize.y() / mSize.y();
float scaleFactor = originalSize.y / mSize.y;
if (scaleFactor * mSize.x() < maxSizeX)
scaleFactor = maxSizeX / mSize.x();
if (scaleFactor * mSize.x < maxSizeX)
scaleFactor = maxSizeX / mSize.x;
if (scaleFactor * mSize.y() < maxSizeY)
scaleFactor = maxSizeY / mSize.y();
if (scaleFactor * mSize.y < maxSizeY)
scaleFactor = maxSizeY / mSize.y;
if (scaleFactor * mSize.x() > maxSizeX)
scaleFactor = maxSizeX / mSize.x();
if (scaleFactor * mSize.x > maxSizeX)
scaleFactor = maxSizeX / mSize.x;
if (scaleFactor * mSize.y() > maxSizeY)
scaleFactor = maxSizeY / mSize.y();
if (scaleFactor * mSize.y > maxSizeY)
scaleFactor = maxSizeY / mSize.y;
setResize(mSize.x() * scaleFactor, mSize.y() * scaleFactor);
setResize(mSize.x * scaleFactor, mSize.y * scaleFactor);
updateVertices();
}
@ -343,23 +343,23 @@ void ImageComponent::updateVertices()
// We go through this mess to make sure everything is properly rounded.
// If we just round vertices at the end, edge cases occur near sizes of 0.5.
const Vector2f topLeft = { 0, 0 };
const Vector2f bottomRight = mSize;
const float px = mTexture->isTiled() ? mSize.x() / getTextureSize().x() : 1.0f;
const float py = mTexture->isTiled() ? mSize.y() / getTextureSize().y() : 1.0f;
const glm::vec2 topLeft{};
const glm::vec2 bottomRight{mSize};
const float px{mTexture->isTiled() ? mSize.x / getTextureSize().x : 1.0f};
const float py{mTexture->isTiled() ? mSize.y / getTextureSize().y : 1.0f};
// clang-format off
mVertices[0] = { { topLeft.x(), topLeft.y() }, { mTopLeftCrop.x(), py - mTopLeftCrop.y() }, 0 };
mVertices[1] = { { topLeft.x(), bottomRight.y() }, { mTopLeftCrop.x(), 1.0f - mBottomRightCrop.y() }, 0 };
mVertices[2] = { { bottomRight.x(), topLeft.y() }, { mBottomRightCrop.x() * px, py - mTopLeftCrop.y() }, 0 };
mVertices[3] = { { bottomRight.x(), bottomRight.y() }, { mBottomRightCrop.x() * px, 1.0f - mBottomRightCrop.y() }, 0 };
mVertices[0] = {{topLeft.x, topLeft.y }, {mTopLeftCrop.x, py - mTopLeftCrop.y }, 0};
mVertices[1] = {{topLeft.x, bottomRight.y}, {mTopLeftCrop.x, 1.0f - mBottomRightCrop.y}, 0};
mVertices[2] = {{bottomRight.x, topLeft.y }, {mBottomRightCrop.x * px, py - mTopLeftCrop.y }, 0};
mVertices[3] = {{bottomRight.x, bottomRight.y}, {mBottomRightCrop.x * px, 1.0f - mBottomRightCrop.y}, 0};
// clang-format on
updateColors();
// Round vertices.
for (int i = 0; i < 4; i++)
mVertices[i].pos.round();
mVertices[i].pos = glm::round(mVertices[i].pos);
if (mFlipX) {
for (int i = 0; i < 4; i++)
@ -387,23 +387,23 @@ void ImageComponent::updateColors()
mVertices[3].col = colorEnd;
}
void ImageComponent::render(const Transform4x4f& parentTrans)
void ImageComponent::render(const glm::mat4& parentTrans)
{
if (!isVisible())
return;
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
Renderer::setMatrix(trans);
if (mTexture && mOpacity > 0) {
if (Settings::getInstance()->getBool("DebugImage")) {
Vector2f targetSizePos = (mTargetSize - mSize) * mOrigin * -1;
Renderer::drawRect(targetSizePos.x(), targetSizePos.y(), mTargetSize.x(),
mTargetSize.y(), 0xFF000033, 0xFF000033);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
glm::vec2 targetSizePos{(mTargetSize - mSize) * mOrigin * glm::vec2{-1.0f}};
Renderer::drawRect(targetSizePos.x, targetSizePos.y, mTargetSize.x, mTargetSize.y,
0xFF000033, 0xFF000033);
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033);
}
// An image with zero size would normally indicate a corrupt image file.
if (mTexture->isInitialized() && mTexture->getSize() != 0) {
if (mTexture->isInitialized() && mTexture->getSize() != glm::ivec2{}) {
// Actually draw the image.
// The bind() function returns false if the texture is not currently loaded. A blank
// texture is bound in this case but we want to handle a fade so it doesn't just
@ -483,17 +483,17 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (!elem)
return;
Vector2f scale = getParent() ? getParent()->getSize() :
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
glm::vec2 scale{getParent() ? getParent()->getSize() :
glm::vec2(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()))};
if (properties & ThemeFlags::SIZE) {
if (elem->has("size"))
setResize(elem->get<Vector2f>("size") * scale);
setResize(elem->get<glm::vec2>("size") * scale);
else if (elem->has("maxSize"))
setMaxSize(elem->get<Vector2f>("maxSize") * scale);
setMaxSize(elem->get<glm::vec2>("maxSize") * scale);
else if (elem->has("minSize"))
setMinSize(elem->get<Vector2f>("minSize") * scale);
setMinSize(elem->get<glm::vec2>("minSize") * scale);
}
if (elem->has("default"))

View file

@ -10,7 +10,6 @@
#define ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
#include "GuiComponent.h"
#include "math/Vector2i.h"
#include "renderers/Renderer.h"
class TextureResource;
@ -25,7 +24,7 @@ public:
// Loads the image at the given filepath. Will tile if tile is true (retrieves texture
// as tiling, creates vertices accordingly).
void setImage(std::string path, bool tile = false);
void setImage(std::string path, bool tile = false, bool linearMagnify = false);
// Loads an image from memory.
void setImage(const char* data, size_t length, bool tile = false);
// Use an already existing texture.
@ -39,18 +38,18 @@ public:
// Can be set before or after an image is loaded.
// setMaxSize() and setResize() are mutually exclusive.
void setResize(float width, float height) override;
void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
void setResize(const glm::vec2& size) { setResize(size.x, size.y); }
// Resize the image to be as large as possible but fit within a box of this size.
// Can be set before or after an image is loaded.
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
void setMaxSize(float width, float height);
void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
void setMaxSize(const glm::vec2& size) { setMaxSize(size.x, size.y); }
void setMinSize(float width, float height);
void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); }
void setMinSize(const glm::vec2& size) { setMinSize(size.x, size.y); }
Vector2f getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; }
glm::vec2 getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; }
// Applied AFTER image positioning and sizing.
// cropTop(0.2) will crop 20% of the top of the image.
@ -83,14 +82,14 @@ public:
// Returns the size of the current texture, or (0, 0) if none is loaded.
// May be different than drawn size (use getSize() for that).
Vector2i getTextureSize() const;
glm::ivec2 getTextureSize() const;
Vector2f getSize() const override;
glm::vec2 getSize() const override;
bool hasImage() { return static_cast<bool>(mTexture); }
std::shared_ptr<TextureResource> getTexture() { return mTexture; }
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
@ -100,7 +99,7 @@ public:
virtual std::vector<HelpPrompt> getHelpPrompts() override;
private:
Vector2f mTargetSize;
glm::vec2 mTargetSize;
bool mFlipX, mFlipY, mTargetIsMax, mTargetIsMin;
@ -127,8 +126,8 @@ private:
bool mDynamic;
bool mRotateByTargetSize;
Vector2f mTopLeftCrop;
Vector2f mBottomRightCrop;
glm::vec2 mTopLeftCrop;
glm::vec2 mBottomRightCrop;
};
#endif // ES_CORE_COMPONENTS_IMAGE_COMPONENT_H

View file

@ -61,7 +61,7 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
@ -98,13 +98,13 @@ private:
// Tiles.
bool mLastRowPartial;
Vector2f mAutoLayout;
glm::vec2 mAutoLayout;
float mAutoLayoutZoom;
Vector4f mPadding;
Vector2f mMargin;
Vector2f mTileSize;
Vector2i mGridDimension;
glm::vec4 mPadding;
glm::vec2 mMargin;
glm::vec2 mTileSize;
glm::ivec2 mGridDimension;
std::shared_ptr<ThemeData> mTheme;
std::vector<std::shared_ptr<GridTileComponent>> mTiles;
@ -126,14 +126,14 @@ template <typename T>
ImageGridComponent<T>::ImageGridComponent(Window* window)
: IList<ImageGridData, T>(window)
{
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
glm::vec2 screen{static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())};
mCamera = 0.0;
mCameraDirection = 1.0;
mCamera = 0.0f;
mCameraDirection = 1.0f;
mAutoLayout = Vector2f::Zero();
mAutoLayoutZoom = 1.0;
mAutoLayout = glm::vec2{};
mAutoLayoutZoom = 1.0f;
mStartPosition = 0;
@ -144,7 +144,7 @@ ImageGridComponent<T>::ImageGridComponent(Window* window)
mSize = screen * 0.80f;
mMargin = screen * 0.07f;
mPadding = Vector4f::Zero();
mPadding = {};
mTileSize = GridTileComponent::getDefaultTileSize();
mAnimate = true;
@ -171,7 +171,7 @@ template <typename T> bool ImageGridComponent<T>::input(InputConfig* config, Inp
if (input.value != 0) {
int idx = isVertical() ? 0 : 1;
Vector2i dir = Vector2i::Zero();
glm::ivec2 dir{};
if (config->isMappedLike("up", input))
dir[1 ^ idx] = -1;
else if (config->isMappedLike("down", input))
@ -181,11 +181,11 @@ template <typename T> bool ImageGridComponent<T>::input(InputConfig* config, Inp
else if (config->isMappedLike("right", input))
dir[0 ^ idx] = 1;
if (dir != Vector2i::Zero()) {
if (dir != glm::ivec2{}) {
if (isVertical())
listInput(dir.x() + dir.y() * mGridDimension.x());
listInput(dir.x + dir.y * mGridDimension.x);
else
listInput(dir.x() + dir.y() * mGridDimension.y());
listInput(dir.x + dir.y * mGridDimension.y);
return true;
}
}
@ -208,17 +208,15 @@ template <typename T> void ImageGridComponent<T>::update(int deltaTime)
(*it)->update(deltaTime);
}
template <typename T> void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
template <typename T> void ImageGridComponent<T>::render(const glm::mat4& parentTrans)
{
Transform4x4f trans = getTransform() * parentTrans;
Transform4x4f tileTrans = trans;
glm::mat4 trans{getTransform() * parentTrans};
glm::mat4 tileTrans{trans};
float offsetX =
isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x() + mMargin.x());
float offsetY =
isVertical() ? mCamera * mCameraDirection * (mTileSize.y() + mMargin.y()) : 0.0f;
float offsetX{isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x + mMargin.x)};
float offsetY{isVertical() ? mCamera * mCameraDirection * (mTileSize.y + mMargin.y) : 0.0f};
tileTrans.translate(Vector3f(offsetX, offsetY, 0.0));
tileTrans = glm::translate(tileTrans, glm::vec3{offsetX, offsetY, 0.0f});
if (mEntriesDirty) {
updateTiles();
@ -226,13 +224,13 @@ template <typename T> void ImageGridComponent<T>::render(const Transform4x4f& pa
}
// Create a clipRect to hide tiles used to buffer texture loading.
float scaleX = trans.r0().x();
float scaleY = trans.r1().y();
float scaleX = trans[0].x;
float scaleY = trans[1].y;
Vector2i pos(static_cast<int>(std::round(trans.translation()[0])),
static_cast<int>(std::round(trans.translation()[1])));
Vector2i size(static_cast<int>(std::round(mSize.x() * scaleX)),
static_cast<int>(std::round(mSize.y() * scaleY)));
glm::ivec2 pos{static_cast<int>(std::round(trans[3].x)),
static_cast<int>(std::round(trans[3].y))};
glm::ivec2 size{static_cast<int>(std::round(mSize.x * scaleX)),
static_cast<int>(std::round(mSize.y * scaleY))};
Renderer::pushClipRect(pos, size);
@ -271,20 +269,20 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
// Keep the theme pointer to apply it on the tiles later on.
mTheme = theme;
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
glm::vec2 screen{static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())};
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid");
if (elem) {
if (elem->has("margin"))
mMargin = elem->get<Vector2f>("margin") * screen;
mMargin = elem->get<glm::vec2>("margin") * screen;
if (elem->has("padding"))
mPadding = elem->get<Vector4f>("padding") *
Vector4f(screen.x(), screen.y(), screen.x(), screen.y());
mPadding =
elem->get<glm::vec4>("padding") * glm::vec4{screen.x, screen.y, screen.x, screen.y};
if (elem->has("autoLayout"))
mAutoLayout = elem->get<Vector2f>("autoLayout");
mAutoLayout = elem->get<glm::vec2>("autoLayout");
if (elem->has("autoLayoutSelectedZoom"))
mAutoLayoutZoom = elem->get<float>("autoLayoutSelectedZoom");
@ -369,7 +367,7 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
// grid dimension, and then (re)build the tiles.
elem = theme->getElement(view, "default", "gridtile");
mTileSize = elem && elem->has("size") ? elem->get<Vector2f>("size") * screen :
mTileSize = elem && elem->has("size") ? elem->get<glm::vec2>("size") * screen :
GridTileComponent::getDefaultTileSize();
// Apply size property which will trigger a call to onSizeChanged() which will build the tiles.
@ -401,8 +399,8 @@ template <typename T> void ImageGridComponent<T>::onCursorChanged(const CursorSt
int oldStart = mStartPosition;
int dimScrollable = (isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
int dimOpposite = isVertical() ? mGridDimension.x() : mGridDimension.y();
int dimScrollable = (isVertical() ? mGridDimension.y : mGridDimension.x) - 2 * EXTRAITEMS;
int dimOpposite = isVertical() ? mGridDimension.x : mGridDimension.y;
int centralCol = static_cast<int>((static_cast<float>(dimScrollable) - 0.5f) / 2.0f);
int maxCentralCol = dimScrollable / 2;
@ -449,7 +447,7 @@ template <typename T> void ImageGridComponent<T>::onCursorChanged(const CursorSt
}
}
Vector3f oldPos = Vector3f::Zero();
glm::vec3 oldPos{};
if (oldTile != nullptr && oldTile != newTile) {
oldPos = oldTile->getBackgroundPosition();
@ -457,7 +455,7 @@ template <typename T> void ImageGridComponent<T>::onCursorChanged(const CursorSt
}
if (newTile != nullptr)
newTile->setSelected(true, true, oldPos == Vector3f::Zero() ? nullptr : &oldPos, true);
newTile->setSelected(true, true, oldPos == glm::vec3{} ? nullptr : &oldPos, true);
}
int firstVisibleCol = mStartPosition / dimOpposite;
@ -506,8 +504,8 @@ template <typename T> void ImageGridComponent<T>::onCursorChanged(const CursorSt
if (!moveCamera)
return;
t -= 1.0f; // Cubic ease out.
float pct = Math::lerp(0, 1.0f, t * t * t + 1.0f);
t -= 1.0f;
float pct = glm::mix(0.0f, 1.0f, t * t * t + 1.0f);
t = startPos * (1.0f - pct) + endPos * pct;
mCamera = t;
};
@ -530,35 +528,33 @@ template <typename T> void ImageGridComponent<T>::buildTiles()
calcGridDimension();
if (mCenterSelection) {
int dimScrollable =
(isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
int dimScrollable = (isVertical() ? mGridDimension.y : mGridDimension.x) - 2 * EXTRAITEMS;
mStartPosition -= static_cast<int>(floorf(dimScrollable / 2.0f));
}
Vector2f tileDistance = mTileSize + mMargin;
glm::vec2 tileDistance{mTileSize + mMargin};
if (mAutoLayout.x() != 0.0f && mAutoLayout.y() != 0.0f) {
auto x =
(mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1.0f)) - mPadding.x() - mPadding.z()) /
static_cast<int>(mAutoLayout.x());
auto y =
(mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1.0f)) - mPadding.y() - mPadding.w()) /
static_cast<int>(mAutoLayout.y());
if (mAutoLayout.x != 0.0f && mAutoLayout.y != 0.0f) {
auto x = (mSize.x - (mMargin.x * (mAutoLayout.x - 1.0f)) - mPadding.x - mPadding.z) /
static_cast<int>(mAutoLayout.x);
auto y = (mSize.y - (mMargin.y * (mAutoLayout.y - 1.0f)) - mPadding.y - mPadding.w) /
static_cast<int>(mAutoLayout.y);
mTileSize = Vector2f(x, y);
mTileSize = glm::vec2{x, y};
tileDistance = mTileSize + mMargin;
}
bool vert = isVertical();
Vector2f startPosition = mTileSize / 2.0f;
startPosition += mPadding.v2();
glm::vec2 startPosition{mTileSize / 2.0f};
startPosition.x += mPadding.x;
startPosition.y += mPadding.y;
int X;
int Y;
// Layout tile size and position.
for (int y = 0; y < (vert ? mGridDimension.y() : mGridDimension.x()); y++) {
for (int x = 0; x < (vert ? mGridDimension.x() : mGridDimension.y()); x++) {
for (int y = 0; y < (vert ? mGridDimension.y : mGridDimension.x); y++) {
for (int x = 0; x < (vert ? mGridDimension.x : mGridDimension.y); x++) {
// Create tiles.
auto tile = std::make_shared<GridTileComponent>(mWindow);
@ -567,15 +563,15 @@ template <typename T> void ImageGridComponent<T>::buildTiles()
X = vert ? x : y - EXTRAITEMS;
Y = vert ? y - EXTRAITEMS : x;
tile->setPosition(X * tileDistance.x() + startPosition.x(),
Y * tileDistance.y() + startPosition.y());
tile->setPosition(X * tileDistance.x + startPosition.x,
Y * tileDistance.y + startPosition.y);
tile->setOrigin(0.5f, 0.5f);
tile->setImage("");
if (mTheme)
tile->applyTheme(mTheme, "grid", "gridtile", ThemeFlags::ALL);
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
if (mAutoLayout.x != 0 && mAutoLayout.y != 0.0f)
tile->forceSize(mTileSize, mAutoLayoutZoom);
mTiles.push_back(tile);
@ -617,7 +613,7 @@ void ImageGridComponent<T>::updateTiles(bool ascending,
int end = ascending ? static_cast<int>(mTiles.size()) : -1;
int img = mStartPosition + ti;
img -= EXTRAITEMS * (isVertical() ? mGridDimension.x() : mGridDimension.y());
img -= EXTRAITEMS * (isVertical() ? mGridDimension.x : mGridDimension.y);
// Update the tiles.
while (ti != end) {
@ -677,7 +673,7 @@ void ImageGridComponent<T>::updateTileAtPos(int tilePos,
if (idx < 0 || idx >= mTiles.size())
idx = 0;
Vector3f pos = mTiles.at(idx)->getBackgroundPosition();
glm::vec3 pos{mTiles.at(idx)->getBackgroundPosition()};
tile->setSelected(true, allowAnimation, &pos);
}
else {
@ -693,30 +689,30 @@ template <typename T> void ImageGridComponent<T>::calcGridDimension()
{
// grid_size = columns * tile_size + (columns - 1) * margin
// <=> columns = (grid_size + margin) / (tile_size + margin)
Vector2f gridDimension = (mSize + mMargin) / (mTileSize + mMargin);
glm::vec2 gridDimension{(mSize + mMargin) / (mTileSize + mMargin)};
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
if (mAutoLayout.x != 0.0f && mAutoLayout.y != 0.0f)
gridDimension = mAutoLayout;
mLastRowPartial = floorf(gridDimension.y()) != gridDimension.y();
mLastRowPartial = floorf(gridDimension.y) != gridDimension.y;
// Ceil y dim so we can display partial last row.
mGridDimension = Vector2i(static_cast<const int>(gridDimension.x()),
static_cast<const int>(ceilf(gridDimension.y())));
mGridDimension = glm::ivec2{static_cast<const int>(gridDimension.x),
static_cast<const int>(ceilf(gridDimension.y))};
// Grid dimension validation.
if (mGridDimension.x() < 1) {
if (mGridDimension.x < 1) {
LOG(LogError) << "Theme defined grid X dimension below 1";
}
if (mGridDimension.y() < 1) {
if (mGridDimension.y < 1) {
LOG(LogError) << "Theme defined grid Y dimension below 1";
}
// Add extra tiles to both sides.
if (isVertical())
mGridDimension.y() += 2 * EXTRAITEMS;
mGridDimension.y += 2 * EXTRAITEMS;
else
mGridDimension.x() += 2 * EXTRAITEMS;
mGridDimension.x += 2 * EXTRAITEMS;
}
template <typename T> bool ImageGridComponent<T>::isScrollLoop()
@ -724,8 +720,8 @@ template <typename T> bool ImageGridComponent<T>::isScrollLoop()
if (!mScrollLoop)
return false;
if (isVertical())
return (mGridDimension.x() * (mGridDimension.y() - 2 * EXTRAITEMS)) <= mEntries.size();
return (mGridDimension.y() * (mGridDimension.x() - 2 * EXTRAITEMS)) <= mEntries.size();
return (mGridDimension.x * (mGridDimension.y - 2 * EXTRAITEMS)) <= mEntries.size();
return (mGridDimension.y * (mGridDimension.x - 2 * EXTRAITEMS)) <= mEntries.size();
}
#endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H

View file

@ -21,7 +21,7 @@ MenuComponent::MenuComponent(Window* window,
const std::shared_ptr<Font>& titleFont)
: GuiComponent(window)
, mBackground(window)
, mGrid(window, Vector2i(1, 3))
, mGrid(window, glm::ivec2{1, 3})
, mNeedsSaving(false)
{
addChild(&mBackground);
@ -34,11 +34,11 @@ MenuComponent::MenuComponent(Window* window,
mTitle->setHorizontalAlignment(ALIGN_CENTER);
mTitle->setColor(0x555555FF);
setTitle(title, titleFont);
mGrid.setEntry(mTitle, Vector2i(0, 0), false);
mGrid.setEntry(mTitle, glm::ivec2{}, false);
// Set up list which will never change (externally, anyway).
mList = std::make_shared<ComponentList>(mWindow);
mGrid.setEntry(mList, Vector2i(0, 1), true);
mGrid.setEntry(mList, glm::ivec2{0, 1}, true);
updateGrid();
updateSize();
@ -74,7 +74,7 @@ void MenuComponent::setTitle(std::string title, const std::shared_ptr<Font>& fon
float MenuComponent::getButtonGridHeight() const
{
return (mButtonGrid ? mButtonGrid->getSize().y() :
return (mButtonGrid ? mButtonGrid->getSize().y :
Font::get(FONT_SIZE_MEDIUM)->getHeight() +
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()));
}
@ -106,11 +106,11 @@ void MenuComponent::updateSize()
void MenuComponent::onSizeChanged()
{
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
// Update grid row/column sizes.
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y());
mGrid.setRowHeightPerc(2, getButtonGridHeight() / mSize.y());
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y);
mGrid.setRowHeightPerc(2, getButtonGridHeight() / mSize.y);
mGrid.setSize(mSize);
}
@ -134,7 +134,7 @@ void MenuComponent::updateGrid()
if (mButtons.size()) {
mButtonGrid = makeButtonGrid(mWindow, mButtons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 2}, true, false);
}
}
@ -142,26 +142,27 @@ std::shared_ptr<ComponentGrid> makeButtonGrid(
Window* window, const std::vector<std::shared_ptr<ButtonComponent>>& buttons)
{
std::shared_ptr<ComponentGrid> buttonGrid =
std::make_shared<ComponentGrid>(window, Vector2i(static_cast<int>(buttons.size()), 2));
std::make_shared<ComponentGrid>(window, glm::ivec2{static_cast<int>(buttons.size()), 2});
// Initialize to padding.
float buttonGridWidth =
BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier() * buttons.size();
for (int i = 0; i < static_cast<int>(buttons.size()); i++) {
buttonGrid->setEntry(buttons.at(i), Vector2i(i, 0), true, false);
buttonGridWidth += buttons.at(i)->getSize().x();
buttonGrid->setEntry(buttons.at(i), glm::ivec2{i, 0}, true, false);
buttonGridWidth += buttons.at(i)->getSize().x;
}
for (unsigned int i = 0; i < buttons.size(); i++)
buttonGrid->setColWidthPerc(
i, (buttons.at(i)->getSize().x() +
i, (buttons.at(i)->getSize().x +
BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier()) /
buttonGridWidth);
buttonGrid->setSize(buttonGridWidth,
buttons.at(0)->getSize().y() +
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()) + 2);
buttons.at(0)->getSize().y +
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()) +
2.0f);
// Spacer row to deal with dropshadow to make buttons look centered.
buttonGrid->setRowHeightPerc(1, 2 / buttonGrid->getSize().y());
buttonGrid->setRowHeightPerc(1, 2.0f / buttonGrid->getSize().y);
return buttonGrid;
}

View file

@ -55,58 +55,58 @@ void NinePatchComponent::buildVertices()
// (e.g. from 720p to 4K) will be within these boundaries though.
float scaleFactor;
if (Renderer::getScreenWidth() > Renderer::getScreenHeight())
scaleFactor = Math::clamp(Renderer::getScreenHeightModifier(), 0.4f, 3.0f);
scaleFactor = glm::clamp(Renderer::getScreenHeightModifier(), 0.4f, 3.0f);
else
scaleFactor = Math::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f);
scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f);
mTexture = TextureResource::get(mPath, false, false, true, scaleFactor);
mTexture = TextureResource::get(mPath, false, false, true, true, scaleFactor);
if (mTexture->getSize() == Vector2i::Zero()) {
if (mTexture->getSize() == glm::ivec2{}) {
mVertices = nullptr;
LOG(LogWarning) << "NinePatchComponent has no texture";
return;
}
Vector2f texSize;
mVertices = new Renderer::Vertex[6 * 9];
texSize = Vector2f(static_cast<float>(mTexture->getSize().x()),
static_cast<float>(mTexture->getSize().y()));
glm::vec2 texSize{static_cast<float>(mTexture->getSize().x),
static_cast<float>(mTexture->getSize().y)};
// clang-format off
const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2.0f, mCornerSize.x() };
const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2.0f, mCornerSize.y() };
const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1] };
const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[1] };
const float imgSizeX[3]{mCornerSize.x, mSize.x - mCornerSize.x * 2.0f, mCornerSize.x};
const float imgSizeY[3]{mCornerSize.y, mSize.y - mCornerSize.y * 2.0f, mCornerSize.y};
const float imgPosX[3]{0, imgSizeX[0], imgSizeX[0] + imgSizeX[1]};
const float imgPosY[3]{0, imgSizeY[0], imgSizeY[0] + imgSizeY[1]};
// The "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom
// left corner origin vs. verticies having a top left origin.
const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2.0f) / texSize.x(), mCornerSize.x() / texSize.x() };
const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2.0f) / texSize.y(), -mCornerSize.y() / texSize.y() };
const float texPosX[3] = { 0.0f, texSizeX[0], texSizeX[0] + texSizeX[1] };
const float texPosY[3] = { 1.0f, 1.0f + texSizeY[0], 1.0f + texSizeY[0] + texSizeY[1] };
// clang-format off
const float texSizeX[3]{mCornerSize.x / texSize.x, (texSize.x - mCornerSize.x * 2.0f) / texSize.x, mCornerSize.x / texSize.x};
const float texSizeY[3]{-mCornerSize.y / texSize.y, -(texSize.y - mCornerSize.y * 2.0f) / texSize.y, -mCornerSize.y / texSize.y};
const float texPosX[3]{0.0f, texSizeX[0], texSizeX[0] + texSizeX[1]};
const float texPosY[3]{1.0f, 1.0f + texSizeY[0], 1.0f + texSizeY[0] + texSizeY[1]};
// clang-format on
int v = 0;
for (int slice = 0; slice < 9; slice++) {
const int sliceX = slice % 3;
const int sliceY = slice / 3;
const Vector2f imgPos = Vector2f(imgPosX[sliceX], imgPosY[sliceY]);
const Vector2f imgSize = Vector2f(imgSizeX[sliceX], imgSizeY[sliceY]);
const Vector2f texPos = Vector2f(texPosX[sliceX], texPosY[sliceY]);
const Vector2f texSize = Vector2f(texSizeX[sliceX], texSizeY[sliceY]);
const int sliceX{slice % 3};
const int sliceY{slice / 3};
const glm::vec2 imgPos{imgPosX[sliceX], imgPosY[sliceY]};
const glm::vec2 imgSize{imgSizeX[sliceX], imgSizeY[sliceY]};
const glm::vec2 texPos{texPosX[sliceX], texPosY[sliceY]};
const glm::vec2 texSize{texSizeX[sliceX], texSizeY[sliceY]};
// clang-format off
mVertices[v + 1] = { { imgPos.x() , imgPos.y() }, { texPos.x(), texPos.y() }, 0 };
mVertices[v + 2] = { { imgPos.x() , imgPos.y() + imgSize.y() }, { texPos.x(), texPos.y() + texSize.y() }, 0 };
mVertices[v + 3] = { { imgPos.x() + imgSize.x(), imgPos.y() }, { texPos.x() + texSize.x(), texPos.y() }, 0 };
mVertices[v + 4] = { { imgPos.x() + imgSize.x(), imgPos.y() + imgSize.y() }, { texPos.x() + texSize.x(), texPos.y() + texSize.y() }, 0 };
mVertices[v + 1] = {{imgPos.x , imgPos.y }, {texPos.x, texPos.y }, 0};
mVertices[v + 2] = {{imgPos.x , imgPos.y + imgSize.y}, {texPos.x, texPos.y + texSize.y}, 0};
mVertices[v + 3] = {{imgPos.x + imgSize.x, imgPos.y }, {texPos.x + texSize.x, texPos.y }, 0};
mVertices[v + 4] = {{imgPos.x + imgSize.x, imgPos.y + imgSize.y}, {texPos.x + texSize.x, texPos.y + texSize.y}, 0};
// clang-format on
// Round vertices.
for (int i = 1; i < 5; i++)
mVertices[v + i].pos.round();
mVertices[v + i].pos = glm::round(mVertices[v + i].pos);
// Make duplicates of first and last vertex so this can be rendered as a triangle strip.
mVertices[v + 0] = mVertices[v + 1];
@ -118,12 +118,12 @@ void NinePatchComponent::buildVertices()
updateColors();
}
void NinePatchComponent::render(const Transform4x4f& parentTrans)
void NinePatchComponent::render(const glm::mat4& parentTrans)
{
if (!isVisible())
return;
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
if (mTexture && mVertices != nullptr) {
Renderer::setMatrix(trans);
@ -144,17 +144,15 @@ void NinePatchComponent::render(const Transform4x4f& parentTrans)
renderChildren(trans);
}
void NinePatchComponent::onSizeChanged() { buildVertices(); }
void NinePatchComponent::fitTo(Vector2f size, Vector3f position, Vector2f padding)
void NinePatchComponent::fitTo(glm::vec2 size, glm::vec3 position, glm::vec2 padding)
{
size += padding;
position[0] -= padding.x() / 2.0f;
position[1] -= padding.y() / 2.0f;
position[0] -= padding.x / 2.0f;
position[1] -= padding.y / 2.0f;
setSize(size + mCornerSize * 2.0f);
setPosition(position.x() + Math::lerp(-mCornerSize.x(), mCornerSize.x(), mOrigin.x()),
position.y() + Math::lerp(-mCornerSize.y(), mCornerSize.y(), mOrigin.y()));
setPosition(position.x + glm::mix(-mCornerSize.x, mCornerSize.x, mOrigin.x),
position.y + glm::mix(-mCornerSize.y, mCornerSize.y, mOrigin.y));
}
void NinePatchComponent::setImagePath(const std::string& path)

View file

@ -35,13 +35,10 @@ public:
unsigned int centerColor = 0xFFFFFFFF);
virtual ~NinePatchComponent();
void render(const Transform4x4f& parentTrans) override;
void render(const glm::mat4& parentTrans) override;
void onSizeChanged() override;
void fitTo(Vector2f size,
Vector3f position = Vector3f::Zero(),
Vector2f padding = Vector2f::Zero());
void onSizeChanged() override { buildVertices(); }
void fitTo(glm::vec2 size, glm::vec3 position = {}, glm::vec2 padding = {});
void setImagePath(const std::string& path);
// Apply a color shift to the "edge" parts of the ninepatch.
@ -54,8 +51,8 @@ public:
const std::string& element,
unsigned int properties) override;
const Vector2f& getCornerSize() const { return mCornerSize; }
void setCornerSize(const Vector2f& size)
const glm::vec2& getCornerSize() const { return mCornerSize; }
void setCornerSize(const glm::vec2& size)
{
mCornerSize = size;
buildVertices();
@ -68,7 +65,7 @@ private:
Renderer::Vertex* mVertices;
std::string mPath;
Vector2f mCornerSize;
glm::vec2 mCornerSize;
unsigned int mEdgeColor;
unsigned int mCenterColor;
std::shared_ptr<TextureResource> mTexture;

View file

@ -58,7 +58,7 @@ public:
addChild(&mRightArrow);
}
setSize(mLeftArrow.getSize().x() + mRightArrow.getSize().x(), font->getHeight());
setSize(mLeftArrow.getSize().x + mRightArrow.getSize().x, font->getHeight());
}
// Handles positioning/resizing of text and arrows.
@ -67,19 +67,19 @@ public:
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
if (mSize.x() < (mLeftArrow.getSize().x() + mRightArrow.getSize().x())) {
if (mSize.x < (mLeftArrow.getSize().x + mRightArrow.getSize().x)) {
LOG(LogWarning) << "OptionListComponent too narrow";
}
mText.setSize(mSize.x() - mLeftArrow.getSize().x() - mRightArrow.getSize().x(),
mText.setSize(mSize.x - mLeftArrow.getSize().x - mRightArrow.getSize().x,
mText.getFont()->getHeight());
// Position.
mLeftArrow.setPosition(0, (mSize.y() - mLeftArrow.getSize().y()) / 2);
mText.setPosition(mLeftArrow.getPosition().x() + mLeftArrow.getSize().x(),
(mSize.y() - mText.getSize().y()) / 2);
mRightArrow.setPosition(mText.getPosition().x() + mText.getSize().x(),
(mSize.y() - mRightArrow.getSize().y()) / 2);
mLeftArrow.setPosition(0.0f, (mSize.y - mLeftArrow.getSize().y) / 2.0f);
mText.setPosition(mLeftArrow.getPosition().x + mLeftArrow.getSize().x,
(mSize.y - mText.getSize().y) / 2.0f);
mRightArrow.setPosition(mText.getPosition().x + mText.getSize().x,
(mSize.y - mRightArrow.getSize().y) / 2.0f);
}
bool input(InputConfig* config, Input input) override
@ -223,7 +223,11 @@ private:
HelpStyle mHelpStyle;
void open() { mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName)); }
void open()
{
// Open the list popup.
mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName));
}
void onSelectedChanged()
{
@ -232,10 +236,10 @@ private:
std::stringstream ss;
ss << getSelectedObjects().size() << " SELECTED";
mText.setText(ss.str());
mText.setSize(0, mText.getSize().y());
setSize(mText.getSize().x() + mRightArrow.getSize().x() +
24 * Renderer::getScreenWidthModifier(),
mText.getSize().y());
mText.setSize(0, mText.getSize().y);
setSize(mText.getSize().x + mRightArrow.getSize().x +
24.0f * Renderer::getScreenWidthModifier(),
mText.getSize().y);
if (mParent) // Hack since there's no "on child size changed" callback.
mParent->onSizeChanged();
}
@ -244,11 +248,10 @@ private:
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
if (it->selected) {
mText.setText(Utils::String::toUpper(it->name));
mText.setSize(0, mText.getSize().y());
setSize(mText.getSize().x() + mLeftArrow.getSize().x() +
mRightArrow.getSize().x() +
mText.setSize(0.0f, mText.getSize().y);
setSize(mText.getSize().x + mLeftArrow.getSize().x + mRightArrow.getSize().x +
24.0f * Renderer::getScreenWidthModifier(),
mText.getSize().y());
mText.getSize().y);
if (mParent) // Hack since there's no "on child size changed" callback.
mParent->onSizeChanged();
break;
@ -356,7 +359,7 @@ private:
});
}
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f,
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x) / 2.0f,
Renderer::getScreenHeight() * 0.13f);
addChild(&mMenu);
}

View file

@ -25,7 +25,7 @@ RatingComponent::RatingComponent(Window* window, bool colorizeChanges)
mFilledTexture = TextureResource::get(":/graphics/star_filled.svg", true);
mUnfilledTexture = TextureResource::get(":/graphics/star_unfilled.svg", true);
mValue = 0.5f;
mSize = Vector2f(64.0f * NUM_RATING_STARS, 64.0f);
mSize = glm::vec2{64.0f * NUM_RATING_STARS, 64.0f};
updateVertices();
updateColors();
}
@ -96,13 +96,13 @@ void RatingComponent::setColorShift(unsigned int color)
void RatingComponent::onSizeChanged()
{
if (mSize.y() == 0)
mSize[1] = mSize.x() / NUM_RATING_STARS;
else if (mSize.x() == 0)
mSize[0] = mSize.y() * NUM_RATING_STARS;
if (mSize.y == 0.0f)
mSize.y = mSize.x / NUM_RATING_STARS;
else if (mSize.x == 0.0f)
mSize.x = mSize.y * NUM_RATING_STARS;
if (mSize.y() > 0) {
size_t heightPx = static_cast<size_t>(std::round(mSize.y()));
if (mSize.y > 0.0f) {
size_t heightPx = static_cast<size_t>(std::round(mSize.y));
if (mFilledTexture)
mFilledTexture->rasterizeAt(heightPx, heightPx);
if (mUnfilledTexture)
@ -115,21 +115,21 @@ void RatingComponent::onSizeChanged()
void RatingComponent::updateVertices()
{
const float numStars = NUM_RATING_STARS;
const float h = getSize().y(); // Ss the same as a single star's width.
const float w = getSize().y() * mValue * numStars;
const float fw = getSize().y() * numStars;
const float h = getSize().y; // Ss the same as a single star's width.
const float w = getSize().y * mValue * numStars;
const float fw = getSize().y * numStars;
const unsigned int color = Renderer::convertRGBAToABGR(mColorShift);
// clang-format off
mVertices[0] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color };
mVertices[1] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
mVertices[2] = { { w, 0.0f }, { mValue * numStars, 1.0f }, color };
mVertices[3] = { { w, h }, { mValue * numStars, 0.0f }, color };
mVertices[0] = {{0.0f, 0.0f}, {0.0f, 1.0f}, color};
mVertices[1] = {{0.0f, h }, {0.0f, 0.0f}, color};
mVertices[2] = {{w, 0.0f}, {mValue * numStars, 1.0f}, color};
mVertices[3] = {{w, h }, {mValue * numStars, 0.0f}, color};
mVertices[4] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color };
mVertices[5] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
mVertices[6] = { { fw, 0.0f }, { numStars, 1.0f }, color };
mVertices[7] = { { fw, h }, { numStars, 0.0f }, color };
mVertices[4] = {{0.0f, 0.0f}, {0.0f, 1.0f}, color};
mVertices[5] = {{0.0f, h }, {0.0f, 0.0f}, color};
mVertices[6] = {{fw, 0.0f}, {numStars, 1.0f}, color};
mVertices[7] = {{fw, h }, {numStars, 0.0f}, color};
// clang-format on
}
@ -141,18 +141,18 @@ void RatingComponent::updateColors()
mVertices[i].col = color;
}
void RatingComponent::render(const Transform4x4f& parentTrans)
void RatingComponent::render(const glm::mat4& parentTrans)
{
if (!isVisible() || mFilledTexture == nullptr || mUnfilledTexture == nullptr)
return;
Transform4x4f trans = parentTrans * getTransform();
glm::mat4 trans{parentTrans * getTransform()};
Renderer::setMatrix(trans);
if (mOpacity > 0) {
if (Settings::getInstance()->getBool("DebugImage")) {
Renderer::drawRect(0.0f, 0.0f, mSize.y() * NUM_RATING_STARS, mSize.y(), 0xFF000033,
Renderer::drawRect(0.0f, 0.0f, mSize.y * NUM_RATING_STARS, mSize.y, 0xFF000033,
0xFF000033);
}

Some files were not shown because too many files have changed in this diff Show more