diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index 86d84ea20..442ef62d9 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -41,6 +41,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperSingle.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScreensaverOptions.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiSettings.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiThemeDownloader.h # Scrapers ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBJSONScraper.h @@ -88,6 +89,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperSingle.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScreensaverOptions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiSettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiThemeDownloader.cpp # Scrapers ${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBJSONScraper.cpp diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index ca278a634..f4821a220 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -34,6 +34,7 @@ #include "guis/GuiScreensaverOptions.h" #include "guis/GuiTextEditKeyboardPopup.h" #include "guis/GuiTextEditPopup.h" +#include "guis/GuiThemeDownloader.h" #include "utils/PlatformUtil.h" #include @@ -107,6 +108,18 @@ void GuiMenu::openUIOptions() auto themeSet = std::make_shared>(getHelpStyle(), "THEME SET", false); + // TODO: Enable and possibly move somewhere else when the theme downloader is implemented. + // ComponentListRow themeDownloaderInputRow; + // themeDownloaderInputRow.elements.clear(); + // themeDownloaderInputRow.addElement(std::make_shared("THEME DOWNLOADER", + // Font::get(FONT_SIZE_MEDIUM), + // 0x777777FF), + // true); + // themeDownloaderInputRow.addElement(makeArrow(), false); + // themeDownloaderInputRow.makeAcceptInputHandler( + // std::bind(&GuiMenu::openThemeDownloader, this, s)); + // s->addRow(themeDownloaderInputRow); + // Theme set. if (!themeSets.empty()) { selectedSet = themeSets.find(Settings::getInstance()->getString("ThemeSet")); @@ -1658,6 +1671,11 @@ void GuiMenu::addVersionInfo() addChild(&mVersion); } +void GuiMenu::openThemeDownloader(GuiSettings* settings) +{ + mWindow->pushGui(new GuiThemeDownloader()); +} + void GuiMenu::openMediaViewerOptions() { mWindow->pushGui(new GuiMediaViewerOptions("MEDIA VIEWER SETTINGS")); diff --git a/es-app/src/guis/GuiMenu.h b/es-app/src/guis/GuiMenu.h index 9f021e6b8..a178dc3e5 100644 --- a/es-app/src/guis/GuiMenu.h +++ b/es-app/src/guis/GuiMenu.h @@ -36,6 +36,7 @@ private: void openScraperOptions(); void openUIOptions(); + void openThemeDownloader(GuiSettings* settings); void openMediaViewerOptions(); void openScreensaverOptions(); void openSoundOptions(); diff --git a/es-app/src/guis/GuiThemeDownloader.cpp b/es-app/src/guis/GuiThemeDownloader.cpp new file mode 100644 index 000000000..0f314ddbd --- /dev/null +++ b/es-app/src/guis/GuiThemeDownloader.cpp @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// GuiThemeDownloader.cpp +// +// Theme downloader. +// Currently only a skeleton with some JSON configuration parsing. +// + +#include "guis/GuiThemeDownloader.h" + +#include "resources/ResourceManager.h" + +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" + +GuiThemeDownloader::GuiThemeDownloader() + : mRenderer {Renderer::getInstance()} + , mBackground {":/graphics/frame.svg"} + , mGrid {glm::ivec2 {3, 2}} +{ + addChild(&mBackground); + addChild(&mGrid); + + // Set up grid. + mTitle = std::make_shared("THEME DOWNLOADER", Font::get(FONT_SIZE_LARGE), + 0x555555FF, ALIGN_CENTER); + mGrid.setEntry(mTitle, glm::ivec2 {1, 0}, false, true, glm::ivec2 {1, 1}); + + float width {mRenderer->getScreenWidth() * 0.85f}; + float height {mRenderer->getScreenHeight() * 0.70f}; + + setSize(width, height); + setPosition((mRenderer->getScreenWidth() - mSize.x) / 2.0f, + (mRenderer->getScreenHeight() - mSize.y) / 2.0f); + + parseThemesList(); +} + +void GuiThemeDownloader::parseThemesList() +{ + // Obviously a temporary location for testing purposes... + const std::string themesFile {Utils::FileSystem::getHomePath() + + "/.emulationstation/themes.json"}; + + if (!Utils::FileSystem::exists(themesFile)) { + LOG(LogInfo) << "GuiThemeDownloader: No themes.json file found"; + return; + } + + const ResourceData& themesFileData {ResourceManager::getInstance().getFileData(themesFile)}; + rapidjson::Document doc; + doc.Parse(reinterpret_cast(themesFileData.ptr.get()), themesFileData.length); + + if (doc.HasParseError()) { + LOG(LogWarning) << "GuiThemeDownloader: Couldn't parse the themes.json file"; + return; + } + + std::vector themeObjects {"themeSets", "legacyThemeSets"}; + + for (auto& themeObject : themeObjects) { + if (doc.HasMember(themeObject.c_str()) && doc[themeObject.c_str()].IsArray()) { + const rapidjson::Value& themeSets {doc[themeObject.c_str()]}; + for (size_t i {0}; i < themeSets.Size(); ++i) { + ThemeEntry themeEntry; + const rapidjson::Value& theme {themeSets[i]}; + + if (theme.HasMember("name") && theme["name"].IsString()) + themeEntry.name = theme["name"].GetString(); + + if (theme.HasMember("reponame") && theme["reponame"].IsString()) + themeEntry.reponame = theme["reponame"].GetString(); + + if (theme.HasMember("url") && theme["url"].IsString()) + themeEntry.url = theme["url"].GetString(); + + if (theme.HasMember("bundled") && theme["bundled"].IsBool()) + themeEntry.bundled = theme["bundled"].GetBool(); + + if (theme.HasMember("variants") && theme["variants"].IsArray()) { + const rapidjson::Value& variants {theme["variants"]}; + for (size_t i {0}; i < variants.Size(); ++i) + themeEntry.variants.emplace_back(variants[i].GetString()); + } + + if (theme.HasMember("colorSchemes") && theme["colorSchemes"].IsArray()) { + const rapidjson::Value& colorSchemes {theme["colorSchemes"]}; + for (size_t i {0}; i < colorSchemes.Size(); ++i) + themeEntry.colorSchemes.emplace_back(colorSchemes[i].GetString()); + } + + if (theme.HasMember("aspectRatios") && theme["aspectRatios"].IsArray()) { + const rapidjson::Value& aspectRatios {theme["aspectRatios"]}; + for (size_t i {0}; i < aspectRatios.Size(); ++i) + themeEntry.aspectRatios.emplace_back(aspectRatios[i].GetString()); + } + + if (theme.HasMember("transitions") && theme["transitions"].IsArray()) { + const rapidjson::Value& transitions {theme["transitions"]}; + for (size_t i {0}; i < transitions.Size(); ++i) + themeEntry.transitions.emplace_back(transitions[i].GetString()); + } + + if (theme.HasMember("screenshots") && theme["screenshots"].IsArray()) { + const rapidjson::Value& screenshots {theme["screenshots"]}; + for (size_t i {0}; i < screenshots.Size(); ++i) { + Screenshot screenshotEntry; + if (screenshots[i].HasMember("image") && screenshots[i]["image"].IsString()) + screenshotEntry.image = screenshots[i]["image"].GetString(); + + if (screenshots[i].HasMember("caption") && + screenshots[i]["caption"].IsString()) + screenshotEntry.caption = screenshots[i]["caption"].GetString(); + + if (screenshotEntry.image != "" && screenshotEntry.caption != "") + themeEntry.screenshots.emplace_back(screenshotEntry); + } + } + + if (themeObject == "themeSets") + mThemeSets.emplace_back(themeEntry); + else + mLegacyThemeSets.emplace_back(themeEntry); + } + } + } + + LOG(LogInfo) << "GuiThemeDownloader: Parsed " << mThemeSets.size() << " theme sets and " + << mLegacyThemeSets.size() << " legacy theme sets"; +} + +void GuiThemeDownloader::onSizeChanged() +{ + const float screenSize {mRenderer->getIsVerticalOrientation() ? mRenderer->getScreenWidth() : + mRenderer->getScreenHeight()}; + mGrid.setRowHeightPerc(0, (mTitle->getFont()->getLetterHeight() + screenSize * 0.2f) / mSize.y / + 2.0f); + + mGrid.setColWidthPerc(0, 0.04f); + mGrid.setColWidthPerc(2, 0.04f); + + mGrid.setSize(mSize); + mBackground.fitTo(mSize, glm::vec3 {0.0f, 0.0f, 0.0f}, glm::vec2 {-32.0f, -32.0f}); +} + +bool GuiThemeDownloader::input(InputConfig* config, Input input) +{ + if (config->isMappedTo("b", input) && input.value) { + delete this; + return true; + } + + return true; + // return GuiComponent::input(config, input); +} + +std::vector GuiThemeDownloader::getHelpPrompts() +{ + std::vector prompts; + // std::vector prompts {mGrid.getHelpPrompts()}; + prompts.push_back(HelpPrompt("b", "back (cancel)")); + + return prompts; +} diff --git a/es-app/src/guis/GuiThemeDownloader.h b/es-app/src/guis/GuiThemeDownloader.h new file mode 100644 index 000000000..9996c5f94 --- /dev/null +++ b/es-app/src/guis/GuiThemeDownloader.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// GuiThemeDownloader.h +// +// Theme downloader. +// Currently only a skeleton with some JSON configuration parsing. +// + +#ifndef ES_APP_GUIS_GUI_THEME_DOWNLOADER_H +#define ES_APP_GUIS_GUI_THEME_DOWNLOADER_H + +#include "GuiComponent.h" +#include "components/ComponentGrid.h" +#include "components/NinePatchComponent.h" +#include "components/TextComponent.h" +#include "renderers/Renderer.h" +#include "views/ViewController.h" + +class GuiThemeDownloader : public GuiComponent +{ +public: + GuiThemeDownloader(); + + void parseThemesList(); + + void onSizeChanged() override; + bool input(InputConfig* config, Input input) override; + + std::vector getHelpPrompts() override; + HelpStyle getHelpStyle() override { return ViewController::getInstance()->getViewHelpStyle(); } + +private: + Renderer* mRenderer; + NinePatchComponent mBackground; + ComponentGrid mGrid; + + struct Screenshot { + std::string image; + std::string caption; + }; + + struct ThemeEntry { + std::string name; + std::string reponame; + std::string url; + bool bundled {false}; + std::vector variants; + std::vector colorSchemes; + std::vector aspectRatios; + std::vector transitions; + std::vector screenshots; + }; + + std::shared_ptr mTitle; + std::vector mThemeSets; + std::vector mLegacyThemeSets; +}; + +#endif // ES_APP_GUIS_GUI_THEME_DOWNLOADER_H