diff --git a/CHANGELOG.md b/CHANGELOG.md index 3069bc8f5..af4d4a9f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,14 +23,16 @@ * Improved the gamelist filter screen to not allow filtering of values where there is no actual data to filter, e.g. Favorites for a system with no favorite games * Grayed out all fields in the gamelist filter screen where there is no data to filter, previously some fields were removed entirely and some could still be used * Added the ability to filter on blank/unknown values for Genre, Player, Developer, Publisher and Alternative emulator. -* Added a filter for "Alternative emulator" and sorted the filters in the same order as the metadata editor fields +* Added filters for "Alternative emulator" and "Controller badges" and sorted the filters in the same order as the metadata editor fields * Added a menu option to change the application exit key combination +* Added an option to preload the gamelists on startup which leads to smoother navigation when first entering each gamelist * Lowered the minimum supported screen resolution from 640x480 to 224x224 to support arcade cabinet displays such as those running at 384x224 and 224x384 * Expanded the themeable options for "helpsystem" to support custom button graphics, dimmed text and icon colors, upper/lower/camel case and custom spacing * Made the scrolling speed of ScrollableContainer more consistent across various screen resolutions and display aspect ratios * Decreased the amount of text that ScrollableContainer renders above and below the starting position as content is scrolled * Made the game name and description stop scrolling when running the media viewer, the screensaver or when running in the background while a game is launched * Added notification popups when plugging in or removing controllers +* Made large optimizations to the SVG rendering which reduces application startup time dramatically when many systems are populated * Changed to loading the default theme set rbsimple-DE instead of the first available theme if the currently configured theme is missing * Added support for using the left and right trigger buttons in the help prompts * Removed the "Choose" entry from the help prompts in the gamelist view @@ -56,6 +58,8 @@ * Added support for a new type of "flat style" button to ButtonComponent * Added support for correctly navigating arbitrarily sized ComponentGrid entries, i.e. those spanning multiple cells * Bundled the bold font version of Fontfabric Akrobat +* Moved the resources/help directory to resources/graphics/help +* Removed the unused graphics files resources/graphics/fav_add.svg and resources/graphics/fav_remove.svg * Added RapidJSON as a Git subtree * Added the GLM (OpenGL Mathematics) library as a Git subtree * Replaced all built-in matrix and vector data types and functions with GLM library equivalents @@ -100,6 +104,7 @@ * 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 had very inconsistent widths at different screen aspect ratios * 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b37752bfe..15f31d09b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,18 +47,18 @@ This plan is under constant review so expect it to change from time to time. Sti #### v1.4 +* Bulk metadata editor * Localization/multi-language support -* Reorganize the menus, possibly adding basic/advanced modes * Authoring tools to clean up orphaned gamelist entries, media files etc. +* Add scraping of game manuals and maps and create a viewer for these (with PDF, GIF, JPG and PNG support) * Scrollbar component for the gamelist view which can be used by the themes * Web proxy support for the scraper * Add "time played" counter per game, similar to how it works in Steam -* Preload all built-in resources and never clear them from the cache * Improve multi-threading #### v1.5 -* Bulk metadata editor +* Reorganize the menus, possibly adding basic/advanced modes * Simple file browsing component * Improve the performance of the GLSL shader code * Animated menu elements like switches, tick boxes, smooth scrolling etc. diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index cd33d350f..cead7d5fd 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -1870,6 +1870,7 @@ There are two basic categories of metadata, `game` and `folders` and the metdata * `nomultiscrape` - bool, indicates whether the game should be excluded from the multi-scraper * `hidemetadata` - bool, indicates whether to hide most of the metadata fields when displaying the game in the gamelist view * `playcount` - integer, the number of times this game has been played +* `controller` - string, used to display controller badges * `altemulator` - string, overrides the emulator/launch command on a per game basis * `lastplayed` - statistic, datetime, the last date and time this game was played @@ -1891,6 +1892,7 @@ For folders, most of the fields are identical although some are removed. In the * `broken` * `nomultiscrape` * `hidemetadata` +* `controller` * `lastplayed` - statistic, for folders this is inherited by the latest game file launched inside the folder. **Additional gamelist.xml information:** diff --git a/THEMES-DEV.md b/THEMES-DEV.md index 711424c46..b94cd4bc8 100644 --- a/THEMES-DEV.md +++ b/THEMES-DEV.md @@ -921,7 +921,7 @@ 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. +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). * `pos` - type: NORMALIZED_PAIR. * `size` - type: NORMALIZED_PAIR. @@ -933,10 +933,12 @@ It's strongly recommended to use the same image dimensions for all badges as var - angle in degrees that the image should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise. Default is `0`. * `rotationOrigin` - type: NORMALIZED_PAIR. - Point around which the image will be rotated. Default is `0.5 0.5`. -* `itemsPerRow` - type: FLOAT. - - 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`. +* `direction` - type: STRING. + - Valid values are "row" or "column". Controls the primary layout direction (line axis) for the badges. Lines will fill up in the specified direction. Default is `row`. +* `lines` - type: FLOAT. + - The number of lines available. Default is `2`. +* `itemsPerLine` - type: FLOAT. + - Number of badges that fit on a line. When more badges are available a new line will be started. Default is `4`. * `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 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. diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 483fb7a3b..d56306a2a 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, 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. +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 via the metadata editor, the corresponding controller badge will be shown. And if an alternative emulator has been selected for the specific game, that badge will be displayed. 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._ @@ -1252,6 +1252,10 @@ Enabling this option offloads video decoding to the GPU. Whether this actually i With this option enabled, videos with lower frame rates than 60 FPS, such as 24 and 30 will get upscaled to 60 FPS. This results in slightly smoother playback for some videos. There is a small performance hit from this option, so on weaker machines it may be necessary to disable it for fluent video playback. This setting has no effect when using the VLC video player. If the VLC video player is not included in the ES-DE build, the "(FFmpeg)" text is omitted from the setting name. +**Preload gamelists on startup** + +When this option is enabled, all gamelists will be loaded on application startup. This will increase the startup time slightly and lead to a higher initial memory utilization, but navigation will be smoother the first time a gamelist is entered. The improvement is especially noticeable when the _slide_ transition style has been selected. + **Enable alternative emulators per game** If enabled, you will be able to select alternative emulators per game using the metadata editor. It's only recommended to disable this option for testing purposes. @@ -1372,9 +1376,11 @@ The following filters can be applied: **Broken** +**Controller badge** + **Alternative emulator** -With the exception of the game name text filter, all available filter values are assembled from metadata from the actual gamelist, so if there is no data to filter for the specific field, the text _Nothing to filter_ will be displayed. This for example happens for the _Completed_ filter if there are no games marked as having been completed in the current gamelist. +With the exception of the game name text filter, all available filter values are assembled from metadata from the actual gamelist, so if there is no data to filter for the specific field, the text _Nothing to filter_ will be displayed. This for example happens for the _Completed_ filter if there are no games marked as having been completed in the current gamelist. The same happens if a metadata setting is identical for all games, such as all games being flagged as favorites. Be aware that although folders can have most of the metadata values set, the filters are only applied to files (this is also true for the game name text filter). So if you for example set a filter to only display your favorite games, any folder that contains a favorite game will be displayed, and other folders which are themselves marked as favorites but that do not contain any favorite games will be hidden. @@ -1476,9 +1482,9 @@ 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** +**Controller badge** -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. +This entry provides a selection of controller icons that are built into ES-DE (although the theme set can override the actual graphics files). The selected icon 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. **Alternative emulator** _(files only)_ diff --git a/es-app/src/FileFilterIndex.cpp b/es-app/src/FileFilterIndex.cpp index 20063dea8..ec4384c18 100644 --- a/es-app/src/FileFilterIndex.cpp +++ b/es-app/src/FileFilterIndex.cpp @@ -33,6 +33,7 @@ FileFilterIndex::FileFilterIndex() , mFilterByKidGame(false) , mFilterByHidden(false) , mFilterByBroken(false) + , mFilterByController(false) , mFilterByAltemulator(false) { clearAllFilters(); @@ -50,6 +51,7 @@ FileFilterIndex::FileFilterIndex() {KIDGAME_FILTER, &mKidGameIndexAllKeys, &mFilterByKidGame, &mKidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME"}, {HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN"}, {BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN"}, + {CONTROLLER_FILTER, &mControllerIndexAllKeys, &mFilterByController, &mControllerIndexFilteredKeys, "controller", false, "", "CONTROLLER BADGE"}, {ALTEMULATOR_FILTER, &mAltemulatorIndexAllKeys, &mFilterByAltemulator, &mAltemulatorIndexFilteredKeys, "altemulator", false, "", "ALTERNATIVE EMULATOR"} }; // clang-format on @@ -82,6 +84,7 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) {&mKidGameIndexAllKeys, &(indexToImport->mKidGameIndexAllKeys)}, {&mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys)}, {&mBrokenIndexAllKeys, &(indexToImport->mBrokenIndexAllKeys)}, + {&mControllerIndexAllKeys, &(indexToImport->mControllerIndexAllKeys)}, {&mAltemulatorIndexAllKeys, &(indexToImport->mAltemulatorIndexAllKeys)}, }; @@ -119,6 +122,7 @@ void FileFilterIndex::resetIndex() clearIndex(mKidGameIndexAllKeys); clearIndex(mHiddenIndexAllKeys); clearIndex(mBrokenIndexAllKeys); + clearIndex(mControllerIndexAllKeys); clearIndex(mAltemulatorIndexAllKeys); } @@ -215,6 +219,12 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, key = Utils::String::toUpper(game->metadata.get("broken")); break; } + case CONTROLLER_FILTER: { + if (getSecondary) + break; + key = Utils::String::toUpper(game->metadata.get("controller")); + break; + } case ALTEMULATOR_FILTER: { if (getSecondary) break; @@ -231,8 +241,8 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, type == PUBLISHER_FILTER) && Utils::String::toUpper(key) == UNKNOWN_LABEL) key = ViewController::CROSSEDCIRCLE_CHAR + " UNKNOWN"; - else if (type == ALTEMULATOR_FILTER && key.empty()) - key = ViewController::CROSSEDCIRCLE_CHAR + " NONE DEFINED"; + else if ((type == CONTROLLER_FILTER || type == ALTEMULATOR_FILTER) && key.empty()) + key = ViewController::CROSSEDCIRCLE_CHAR + " NONE SELECTED"; else if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) key = UNKNOWN_LABEL; @@ -251,6 +261,7 @@ void FileFilterIndex::addToIndex(FileData* game) manageKidGameEntryInIndex(game); manageHiddenEntryInIndex(game); manageBrokenEntryInIndex(game); + manageControllerEntryInIndex(game); manageAltemulatorEntryInIndex(game); } @@ -266,6 +277,7 @@ void FileFilterIndex::removeFromIndex(FileData* game) manageKidGameEntryInIndex(game, true); manageHiddenEntryInIndex(game, true); manageBrokenEntryInIndex(game, true); + manageControllerEntryInIndex(game, true); manageAltemulatorEntryInIndex(game, true); } @@ -365,6 +377,9 @@ void FileFilterIndex::debugPrintIndexes() for (auto x : mBrokenIndexAllKeys) { LOG(LogInfo) << "Broken Index: " << x.first << ": " << x.second; } + for (auto x : mControllerIndexAllKeys) { + LOG(LogInfo) << "Controller Index: " << x.first << ": " << x.second; + } for (auto x : mAltemulatorIndexAllKeys) { LOG(LogInfo) << "Altemulator Index: " << x.first << ": " << x.second; } @@ -444,28 +459,29 @@ bool FileFilterIndex::isFiltered() if (UIModeController::getInstance()->isUIModeKid()) { return (mFilterByText || mFilterByRatings || mFilterByDeveloper || mFilterByPublisher || mFilterByGenre || mFilterByPlayers || mFilterByFavorites || mFilterByCompleted || - mFilterByHidden || mFilterByBroken || mFilterByAltemulator); + mFilterByHidden || mFilterByBroken || mFilterByController || mFilterByAltemulator); } else { return (mFilterByText || mFilterByRatings || mFilterByDeveloper || mFilterByPublisher || mFilterByGenre || mFilterByPlayers || mFilterByFavorites || mFilterByCompleted || - mFilterByKidGame || mFilterByHidden || mFilterByBroken || mFilterByAltemulator); + mFilterByKidGame || mFilterByHidden || mFilterByBroken || mFilterByController || + mFilterByAltemulator); } } bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) { - const FilterIndexType filterTypes[11] = {RATINGS_FILTER, DEVELOPER_FILTER, PUBLISHER_FILTER, - GENRE_FILTER, PLAYER_FILTER, FAVORITES_FILTER, - COMPLETED_FILTER, KIDGAME_FILTER, HIDDEN_FILTER, - BROKEN_FILTER, ALTEMULATOR_FILTER}; - std::vector filterKeysList[11] = { + const FilterIndexType filterTypes[12] = { + RATINGS_FILTER, DEVELOPER_FILTER, PUBLISHER_FILTER, GENRE_FILTER, + PLAYER_FILTER, FAVORITES_FILTER, COMPLETED_FILTER, KIDGAME_FILTER, + HIDDEN_FILTER, BROKEN_FILTER, CONTROLLER_FILTER, ALTEMULATOR_FILTER}; + std::vector filterKeysList[12] = { mRatingsIndexFilteredKeys, mDeveloperIndexFilteredKeys, mPublisherIndexFilteredKeys, mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys, mFavoritesIndexFilteredKeys, mCompletedIndexFilteredKeys, mKidGameIndexFilteredKeys, mHiddenIndexFilteredKeys, - mBrokenIndexFilteredKeys, mAltemulatorIndexFilteredKeys}; + mBrokenIndexFilteredKeys, mControllerIndexFilteredKeys, mAltemulatorIndexFilteredKeys}; - for (int i = 0; i < 11; i++) { + for (int i = 0; i < 12; i++) { if (filterTypes[i] == type) { for (std::vector::const_iterator it = filterKeysList[i].cbegin(); it != filterKeysList[i].cend(); it++) { @@ -611,6 +627,12 @@ void FileFilterIndex::manageBrokenEntryInIndex(FileData* game, bool remove) manageIndexEntry(&mBrokenIndexAllKeys, key, remove); } +void FileFilterIndex::manageControllerEntryInIndex(FileData* game, bool remove) +{ + std::string key = getIndexableKey(game, CONTROLLER_FILTER, false); + manageIndexEntry(&mControllerIndexAllKeys, key, remove); +} + void FileFilterIndex::manageAltemulatorEntryInIndex(FileData* game, bool remove) { std::string key = getIndexableKey(game, ALTEMULATOR_FILTER, false); diff --git a/es-app/src/FileFilterIndex.h b/es-app/src/FileFilterIndex.h index d4d744a7f..975c58432 100644 --- a/es-app/src/FileFilterIndex.h +++ b/es-app/src/FileFilterIndex.h @@ -31,6 +31,7 @@ enum FilterIndexType { KIDGAME_FILTER, HIDDEN_FILTER, BROKEN_FILTER, + CONTROLLER_FILTER, ALTEMULATOR_FILTER }; @@ -82,6 +83,7 @@ private: void manageKidGameEntryInIndex(FileData* game, bool remove = false); void manageHiddenEntryInIndex(FileData* game, bool remove = false); void manageBrokenEntryInIndex(FileData* game, bool remove = false); + void manageControllerEntryInIndex(FileData* game, bool remove = false); void manageAltemulatorEntryInIndex(FileData* game, bool remove = false); void manageIndexEntry(std::map* index, std::string key, bool remove); @@ -102,6 +104,7 @@ private: bool mFilterByKidGame; bool mFilterByHidden; bool mFilterByBroken; + bool mFilterByController; bool mFilterByAltemulator; std::map mRatingsIndexAllKeys; @@ -114,6 +117,7 @@ private: std::map mKidGameIndexAllKeys; std::map mHiddenIndexAllKeys; std::map mBrokenIndexAllKeys; + std::map mControllerIndexAllKeys; std::map mAltemulatorIndexAllKeys; std::vector mRatingsIndexFilteredKeys; @@ -126,6 +130,7 @@ private: std::vector mKidGameIndexFilteredKeys; std::vector mHiddenIndexFilteredKeys; std::vector mBrokenIndexFilteredKeys; + std::vector mControllerIndexFilteredKeys; std::vector mAltemulatorIndexFilteredKeys; }; diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 943099fcb..1dc7a10e1 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -37,7 +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}, +{"controller", MD_CONTROLLER, "", false, "controller badge", "select controller badge", false}, {"altemulator", MD_ALT_EMULATOR, "", false, "alternative emulator", "select alternative emulator", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; @@ -58,7 +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}, +{"controller", MD_CONTROLLER, "", false, "controller badge", "select controller badge", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; // clang-format on diff --git a/es-app/src/guis/GuiGamelistFilter.cpp b/es-app/src/guis/GuiGamelistFilter.cpp index 6feda35e7..7721e38bf 100644 --- a/es-app/src/guis/GuiGamelistFilter.cpp +++ b/es-app/src/guis/GuiGamelistFilter.cpp @@ -11,9 +11,11 @@ #include "guis/GuiGamelistFilter.h" #include "SystemData.h" +#include "components/BadgeComponent.h" #include "components/OptionListComponent.h" #include "guis/GuiTextEditKeyboardPopup.h" #include "guis/GuiTextEditPopup.h" +#include "utils/StringUtil.h" #include "views/UIModeController.h" #include "views/ViewController.h" @@ -185,8 +187,21 @@ void GuiGamelistFilter::addFiltersToMenu() optionList->setOverrideMultiText("NOTHING TO FILTER"); } - for (auto it : *allKeys) - optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type)); + if (type == CONTROLLER_FILTER) { + for (auto it : *allKeys) { + std::string displayName = + BadgeComponent::getDisplayName(Utils::String::toLower(it.first)); + if (displayName == "unknown") + displayName = it.first; + optionList->add(displayName, it.first, + mFilterIndex->isKeyBeingFilteredBy(it.first, type)); + } + } + else { + for (auto it : *allKeys) + optionList->add(it.first, it.first, + mFilterIndex->isKeyBeingFilteredBy(it.first, type)); + } if (allKeys->size() == 0) optionList->add("", "", false); diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index ffa9cd0f2..ebcfee71a 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -593,6 +593,7 @@ void GuiMenu::openUIOptions() } }); + s->setSize(mSize); mWindow->pushGui(s); } @@ -700,6 +701,7 @@ void GuiMenu::openSoundOptions() }); } + s->setSize(mSize); mWindow->pushGui(s); } @@ -756,6 +758,7 @@ void GuiMenu::openInputDeviceOptions() configure_input_row.makeAcceptInputHandler(std::bind(&GuiMenu::openConfigInput, this, s)); s->addRow(configure_input_row); + s->setSize(mSize); mWindow->pushGui(s); } @@ -1043,6 +1046,17 @@ void GuiMenu::openOtherOptions() } }); + // Whether to preload the gamelists on application startup. + auto preloadGamelists = std::make_shared(mWindow); + preloadGamelists->setState(Settings::getInstance()->getBool("PreloadGamelists")); + s->addWithLabel("PRELOAD GAMELISTS ON STARTUP", preloadGamelists); + s->addSaveFunc([preloadGamelists, s] { + if (preloadGamelists->getState() != Settings::getInstance()->getBool("PreloadGamelists")) { + Settings::getInstance()->setBool("PreloadGamelists", preloadGamelists->getState()); + s->setNeedsSaving(); + } + }); + // Whether to enable alternative emulators per game (the option to disable this is intended // primarily for testing purposes). auto alternativeEmulatorPerGame = std::make_shared(mWindow); @@ -1180,12 +1194,14 @@ void GuiMenu::openOtherOptions() run_in_background->setCallback(launchWorkaroundToggleFunc); #endif + s->setSize(mSize); mWindow->pushGui(s); } void GuiMenu::openUtilitiesMenu() { auto s = new GuiSettings(mWindow, "UTILITIES"); + s->setSize(mSize); mWindow->pushGui(s); } @@ -1263,6 +1279,7 @@ void GuiMenu::openQuitMenu() row.addElement(powerOffText, true); s->addRow(row); + s->setSize(mSize); mWindow->pushGui(s); } } diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index e46c1bb62..02d713c42 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -53,11 +53,11 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, , mMediaFilesUpdated{false} , mInvalidEmulatorEntry{false} { - mGameControllers = BadgeComponent::getGameControllers(); + mControllerBadges = BadgeComponent::getGameControllers(); // Remove the last "unknown" controller entry. - if (mGameControllers.size() > 1) - mGameControllers.pop_back(); + if (mControllerBadges.size() > 1) + mControllerBadges.pop_back(); addChild(&mBackground); addChild(&mGrid); @@ -206,11 +206,10 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, ed->setColor(TEXTCOLOR_USERMARKED); }; - row.makeAcceptInputHandler([this, title, scraperParams, ed, updateVal, - originalValue] { + row.makeAcceptInputHandler([this, title, ed, updateVal] { GuiSettings* s = new GuiSettings(mWindow, title); - for (auto controller : mGameControllers) { + for (auto controller : mControllerBadges) { std::string selectedLabel = ed->getValue(); std::string label; ComponentListRow row; diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index a4833c257..ec4ca13af 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -61,7 +61,7 @@ private: ScraperSearchParams mScraperParams; - std::vector mGameControllers; + std::vector mControllerBadges; std::vector> mEditors; std::vector mMetaDataDecl; diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp index ea6eaf037..cb5bffae2 100644 --- a/es-app/src/guis/GuiScreensaverOptions.cpp +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -17,6 +17,7 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& title) : GuiSettings(window, title) + , mWindow(window) { // Screensaver timer. auto screensaver_timer = std::make_shared(mWindow, 0.0f, 30.0f, 1.0f, "m"); @@ -95,6 +96,8 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& row.makeAcceptInputHandler( std::bind(&GuiScreensaverOptions::openVideoScreensaverOptions, this)); addRow(row); + + setSize(getMenuSize()); } void GuiScreensaverOptions::openSlideshowScreensaverOptions() @@ -206,6 +209,7 @@ void GuiScreensaverOptions::openSlideshowScreensaverOptions() } }); + s->setSize(mSize); mWindow->pushGui(s); } @@ -287,5 +291,6 @@ void GuiScreensaverOptions::openVideoScreensaverOptions() }); #endif + s->setSize(mSize); mWindow->pushGui(s); } diff --git a/es-app/src/guis/GuiScreensaverOptions.h b/es-app/src/guis/GuiScreensaverOptions.h index 9423ab6fe..1fe1993e3 100644 --- a/es-app/src/guis/GuiScreensaverOptions.h +++ b/es-app/src/guis/GuiScreensaverOptions.h @@ -18,6 +18,8 @@ public: GuiScreensaverOptions(Window* window, const std::string& title); private: + Window* mWindow; + void openSlideshowScreensaverOptions(); void openVideoScreensaverOptions(); }; diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 5daa8fdc9..366c30a81 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -961,7 +961,11 @@ void ViewController::preload() std::to_string(systemCount) + ")"); } (*it)->getIndex()->resetFilters(); - getGameListView(*it); + + if (Settings::getInstance()->getBool("PreloadGamelists")) + getGameListView(*it)->preloadGamelist(); + else + getGameListView(*it); } // Load navigation sounds, either from the theme if it supports it, or otherwise from diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index ed4db80c2..08302d5a7 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -25,6 +25,8 @@ public: virtual std::string getName() const override { return "detailed"; } virtual void launch(FileData* game) override; + virtual void preloadGamelist() override { updateInfoPanel(); } + protected: virtual void update(int deltaTime) override; diff --git a/es-app/src/views/gamelist/IGameListView.h b/es-app/src/views/gamelist/IGameListView.h index 853aa925e..212a062e9 100644 --- a/es-app/src/views/gamelist/IGameListView.h +++ b/es-app/src/views/gamelist/IGameListView.h @@ -32,6 +32,8 @@ public: void setTheme(const std::shared_ptr& theme); const std::shared_ptr& getTheme() const { return mTheme; } + virtual void preloadGamelist(){}; + virtual FileData* getCursor() = 0; virtual void setCursor(FileData*) = 0; virtual FileData* getNextEntry() = 0; diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index 06d1c1034..e0d6775de 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -28,6 +28,8 @@ public: virtual std::string getName() const override { return "video"; } virtual void launch(FileData* game) override; + virtual void preloadGamelist() override { updateInfoPanel(); } + protected: virtual void update(int deltaTime) override; diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index e7035633c..34c3a8c8b 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -242,6 +242,7 @@ void Settings::setDefaults() mBoolMap["VideoHardwareDecoding"] = {false, false}; #endif mBoolMap["VideoUpscaleFrameRate"] = {false, false}; + mBoolMap["PreloadGamelists"] = {true, true}; mBoolMap["AlternativeEmulatorPerGame"] = {true, true}; mBoolMap["ShowHiddenFiles"] = {true, true}; mBoolMap["ShowHiddenGames"] = {true, true}; diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 63039fceb..6f9e2020c 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -153,8 +153,9 @@ std::map> The {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, {"alignment", STRING}, - {"itemsPerRow", FLOAT}, - {"rows", FLOAT}, + {"direction", STRING}, + {"lines", FLOAT}, + {"itemsPerLine", FLOAT}, {"itemMargin", NORMALIZED_PAIR}, {"slots", STRING}, {"controllerPos", NORMALIZED_PAIR}, diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 0943e8f04..39c419136 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -24,7 +24,9 @@ #include #include +#if defined(USE_OPENGL_21) #define CLOCK_BACKGROUND_CREATION false +#endif Window::Window() : mScreensaver(nullptr) @@ -600,8 +602,8 @@ void Window::renderLoadingScreen(std::string text) static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); ImageComponent splash(this, true); - splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f); splash.setImage(":/graphics/splash.svg"); + splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f); splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x) / 2.0f, (Renderer::getScreenHeight() - splash.getSize().y) / 2.0f * 0.6f); splash.render(trans); diff --git a/es-core/src/components/BadgeComponent.cpp b/es-core/src/components/BadgeComponent.cpp index 5402b62a9..abe74f0d7 100644 --- a/es-core/src/components/BadgeComponent.cpp +++ b/es-core/src/components/BadgeComponent.cpp @@ -187,26 +187,37 @@ void BadgeComponent::applyTheme(const std::shared_ptr& theme, } } - 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 << "\""; + if (elem->has("direction")) { + const std::string direction{elem->get("direction")}; + if (direction != "row" && direction != "column") { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << direction << "\""; } else { - mFlexboxComponent.setItemsPerLine(static_cast(itemsPerRow)); + mFlexboxComponent.setDirection(direction); } } - 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 << "\""; + if (elem->has("lines")) { + const float lines{elem->get("lines")}; + if (lines < 1.0f || lines > 10.0f) { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << lines << "\""; } else { - mFlexboxComponent.setLines(static_cast(rows)); + mFlexboxComponent.setLines(static_cast(lines)); + } + } + + if (elem->has("itemsPerLine")) { + const float itemsPerLine{elem->get("itemsPerLine")}; + if (itemsPerLine < 1.0f || itemsPerLine > 10.0f) { + LOG(LogWarning) + << "BadgeComponent: Invalid theme configuration, set to \"" + << itemsPerLine << "\""; + } + else { + mFlexboxComponent.setItemsPerLine(static_cast(itemsPerLine)); } } @@ -266,8 +277,7 @@ void BadgeComponent::applyTheme(const std::shared_ptr& theme, FlexboxComponent::FlexboxItem item; item.label = slot; - ImageComponent badgeImage{mWindow}; - badgeImage.setForceLoad(true); + ImageComponent badgeImage{mWindow, false, false}; badgeImage.setImage(mBadgeIcons[slot]); item.baseImage = badgeImage; item.overlayImage = ImageComponent{mWindow}; diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 1457e3d14..cee1ab3bb 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -249,8 +249,16 @@ void ComponentList::updateCameraOffset() while (mCameraOffset < target && i < mEntries.size()) { mCameraOffset += getRowHeight(mEntries.at(i).data); if (mCameraOffset > totalHeight - mSize.y) { - if (mSetupCompleted && mCameraOffset != oldCameraOffset) - mBottomCameraOffset = true; + if (mSetupCompleted) { + if (mScrollIndicatorStatus == ComponentList::SCROLL_NONE && + oldCameraOffset == 0.0f) + break; + if (mScrollIndicatorStatus != ComponentList::SCROLL_NONE && + oldCameraOffset == 0.0f) + mBottomCameraOffset = true; + else if (mCameraOffset != oldCameraOffset) + mBottomCameraOffset = true; + } break; } i++; diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index da3bf1b1c..3b9fe9a0d 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -24,8 +24,8 @@ FlexboxComponent::FlexboxComponent(Window* window, std::vector& ite , mItems(items) , mDirection{DEFAULT_DIRECTION} , mAlignment{DEFAULT_ALIGNMENT} - , mItemsPerLine{DEFAULT_ITEMS_PER_LINE} , mLines{DEFAULT_LINES} + , mItemsPerLine{DEFAULT_ITEMS_PER_LINE} , mItemPlacement{DEFAULT_ITEM_PLACEMENT} , mItemMargin{glm::vec2{DEFAULT_MARGIN_X, DEFAULT_MARGIN_Y}} , mOverlayPosition{0.5f, 0.5f} @@ -86,8 +86,6 @@ void FlexboxComponent::setItemMargin(glm::vec2 value) void FlexboxComponent::computeLayout() { - // 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. mItemMargin.x = glm::clamp(mItemMargin.x, 0.0f, mSize.x / 2.0f); @@ -107,7 +105,13 @@ void FlexboxComponent::computeLayout() mItemsPerLine = static_cast(mItems.size()); } - glm::vec2 grid{mItemsPerLine, mLines}; + glm::vec2 grid{}; + + if (mDirection == "row") + grid = {mItemsPerLine, mLines}; + else + grid = {mLines, mItemsPerLine}; + glm::vec2 maxItemSize{(mSize + mItemMargin - grid * mItemMargin) / grid}; float rowHeight{0.0f}; @@ -146,11 +150,11 @@ void FlexboxComponent::computeLayout() maxItemSize = glm::round(maxItemSize); - bool alignRight{mAlignment == "right" && mDirection == "row"}; + bool alignRight{mAlignment == "right"}; float alignRightComp{0.0f}; // If right-aligning, move the overall container contents during grid setup. - if (alignRight) + if (alignRight && mDirection == "row") alignRightComp = std::round(mSize.x - ((maxItemSize.x + mItemMargin.x) * grid.x) + mItemMargin.x); @@ -166,11 +170,19 @@ void FlexboxComponent::computeLayout() } } } - else { // Column mode. + else if (mDirection == "column" && !alignRight) { + 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)), + y * (rowHeight + mItemMargin.y)}); + } + } + } + else { // Right-aligned. 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), + glm::vec2{(mSize.x - (x * (maxItemSize.x + mItemMargin.x)) - maxItemSize.x), y * (rowHeight + mItemMargin.y)}); } } @@ -185,7 +197,7 @@ void FlexboxComponent::computeLayout() if (!item.visible) continue; - if (pos > 0) { + if (mDirection == "row" && pos > 0) { if (itemPositions[pos - 1].y < itemPositions[pos].y) { lastY = itemPositions[pos].y; itemsOnLastRow = 0; @@ -225,8 +237,8 @@ void FlexboxComponent::computeLayout() pos++; } - // Apply right-align to the items (only works in row mode). - if (alignRight) { + // Apply right-align to the items if we're using row mode. + if (alignRight && mDirection == "row") { for (auto& item : mItems) { if (!item.visible) continue; diff --git a/es-core/src/components/FlexboxComponent.h b/es-core/src/components/FlexboxComponent.h index 4ab5ee5c3..ce5a6c60f 100644 --- a/es-core/src/components/FlexboxComponent.h +++ b/es-core/src/components/FlexboxComponent.h @@ -44,13 +44,6 @@ public: mLayoutValid = false; } - unsigned int getItemsPerLine() const { return mItemsPerLine; } - void setItemsPerLine(unsigned int value) - { - mItemsPerLine = value; - mLayoutValid = false; - } - unsigned int getLines() const { return mLines; } void setLines(unsigned int value) { @@ -58,6 +51,13 @@ public: mLayoutValid = false; } + unsigned int getItemsPerLine() const { return mItemsPerLine; } + void setItemsPerLine(unsigned int value) + { + mItemsPerLine = value; + mLayoutValid = false; + } + std::string getItemPlacement() const { return mItemPlacement; } void setItemPlacement(const std::string& value) { @@ -87,8 +87,8 @@ private: // Layout options. std::string mDirection; std::string mAlignment; - unsigned int mItemsPerLine; unsigned int mLines; + unsigned int mItemsPerLine; std::string mItemPlacement; glm::vec2 mItemMargin; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 4055c9989..92aa7c7d7 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -333,7 +333,7 @@ void ImageComponent::setSaturation(float saturation) void ImageComponent::updateVertices() { - if (!mTexture || !mTexture->isInitialized()) + if (!mTexture) return; // We go through this mess to make sure everything is properly rounded. @@ -398,7 +398,7 @@ void ImageComponent::render(const glm::mat4& parentTrans) Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); } // An image with zero size would normally indicate a corrupt image file. - if (mTexture->isInitialized() && mTexture->getSize() != glm::ivec2{}) { + if (mTexture->getSize() != glm::ivec2{}) { // Actually draw the image. // The bind() function returns false if the texture is not currently loaded. A blank // texture is bound in this case but we want to handle a fade so it doesn't just diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 40b15cf3d..e91955204 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -30,8 +30,6 @@ 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 @@ -109,7 +107,8 @@ private: bool mTargetIsMin; // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). - // Used internally whenever the resizing parameters or texture change. + // Used internally whenever the resizing parameters or texture change. This function also + // initiates the SVG rasterization. void resize(); Renderer::Vertex mVertices[4]; diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index d908499fd..e1b62b366 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -59,7 +59,7 @@ void NinePatchComponent::buildVertices() else scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f); - mTexture = TextureResource::get(mPath, false, false, true, true, scaleFactor); + mTexture = TextureResource::get(mPath, false, false, false, true, true, scaleFactor); if (mTexture->getSize() == glm::ivec2{}) { mVertices = nullptr; diff --git a/es-core/src/components/OptionListComponent.h b/es-core/src/components/OptionListComponent.h index c25812743..181b8693a 100644 --- a/es-core/src/components/OptionListComponent.h +++ b/es-core/src/components/OptionListComponent.h @@ -76,8 +76,12 @@ public: // Handles positioning/resizing of text and arrows. void onSizeChanged() override { - mLeftArrow.setResize(0, mText.getFont()->getLetterHeight()); - mRightArrow.setResize(0, mText.getFont()->getLetterHeight()); + if (mText.getFont()->getLetterHeight() != mLeftArrow.getSize().y || + mLeftArrow.getTexture()->getPendingRasterization()) + mLeftArrow.setResize(0, mText.getFont()->getLetterHeight()); + if (mText.getFont()->getLetterHeight() != mRightArrow.getSize().y || + mRightArrow.getTexture()->getPendingRasterization()) + mRightArrow.setResize(0, mText.getFont()->getLetterHeight()); if (mSize.x < (mLeftArrow.getSize().x + mRightArrow.getSize().x)) { LOG(LogWarning) << "OptionListComponent too narrow"; diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index 07eb7d36c..bcf0ca9d9 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -8,6 +8,7 @@ #include "components/SliderComponent.h" +#include "Window.h" #include "resources/Font.h" #define MOVE_REPEAT_DELAY 500 @@ -32,7 +33,7 @@ SliderComponent::SliderComponent( mKnob.setOrigin(0.5f, 0.5f); mKnob.setImage(":/graphics/slider_knob.svg"); - setSize(Renderer::getScreenWidth() * 0.15f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()); + setSize(window->peekGui()->getSize().x * 0.26f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()); } bool SliderComponent::input(InputConfig* config, Input input) @@ -80,15 +81,23 @@ void SliderComponent::render(const glm::mat4& parentTrans) glm::mat4 trans{parentTrans * getTransform()}; Renderer::setMatrix(trans); - // Render suffix. - if (mValueCache) - mFont->renderTextCache(mValueCache.get()); + if (Settings::getInstance()->getBool("DebugText")) { + Renderer::drawRect( + mSize.x - mTextCache->metrics.size.x, (mSize.y - mTextCache->metrics.size.y) / 2.0f, + mTextCache->metrics.size.x, mTextCache->metrics.size.y, 0x0000FF33, 0x0000FF33); + Renderer::drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x, + mSize.y, 0x00000033, 0x00000033); + } float width{mSize.x - mKnob.getSize().x - - (mValueCache ? - mValueCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : + (mTextCache ? + mTextCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : 0.0f)}; + // Render suffix. + if (mTextCache) + mFont->renderTextCache(mTextCache.get()); + // Render bar. Renderer::drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f - mBarHeight / 2.0f, width, mBarHeight, 0x777777FF, 0x777777FF); @@ -138,17 +147,17 @@ void SliderComponent::onValueChanged() const std::string max = ss.str(); glm::vec2 textSize = mFont->sizeText(max); - mValueCache = std::shared_ptr(mFont->buildTextCache( + mTextCache = std::shared_ptr(mFont->buildTextCache( val, mSize.x - textSize.x, (mSize.y - textSize.y) / 2.0f, 0x777777FF)); - mValueCache->metrics.size.x = textSize.x; // Fudge the width. + mTextCache->metrics.size.x = textSize.x; // Fudge the width. } mKnob.setResize(0.0f, std::round(mSize.y * 0.7f)); float barLength = mSize.x - mKnob.getSize().x - - (mValueCache ? mValueCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : - 0.0f); + (mTextCache ? mTextCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : + 0.0f); int barHeight = static_cast(std::round(2.0f * Renderer::getScreenHeightModifier())); diff --git a/es-core/src/components/SliderComponent.h b/es-core/src/components/SliderComponent.h index 9392364d9..2ca75cb9a 100644 --- a/es-core/src/components/SliderComponent.h +++ b/es-core/src/components/SliderComponent.h @@ -52,7 +52,7 @@ private: std::string mSuffix; std::shared_ptr mFont; - std::shared_ptr mValueCache; + std::shared_ptr mTextCache; }; #endif // ES_CORE_COMPONENTS_SLIDER_COMPONENT_H diff --git a/es-core/src/components/SwitchComponent.cpp b/es-core/src/components/SwitchComponent.cpp index a2712ec5d..e1249009a 100644 --- a/es-core/src/components/SwitchComponent.cpp +++ b/es-core/src/components/SwitchComponent.cpp @@ -49,16 +49,6 @@ void SwitchComponent::render(const glm::mat4& parentTrans) renderChildren(trans); } -void SwitchComponent::setResize(float width, float height) -{ - // Reposition the switch after resizing to make it centered. - const glm::vec2 oldSize = mImage.getSize(); - mImage.setResize(width, height); - const float xDiff = oldSize.x - mImage.getSize().x; - const float yDiff = oldSize.y - mImage.getSize().y; - mImage.setPosition(mImage.getPosition().x + xDiff, mImage.getPosition().y + yDiff / 2.0f); -} - void SwitchComponent::setState(bool state) { mState = state; @@ -81,6 +71,7 @@ void SwitchComponent::setValue(const std::string& statestring) void SwitchComponent::onStateChanged() { mImage.setImage(mState ? ":/graphics/on.svg" : ":/graphics/off.svg"); + mImage.setResize(mSize); // Change the color of the switch to reflect the changes. if (mState == mOriginalValue) diff --git a/es-core/src/components/SwitchComponent.h b/es-core/src/components/SwitchComponent.h index a0d1ac33c..47011b29d 100644 --- a/es-core/src/components/SwitchComponent.h +++ b/es-core/src/components/SwitchComponent.h @@ -22,8 +22,7 @@ public: void render(const glm::mat4& parentTrans) override; void onSizeChanged() override { mImage.setSize(mSize); } - void setResize(float width, float height) override; - + void setResize(float width, float height) override { setSize(width, height); } bool getState() const { return mState; } void setState(bool state); std::string getValue() const override; diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 9537b25d7..e9c05e198 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -24,16 +24,18 @@ #define DPI 96 TextureData::TextureData(bool tile) - : mTile(tile) - , mTextureID(0) + : mTile{tile} + , mTextureID{0} , mDataRGBA({}) - , mWidth(0) - , mHeight(0) - , mSourceWidth(0.0f) - , mSourceHeight(0.0f) - , mScaleDuringLoad(1.0f) - , mScalable(false) - , mLinearMagnify(false) + , mWidth{0} + , mHeight{0} + , mSourceWidth{0.0f} + , mSourceHeight{0.0f} + , mScaleDuringLoad{1.0f} + , mScalable{false} + , mLinearMagnify{false} + , mForceRasterization{false} + , mPendingRasterization{false} { } @@ -54,23 +56,28 @@ void TextureData::initFromPath(const std::string& path) bool TextureData::initSVGFromMemory(const std::string& fileData) { // If already initialized then don't process it again. - std::unique_lock lock(mMutex); + std::unique_lock lock{mMutex}; if (!mDataRGBA.empty()) return true; - NSVGimage* svgImage = nsvgParse(const_cast(fileData.c_str()), "px", DPI); + NSVGimage* svgImage{nsvgParse(const_cast(fileData.c_str()), "px", DPI)}; if (!svgImage) { LOG(LogError) << "Couldn't parse SVG image"; return false; } - // We want to rasterize this texture at a specific resolution. If the source size - // variables are set then use them, otherwise get them from the parsed file. - if ((mSourceWidth == 0.0f) && (mSourceHeight == 0.0f)) { - mSourceWidth = svgImage->width; - mSourceHeight = svgImage->height; + bool rasterize{true}; + + // If there is no image size defined yet, then don't rasterize unless mForceRasterization has + // been set (this is only used by NinePatchComponent to avoid flickering menus). + if (mSourceWidth == 0.0f && mSourceHeight == 0.0f) { + if (!mForceRasterization) + rasterize = false; + // Set a small temporary size that maintains the image aspect ratio. + mSourceWidth = 64.0f; + mSourceHeight = 64.0f * (svgImage->height / svgImage->width); } mWidth = static_cast(std::round(mSourceWidth * mScaleDuringLoad)); @@ -87,23 +94,31 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) std::round((static_cast(mWidth) / svgImage->width) * svgImage->height)); } - std::vector tempVector; - tempVector.reserve(mWidth * mHeight * 4); + if (rasterize) { + std::vector tempVector; + tempVector.reserve(mWidth * mHeight * 4); - NSVGrasterizer* rast = nsvgCreateRasterizer(); + NSVGrasterizer* rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth, - mHeight, mWidth * 4); + nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth, + mHeight, mWidth * 4); + + nsvgDeleteRasterizer(rast); + + mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(), + tempVector.data() + (mWidth * mHeight * 4)); + + ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight); + mPendingRasterization = false; + } + else { + // TODO: Fix this properly instead of using the single byte texture workaround. + mDataRGBA.push_back(0); + mPendingRasterization = true; + } - // This is important in order to avoid memory leaks. - nsvgDeleteRasterizer(rast); nsvgDelete(svgImage); - mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(), - tempVector.data() + (mWidth * mHeight * 4)); - - ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight); - return true; } diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index 0e76d8159..1717bf101 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -61,8 +61,13 @@ public: void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; } // Whether to use linear filtering when magnifying the texture. void setLinearMagnify(bool setting) { mLinearMagnify = setting; } + // Whether to rasterize the image even if a size has not been set yet. + void setForceRasterization(bool setting) { mForceRasterization = setting; } - std::vector getRawRGBAData() { return mDataRGBA; } + // Has the image been loaded but not yet been rasterized as the size was not known? + bool getPendingRasterization() { return mPendingRasterization; } + + std::vector& getRawRGBAData() { return mDataRGBA; } std::string getTextureFilePath() { return mPath; } bool tiled() { return mTile; } @@ -80,6 +85,8 @@ private: bool mScalable; bool mLinearMagnify; bool mReloadable; + bool mForceRasterization; + bool mPendingRasterization; }; #endif // ES_CORE_RESOURCES_TEXTURE_DATA_H diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index b6f7b8d8a..8217a9f58 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -8,16 +8,20 @@ #include "resources/TextureResource.h" -#include "resources/TextureData.h" #include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" TextureDataManager TextureResource::sTextureDataManager; std::map> TextureResource::sTextureMap; std::set TextureResource::sAllTextures; -TextureResource::TextureResource( - const std::string& path, bool tile, bool dynamic, bool linearMagnify, float scaleDuringLoad) +TextureResource::TextureResource(const std::string& path, + bool tile, + bool dynamic, + bool linearMagnify, + bool forceRasterization, + float scaleDuringLoad) : mTextureData(nullptr) , mForceLoad(false) { @@ -32,6 +36,7 @@ TextureResource::TextureResource( if (scaleDuringLoad != 1.0f) data->setScaleDuringLoad(scaleDuringLoad); data->setLinearMagnify(linearMagnify); + data->setForceRasterization(forceRasterization); // Force the texture manager to load it using a blocking load. sTextureDataManager.load(data, true); } @@ -42,6 +47,7 @@ TextureResource::TextureResource( if (scaleDuringLoad != 1.0f) data->setScaleDuringLoad(scaleDuringLoad); data->setLinearMagnify(linearMagnify); + data->setForceRasterization(forceRasterization); // Load it so we can read the width/height. data->load(); } @@ -148,14 +154,15 @@ std::shared_ptr TextureResource::get(const std::string& path, bool forceLoad, bool dynamic, bool linearMagnify, + bool forceRasterization, float scaleDuringLoad) { std::shared_ptr& rm = ResourceManager::getInstance(); const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); if (canonicalPath.empty()) { - std::shared_ptr tex( - new TextureResource("", tile, false, linearMagnify, scaleDuringLoad)); + std::shared_ptr tex(new TextureResource( + "", tile, false, linearMagnify, forceRasterization, scaleDuringLoad)); // Make sure we get properly deinitialized even though we do nothing on reinitialization. rm->addReloadable(tex); return tex; @@ -171,12 +178,13 @@ std::shared_ptr TextureResource::get(const std::string& path, // Need to create it. std::shared_ptr tex; - tex = std::shared_ptr( - new TextureResource(key.first, tile, dynamic, linearMagnify, scaleDuringLoad)); + tex = std::shared_ptr(new TextureResource( + key.first, tile, dynamic, linearMagnify, forceRasterization, scaleDuringLoad)); std::shared_ptr data = sTextureDataManager.get(tex.get()); // Is it an SVG? - if (key.first.substr(key.first.size() - 4, std::string::npos) != ".svg") { + if (Utils::String::toLower(key.first.substr(key.first.size() - 4, std::string::npos)) != + ".svg") { // Probably not. Add it to our map. We don't add SVGs because 2 SVGs might be // rasterized at different sizes. sTextureMap[key] = std::weak_ptr(tex); @@ -209,7 +217,7 @@ void TextureResource::rasterizeAt(float width, float height) data = sTextureDataManager.get(this); mSourceSize = glm::vec2{static_cast(width), static_cast(height)}; data->setSourceSize(static_cast(width), static_cast(height)); - if (mForceLoad || (mTextureData != nullptr)) + if (mForceLoad || mTextureData != nullptr) data->load(); } diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 91ba5ef7e..7ae64728b 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -10,6 +10,7 @@ #define ES_CORE_RESOURCES_TEXTURE_RESOURCE_H #include "resources/ResourceManager.h" +#include "resources/TextureData.h" #include "resources/TextureDataManager.h" #include "utils/MathUtil.h" @@ -30,6 +31,7 @@ public: bool forceLoad = false, bool dynamic = true, bool linearMagnify = false, + bool forceRasterization = false, float scaleDuringLoad = 1.0f); void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height); virtual void initFromMemory(const char* data, size_t length); @@ -38,6 +40,12 @@ public: // Returns the raw pixel values. std::vector getRawRGBAData(); + // Has the image been loaded but not yet been rasterized as the size was not known? + bool getPendingRasterization() + { + return (mTextureData != nullptr ? mTextureData->getPendingRasterization() : false); + } + std::string getTextureFilePath(); // For SVG graphics this function effectively rescales the image to the defined size. @@ -49,7 +57,6 @@ public: virtual ~TextureResource(); - bool isInitialized() const { return true; } bool isTiled() const; const glm::ivec2 getSize() const { return mSize; } @@ -65,6 +72,7 @@ protected: bool tile, bool dynamic, bool linearMagnify, + bool forceRasterization, float scaleDuringLoad); virtual void unload(std::shared_ptr& rm); virtual void reload(std::shared_ptr& rm); diff --git a/resources/graphics/badge_completed.svg b/resources/graphics/badge_completed.svg index 376d4f21b..941c24cc5 100644 --- a/resources/graphics/badge_completed.svg +++ b/resources/graphics/badge_completed.svg @@ -33,9 +33,9 @@ fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" - inkscape:zoom="9.6754505" - inkscape:cx="14.68785" - inkscape:cy="65.424484" + inkscape:zoom="3.0165741" + inkscape:cx="-308.2567" + inkscape:cy="78.979511" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -143,7 +143,7 @@ style="fill:#f0f0f0;fill-opacity:1" /> diff --git a/resources/graphics/controllers/gamepad_nintendo_64.svg b/resources/graphics/controllers/gamepad_nintendo_64.svg index b6b01c15a..48c9bf4e8 100644 --- a/resources/graphics/controllers/gamepad_nintendo_64.svg +++ b/resources/graphics/controllers/gamepad_nintendo_64.svg @@ -15,9 +15,34 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="gamepad_nintendo_64.svg"> + id="defs4919"> + + + + + + + + + + + inkscape:window-maximized="1" + inkscape:snap-bbox="false" /> @@ -55,14 +81,213 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g6500"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_nes.svg b/resources/graphics/controllers/gamepad_nintendo_nes.svg index b6b01c15a..e546a52eb 100644 --- a/resources/graphics/controllers/gamepad_nintendo_nes.svg +++ b/resources/graphics/controllers/gamepad_nintendo_nes.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="gamepad_nintendo_nes.svg"> + inkscape:window-maximized="1" + inkscape:snap-global="false" /> @@ -55,14 +56,278 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5404" + transform="matrix(0.98413614,0,0,0.98413614,0.08814355,4.6234203)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_snes.svg b/resources/graphics/controllers/gamepad_nintendo_snes.svg index b6b01c15a..f4e823894 100644 --- a/resources/graphics/controllers/gamepad_nintendo_snes.svg +++ b/resources/graphics/controllers/gamepad_nintendo_snes.svg @@ -2,6 +2,7 @@ + sodipodi:docname="gamepad_nintendo_snes.svg"> + id="g5490" + transform="translate(-1e-7,3.169167e-6)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_playstation.svg b/resources/graphics/controllers/gamepad_playstation.svg index b6b01c15a..9ae7276e1 100644 --- a/resources/graphics/controllers/gamepad_playstation.svg +++ b/resources/graphics/controllers/gamepad_playstation.svg @@ -15,9 +15,34 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="gamepad_playstation.svg"> + id="defs4919"> + + + + + + + + + + + id="g6486"> + inkscape:connector-curvature="0" + d="m 7.6061742,288.87228 c 0,-0.1636 0.13386,-0.29746 0.297458,-0.29746 h 0.484651 c 0.163598,0 0.297459,0.13386 0.297459,0.29746" + style="fill:#5a5a5a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.02098775" + id="path22-3" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg index b6b01c15a..3a8554f7d 100644 --- a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg +++ b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="joycon_left_or_right_nintendo.svg"> image/svg+xml - + @@ -55,15 +55,260 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g4910" + transform="matrix(1.0578414,0,0,1.0578414,-0.32138125,-15.666888)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inkscape:transform-center-y="-0.036071626" + d="m 2.95681,291.04147 0.1249556,0.21643 -0.2499112,0 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="-0.52359878" + sodipodi:arg1="-1.5707963" + sodipodi:r2="0.072143152" + sodipodi:r1="0.1442863" + sodipodi:cy="291.18576" + sodipodi:cx="2.95681" + sodipodi:sides="3" + id="path5401" + style="opacity:1;vector-effect:none;fill:#282828;fill-opacity:1;stroke:none;stroke-width:0.08117354;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" + sodipodi:type="star" /> + + + diff --git a/resources/graphics/controllers/joycon_pair_nintendo.svg b/resources/graphics/controllers/joycon_pair_nintendo.svg index b6b01c15a..649377f0a 100644 --- a/resources/graphics/controllers/joycon_pair_nintendo.svg +++ b/resources/graphics/controllers/joycon_pair_nintendo.svg @@ -15,9 +15,17 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="joycon_pair_nintendo.svg"> + id="defs4919"> + + + id="g4910" + transform="matrix(1.0578414,0,0,1.0578414,-0.32138125,-16.857513)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inkscape:transform-center-y="-0.036071626" + d="m 2.95681,291.04147 0.1249556,0.21643 -0.2499112,0 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="-0.52359878" + sodipodi:arg1="-1.5707963" + sodipodi:r2="0.072143152" + sodipodi:r1="0.1442863" + sodipodi:cy="291.18576" + sodipodi:cx="2.95681" + sodipodi:sides="3" + id="path5401" + style="opacity:1;vector-effect:none;fill:#282828;fill-opacity:1;stroke:none;stroke-width:0.08117354;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" + sodipodi:type="star" /> + + + diff --git a/resources/graphics/controllers/joystick_generic.svg b/resources/graphics/controllers/joystick_generic.svg index b6b01c15a..ebabd7953 100644 --- a/resources/graphics/controllers/joystick_generic.svg +++ b/resources/graphics/controllers/joystick_generic.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="joystick_generic.svg"> + id="g4868"> + + + + + + + + + + + + style="fill:#3c3c3b;fill-opacity:1;stroke-width:0.05127106" + inkscape:connector-curvature="0" + id="path30" + d="m 4.1096097,289.29845 c 0,0.46657 -0.3794056,0.84597 -0.8459724,0.84597 -0.4665665,0 -0.8459723,-0.3794 -0.8459723,-0.84597 0,-0.46657 0.3794058,-0.84598 0.8459723,-0.84598 0.4665668,0 0.8459724,0.37941 0.8459724,0.84598 z m -0.8459724,-0.74344 c -0.4101683,0 -0.7434302,0.33327 -0.7434302,0.74344 0,0.41017 0.3332619,0.74343 0.7434302,0.74343 0.4101685,0 0.7434305,-0.33326 0.7434305,-0.74343 0,-0.41017 -0.333262,-0.74344 -0.7434305,-0.74344 z" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/mouse_amiga.svg b/resources/graphics/controllers/mouse_amiga.svg index b6b01c15a..696cbd88d 100644 --- a/resources/graphics/controllers/mouse_amiga.svg +++ b/resources/graphics/controllers/mouse_amiga.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="mouse_amiga.svg"> + inkscape:window-maximized="1" + inkscape:snap-bbox="false" + inkscape:snap-global="false" /> @@ -55,15 +57,54 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5117"> + inkscape:connector-curvature="0" + id="rect4891" + d="m 5.4289836,285.88748 h 0.2545328 v 2.06353 H 5.4289836 Z" + style="opacity:1;vector-effect:none;fill:#60605d;fill-opacity:1;stroke:none;stroke-width:0.04231874;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + + + + + + + + + + + diff --git a/resources/graphics/controllers/unknown.svg b/resources/graphics/controllers/unknown.svg index b6b01c15a..e7b99fea2 100644 --- a/resources/graphics/controllers/unknown.svg +++ b/resources/graphics/controllers/unknown.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="6.7728148" - inkscape:cx="-21.346482" - inkscape:cy="-0.76992782" + inkscape:zoom="16.892814" + inkscape:cx="-37.558608" + inkscape:cy="23.448283" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -58,7 +58,7 @@ aria-label="?" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.72348785px;line-height:1.25;font-family:Digitalt;-inkscape-font-specification:Digitalt;letter-spacing:0px;word-spacing:0px;fill:#f00000;fill-opacity:1;stroke:#000000;stroke-width:0.2605817;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="text4825" - transform="matrix(0.91316098,0,0,0.91316098,1.9388092,25.308702)"> + transform="matrix(0.76718259,0,0,0.76718259,2.5170951,67.853186)"> + sodipodi:docname="wii_remote_nintendo.svg"> + id="g5266"> + inkscape:connector-curvature="0" + id="path6" + d="m 4.492749,286.76443 c -0.150954,0 -0.274402,0.12397 -0.274402,0.27492 v 9.05681 c 0,0.15095 0.123448,0.27492 0.274402,0.27492 h 2.1270018 c 0.1509541,0 0.2744021,-0.12397 0.2744021,-0.27492 v -9.05681 c 0,-0.15095 -0.123448,-0.27492 -0.2744021,-0.27492 z" + style="fill:#f8f8f8;stroke:#000000;stroke-width:0.079375;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg index b6b01c15a..d9699c383 100644 --- a/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg +++ b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="wii_remote_nunchuck_nintendo.svg"> + inkscape:window-maximized="1" + inkscape:snap-bbox="true" /> @@ -55,15 +56,313 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5629" + transform="matrix(0.97763205,0,0,0.97763205,0.12423766,6.4933408)"> + id="path188" + d="m 3.330473,296.25878 c -0.6814899,0 -0.8835396,-1.82872 -1.0821648,-3.5958 -0.034246,-0.29793 -0.068491,-0.60615 -0.1027371,-0.89723 -0.1678041,-1.35271 0.1335583,-2.01708 0.4177977,-2.33556 0.2260218,-0.25684 0.5239596,-0.3904 0.8629922,-0.3904 0.3390326,0 0.6369704,0.13356 0.8629921,0.3904 0.2808149,0.31848 0.5821773,0.98285 0.4177978,2.33556 -0.034246,0.29108 -0.068491,0.5993 -0.1027372,0.89723 -0.1952006,1.76708 -0.4006749,3.5958 -1.0821647,3.5958 z" + inkscape:connector-curvature="0" + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.10556875;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/themes/rbsimple-DE/c64/images/controller.svg b/themes/rbsimple-DE/c64/images/controller.svg index f38851465..c30aa519d 100644 --- a/themes/rbsimple-DE/c64/images/controller.svg +++ b/themes/rbsimple-DE/c64/images/controller.svg @@ -19,14 +19,37 @@ inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">image/svg+xml + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - + - - + id="g4878"> - - - - - - - - - - - - - - - - - + - \ No newline at end of file + style="opacity:1;fill:#0c0c0c;fill-opacity:1;stroke-width:0.4416638" /> \ No newline at end of file diff --git a/themes/rbsimple-DE/switch/images/logo.svg b/themes/rbsimple-DE/switch/images/logo.svg index 04414e24f..5ca4ddafb 100644 --- a/themes/rbsimple-DE/switch/images/logo.svg +++ b/themes/rbsimple-DE/switch/images/logo.svg @@ -7,10 +7,10 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="2922" - height="644" + width="800" + height="176.36418" version="1.1" - viewBox="0 0 2922 644" + viewBox="0 0 800 176.36418" id="svg2" sodipodi:docname="logo.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> @@ -41,31 +41,35 @@ inkscape:window-height="2065" id="namedview4" showgrid="false" - inkscape:zoom="0.30969238" - inkscape:cx="338.77838" - inkscape:cy="458.73741" + inkscape:zoom="2.184783" + inkscape:cx="366.47128" + inkscape:cy="193.80534" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" - inkscape:current-layer="svg2" /> + inkscape:current-layer="svg2" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" /> + d="M 37.76031,0.58425297 C 20.19122,3.7246229 6.0555905,16.724283 1.5993975,33.813713 c -1.60715171,6.17119 -1.71673023,9.78626 -1.53409936,57.6586 0.10957852,43.965127 0.14610469,44.987577 0.87662817,48.383547 4.05440539,18.29447 17.05772269,31.33064 35.53996669,35.63952 2.410727,0.54774 5.478927,0.65728 25.239586,0.76683 20.454656,0.14606 22.573174,0.10955 23.121068,-0.43819 0.547893,-0.54774 0.584418,-7.5953 0.584418,-87.492077 0,-59.22878 -0.109579,-87.1269201 -0.365262,-87.63814003 -0.365262,-0.65729 -0.986206,-0.6938000088983 -22.390543,-0.65729 -17.386458,0.0365 -22.609702,0.14607 -24.91085,0.54774 z M 70.816496,88.222393 v 73.981197 l -14.866152,-0.18258 c -13.697316,-0.14606 -15.158362,-0.21909 -17.897826,-0.91289 -11.761427,-3.03082 -20.491183,-12.08677 -22.938436,-23.8814 -0.803576,-3.6881 -0.803576,-94.539647 -0.03653,-98.154717 2.191571,-10.26096 9.314175,-18.76917 18.920557,-22.60334 4.821456,-1.93534 7.049552,-2.15443 22.682754,-2.19095 l 14.13563,-0.0365 z" /> + d="m 40.901559,36.552403 c -2.301147,0.4382 -5.807661,2.19096 -7.670494,3.83417 -3.83525,3.32295 -5.734609,8.0335 -5.442401,13.58392 0.146105,2.88475 0.328736,3.65158 1.497574,5.9886 1.716728,3.54204 4.310086,6.13467 7.853125,7.88744 2.447255,1.20502 3.0682,1.35108 6.245977,1.46063 2.885568,0.10955 3.908301,0 5.844189,-0.65729 7.926179,-2.66565 12.711107,-10.37051 11.359638,-18.25794 -1.570626,-9.38459 -10.483011,-15.66532 -19.687608,-13.83953 z" /> + d="m 103.21521,0.25561297 c -0.14611,0.10955 -0.25568,39.69277003 -0.25568,87.96678003 0,79.641157 0.0365,87.711177 0.58441,87.930267 0.98621,0.36516 29.33052,0.2191 32.83703,-0.14606 14.82963,-1.67973 27.906,-10.69916 35.0286,-24.10049 0.91316,-1.71625 2.11852,-4.601 2.73947,-6.39028 2.30115,-6.86499 2.22809,-5.03919 2.22809,-57.512527 0,-41.88373 -0.073,-48.12795 -0.58441,-50.79361 C 172.17662,18.184913 157.67573,3.8341629 138.60907,0.54773297 136.05224,0.10954297 132.10741,2.9611016e-6 119.3963,2.9611016e-6 110.63002,2.9611016e-6 103.32479,0.10954297 103.21521,0.25561297 Z M 142.22516,79.787223 c 5.69808,1.49715 10.37343,5.84254 12.2728,11.39296 1.20536,3.4325 1.16883,8.47169 -0.0365,11.612047 -2.2281,5.76952 -6.61124,9.82278 -12.23627,11.31993 -9.13154,2.37353 -18.84751,-3.14037 -21.51391,-12.19631 -0.80358,-2.775207 -0.76705,-7.449237 0.1461,-10.297477 2.73946,-8.94639 12.23627,-14.20468 21.36781,-11.83115 z" /> + transform="matrix(0.62745924,0,0,0.62745924,-274.21022,-113.86845)"> 0.13 0.1635 0.5 0.5 left - 3 - 2 + row + 2 + 3 0.5 0.572 - 0.67 + 0.81 -1.0 0.005 favorite, completed, kidgame, broken, controller, altemulator