diff --git a/CHANGELOG.md b/CHANGELOG.md index f21da3ca2..3069bc8f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * Populated the bundled es_systems.xml files with alternative emulator entries for most RetroArch cores * Added a virtual keyboard, partly based on code from batocera-emulationstation * Added badges that indicate favorite/completed/broken games as well as games suitable for children and those with a selected alternative emulator +* Added game-specific controller images that are selectable via the metadata editor and displayed as a controller badge * Added the ability to make complementary game system customizations without having to replace the entire bundled es_systems.xml file * Added support for an optional \ tag for es_systems.xml that can be used to override the default \ systems sorting * Added menu scroll indicators showing if there are additional entries available below or above what's currently shown on screen @@ -80,6 +81,7 @@ * Under some circumstances ScrollableContainer (used for the game descriptions) would contain a partially rendered bottom line * If the TextListComponent height was not evenly dividable by the font height + line spacing, a partial bottom row would get rendered * The line spacing for TextListComponent was incorrectly calculated for some resolutions such as 2560x1440 +* Fixed multiple issues with scaling of images which lead to various inconsistencies and sometimes cut-off graphics * Removing games from custom collections did not remove their filter index entries * Input consisting of only whitespace characters would get accepted by TextEditComponent which led to various strange behaviors * Leading and trailing whitespace characters would not get trimmed from the collection name when creating a new custom collection @@ -98,6 +100,8 @@ * Really long theme set names would not get abbreviated in the UI settings menu, leading to a garbled "Theme set" setting row * Disabling a collection while its gamelist was displayed would lead to a slide transition from a black screen if a gamelist on startup had been set * When marking a game to not be counted in the metadata editor and the game was part of a custom collection, no collection disabling notification was displayed +* SliderComponent did not properly align the knob and bar vertically +* Resizing in SwitchComponent did not reposition the image properly leading to a non-centered image * Horizontal sizing of the TextComponent input field was not consistent across different screen resolutions * The sizing of the metadata editor was strange, which was clearly visible when activating the Ctrl+G debug mode * The "sortname" window header was incorrectly spelled when editing this type of entry in the metadata editor diff --git a/THEMES-DEV.md b/THEMES-DEV.md index 92d387bea..711424c46 100644 --- a/THEMES-DEV.md +++ b/THEMES-DEV.md @@ -921,6 +921,8 @@ ES-DE borrows the concept of "nine patches" from Android (or "9-Slices"). Curren #### badges +It's strongly recommended to use the same image dimensions for all badges as varying aspect ratios will lead to alignment issues. For the controller images it's recommended to keep to the square canvas size used by the default bundled graphics as otherwise sizing and placement will be inconsistent (unless all controller graphic files are customized of course). Overall it's a very good idea to keep the image dimensions small. This is especially true for SVG graphics as rasterization will otherwise take a long time which will slow down application startup and gamelist navigation. + * `pos` - type: NORMALIZED_PAIR. * `size` - type: NORMALIZED_PAIR. - Possible combinations: @@ -935,20 +937,26 @@ ES-DE borrows the concept of "nine patches" from Android (or "9-Slices"). Curren - Number of badges that fit on a row. When more badges are available a new row will be started. Default is `4`. * `rows` - type: FLOAT. - The number of rows available. Default is `2`. -* `itemPlacement` - type: STRING. - - Valid values are "top", "center", "bottom", or "stretch". Controls vertical alignment of each badge if images of different heights are used. "Stretch" will stretch the badge to the full height. Default is `center`. * `itemMargin` - type: NORMALIZED_PAIR. - The margins between badges. Possible combinations: - - `x y` - horizontal and vertical margins. Minimum value per axis is `0`, maximum value is `0.2`. Default is `0.01`. + - `x y` - horizontal and vertical margins. Minimum value per axis is `0`, maximum value is `0.2`. Default is `0.01 0.01`. If one of the axis is set to `-1` the margin of the other axis (in pixels) will be used, which makes it possible to get identical spacing between all items regardless of screen aspect ratio. * `slots` - type: STRING. - - The badge types that should be displayed. Should be specified as a list of strings separated by spaces. The order will be followed when placing badges on the screen. Available badges are: + - The badge types that should be displayed. Should be specified as a list of strings delimited by any characters of `\t\r\n ,` - that is, whitespace and commas. The order will be followed when placing badges on the screen. Available badges are: - `favorite`: Will be shown when the game is marked as favorite. - `completed`: Will be shown when the game is marked as completed. - `kidgame`: Will be shown when the game is marked as a kids game. - `broken`: Will be shown when the game is marked as broken. + - `controller`: Will be shown and overlaid by the corresponding controller icon if a controller type has been selected for the game using the metadata editor. - `altemulator`: Will be shown when an alternative emulator is setup for the game. +* `controllerPos` - type: NORMALIZED_PAIR. + - The position of the controller icon relative to the parent `controller` badge. Minimum value per axis is `-1.0`, maximum value is `2.0`. Default is `0.5 0.5` which centers the controller icon on the badge. +* `controllerSize` - type: FLOAT. + - The size of the controller icon relative to the parent `controller` badge. Minimum value is `0.1`, maximum value is `2.0`. Setting the value to `1.0` sizes the controller icon to the same width as the parent badge. The image aspect ratio is always maintained. * `customBadgeIcon` - type: PATH. - A badge icon override. Specify the badge type in the attribute `badge`. The available badges are the ones listed above. +* `customControllerIcon` - type: PATH. + - A controller icon override. Specify the controller type in the attribute `controller`. These are the available types: + - `gamepad_generic`, `gamepad_xbox`, `gamepad_playstation`, `gamepad_nintendo_nes`, `gamepad_nintendo_snes`, `gamepad_nintendo_64`, `joystick_generic`, `joystick_arcade_2_buttons`, `joystick_arcade_3_buttons`, `joystick_arcade_4_buttons`, `joystick_arcade_6_buttons`, `trackball_generic`, `lightgun_generic`, `lightgun_nintendo`, `keyboard_generic`, `mouse_generic`, `mouse_amiga`, `keyboard_mouse_generic`, `steering_wheel_generic`, `wii_remote_nintendo`, `wii_remote_nunchuck_nintendo`, `joycon_left_or_right_nintendo`, `joycon_pair_nintendo`, `unknown`. * `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. diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 6c944394a..483fb7a3b 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -215,7 +215,7 @@ In addition to the styles just described, there is a **Grid** view style as well If the theme supports it, there's a gamelist information field displayed in the gamelist view, showing the number of games for the system (total and favorites) as well as a folder icon if a folder has been entered. When applying any filters to the gamelist, the game counter is replaced with the amount of games filtered, as in 'filtered / total games', e.g. '19 / 77'. If there are game entries in the filter result that are marked not to be counted as games, the number of such files will be indicated as 'filtered + filtered non-games / total games', for example '23 + 4 / 77' indicating 23 normal games, 4 non-games out of a total of 77. Due to this approach it's theoretically possible that the combined filtered game amount exceeds the number of counted games in the collection, for instance '69 + 11 / 77'. This is not considered a bug and is so by design. This gamelist information field functionality is specific to EmulationStation Desktop Edition so older themes will not support this. -Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. +Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken, controller_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed. If a game-specific controller has been selected, the corresponding controller icon will be shown on the controller badge, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. ![alt text](images/es-de_gamelist_view.png "ES-DE Gamelist View") _The **Gamelist view** is where you browse the games for a specific system._ @@ -1476,6 +1476,10 @@ This option will hide most metadata fields as well as any badges. The intention A statistics counter that tracks how many times you have played the game. You normally don't need to touch this, but if you want to, the possibility is there. +**Controller** + +Contains a list of controller images that are built into ES-DE. The selected controller will be displayed as a badge if the current theme set support badges. This functionality is only cosmetic and will not affect the actual emulators, and it will not affect the controller input for ES-DE itself. + **Alternative emulator** _(files only)_ If the option _Enable alternative emulators per game_ has been enabled, there will be an entry shown where you can select between alternative emulators for the specific game. There is a similar _Alternative emulators_ entry under the _Other settings_ menu, but that will apply the selection to the entire game system. If you select an alternative for a specific game using the metadata editor, that will take precedence and override any system-wide emulator selection (the currently selected system-wide emulator will be clearly marked on the selection screen). The alternative emulators need to be defined in the es_systems.xml file, and if there are no alternatives available for the current system, this row in the metadata editor will be grayed out. If you select an alternative emulator and later remove its corresponding entry from the es_systems.xml file, an error notice will be shown on this row. In this case you have the option to remove the invalid entry. But even if there is an invalid entry, games will still launch using the default emulator while logging a warning message to the es_log.txt file. Apart from this, the emulator selection should hopefully be self-explanatory. diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 98a354e59..943099fcb 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -37,6 +37,7 @@ MetaDataDecl gameDecls[] = { {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"playcount", MD_INT, "0", false, "times played", "enter number of times played", false}, +{"controller", MD_CONTROLLER, "", false, "controller", "select controller", false}, {"altemulator", MD_ALT_EMULATOR, "", false, "alternative emulator", "select alternative emulator", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; @@ -57,6 +58,7 @@ MetaDataDecl folderDecls[] = { {"broken", MD_BOOL, "false", false, "broken/not working", "enter broken off/on", false}, {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, +{"controller", MD_CONTROLLER, "", false, "controller", "select controller", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; // clang-format on diff --git a/es-app/src/MetaData.h b/es-app/src/MetaData.h index 8f0ac5964..e7867e9e4 100644 --- a/es-app/src/MetaData.h +++ b/es-app/src/MetaData.h @@ -32,6 +32,7 @@ enum MetaDataType { // Specialized types. MD_MULTILINE_STRING, + MD_CONTROLLER, MD_ALT_EMULATOR, MD_PATH, MD_RATING, diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index defc04f04..e46c1bb62 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -53,6 +53,12 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, , mMediaFilesUpdated{false} , mInvalidEmulatorEntry{false} { + mGameControllers = BadgeComponent::getGameControllers(); + + // Remove the last "unknown" controller entry. + if (mGameControllers.size() > 1) + mGameControllers.pop_back(); + addChild(&mBackground); addChild(&mGrid); @@ -137,8 +143,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, ed = std::make_shared(window); // Make the switches slightly smaller. glm::vec2 switchSize{ed->getSize() * 0.9f}; - ed->setResize(switchSize.x, switchSize.y); - ed->setOrigin(-0.05f, -0.09f); + ed->setResize(ceilf(switchSize.x), switchSize.y); ed->setChangedColor(ICONCOLOR_USERMARKED); row.addElement(ed, false, true); @@ -175,6 +180,89 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, std::placeholders::_2); break; } + case MD_CONTROLLER: { + ed = std::make_shared(window, "", + Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), + 0x777777FF, ALIGN_RIGHT); + row.addElement(ed, true); + + auto spacer = std::make_shared(mWindow); + spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f); + row.addElement(spacer, false); + + auto bracket = std::make_shared(mWindow); + bracket->setImage(":/graphics/arrow.svg"); + bracket->setResize(glm::vec2{0.0f, lbl->getFont()->getLetterHeight()}); + row.addElement(bracket, false); + + const std::string title = iter->displayPrompt; + + // OK callback (apply new value to ed). + auto updateVal = [ed, originalValue](const std::string& newVal) { + ed->setValue(newVal); + if (newVal == BadgeComponent::getDisplayName(originalValue)) + ed->setColor(DEFAULT_TEXTCOLOR); + else + ed->setColor(TEXTCOLOR_USERMARKED); + }; + + row.makeAcceptInputHandler([this, title, scraperParams, ed, updateVal, + originalValue] { + GuiSettings* s = new GuiSettings(mWindow, title); + + for (auto controller : mGameControllers) { + std::string selectedLabel = ed->getValue(); + std::string label; + ComponentListRow row; + + std::shared_ptr labelText = std::make_shared( + mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + labelText->setSelectable(true); + labelText->setValue(controller.displayName); + + label = controller.displayName; + + row.addElement(labelText, true); + + row.makeAcceptInputHandler([s, updateVal, controller] { + updateVal(controller.displayName); + delete s; + }); + + // Select the row that corresponds to the selected label. + if (selectedLabel == label) + s->addRow(row, true); + else + s->addRow(row, false); + } + + // If a value is set, then display "Clear entry" as the last entry. + if (ed->getValue() != "") { + ComponentListRow row; + std::shared_ptr clearText = std::make_shared( + mWindow, ViewController::CROSSEDCIRCLE_CHAR + " CLEAR ENTRY", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + clearText->setSelectable(true); + row.addElement(clearText, true); + row.makeAcceptInputHandler([s, ed] { + ed->setValue(""); + delete s; + }); + s->addRow(row, false); + } + + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float maxWidthModifier = glm::clamp(0.64f * aspectValue, 0.42f, 0.92f); + float maxWidth = + static_cast(Renderer::getScreenWidth()) * maxWidthModifier; + + s->setMenuSize(glm::vec2{maxWidth, s->getMenuSize().y}); + s->setMenuPosition( + glm::vec3{(s->getSize().x - maxWidth) / 2.0f, mPosition.y, mPosition.z}); + mWindow->pushGui(s); + }); + break; + } case MD_ALT_EMULATOR: { mInvalidEmulatorEntry = false; @@ -296,11 +384,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, 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.64f * aspectValue, 0.42f, 0.92f); float maxWidth = static_cast(Renderer::getScreenWidth()) * maxWidthModifier; @@ -390,10 +474,19 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, assert(ed); mList->addRow(row); - if (iter->type == MD_ALT_EMULATOR && mInvalidEmulatorEntry == true) + if (iter->type == MD_ALT_EMULATOR && mInvalidEmulatorEntry == true) { ed->setValue(ViewController::EXCLAMATION_CHAR + " " + originalValue); - else + } + else if (iter->type == MD_CONTROLLER && mMetaData->get(iter->key) != "") { + std::string displayName = BadgeComponent::getDisplayName(mMetaData->get(iter->key)); + if (displayName != "unknown") + ed->setValue(displayName); + else + ed->setValue(ViewController::EXCLAMATION_CHAR + " " + mMetaData->get(iter->key)); + } + else { ed->setValue(mMetaData->get(iter->key)); + } mEditors.push_back(ed); } @@ -524,6 +617,13 @@ void GuiMetaDataEd::save() if (mMetaDataDecl.at(i).key == "altemulator" && mInvalidEmulatorEntry == true) continue; + if (mMetaDataDecl.at(i).key == "controller" && mEditors.at(i)->getValue() != "") { + std::string shortName = BadgeComponent::getShortName(mEditors.at(i)->getValue()); + if (shortName != "unknown") + mMetaData->set(mMetaDataDecl.at(i).key, shortName); + continue; + } + if (!showHiddenGames && mMetaDataDecl.at(i).key == "hidden" && mEditors.at(i)->getValue() != mMetaData->get("hidden")) hideGameWhileHidden = true; @@ -668,6 +768,12 @@ void GuiMetaDataEd::close() if (key == "altemulator" && mInvalidEmulatorEntry == true) continue; + if (mMetaDataDecl.at(i).key == "controller" && mEditors.at(i)->getValue() != "") { + std::string shortName = BadgeComponent::getShortName(mEditors.at(i)->getValue()); + if (shortName == "unknown" || mMetaDataValue == shortName) + continue; + } + if (mMetaDataValue != mEditorsValue) { metadataUpdated = true; break; diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index ec1793145..a4833c257 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -13,6 +13,7 @@ #include "GuiComponent.h" #include "MetaData.h" +#include "components/BadgeComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" #include "components/ScrollIndicatorComponent.h" @@ -60,6 +61,7 @@ private: ScraperSearchParams mScraperParams; + std::vector mGameControllers; std::vector> mEditors; std::vector mMetaDataDecl; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index b343e8b3c..5a0846ff0 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -104,7 +104,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) // Badges. addChild(&mBadges); - mBadges.setOrigin(0.0f, 0.0f); + mBadges.setOrigin(0.5f, 0.5f); mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f); mBadges.setSize(mSize.x * 0.15f, mSize.y * 0.2f); mBadges.setDefaultZIndex(50.0f); @@ -410,15 +410,23 @@ void DetailedGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); // Populate the badge slots based on game metadata. - std::vector badgeSlots; + std::vector badgeSlots; for (auto badge : mBadges.getBadgeTypes()) { - if (badge == "altemulator") { + BadgeComponent::BadgeInfo badgeInfo; + badgeInfo.badgeType = badge; + if (badge == "controller") { + if (file->metadata.get("controller").compare("") != 0) { + badgeInfo.gameController = file->metadata.get("controller"); + badgeSlots.push_back(badgeInfo); + } + } + else if (badge == "altemulator") { if (file->metadata.get(badge).compare("") != 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } else { if (file->metadata.get(badge).compare("true") == 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } } mBadges.setBadges(badgeSlots); diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index a6033b2f6..ed4db80c2 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -9,7 +9,7 @@ #ifndef ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/DateTimeComponent.h" #include "components/RatingComponent.h" #include "components/ScrollableContainer.h" @@ -56,7 +56,7 @@ private: DateTimeComponent mLastPlayed; TextComponent mPlayCount; TextComponent mName; - BadgesComponent mBadges; + BadgeComponent mBadges; std::vector getMDLabels(); std::vector getMDValues(); diff --git a/es-app/src/views/gamelist/GridGameListView.h b/es-app/src/views/gamelist/GridGameListView.h index 2c2f50458..f84d52cf7 100644 --- a/es-app/src/views/gamelist/GridGameListView.h +++ b/es-app/src/views/gamelist/GridGameListView.h @@ -9,7 +9,7 @@ #ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/DateTimeComponent.h" #include "components/ImageGridComponent.h" #include "components/RatingComponent.h" @@ -89,7 +89,7 @@ private: TextComponent mLblLastPlayed; TextComponent mLblPlayCount; - BadgesComponent mBadges; + BadgeComponent mBadges; RatingComponent mRating; DateTimeComponent mReleaseDate; TextComponent mDeveloper; diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index c0f1c1bc4..ef76d29e0 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -121,7 +121,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) // Badges. addChild(&mBadges); - mBadges.setOrigin(0.0f, 0.0f); + mBadges.setOrigin(0.5f, 0.5f); mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f); mBadges.setSize(mSize.x * 0.15f, mSize.y * 0.2f); mBadges.setDefaultZIndex(50.0f); @@ -450,15 +450,23 @@ void VideoGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); // Populate the badge slots based on game metadata. - std::vector badgeSlots; + std::vector badgeSlots; for (auto badge : mBadges.getBadgeTypes()) { - if (badge == "altemulator") { + BadgeComponent::BadgeInfo badgeInfo; + badgeInfo.badgeType = badge; + if (badge == "controller") { + if (file->metadata.get("controller").compare("") != 0) { + badgeInfo.gameController = file->metadata.get("controller"); + badgeSlots.push_back(badgeInfo); + } + } + else if (badge == "altemulator") { if (file->metadata.get(badge).compare("") != 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } else { if (file->metadata.get(badge).compare("true") == 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } } mBadges.setBadges(badgeSlots); diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index f5cae00d0..06d1c1034 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -9,7 +9,7 @@ #ifndef ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/DateTimeComponent.h" #include "components/RatingComponent.h" #include "components/ScrollableContainer.h" @@ -60,7 +60,7 @@ private: DateTimeComponent mLastPlayed; TextComponent mPlayCount; TextComponent mName; - BadgesComponent mBadges; + BadgeComponent mBadges; std::vector getMDLabels(); std::vector getMDValues(); diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index 6949f2a32..d579317c6 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -34,7 +34,7 @@ set(CORE_HEADERS # GUI components ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgesComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgeComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BusyComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.h @@ -112,7 +112,7 @@ set(CORE_SOURCES # GUI components ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgesComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgeComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BusyComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.cpp diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 8c22d1968..63039fceb 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -155,10 +155,12 @@ std::map> The {"alignment", STRING}, {"itemsPerRow", FLOAT}, {"rows", FLOAT}, - {"itemPlacement", STRING}, {"itemMargin", NORMALIZED_PAIR}, {"slots", STRING}, + {"controllerPos", NORMALIZED_PAIR}, + {"controllerSize", FLOAT}, {"customBadgeIcon", PATH}, + {"customControllerIcon", PATH}, {"visible", BOOLEAN}, {"zIndex", FLOAT}}}, {"sound", {{"path", PATH}}}, @@ -520,21 +522,29 @@ void ThemeData::parseElement(const pugi::xml_node& root, } // Special parsing instruction for recurring options. - // Store as it's attribute to prevent nodes overwriting each other. + // Store as its 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) + const auto button = node.attribute("button").as_string(""); + if (strcmp(button, "") == 0) LOG(LogError) << " element requires the `button` property."; else - element.properties[btn] = path; + element.properties[button] = path; } else if (strcmp(node.name(), "customBadgeIcon") == 0) { - const auto btn = node.attribute("badge").as_string(""); - if (strcmp(btn, "") == 0) + const auto badge = node.attribute("badge").as_string(""); + if (strcmp(badge, "") == 0) LOG(LogError) << " element requires the `badge` property."; else - element.properties[btn] = path; + element.properties[badge] = path; + } + else if (strcmp(node.name(), "customControllerIcon") == 0) { + const auto controller = node.attribute("controller").as_string(""); + if (strcmp(controller, "") == 0) + LOG(LogError) + << " element requires the `controller` property."; + else + element.properties[controller] = path; } else element.properties[node.name()] = path; diff --git a/es-core/src/components/BadgeComponent.cpp b/es-core/src/components/BadgeComponent.cpp new file mode 100644 index 000000000..5402b62a9 --- /dev/null +++ b/es-core/src/components/BadgeComponent.cpp @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// BadgeComponent.cpp +// +// Game badges icons. +// Used by the gamelist views. +// + +#define SLOT_FAVORITE "favorite" +#define SLOT_COMPLETED "completed" +#define SLOT_KIDGAME "kidgame" +#define SLOT_BROKEN "broken" +#define SLOT_CONTROLLER "controller" +#define SLOT_ALTEMULATOR "altemulator" + +#include "components/BadgeComponent.h" + +#include "Log.h" +#include "ThemeData.h" +#include "utils/StringUtil.h" + +std::vector BadgeComponent::sGameControllers; + +// clang-format off + +// The "unknown" controller entry has to be placed last. +GameControllers sControllerDefinitions [] = { + // shortName displayName fileName + {"gamepad_generic", "Gamepad (Generic)", ":/graphics/controllers/gamepad_generic.svg"}, + {"gamepad_xbox", "Gamepad (Xbox)", ":/graphics/controllers/gamepad_xbox.svg"}, + {"gamepad_playstation", "Gamepad (PlayStation)", ":/graphics/controllers/gamepad_playstation.svg"}, + {"gamepad_nintendo_nes", "Gamepad (Nintendo NES)", ":/graphics/controllers/gamepad_nintendo_nes.svg"}, + {"gamepad_nintendo_snes", "Gamepad (Nintendo SNES)", ":/graphics/controllers/gamepad_nintendo_snes.svg"}, + {"gamepad_nintendo_64", "Gamepad (Nintendo 64)", ":/graphics/controllers/gamepad_nintendo_64.svg"}, + {"joystick_generic", "Joystick (Generic)", ":/graphics/controllers/joystick_generic.svg"}, + {"joystick_arcade_2_buttons", "Joystick (Arcade 2 Buttons)", ":/graphics/controllers/joystick_arcade_2_buttons.svg"}, + {"joystick_arcade_3_buttons", "Joystick (Arcade 3 Buttons)", ":/graphics/controllers/joystick_arcade_3_buttons.svg"}, + {"joystick_arcade_4_buttons", "Joystick (Arcade 4 Buttons)", ":/graphics/controllers/joystick_arcade_4_buttons.svg"}, + {"joystick_arcade_6_buttons", "Joystick (Arcade 6 Buttons)", ":/graphics/controllers/joystick_arcade_6_buttons.svg"}, + {"trackball_generic", "Trackball (Generic)", ":/graphics/controllers/trackball_generic.svg"}, + {"lightgun_generic", "Lightgun (Generic)", ":/graphics/controllers/lightgun_generic.svg"}, + {"lightgun_nintendo", "Lightgun (Nintendo)", ":/graphics/controllers/lightgun_nintendo.svg"}, + {"keyboard_generic", "Keyboard (Generic)", ":/graphics/controllers/keyboard_generic.svg"}, + {"mouse_generic", "Mouse (Generic)", ":/graphics/controllers/mouse_generic.svg"}, + {"mouse_amiga", "Mouse (Amiga)", ":/graphics/controllers/mouse_amiga.svg"}, + {"keyboard_mouse_generic", "Keyboard and Mouse (Generic)", ":/graphics/controllers/keyboard_mouse_generic.svg"}, + {"steering_wheel_generic", "Steering Wheel (Generic)", ":/graphics/controllers/steering_wheel_generic.svg"}, + {"wii_remote_nintendo", "Wii Remote (Nintendo)", ":/graphics/controllers/wii_remote_nintendo.svg"}, + {"wii_remote_nunchuck_nintendo", "Wii Remote and Nunchuck (Nintendo)", ":/graphics/controllers/wii_remote_nunchuck_nintendo.svg"}, + {"joycon_left_or_right_nintendo", "Joy-Con Left or Right (Nintendo)", ":/graphics/controllers/joycon_left_or_right_nintendo.svg"}, + {"joycon_pair_nintendo", "Joy-Con Pair (Nintendo)", ":/graphics/controllers/joycon_pair_nintendo.svg"}, + {"unknown", "Unknown Controller", ":/graphics/controllers/unknown.svg"} +}; + +// clang-format on + +BadgeComponent::BadgeComponent(Window* window) + : GuiComponent{window} + , mFlexboxItems{} + , mFlexboxComponent{window, mFlexboxItems} + , mBadgeTypes{{SLOT_FAVORITE, SLOT_COMPLETED, SLOT_KIDGAME, SLOT_BROKEN, SLOT_CONTROLLER, + SLOT_ALTEMULATOR}} +{ + mBadgeIcons[SLOT_FAVORITE] = ":/graphics/badge_favorite.svg"; + mBadgeIcons[SLOT_COMPLETED] = ":/graphics/badge_completed.svg"; + mBadgeIcons[SLOT_KIDGAME] = ":/graphics/badge_kidgame.svg"; + mBadgeIcons[SLOT_BROKEN] = ":/graphics/badge_broken.svg"; + mBadgeIcons[SLOT_CONTROLLER] = ":/graphics/badge_controller.svg"; + mBadgeIcons[SLOT_ALTEMULATOR] = ":/graphics/badge_altemulator.svg"; +} + +void BadgeComponent::populateGameControllers() +{ + sGameControllers.clear(); + for (auto controller : sControllerDefinitions) + sGameControllers.push_back(controller); +} + +void BadgeComponent::setBadges(const std::vector& badges) +{ + std::map prevVisibility; + std::map prevPlayers; + std::map prevController; + + // Save the visibility status to know whether any badges changed. + for (auto& item : mFlexboxItems) { + prevVisibility[item.label] = item.visible; + prevController[item.label] = item.overlayImage.getTexture()->getTextureFilePath(); + item.visible = false; + } + + for (auto& badge : badges) { + auto it = std::find_if( + mFlexboxItems.begin(), mFlexboxItems.end(), + [badge](FlexboxComponent::FlexboxItem item) { return item.label == badge.badgeType; }); + + if (it != mFlexboxItems.end()) { + it->visible = true; + if (badge.gameController != "" && + badge.gameController != it->overlayImage.getTexture()->getTextureFilePath()) { + + auto it2 = std::find_if(sGameControllers.begin(), sGameControllers.end(), + [badge](GameControllers gameController) { + return gameController.shortName == badge.gameController; + }); + + if (it2 != sGameControllers.cend()) { + it->overlayImage.setImage((*it2).fileName); + } + else if (badge.gameController != "") + it->overlayImage.setImage(sGameControllers.back().fileName); + } + } + } + + // Only recalculate the flexbox if any badges changed. + for (auto& item : mFlexboxItems) { + if (prevVisibility[item.label] != item.visible || + prevController[item.label] != item.label) { + mFlexboxComponent.onSizeChanged(); + break; + } + } +} + +const std::string BadgeComponent::getShortName(const std::string& displayName) +{ + auto it = std::find_if(sGameControllers.begin(), sGameControllers.end(), + [displayName](GameControllers gameController) { + return gameController.displayName == displayName; + }); + if (it != sGameControllers.end()) + return (*it).shortName; + else + return "unknown"; +} + +const std::string BadgeComponent::getDisplayName(const std::string& shortName) +{ + auto it = std::find_if(sGameControllers.begin(), sGameControllers.end(), + [shortName](GameControllers gameController) { + return gameController.shortName == shortName; + }); + if (it != sGameControllers.end()) + return (*it).displayName; + else + return "unknown"; +} + +void BadgeComponent::render(const glm::mat4& parentTrans) +{ + if (!isVisible()) + return; + + if (mOpacity == 255) { + mFlexboxComponent.render(parentTrans); + } + else { + mFlexboxComponent.setOpacity(mOpacity); + mFlexboxComponent.render(parentTrans); + mFlexboxComponent.setOpacity(255); + } +} + +void BadgeComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) +{ + populateGameControllers(); + + using namespace ThemeFlags; + + const ThemeData::ThemeElement* elem{theme->getElement(view, element, "badges")}; + if (!elem) + return; + + if (elem->has("alignment")) { + const std::string alignment{elem->get("alignment")}; + if (alignment != "left" && alignment != "right") { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << alignment << "\""; + } + else { + mFlexboxComponent.setAlignment(alignment); + } + } + + if (elem->has("itemsPerRow")) { + const float itemsPerRow{elem->get("itemsPerRow")}; + if (itemsPerRow < 1.0f || itemsPerRow > 10.0f) { + LOG(LogWarning) + << "BadgeComponent: Invalid theme configuration, set to \"" + << itemsPerRow << "\""; + } + else { + mFlexboxComponent.setItemsPerLine(static_cast(itemsPerRow)); + } + } + + if (elem->has("rows")) { + const float rows{elem->get("rows")}; + if (rows < 1.0f || rows > 10.0f) { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << rows << "\""; + } + else { + mFlexboxComponent.setLines(static_cast(rows)); + } + } + + if (elem->has("itemMargin")) { + glm::vec2 itemMargin = elem->get("itemMargin"); + if ((itemMargin.x != -1.0 && itemMargin.y != -1.0) && + (itemMargin.x < 0.0f || itemMargin.x > 0.2f || itemMargin.y < 0.0f || + itemMargin.y > 0.2f)) { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << itemMargin.x << " " << itemMargin.y << "\""; + } + else { + mFlexboxComponent.setItemMargin(itemMargin); + } + } + + if (elem->has("controllerPos")) { + const glm::vec2 controllerPos = elem->get("controllerPos"); + if (controllerPos.x < -1.0f || controllerPos.x > 2.0f || controllerPos.y < -1.0f || + controllerPos.y > 2.0f) { + LOG(LogWarning) + << "BadgeComponent: Invalid theme configuration, set to \"" + << controllerPos.x << " " << controllerPos.y << "\""; + } + else { + mFlexboxComponent.setOverlayPosition(controllerPos); + } + } + + if (elem->has("controllerSize")) { + const float controllerSize = elem->get("controllerSize"); + if (controllerSize < 0.1f || controllerSize > 2.0f) { + LOG(LogWarning) + << "BadgeComponent: Invalid theme configuration, set to \"" + << controllerSize << "\""; + } + else { + mFlexboxComponent.setOverlaySize(controllerSize); + } + } + + if (elem->has("slots")) { + // Replace possible whitespace separators with commas. + std::string slotsTag = Utils::String::toLower(elem->get("slots")); + for (auto& character : slotsTag) { + if (std::isspace(character)) + character = ','; + } + slotsTag = Utils::String::replace(slotsTag, ",,", ","); + std::vector slots = Utils::String::delimitedStringToVector(slotsTag, ","); + + for (auto slot : slots) { + if (std::find(mBadgeTypes.cbegin(), mBadgeTypes.cend(), slot) != mBadgeTypes.end()) { + if (properties & PATH && elem->has(slot)) + mBadgeIcons[slot] = elem->get(slot); + + FlexboxComponent::FlexboxItem item; + item.label = slot; + + ImageComponent badgeImage{mWindow}; + badgeImage.setForceLoad(true); + badgeImage.setImage(mBadgeIcons[slot]); + item.baseImage = badgeImage; + item.overlayImage = ImageComponent{mWindow}; + + mFlexboxItems.push_back(item); + } + else { + LOG(LogError) << "Invalid badge slot \"" << slot << "\" defined"; + } + } + + for (auto& gameController : sGameControllers) { + if (properties & PATH && elem->has(gameController.shortName)) + gameController.fileName = elem->get(gameController.shortName); + } + + GuiComponent::applyTheme(theme, view, element, properties); + + mFlexboxComponent.setPosition(mPosition); + mFlexboxComponent.setSize(mSize); + mFlexboxComponent.setOrigin(mOrigin); + mFlexboxComponent.setRotation(mRotation); + mFlexboxComponent.setRotationOrigin(mRotationOrigin); + mFlexboxComponent.setVisible(mVisible); + mFlexboxComponent.setDefaultZIndex(mDefaultZIndex); + mFlexboxComponent.setZIndex(mZIndex); + } +} diff --git a/es-core/src/components/BadgeComponent.h b/es-core/src/components/BadgeComponent.h new file mode 100644 index 000000000..2f998b9da --- /dev/null +++ b/es-core/src/components/BadgeComponent.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// BadgeComponent.h +// +// Game badges icons. +// Used by the gamelist views. +// + +#ifndef ES_CORE_COMPONENTS_BADGE_COMPONENT_H +#define ES_CORE_COMPONENTS_BADGE_COMPONENT_H + +#include "FlexboxComponent.h" +#include "GuiComponent.h" + +struct GameControllers { + std::string shortName; + std::string displayName; + std::string fileName; +}; + +class BadgeComponent : public GuiComponent +{ +public: + BadgeComponent(Window* window); + + struct BadgeInfo { + std::string badgeType; + std::string gameController; + }; + + static void populateGameControllers(); + std::vector getBadgeTypes() { return mBadgeTypes; } + void setBadges(const std::vector& badges); + static const std::vector& getGameControllers() + { + if (sGameControllers.empty()) + populateGameControllers(); + return sGameControllers; + } + + static const std::string getShortName(const std::string& displayName); + static const std::string getDisplayName(const std::string& shortName); + + void render(const glm::mat4& parentTrans) override; + void onSizeChanged() override { mFlexboxComponent.onSizeChanged(); } + + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; + +private: + static std::vector sGameControllers; + + std::vector mFlexboxItems; + FlexboxComponent mFlexboxComponent; + + std::vector mBadgeTypes; + std::map mBadgeIcons; +}; + +#endif // ES_CORE_COMPONENTS_BADGE_COMPONENT_H diff --git a/es-core/src/components/BadgesComponent.cpp b/es-core/src/components/BadgesComponent.cpp deleted file mode 100644 index 00c023b64..000000000 --- a/es-core/src/components/BadgesComponent.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// BadgesComponent.cpp -// -// Game badges icons. -// Used by the gamelist views. -// - -#define SLOT_FAVORITE "favorite" -#define SLOT_COMPLETED "completed" -#define SLOT_KIDGAME "kidgame" -#define SLOT_BROKEN "broken" -#define SLOT_ALTEMULATOR "altemulator" - -#include "components/BadgesComponent.h" - -#include "Log.h" -#include "ThemeData.h" -#include "utils/StringUtil.h" - -BadgesComponent::BadgesComponent(Window* window) - : GuiComponent{window} - , mFlexboxComponent{window, mBadgeImages} - , mBadgeTypes{{SLOT_FAVORITE, SLOT_COMPLETED, SLOT_KIDGAME, SLOT_BROKEN, SLOT_ALTEMULATOR}} -{ - mBadgeIcons[SLOT_FAVORITE] = ":/graphics/badge_favorite.svg"; - mBadgeIcons[SLOT_COMPLETED] = ":/graphics/badge_completed.svg"; - mBadgeIcons[SLOT_KIDGAME] = ":/graphics/badge_kidgame.svg"; - mBadgeIcons[SLOT_BROKEN] = ":/graphics/badge_broken.svg"; - mBadgeIcons[SLOT_ALTEMULATOR] = ":/graphics/badge_altemulator.svg"; -} - -void BadgesComponent::setBadges(const std::vector& badges) -{ - std::map prevVisibility; - - // Save the visibility status to know whether any badges changed. - for (auto& image : mBadgeImages) { - prevVisibility[image.first] = image.second.isVisible(); - image.second.setVisible(false); - } - - for (auto& badge : badges) { - auto it = std::find_if( - mBadgeImages.begin(), mBadgeImages.end(), - [badge](std::pair image) { return image.first == badge; }); - - if (it != mBadgeImages.cend()) - it->second.setVisible(true); - } - - // Only recalculate the flexbox if any badges changed. - for (auto& image : mBadgeImages) { - if (prevVisibility[image.first] != image.second.isVisible()) { - mFlexboxComponent.onSizeChanged(); - break; - } - } -} - -void BadgesComponent::render(const glm::mat4& parentTrans) -{ - if (!isVisible()) - return; - - if (mOpacity == 255) { - mFlexboxComponent.render(parentTrans); - } - else { - mFlexboxComponent.setOpacity(mOpacity); - mFlexboxComponent.render(parentTrans); - mFlexboxComponent.setOpacity(255); - } -} - -void BadgesComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& element, - unsigned int properties) -{ - using namespace ThemeFlags; - - const ThemeData::ThemeElement* elem{theme->getElement(view, element, "badges")}; - if (!elem) - return; - - if (elem->has("alignment")) { - const std::string alignment{elem->get("alignment")}; - if (alignment != "left" && alignment != "right") { - LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, set to \"" - << alignment << "\""; - } - else { - mFlexboxComponent.setAlignment(alignment); - } - } - - if (elem->has("itemsPerRow")) { - const float itemsPerRow{elem->get("itemsPerRow")}; - if (itemsPerRow < 1.0f || itemsPerRow > 10.0f) { - LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" - << itemsPerRow << "\""; - } - else { - mFlexboxComponent.setItemsPerLine(static_cast(itemsPerRow)); - } - } - - if (elem->has("rows")) { - const float rows{elem->get("rows")}; - if (rows < 1.0f || rows > 10.0f) { - LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, set to \"" - << rows << "\""; - } - else { - mFlexboxComponent.setLines(static_cast(rows)); - } - } - - if (elem->has("itemPlacement")) { - std::string itemPlacement{elem->get("itemPlacement")}; - if (itemPlacement != "top" && itemPlacement != "center" && itemPlacement != "bottom" && - itemPlacement != "stretch") { - LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" - << itemPlacement << "\""; - } - else { - if (itemPlacement == "top") - itemPlacement = "start"; - else if (itemPlacement == "bottom") - itemPlacement = "end"; - mFlexboxComponent.setItemPlacement(itemPlacement); - } - } - - if (elem->has("itemMargin")) { - const glm::vec2 itemMargin = elem->get("itemMargin"); - if (itemMargin.x < 0.0f || itemMargin.x > 0.2f || itemMargin.y < 0.0f || - itemMargin.y > 0.2f) { - LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" - << itemMargin.x << " " << itemMargin.y << "\""; - } - else { - mFlexboxComponent.setItemMargin(itemMargin); - } - } - - if (elem->has("slots")) { - std::vector slots = Utils::String::delimitedStringToVector( - Utils::String::toLower(elem->get("slots")), " "); - - for (auto slot : slots) { - if (std::find(mBadgeTypes.cbegin(), mBadgeTypes.cend(), slot) != mBadgeTypes.end()) { - if (properties & PATH && elem->has(slot)) - mBadgeIcons[slot] = elem->get(slot); - - ImageComponent badgeImage{mWindow}; - - badgeImage.setImage(mBadgeIcons[slot]); - badgeImage.setVisible(false); - mBadgeImages.push_back(std::make_pair(slot, badgeImage)); - } - else { - LOG(LogError) << "Invalid badge slot \"" << slot << "\" defined"; - } - } - - GuiComponent::applyTheme(theme, view, element, properties); - - mFlexboxComponent.setPosition(mPosition); - mFlexboxComponent.setSize(mSize); - mFlexboxComponent.setOrigin(mOrigin); - mFlexboxComponent.setRotation(mRotation); - mFlexboxComponent.setRotationOrigin(mRotationOrigin); - mFlexboxComponent.setVisible(mVisible); - mFlexboxComponent.setDefaultZIndex(mDefaultZIndex); - mFlexboxComponent.setZIndex(mZIndex); - } -} diff --git a/es-core/src/components/BadgesComponent.h b/es-core/src/components/BadgesComponent.h deleted file mode 100644 index 12cbd5927..000000000 --- a/es-core/src/components/BadgesComponent.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// BadgesComponent.h -// -// Game badges icons. -// Used by the gamelist views. -// - -#ifndef ES_CORE_COMPONENTS_BADGES_COMPONENT_H -#define ES_CORE_COMPONENTS_BADGES_COMPONENT_H - -#include "FlexboxComponent.h" -#include "GuiComponent.h" - -class BadgesComponent : public GuiComponent -{ -public: - BadgesComponent(Window* window); - - std::vector getBadgeTypes() { return mBadgeTypes; } - void setBadges(const std::vector& badges); - - void render(const glm::mat4& parentTrans) override; - void onSizeChanged() override { mFlexboxComponent.onSizeChanged(); } - - virtual void applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& element, - unsigned int properties) override; - -private: - FlexboxComponent mFlexboxComponent; - - std::vector mBadgeTypes; - std::map mBadgeIcons; - std::vector> mBadgeImages; -}; - -#endif // ES_CORE_COMPONENTS_BADGES_COMPONENT_H diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index 4979de675..da3bf1b1c 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -19,16 +19,17 @@ #include "Settings.h" #include "ThemeData.h" -FlexboxComponent::FlexboxComponent(Window* window, - std::vector>& images) +FlexboxComponent::FlexboxComponent(Window* window, std::vector& items) : GuiComponent{window} - , mImages(images) + , mItems(items) , mDirection{DEFAULT_DIRECTION} , mAlignment{DEFAULT_ALIGNMENT} , mItemsPerLine{DEFAULT_ITEMS_PER_LINE} , mLines{DEFAULT_LINES} , mItemPlacement{DEFAULT_ITEM_PLACEMENT} , mItemMargin{glm::vec2{DEFAULT_MARGIN_X, DEFAULT_MARGIN_Y}} + , mOverlayPosition{0.5f, 0.5f} + , mOverlaySize{0.5f} , mLayoutValid{false} { } @@ -45,35 +46,47 @@ void FlexboxComponent::render(const glm::mat4& parentTrans) Renderer::setMatrix(trans); if (Settings::getInstance()->getBool("DebugImage")) - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + Renderer::drawRect(0.0f, 0.0f, ceilf(mSize.x), ceilf(mSize.y), 0xFF000033, 0xFF000033); - for (auto& image : mImages) { + for (auto& item : mItems) { + if (!item.visible) + continue; if (mOpacity == 255) { - image.second.render(trans); + item.baseImage.render(trans); + if (item.overlayImage.getTexture() != nullptr) + item.overlayImage.render(trans); } else { - image.second.setOpacity(mOpacity); - image.second.render(trans); - image.second.setOpacity(255); + item.baseImage.setOpacity(mOpacity); + item.baseImage.render(trans); + item.baseImage.setOpacity(255); + if (item.overlayImage.getTexture() != nullptr) { + item.overlayImage.setOpacity(mOpacity); + item.overlayImage.render(trans); + item.overlayImage.setOpacity(255); + } } } } +void FlexboxComponent::setItemMargin(glm::vec2 value) +{ + if (value.x == -1.0f) + mItemMargin.x = std::roundf(value.y * Renderer::getScreenHeight()); + else + mItemMargin.x = std::roundf(value.x * Renderer::getScreenWidth()); + + if (value.y == -1.0f) + mItemMargin.y = std::roundf(value.x * Renderer::getScreenWidth()); + else + mItemMargin.y = std::roundf(value.y * Renderer::getScreenHeight()); + + mLayoutValid = false; +} + void FlexboxComponent::computeLayout() { - // Start placing items in the top-left. - float anchorX{0.0f}; - float anchorY{0.0f}; - - // Translation directions when placing items. - glm::vec2 directionLine{1, 0}; - glm::vec2 directionRow{0, 1}; - - // Change direction. - if (mDirection == "column") { - directionLine = {0, 1}; - directionRow = {1, 0}; - } + // TODO: There is no right-alignment support for column mode. // If we're not clamping itemMargin to a reasonable value, all kinds of weird rendering // issues could occur. @@ -86,125 +99,146 @@ void FlexboxComponent::computeLayout() mSize.y = glm::clamp(mSize.y, static_cast(Renderer::getScreenHeight()) * 0.03f, static_cast(Renderer::getScreenHeight())); - // Compute maximum image dimensions. - glm::vec2 grid; - if (mDirection == "row") - grid = {mItemsPerLine, mLines}; - else - grid = {mLines, mItemsPerLine}; + if (mItemsPerLine * mLines < mItems.size()) { + LOG(LogWarning) + << "FlexboxComponent: Invalid theme configuration, the number of badges" + " exceeds the product of times , setting to " + << mItems.size(); + mItemsPerLine = static_cast(mItems.size()); + } + glm::vec2 grid{mItemsPerLine, mLines}; glm::vec2 maxItemSize{(mSize + mItemMargin - grid * mItemMargin) / grid}; - maxItemSize.x = floorf(maxItemSize.x); - maxItemSize.y = floorf(maxItemSize.y); - if (grid.x * grid.y < static_cast(mImages.size())) { - LOG(LogWarning) << "FlexboxComponent: Invalid theme configuration, the number of badges " - "exceeds the product of times "; - } + float rowHeight{0.0f}; + bool firstItem{true}; - glm::vec2 sizeChange{0.0f, 0.0f}; - - // Set final image dimensions. - for (auto& image : mImages) { - if (!image.second.isVisible()) - continue; - auto oldSize{image.second.getSize()}; - if (oldSize.x == 0) - oldSize.x = maxItemSize.x; - glm::vec2 sizeMaxX{maxItemSize.x, oldSize.y * (maxItemSize.x / oldSize.x)}; - glm::vec2 sizeMaxY{oldSize.x * (maxItemSize.y / oldSize.y), maxItemSize.y}; - glm::vec2 newSize; - if (sizeMaxX.y > maxItemSize.y) - newSize = sizeMaxY; - else if (sizeMaxY.x > maxItemSize.x) - newSize = sizeMaxX; - else - newSize = sizeMaxX.x * sizeMaxX.y >= sizeMaxY.x * sizeMaxY.y ? sizeMaxX : sizeMaxY; - - if (image.second.getSize() != newSize) - image.second.setResize(std::round(newSize.x), std::round(newSize.y)); - - // In case maxItemSize needs to be updated. - if (newSize.x != sizeChange.x) - sizeChange.x = newSize.x; - if (newSize.y != sizeChange.y) - sizeChange.y = newSize.y; - } - - if (maxItemSize.x != sizeChange.x) - maxItemSize.x = std::round(sizeChange.x); - if (maxItemSize.y != sizeChange.y) - maxItemSize.y = std::round(sizeChange.y); - - // Pre-compute layout parameters. - float anchorXStart{anchorX}; - float anchorYStart{anchorY}; - - int i = 0; - - // Iterate through the images. - for (auto& image : mImages) { - if (!image.second.isVisible()) + // Calculate maximum item dimensions. + for (auto& item : mItems) { + if (!item.visible) continue; - auto size{image.second.getSize()}; + glm::vec2 sizeDiff{item.baseImage.getSize() / maxItemSize}; - // Top-left anchor position. - float x{anchorX}; - float y{anchorY}; - - // Apply alignment. - if (mItemPlacement == "end") { - x += directionLine.x == 0 ? (maxItemSize.x - size.x) : 0; - y += directionLine.y == 0 ? (maxItemSize.y - size.y) : 0; - } - else if (mItemPlacement == "center") { - x += directionLine.x == 0 ? (maxItemSize.x - size.x) / 2 : 0; - y += directionLine.y == 0 ? (maxItemSize.y - size.y) / 2 : 0; - } - else if (mItemPlacement == "stretch" && mDirection == "row") { - image.second.setSize(image.second.getSize().x, maxItemSize.y); - } - - // Apply overall container alignment. - if (mAlignment == "right") - x += (mSize.x - maxItemSize.x * grid.x - (grid.x - 1) * mItemMargin.x); - - // Store final item position. - image.second.setPosition(x, y); - - // Translate anchor. - if ((i++ + 1) % std::max(1, static_cast(mItemsPerLine)) != 0) { - // Translate on same line. - anchorX += (size.x + mItemMargin.x) * static_cast(directionLine.x); - anchorY += (size.y + mItemMargin.y) * static_cast(directionLine.y); + // The first item dictates the maximum width for the rest. + if (firstItem) { + maxItemSize.x = (item.baseImage.getSize() / std::max(sizeDiff.x, sizeDiff.y)).x; + sizeDiff = item.baseImage.getSize() / maxItemSize; + item.baseImage.setSize((item.baseImage.getSize() / std::max(sizeDiff.x, sizeDiff.y))); + firstItem = false; } else { - // Translate to first position of next line. - if (directionRow.x == 0) { - anchorY += size.y + mItemMargin.y; - anchorX = anchorXStart; + item.baseImage.setSize((item.baseImage.getSize() / std::max(sizeDiff.x, sizeDiff.y))); + } + + if (item.baseImage.getSize().y > rowHeight) + rowHeight = item.baseImage.getSize().y; + } + + // Update the maximum item height. + maxItemSize.y = 0.0f; + for (auto& item : mItems) { + if (!item.visible) + continue; + if (item.baseImage.getSize().y > maxItemSize.y) + maxItemSize.y = item.baseImage.getSize().y; + } + + maxItemSize = glm::round(maxItemSize); + + bool alignRight{mAlignment == "right" && mDirection == "row"}; + float alignRightComp{0.0f}; + + // If right-aligning, move the overall container contents during grid setup. + if (alignRight) + alignRightComp = + std::round(mSize.x - ((maxItemSize.x + mItemMargin.x) * grid.x) + mItemMargin.x); + + std::vector itemPositions; + + // Lay out the grid. + if (mDirection == "row") { + for (int y = 0; y < grid.y; y++) { + for (int x = 0; x < grid.x; x++) { + itemPositions.push_back( + glm::vec2{(x * (maxItemSize.x + mItemMargin.x) + alignRightComp), + y * (rowHeight + mItemMargin.y)}); } - else { - anchorX += size.x + mItemMargin.x; - anchorY = anchorYStart; + } + } + else { // Column mode. + for (int x = 0; x < grid.x; x++) { + for (int y = 0; y < grid.y; y++) { + itemPositions.push_back( + glm::vec2{(x * (maxItemSize.x + mItemMargin.x) + alignRightComp), + y * (rowHeight + mItemMargin.y)}); } } } - // Apply right-align to the images on the last row, if needed. - if (mAlignment == "right") { - std::vector imagesToAlign; - for (auto& image : mImages) { - if (!image.second.isVisible()) - continue; - // Only include images on the last row. - if (image.second.getPosition().y == anchorY) - imagesToAlign.push_back(&image.second); + int pos{0}; + float lastY{0.0f}; + float itemsOnLastRow{0}; + + // Position items on the grid. + for (auto& item : mItems) { + if (!item.visible) + continue; + + if (pos > 0) { + if (itemPositions[pos - 1].y < itemPositions[pos].y) { + lastY = itemPositions[pos].y; + itemsOnLastRow = 0; + } } - for (auto& moveImage : imagesToAlign) { - float offset = (maxItemSize.x + mItemMargin.x) * (grid.x - imagesToAlign.size()); - moveImage->setPosition(moveImage->getPosition().x + offset, moveImage->getPosition().y); + + float verticalOffset{0.0f}; + + // For any items that do not fill the maximum height, position these either on + // top/start (implicit), center or bottom/end. + if (item.baseImage.getSize().y < maxItemSize.y) { + if (mItemPlacement == "center") { + verticalOffset = std::floor((maxItemSize.y - item.baseImage.getSize().y) / 2.0f); + } + else if (mItemPlacement == "end") { + verticalOffset = maxItemSize.y - item.baseImage.getSize().y; + } + } + + item.baseImage.setPosition(itemPositions[pos].x, itemPositions[pos].y + verticalOffset, + 0.0f); + + // Optional overlay image. + if (item.overlayImage.getTexture() != nullptr) { + item.overlayImage.setResize(item.baseImage.getSize().x * mOverlaySize, 0.0f); + item.overlayImage.setPosition( + item.baseImage.getPosition().x + (item.baseImage.getSize().x * mOverlayPosition.x) - + item.overlayImage.getSize().x / 2.0f, + item.baseImage.getPosition().y + (item.baseImage.getSize().y * mOverlayPosition.y) - + item.overlayImage.getSize().y / 2.0f); + } + + // This rasterizes the SVG images so they look nice and smooth. + item.baseImage.setResize(item.baseImage.getSize()); + + itemsOnLastRow++; + pos++; + } + + // Apply right-align to the items (only works in row mode). + if (alignRight) { + for (auto& item : mItems) { + if (!item.visible) + continue; + glm::vec3 currPos{item.baseImage.getPosition()}; + if (currPos.y == lastY) { + const float offset{(grid.x - itemsOnLastRow) * (maxItemSize.x + mItemMargin.x)}; + item.baseImage.setPosition(currPos.x + offset, currPos.y, currPos.z); + if (item.overlayImage.getTexture() != nullptr) { + currPos = item.overlayImage.getPosition(); + item.overlayImage.setPosition(currPos.x + offset, currPos.y, currPos.z); + } + } } } diff --git a/es-core/src/components/FlexboxComponent.h b/es-core/src/components/FlexboxComponent.h index 41c24433e..4ab5ee5c3 100644 --- a/es-core/src/components/FlexboxComponent.h +++ b/es-core/src/components/FlexboxComponent.h @@ -10,12 +10,23 @@ #define ES_CORE_COMPONENTS_FLEXBOX_COMPONENT_H #include "GuiComponent.h" +#include "Window.h" #include "components/ImageComponent.h" class FlexboxComponent : public GuiComponent { public: - FlexboxComponent(Window* window, std::vector>& images); + struct FlexboxItem { + // Optional label, mostly a convenience for the calling class to track items. + std::string label; + // Main image that governs grid sizing and placement. + ImageComponent baseImage{nullptr}; + // Optional overlay image that can be sized and positioned relative to the base image. + ImageComponent overlayImage{nullptr}; + bool visible = false; + }; + + FlexboxComponent(Window* window, std::vector& items); // Getters/setters for the layout. std::string getDirection() const { return mDirection; } @@ -56,12 +67,13 @@ public: } glm::vec2 getItemMargin() const { return mItemMargin; } - void setItemMargin(glm::vec2 value) - { - mItemMargin.x = std::roundf(value.x * Renderer::getScreenWidth()); - mItemMargin.y = std::roundf(value.y * Renderer::getScreenHeight()); - mLayoutValid = false; - } + void setItemMargin(glm::vec2 value); + + glm::vec2 getOverlayPosition() const { return mOverlayPosition; } + void setOverlayPosition(glm::vec2 position) { mOverlayPosition = position; } + + float getOverlaySize() const { return mOverlaySize; } + void setOverlaySize(float size) { mOverlaySize = size; } void onSizeChanged() override { mLayoutValid = false; } void render(const glm::mat4& parentTrans) override; @@ -70,7 +82,7 @@ private: // Calculate flexbox layout. void computeLayout(); - std::vector>& mImages; + std::vector& mItems; // Layout options. std::string mDirection; @@ -80,6 +92,9 @@ private: std::string mItemPlacement; glm::vec2 mItemMargin; + glm::vec2 mOverlayPosition; + float mOverlaySize; + bool mLayoutValid; }; diff --git a/es-core/src/components/HelpComponent.cpp b/es-core/src/components/HelpComponent.cpp index 7b6c7197f..49b68e354 100644 --- a/es-core/src/components/HelpComponent.cpp +++ b/es-core/src/components/HelpComponent.cpp @@ -34,129 +34,129 @@ void HelpComponent::assignIcons() // These graphics files are common between all controller types. sIconPathMap["up/down"] = mStyle.mCustomButtons.dpad_updown.empty() ? - ":/help/dpad_updown.svg" : + ":/graphics/help/dpad_updown.svg" : mStyle.mCustomButtons.dpad_updown; sIconPathMap["left/right"] = mStyle.mCustomButtons.dpad_leftright.empty() ? - ":/help/dpad_leftright.svg" : + ":/graphics/help/dpad_leftright.svg" : mStyle.mCustomButtons.dpad_leftright; sIconPathMap["up/down/left/right"] = mStyle.mCustomButtons.dpad_all.empty() ? - ":/help/dpad_all.svg" : + ":/graphics/help/dpad_all.svg" : mStyle.mCustomButtons.dpad_all; sIconPathMap["thumbstickclick"] = mStyle.mCustomButtons.thumbstick_click.empty() ? - ":/help/thumbstick_click.svg" : + ":/graphics/help/thumbstick_click.svg" : mStyle.mCustomButtons.thumbstick_click; - sIconPathMap["l"] = mStyle.mCustomButtons.button_l.empty() ? ":/help/button_l.svg" : + sIconPathMap["l"] = mStyle.mCustomButtons.button_l.empty() ? ":/graphics/help/button_l.svg" : mStyle.mCustomButtons.button_l; - sIconPathMap["r"] = mStyle.mCustomButtons.button_r.empty() ? ":/help/button_r.svg" : + sIconPathMap["r"] = mStyle.mCustomButtons.button_r.empty() ? ":/graphics/help/button_r.svg" : mStyle.mCustomButtons.button_r; - sIconPathMap["lr"] = mStyle.mCustomButtons.button_lr.empty() ? ":/help/button_lr.svg" : + sIconPathMap["lr"] = mStyle.mCustomButtons.button_lr.empty() ? ":/graphics/help/button_lr.svg" : mStyle.mCustomButtons.button_lr; - sIconPathMap["lt"] = mStyle.mCustomButtons.button_lt.empty() ? ":/help/button_lt.svg" : + sIconPathMap["lt"] = mStyle.mCustomButtons.button_lt.empty() ? ":/graphics/help/button_lt.svg" : mStyle.mCustomButtons.button_lt; - sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/help/button_rt.svg" : + sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/graphics/help/button_rt.svg" : mStyle.mCustomButtons.button_rt; // These graphics files are custom per controller type. if (controllerType == "snes") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_SNES.empty() ? - ":/help/button_a_SNES.svg" : + ":/graphics/help/button_a_SNES.svg" : mStyle.mCustomButtons.button_a_SNES; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_SNES.empty() ? - ":/help/button_b_SNES.svg" : + ":/graphics/help/button_b_SNES.svg" : mStyle.mCustomButtons.button_b_SNES; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_SNES.empty() ? - ":/help/button_x_SNES.svg" : + ":/graphics/help/button_x_SNES.svg" : mStyle.mCustomButtons.button_x_SNES; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_SNES.empty() ? - ":/help/button_y_SNES.svg" : + ":/graphics/help/button_y_SNES.svg" : mStyle.mCustomButtons.button_y_SNES; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_SNES.empty() ? - ":/help/button_start_SNES.svg" : + ":/graphics/help/button_start_SNES.svg" : mStyle.mCustomButtons.button_start_SNES; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_SNES.empty() ? - ":/help/button_back_SNES.svg" : + ":/graphics/help/button_back_SNES.svg" : mStyle.mCustomButtons.button_back_SNES; } else if (controllerType == "ps4") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ? - ":/help/button_a_PS.svg" : + ":/graphics/help/button_a_PS.svg" : mStyle.mCustomButtons.button_a_PS; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ? - ":/help/button_b_PS.svg" : + ":/graphics/help/button_b_PS.svg" : mStyle.mCustomButtons.button_b_PS; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ? - ":/help/button_x_PS.svg" : + ":/graphics/help/button_x_PS.svg" : mStyle.mCustomButtons.button_x_PS; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ? - ":/help/button_y_PS.svg" : + ":/graphics/help/button_y_PS.svg" : mStyle.mCustomButtons.button_y_PS; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS4.empty() ? - ":/help/button_start_PS4.svg" : + ":/graphics/help/button_start_PS4.svg" : mStyle.mCustomButtons.button_start_PS4; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS4.empty() ? - ":/help/button_back_PS4.svg" : + ":/graphics/help/button_back_PS4.svg" : mStyle.mCustomButtons.button_back_PS4; } else if (controllerType == "ps5") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ? - ":/help/button_a_PS.svg" : + ":/graphics/help/button_a_PS.svg" : mStyle.mCustomButtons.button_a_PS; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ? - ":/help/button_b_PS.svg" : + ":/graphics/help/button_b_PS.svg" : mStyle.mCustomButtons.button_b_PS; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ? - ":/help/button_x_PS.svg" : + ":/graphics/help/button_x_PS.svg" : mStyle.mCustomButtons.button_x_PS; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ? - ":/help/button_y_PS.svg" : + ":/graphics/help/button_y_PS.svg" : mStyle.mCustomButtons.button_y_PS; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS5.empty() ? - ":/help/button_start_PS5.svg" : + ":/graphics/help/button_start_PS5.svg" : mStyle.mCustomButtons.button_start_PS5; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS5.empty() ? - ":/help/button_back_PS5.svg" : + ":/graphics/help/button_back_PS5.svg" : mStyle.mCustomButtons.button_back_PS5; } else if (controllerType == "xbox360") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ? - ":/help/button_a_XBOX.svg" : + ":/graphics/help/button_a_XBOX.svg" : mStyle.mCustomButtons.button_a_XBOX; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ? - ":/help/button_b_XBOX.svg" : + ":/graphics/help/button_b_XBOX.svg" : mStyle.mCustomButtons.button_b_XBOX; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ? - ":/help/button_x_XBOX.svg" : + ":/graphics/help/button_x_XBOX.svg" : mStyle.mCustomButtons.button_x_XBOX; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ? - ":/help/button_y_XBOX.svg" : + ":/graphics/help/button_y_XBOX.svg" : mStyle.mCustomButtons.button_y_XBOX; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX360.empty() ? - ":/help/button_start_XBOX360.svg" : + ":/graphics/help/button_start_XBOX360.svg" : mStyle.mCustomButtons.button_start_XBOX360; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX360.empty() ? - ":/help/button_back_XBOX360.svg" : + ":/graphics/help/button_back_XBOX360.svg" : mStyle.mCustomButtons.button_back_XBOX360; } else { // Xbox One and later. sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ? - ":/help/button_a_XBOX.svg" : + ":/graphics/help/button_a_XBOX.svg" : mStyle.mCustomButtons.button_a_XBOX; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ? - ":/help/button_b_XBOX.svg" : + ":/graphics/help/button_b_XBOX.svg" : mStyle.mCustomButtons.button_b_XBOX; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ? - ":/help/button_x_XBOX.svg" : + ":/graphics/help/button_x_XBOX.svg" : mStyle.mCustomButtons.button_x_XBOX; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ? - ":/help/button_y_XBOX.svg" : + ":/graphics/help/button_y_XBOX.svg" : mStyle.mCustomButtons.button_y_XBOX; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX.empty() ? - ":/help/button_start_XBOX.svg" : + ":/graphics/help/button_start_XBOX.svg" : mStyle.mCustomButtons.button_start_XBOX; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX.empty() ? - ":/help/button_back_XBOX.svg" : + ":/graphics/help/button_back_XBOX.svg" : mStyle.mCustomButtons.button_back_XBOX; } diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 6da5f3d1a..4055c9989 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -123,9 +123,9 @@ void ImageComponent::resize() // Make sure sub-pixel values are not rounded to zero. if (mSize.x < 1.0f) - mSize.x = ceilf(mSize.x); + mSize.x = 1.0f; if (mSize.y < 1.0f) - mSize.y = ceilf(mSize.y); + mSize.y = 1.0f; mTexture->rasterizeAt(mSize.x, mSize.y); diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 159d65e3b..40b15cf3d 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -30,6 +30,8 @@ public: // Use an already existing texture. void setImage(const std::shared_ptr& texture, bool resizeTexture = true); + void setForceLoad(bool status) { mForceLoad = status; } + void onSizeChanged() override { updateVertices(); } // Resize the image to fit this size. If one axis is zero, scale that axis to maintain diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index 516d17c6e..f9bf3d10f 100644 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -201,67 +201,67 @@ void GuiInputConfig::populateConfigList() std::string controllerType = Settings::getInstance()->getString("InputControllerType"); // clang-format off - sGuiInputConfigList[0] = {"Up", false, "D-PAD UP", ":/help/dpad_up.svg"}; - sGuiInputConfigList[1] = {"Down", false, "D-PAD DOWN", ":/help/dpad_down.svg"}; - sGuiInputConfigList[2] = {"Left", false, "D-PAD LEFT", ":/help/dpad_left.svg"}; - sGuiInputConfigList[3] = {"Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg"}; + sGuiInputConfigList[0] = {"Up", false, "D-PAD UP", ":/graphics/help/dpad_up.svg"}; + sGuiInputConfigList[1] = {"Down", false, "D-PAD DOWN", ":/graphics/help/dpad_down.svg"}; + sGuiInputConfigList[2] = {"Left", false, "D-PAD LEFT", ":/graphics/help/dpad_left.svg"}; + sGuiInputConfigList[3] = {"Right", false, "D-PAD RIGHT", ":/graphics/help/dpad_right.svg"}; if (controllerType == "snes") { - sGuiInputConfigList[4] = {"Back", false, "SELECT", ":/help/button_back_SNES.svg"}; - sGuiInputConfigList[5] = {"Start", false, "START", ":/help/button_start_SNES.svg"}; - sGuiInputConfigList[6] = {"A", false, "B", ":/help/mbuttons_a_SNES.svg"}; - sGuiInputConfigList[7] = {"B", false, "A", ":/help/mbuttons_b_SNES.svg"}; - sGuiInputConfigList[8] = {"X", true, "Y", ":/help/mbuttons_x_SNES.svg"}; - sGuiInputConfigList[9] = {"Y", true, "X", ":/help/mbuttons_y_SNES.svg"}; + sGuiInputConfigList[4] = {"Back", false, "SELECT", ":/graphics/help/button_back_SNES.svg"}; + sGuiInputConfigList[5] = {"Start", false, "START", ":/graphics/help/button_start_SNES.svg"}; + sGuiInputConfigList[6] = {"A", false, "B", ":/graphics/help/mbuttons_a_SNES.svg"}; + sGuiInputConfigList[7] = {"B", false, "A", ":/graphics/help/mbuttons_b_SNES.svg"}; + sGuiInputConfigList[8] = {"X", true, "Y", ":/graphics/help/mbuttons_x_SNES.svg"}; + sGuiInputConfigList[9] = {"Y", true, "X", ":/graphics/help/mbuttons_y_SNES.svg"}; } else if (controllerType == "ps4") { - sGuiInputConfigList[4] = {"Back", false, "SHARE", ":/help/button_back_PS4.svg"}; - sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/help/button_start_PS4.svg"}; - sGuiInputConfigList[6] = {"A", false, "CROSS", ":/help/mbuttons_a_PS.svg"}; - sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg"}; - sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/help/mbuttons_x_PS.svg"}; - sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg"}; + sGuiInputConfigList[4] = {"Back", false, "SHARE", ":/graphics/help/button_back_PS4.svg"}; + sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/graphics/help/button_start_PS4.svg"}; + sGuiInputConfigList[6] = {"A", false, "CROSS", ":/graphics/help/mbuttons_a_PS.svg"}; + sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/graphics/help/mbuttons_b_PS.svg"}; + sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/graphics/help/mbuttons_x_PS.svg"}; + sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/graphics/help/mbuttons_y_PS.svg"}; } else if (controllerType == "ps5") { - sGuiInputConfigList[4] = {"Back", false, "CREATE", ":/help/button_back_PS5.svg"}; - sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/help/button_start_PS5.svg"}; - sGuiInputConfigList[6] = {"A", false, "CROSS", ":/help/mbuttons_a_PS.svg"}; - sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg"}; - sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/help/mbuttons_x_PS.svg"}; - sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg"}; + sGuiInputConfigList[4] = {"Back", false, "CREATE", ":/graphics/help/button_back_PS5.svg"}; + sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/graphics/help/button_start_PS5.svg"}; + sGuiInputConfigList[6] = {"A", false, "CROSS", ":/graphics/help/mbuttons_a_PS.svg"}; + sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/graphics/help/mbuttons_b_PS.svg"}; + sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/graphics/help/mbuttons_x_PS.svg"}; + sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/graphics/help/mbuttons_y_PS.svg"}; } else if (controllerType == "xbox360") { - sGuiInputConfigList[4] = {"Back", false, "BACK", ":/help/button_back_XBOX360.svg"}; - sGuiInputConfigList[5] = {"Start", false, "START", ":/help/button_start_XBOX360.svg"}; - sGuiInputConfigList[6] = {"A", false, "A", ":/help/mbuttons_a_XBOX.svg"}; - sGuiInputConfigList[7] = {"B", false, "B", ":/help/mbuttons_b_XBOX.svg"}; - sGuiInputConfigList[8] = {"X", true, "X", ":/help/mbuttons_x_XBOX.svg"}; - sGuiInputConfigList[9] = {"Y", true, "Y", ":/help/mbuttons_y_XBOX.svg"}; + sGuiInputConfigList[4] = {"Back", false, "BACK", ":/graphics/help/button_back_XBOX360.svg"}; + sGuiInputConfigList[5] = {"Start", false, "START", ":/graphics/help/button_start_XBOX360.svg"}; + sGuiInputConfigList[6] = {"A", false, "A", ":/graphics/help/mbuttons_a_XBOX.svg"}; + sGuiInputConfigList[7] = {"B", false, "B", ":/graphics/help/mbuttons_b_XBOX.svg"}; + sGuiInputConfigList[8] = {"X", true, "X", ":/graphics/help/mbuttons_x_XBOX.svg"}; + sGuiInputConfigList[9] = {"Y", true, "Y", ":/graphics/help/mbuttons_y_XBOX.svg"}; } else { // Xbox One and later. - sGuiInputConfigList[4] = {"Back", false, "VIEW", ":/help/button_back_XBOX.svg"}; - sGuiInputConfigList[5] = {"Start", false, "MENU", ":/help/button_start_XBOX.svg"}; - sGuiInputConfigList[6] = {"A", false, "A", ":/help/mbuttons_a_XBOX.svg"}; - sGuiInputConfigList[7] = {"B", false, "B", ":/help/mbuttons_b_XBOX.svg"}; - sGuiInputConfigList[8] = {"X", true, "X", ":/help/mbuttons_x_XBOX.svg"}; - sGuiInputConfigList[9] = {"Y", true, "Y", ":/help/mbuttons_y_XBOX.svg"}; + sGuiInputConfigList[4] = {"Back", false, "VIEW", ":/graphics/help/button_back_XBOX.svg"}; + sGuiInputConfigList[5] = {"Start", false, "MENU", ":/graphics/help/button_start_XBOX.svg"}; + sGuiInputConfigList[6] = {"A", false, "A", ":/graphics/help/mbuttons_a_XBOX.svg"}; + sGuiInputConfigList[7] = {"B", false, "B", ":/graphics/help/mbuttons_b_XBOX.svg"}; + sGuiInputConfigList[8] = {"X", true, "X", ":/graphics/help/mbuttons_x_XBOX.svg"}; + sGuiInputConfigList[9] = {"Y", true, "Y", ":/graphics/help/mbuttons_y_XBOX.svg"}; } - sGuiInputConfigList[10] = {"LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg"}; - sGuiInputConfigList[11] = {"RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg"}; - sGuiInputConfigList[12] = {"LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg"}; - sGuiInputConfigList[13] = {"RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg"}; - sGuiInputConfigList[14] = {"LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/help/thumbstick_up.svg"}; - sGuiInputConfigList[15] = {"LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/help/thumbstick_down.svg"}; - sGuiInputConfigList[16] = {"LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/help/thumbstick_left.svg"}; - sGuiInputConfigList[17] = {"LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg"}; - sGuiInputConfigList[18] = {"LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/help/thumbstick_click.svg"}; - sGuiInputConfigList[19] = {"RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/help/thumbstick_up.svg"}; - sGuiInputConfigList[20] = {"RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/help/thumbstick_down.svg"}; - sGuiInputConfigList[21] = {"RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/help/thumbstick_left.svg"}; - sGuiInputConfigList[22] = {"RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg"}; - sGuiInputConfigList[23] = {"RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/help/thumbstick_click.svg"}; + sGuiInputConfigList[10] = {"LeftShoulder", true, "LEFT SHOULDER", ":/graphics/help/button_l.svg"}; + sGuiInputConfigList[11] = {"RightShoulder", true, "RIGHT SHOULDER", ":/graphics/help/button_r.svg"}; + sGuiInputConfigList[12] = {"LeftTrigger", true, "LEFT TRIGGER", ":/graphics/help/button_lt.svg"}; + sGuiInputConfigList[13] = {"RightTrigger", true, "RIGHT TRIGGER", ":/graphics/help/button_rt.svg"}; + sGuiInputConfigList[14] = {"LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/graphics/help/thumbstick_up.svg"}; + sGuiInputConfigList[15] = {"LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/graphics/help/thumbstick_down.svg"}; + sGuiInputConfigList[16] = {"LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/graphics/help/thumbstick_left.svg"}; + sGuiInputConfigList[17] = {"LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/graphics/help/thumbstick_right.svg"}; + sGuiInputConfigList[18] = {"LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/graphics/help/thumbstick_click.svg"}; + sGuiInputConfigList[19] = {"RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/graphics/help/thumbstick_up.svg"}; + sGuiInputConfigList[20] = {"RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/graphics/help/thumbstick_down.svg"}; + sGuiInputConfigList[21] = {"RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/graphics/help/thumbstick_left.svg"}; + sGuiInputConfigList[22] = {"RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/graphics/help/thumbstick_right.svg"}; + sGuiInputConfigList[23] = {"RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/graphics/help/thumbstick_click.svg"}; // clang-format on } diff --git a/resources/graphics/badge_controller.svg b/resources/graphics/badge_controller.svg new file mode 100644 index 000000000..53faecd2f --- /dev/null +++ b/resources/graphics/badge_controller.svg @@ -0,0 +1,147 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_generic.svg b/resources/graphics/controllers/gamepad_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_64.svg b/resources/graphics/controllers/gamepad_nintendo_64.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_nintendo_64.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_nes.svg b/resources/graphics/controllers/gamepad_nintendo_nes.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_nintendo_nes.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_snes.svg b/resources/graphics/controllers/gamepad_nintendo_snes.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_nintendo_snes.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_playstation.svg b/resources/graphics/controllers/gamepad_playstation.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_playstation.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_xbox.svg b/resources/graphics/controllers/gamepad_xbox.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_xbox.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joycon_pair_nintendo.svg b/resources/graphics/controllers/joycon_pair_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joycon_pair_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_2_buttons.svg b/resources/graphics/controllers/joystick_arcade_2_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_2_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_3_buttons.svg b/resources/graphics/controllers/joystick_arcade_3_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_3_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_4_buttons.svg b/resources/graphics/controllers/joystick_arcade_4_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_4_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_6_buttons.svg b/resources/graphics/controllers/joystick_arcade_6_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_6_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_generic.svg b/resources/graphics/controllers/joystick_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/keyboard_generic.svg b/resources/graphics/controllers/keyboard_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/keyboard_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/keyboard_mouse_generic.svg b/resources/graphics/controllers/keyboard_mouse_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/keyboard_mouse_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/lightgun_generic.svg b/resources/graphics/controllers/lightgun_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/lightgun_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/lightgun_nintendo.svg b/resources/graphics/controllers/lightgun_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/lightgun_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/mouse_amiga.svg b/resources/graphics/controllers/mouse_amiga.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/mouse_amiga.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/mouse_generic.svg b/resources/graphics/controllers/mouse_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/mouse_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/steering_wheel_generic.svg b/resources/graphics/controllers/steering_wheel_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/steering_wheel_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/trackball_generic.svg b/resources/graphics/controllers/trackball_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/trackball_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/unknown.svg b/resources/graphics/controllers/unknown.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/unknown.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nintendo.svg b/resources/graphics/controllers/wii_remote_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/wii_remote_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/fav_add.svg b/resources/graphics/fav_add.svg deleted file mode 100644 index 34402f462..000000000 --- a/resources/graphics/fav_add.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/resources/graphics/fav_remove.svg b/resources/graphics/fav_remove.svg deleted file mode 100644 index bb5f3f2a7..000000000 --- a/resources/graphics/fav_remove.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/resources/help/button_1.svg b/resources/graphics/help/button_1.svg similarity index 100% rename from resources/help/button_1.svg rename to resources/graphics/help/button_1.svg diff --git a/resources/help/button_2.svg b/resources/graphics/help/button_2.svg similarity index 100% rename from resources/help/button_2.svg rename to resources/graphics/help/button_2.svg diff --git a/resources/help/button_3.svg b/resources/graphics/help/button_3.svg similarity index 100% rename from resources/help/button_3.svg rename to resources/graphics/help/button_3.svg diff --git a/resources/help/button_4.svg b/resources/graphics/help/button_4.svg similarity index 100% rename from resources/help/button_4.svg rename to resources/graphics/help/button_4.svg diff --git a/resources/help/button_a_PS.svg b/resources/graphics/help/button_a_PS.svg similarity index 100% rename from resources/help/button_a_PS.svg rename to resources/graphics/help/button_a_PS.svg diff --git a/resources/help/button_a_SNES.svg b/resources/graphics/help/button_a_SNES.svg similarity index 100% rename from resources/help/button_a_SNES.svg rename to resources/graphics/help/button_a_SNES.svg diff --git a/resources/help/button_a_XBOX.svg b/resources/graphics/help/button_a_XBOX.svg similarity index 100% rename from resources/help/button_a_XBOX.svg rename to resources/graphics/help/button_a_XBOX.svg diff --git a/resources/help/button_b_PS.svg b/resources/graphics/help/button_b_PS.svg similarity index 100% rename from resources/help/button_b_PS.svg rename to resources/graphics/help/button_b_PS.svg diff --git a/resources/help/button_b_SNES.svg b/resources/graphics/help/button_b_SNES.svg similarity index 100% rename from resources/help/button_b_SNES.svg rename to resources/graphics/help/button_b_SNES.svg diff --git a/resources/help/button_b_XBOX.svg b/resources/graphics/help/button_b_XBOX.svg similarity index 100% rename from resources/help/button_b_XBOX.svg rename to resources/graphics/help/button_b_XBOX.svg diff --git a/resources/help/button_back_PS4.svg b/resources/graphics/help/button_back_PS4.svg similarity index 100% rename from resources/help/button_back_PS4.svg rename to resources/graphics/help/button_back_PS4.svg diff --git a/resources/help/button_back_PS5.svg b/resources/graphics/help/button_back_PS5.svg similarity index 100% rename from resources/help/button_back_PS5.svg rename to resources/graphics/help/button_back_PS5.svg diff --git a/resources/help/button_back_SNES.svg b/resources/graphics/help/button_back_SNES.svg similarity index 100% rename from resources/help/button_back_SNES.svg rename to resources/graphics/help/button_back_SNES.svg diff --git a/resources/help/button_back_XBOX.svg b/resources/graphics/help/button_back_XBOX.svg similarity index 100% rename from resources/help/button_back_XBOX.svg rename to resources/graphics/help/button_back_XBOX.svg diff --git a/resources/help/button_back_XBOX360.svg b/resources/graphics/help/button_back_XBOX360.svg similarity index 100% rename from resources/help/button_back_XBOX360.svg rename to resources/graphics/help/button_back_XBOX360.svg diff --git a/resources/help/button_hotkey.svg b/resources/graphics/help/button_hotkey.svg similarity index 100% rename from resources/help/button_hotkey.svg rename to resources/graphics/help/button_hotkey.svg diff --git a/resources/help/button_l.svg b/resources/graphics/help/button_l.svg similarity index 100% rename from resources/help/button_l.svg rename to resources/graphics/help/button_l.svg diff --git a/resources/help/button_lr.svg b/resources/graphics/help/button_lr.svg similarity index 100% rename from resources/help/button_lr.svg rename to resources/graphics/help/button_lr.svg diff --git a/resources/help/button_lt.svg b/resources/graphics/help/button_lt.svg similarity index 100% rename from resources/help/button_lt.svg rename to resources/graphics/help/button_lt.svg diff --git a/resources/help/button_r.svg b/resources/graphics/help/button_r.svg similarity index 100% rename from resources/help/button_r.svg rename to resources/graphics/help/button_r.svg diff --git a/resources/help/button_rt.svg b/resources/graphics/help/button_rt.svg similarity index 100% rename from resources/help/button_rt.svg rename to resources/graphics/help/button_rt.svg diff --git a/resources/help/button_start_PS4.svg b/resources/graphics/help/button_start_PS4.svg similarity index 100% rename from resources/help/button_start_PS4.svg rename to resources/graphics/help/button_start_PS4.svg diff --git a/resources/help/button_start_PS5.svg b/resources/graphics/help/button_start_PS5.svg similarity index 100% rename from resources/help/button_start_PS5.svg rename to resources/graphics/help/button_start_PS5.svg diff --git a/resources/help/button_start_SNES.svg b/resources/graphics/help/button_start_SNES.svg similarity index 100% rename from resources/help/button_start_SNES.svg rename to resources/graphics/help/button_start_SNES.svg diff --git a/resources/help/button_start_XBOX.svg b/resources/graphics/help/button_start_XBOX.svg similarity index 100% rename from resources/help/button_start_XBOX.svg rename to resources/graphics/help/button_start_XBOX.svg diff --git a/resources/help/button_start_XBOX360.svg b/resources/graphics/help/button_start_XBOX360.svg similarity index 100% rename from resources/help/button_start_XBOX360.svg rename to resources/graphics/help/button_start_XBOX360.svg diff --git a/resources/help/button_x_PS.svg b/resources/graphics/help/button_x_PS.svg similarity index 100% rename from resources/help/button_x_PS.svg rename to resources/graphics/help/button_x_PS.svg diff --git a/resources/help/button_x_SNES.svg b/resources/graphics/help/button_x_SNES.svg similarity index 100% rename from resources/help/button_x_SNES.svg rename to resources/graphics/help/button_x_SNES.svg diff --git a/resources/help/button_x_XBOX.svg b/resources/graphics/help/button_x_XBOX.svg similarity index 100% rename from resources/help/button_x_XBOX.svg rename to resources/graphics/help/button_x_XBOX.svg diff --git a/resources/help/button_y_PS.svg b/resources/graphics/help/button_y_PS.svg similarity index 100% rename from resources/help/button_y_PS.svg rename to resources/graphics/help/button_y_PS.svg diff --git a/resources/help/button_y_SNES.svg b/resources/graphics/help/button_y_SNES.svg similarity index 100% rename from resources/help/button_y_SNES.svg rename to resources/graphics/help/button_y_SNES.svg diff --git a/resources/help/button_y_XBOX.svg b/resources/graphics/help/button_y_XBOX.svg similarity index 100% rename from resources/help/button_y_XBOX.svg rename to resources/graphics/help/button_y_XBOX.svg diff --git a/resources/help/dpad_all.svg b/resources/graphics/help/dpad_all.svg similarity index 100% rename from resources/help/dpad_all.svg rename to resources/graphics/help/dpad_all.svg diff --git a/resources/help/dpad_down.svg b/resources/graphics/help/dpad_down.svg similarity index 100% rename from resources/help/dpad_down.svg rename to resources/graphics/help/dpad_down.svg diff --git a/resources/help/dpad_left.svg b/resources/graphics/help/dpad_left.svg similarity index 100% rename from resources/help/dpad_left.svg rename to resources/graphics/help/dpad_left.svg diff --git a/resources/help/dpad_leftright.svg b/resources/graphics/help/dpad_leftright.svg similarity index 100% rename from resources/help/dpad_leftright.svg rename to resources/graphics/help/dpad_leftright.svg diff --git a/resources/help/dpad_right.svg b/resources/graphics/help/dpad_right.svg similarity index 100% rename from resources/help/dpad_right.svg rename to resources/graphics/help/dpad_right.svg diff --git a/resources/help/dpad_up.svg b/resources/graphics/help/dpad_up.svg similarity index 100% rename from resources/help/dpad_up.svg rename to resources/graphics/help/dpad_up.svg diff --git a/resources/help/dpad_updown.svg b/resources/graphics/help/dpad_updown.svg similarity index 100% rename from resources/help/dpad_updown.svg rename to resources/graphics/help/dpad_updown.svg diff --git a/resources/help/mbuttons_a_PS.svg b/resources/graphics/help/mbuttons_a_PS.svg similarity index 100% rename from resources/help/mbuttons_a_PS.svg rename to resources/graphics/help/mbuttons_a_PS.svg diff --git a/resources/help/mbuttons_a_SNES.svg b/resources/graphics/help/mbuttons_a_SNES.svg similarity index 100% rename from resources/help/mbuttons_a_SNES.svg rename to resources/graphics/help/mbuttons_a_SNES.svg diff --git a/resources/help/mbuttons_a_XBOX.svg b/resources/graphics/help/mbuttons_a_XBOX.svg similarity index 100% rename from resources/help/mbuttons_a_XBOX.svg rename to resources/graphics/help/mbuttons_a_XBOX.svg diff --git a/resources/help/mbuttons_b_PS.svg b/resources/graphics/help/mbuttons_b_PS.svg similarity index 100% rename from resources/help/mbuttons_b_PS.svg rename to resources/graphics/help/mbuttons_b_PS.svg diff --git a/resources/help/mbuttons_b_SNES.svg b/resources/graphics/help/mbuttons_b_SNES.svg similarity index 100% rename from resources/help/mbuttons_b_SNES.svg rename to resources/graphics/help/mbuttons_b_SNES.svg diff --git a/resources/help/mbuttons_b_XBOX.svg b/resources/graphics/help/mbuttons_b_XBOX.svg similarity index 100% rename from resources/help/mbuttons_b_XBOX.svg rename to resources/graphics/help/mbuttons_b_XBOX.svg diff --git a/resources/help/mbuttons_x_PS.svg b/resources/graphics/help/mbuttons_x_PS.svg similarity index 100% rename from resources/help/mbuttons_x_PS.svg rename to resources/graphics/help/mbuttons_x_PS.svg diff --git a/resources/help/mbuttons_x_SNES.svg b/resources/graphics/help/mbuttons_x_SNES.svg similarity index 100% rename from resources/help/mbuttons_x_SNES.svg rename to resources/graphics/help/mbuttons_x_SNES.svg diff --git a/resources/help/mbuttons_x_XBOX.svg b/resources/graphics/help/mbuttons_x_XBOX.svg similarity index 100% rename from resources/help/mbuttons_x_XBOX.svg rename to resources/graphics/help/mbuttons_x_XBOX.svg diff --git a/resources/help/mbuttons_y_PS.svg b/resources/graphics/help/mbuttons_y_PS.svg similarity index 100% rename from resources/help/mbuttons_y_PS.svg rename to resources/graphics/help/mbuttons_y_PS.svg diff --git a/resources/help/mbuttons_y_SNES.svg b/resources/graphics/help/mbuttons_y_SNES.svg similarity index 100% rename from resources/help/mbuttons_y_SNES.svg rename to resources/graphics/help/mbuttons_y_SNES.svg diff --git a/resources/help/mbuttons_y_XBOX.svg b/resources/graphics/help/mbuttons_y_XBOX.svg similarity index 100% rename from resources/help/mbuttons_y_XBOX.svg rename to resources/graphics/help/mbuttons_y_XBOX.svg diff --git a/resources/help/thumbstick.svg b/resources/graphics/help/thumbstick.svg similarity index 100% rename from resources/help/thumbstick.svg rename to resources/graphics/help/thumbstick.svg diff --git a/resources/help/thumbstick_click.svg b/resources/graphics/help/thumbstick_click.svg similarity index 100% rename from resources/help/thumbstick_click.svg rename to resources/graphics/help/thumbstick_click.svg diff --git a/resources/help/thumbstick_down.svg b/resources/graphics/help/thumbstick_down.svg similarity index 100% rename from resources/help/thumbstick_down.svg rename to resources/graphics/help/thumbstick_down.svg diff --git a/resources/help/thumbstick_left.svg b/resources/graphics/help/thumbstick_left.svg similarity index 100% rename from resources/help/thumbstick_left.svg rename to resources/graphics/help/thumbstick_left.svg diff --git a/resources/help/thumbstick_right.svg b/resources/graphics/help/thumbstick_right.svg similarity index 100% rename from resources/help/thumbstick_right.svg rename to resources/graphics/help/thumbstick_right.svg diff --git a/resources/help/thumbstick_up.svg b/resources/graphics/help/thumbstick_up.svg similarity index 100% rename from resources/help/thumbstick_up.svg rename to resources/graphics/help/thumbstick_up.svg diff --git a/themes/rbsimple-DE/theme.xml b/themes/rbsimple-DE/theme.xml index 690d7f0c4..130467fbc 100644 --- a/themes/rbsimple-DE/theme.xml +++ b/themes/rbsimple-DE/theme.xml @@ -237,14 +237,16 @@ based on: 'recalbox-multi' by the Recalbox community right - 0.815 0.675 + 0.880 0.757 0.13 0.1635 - 0 0 + 0.5 0.5 left 3 2 - 0.0028125 0.005 - favorite completed kidgame broken altemulator + 0.5 0.572 + 0.67 + -1.0 0.005 + favorite, completed, kidgame, broken, controller, altemulator